Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
# SPDX-License-Identifier: GPL-2.0-only
menu "Rockchip"
depends on COMPILE_TEST || ARCH_ROCKCHIP
depends on HAVE_CLK
config SND_SOC_ROCKCHIP_I2S
tristate "Rockchip I2S Device Driver"
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S driver for
Rockchip I2S device. The device supports up to maximum of
8 channels each for play and record.
config SND_SOC_ROCKCHIP_I2S_TDM
tristate "Rockchip I2S/TDM Device Driver"
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for the I2S/TDM driver for
Rockchip I2S/TDM devices, found in Rockchip SoCs. These devices
interface between the AHB bus and the I2S bus, and support up to a
maximum of 8 channels each for playback and recording.
config SND_SOC_ROCKCHIP_PDM
tristate "Rockchip PDM Controller Driver"
select SND_SOC_GENERIC_DMAENGINE_PCM
select RATIONAL
help
Say Y or M if you want to add support for PDM driver for
Rockchip PDM Controller. The Controller supports up to maximum of
8 channels record.
config SND_SOC_ROCKCHIP_SAI
tristate "Rockchip SAI Controller Driver"
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for the Rockchip Serial Audio
Interface controller found on Rockchip SoCs such as the RK3576. The
controller may support both playback and recording, with up to 4 lanes
for each and up to 128 channels per lane in TDM mode.
config SND_SOC_ROCKCHIP_SPDIF
tristate "Rockchip SPDIF Device Driver"
select SND_PCM_IEC958
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF driver for
Rockchip SPDIF transceiver device.
config SND_SOC_ROCKCHIP_MAX98090
tristate "ASoC support for Rockchip boards using a MAX98090 codec"
depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
select SND_SOC_HDMI_CODEC
help
Say Y or M here if you want to add support for SoC audio on Rockchip
boards using the MAX98090 codec and HDMI codec, such as Veyron.
config SND_SOC_ROCKCHIP_RT5645
tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_RT5645
help
Say Y or M here if you want to add support for SoC audio on Rockchip
boards using the RT5645/RT5650 codec, such as Veyron.
config SND_SOC_RK3288_HDMI_ANALOG
tristate "ASoC support multiple codecs for Rockchip RK3288 boards"
depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_HDMI_CODEC
select SND_SOC_ES8328_I2C
select SND_SOC_ES8328_SPI if SPI_MASTER
select DRM_DW_HDMI_I2S_AUDIO if DRM_DW_HDMI
help
Say Y or M here if you want to add support for SoC audio on Rockchip
RK3288 boards using an analog output and the built-in HDMI audio.
config SND_SOC_RK3399_GRU_SOUND
tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards"
depends on I2C && GPIOLIB && SPI
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98357A
select SND_SOC_RT5514
select SND_SOC_DA7219
select SND_SOC_RT5514_SPI
select SND_SOC_HDMI_CODEC
select SND_SOC_DMIC
help
Say Y or M here if you want to add support multiple codecs for SoC
audio on Rockchip RK3399 GRU boards.
endmenu
+23
View File
@@ -0,0 +1,23 @@
# SPDX-License-Identifier: GPL-2.0
# ROCKCHIP Platform Support
snd-soc-rockchip-i2s-y := rockchip_i2s.o
snd-soc-rockchip-i2s-tdm-y := rockchip_i2s_tdm.o
snd-soc-rockchip-pdm-y := rockchip_pdm.o
snd-soc-rockchip-sai-y := rockchip_sai.o
snd-soc-rockchip-spdif-y := rockchip_spdif.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_SAI) += snd-soc-rockchip-sai.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o
snd-soc-rockchip-max98090-y := rockchip_max98090.o
snd-soc-rockchip-rt5645-y := rockchip_rt5645.o
snd-soc-rk3288-hdmi-analog-y := rk3288_hdmi_analog.o
snd-soc-rk3399-gru-sound-y := rk3399_gru_sound.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_MAX98090) += snd-soc-rockchip-max98090.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_RT5645) += snd-soc-rockchip-rt5645.o
obj-$(CONFIG_SND_SOC_RK3288_HDMI_ANALOG) += snd-soc-rk3288-hdmi-analog.o
obj-$(CONFIG_SND_SOC_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o
@@ -0,0 +1,263 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip machine ASoC driver for RK3288 boards that have an HDMI and analog
* audio output
*
* Copyright (c) 2016, Collabora Ltd.
*
* Authors: Sjoerd Simons <sjoerd.simons@collabora.com>,
* Romain Perier <romain.perier@collabora.com>
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include "rockchip_i2s.h"
#define DRV_NAME "rk3288-snd-hdmi-analog"
struct rk_drvdata {
struct gpio_desc *gpio_hp_en;
};
static int rk_hp_power(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
struct snd_soc_card *card = snd_soc_dapm_to_card(w->dapm);
struct rk_drvdata *machine = snd_soc_card_get_drvdata(card);
gpiod_set_value_cansleep(machine->gpio_hp_en,
SND_SOC_DAPM_EVENT_ON(event));
return 0;
}
static struct snd_soc_jack headphone_jack;
static struct snd_soc_jack_pin headphone_jack_pins[] = {
{
.pin = "Analog",
.mask = SND_JACK_HEADPHONE
},
};
static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
SND_SOC_DAPM_HP("Analog", rk_hp_power),
SND_SOC_DAPM_LINE("HDMI", NULL),
};
static const struct snd_kcontrol_new rk_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Analog"),
SOC_DAPM_PIN_SWITCH("HDMI"),
};
static int rk_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int mclk;
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 192000:
mclk = 24576000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret && ret != -ENOTSUPP) {
dev_err(codec_dai->dev, "Can't set cpu clock %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP) {
dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
return ret;
}
return 0;
}
static struct snd_soc_jack_gpio rk_hp_jack_gpio = {
.name = "rockchip,hp-det",
.report = SND_JACK_HEADPHONE,
.debounce_time = 150
};
static int rk_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct device *dev = card->dev;
/* Enable optional Headset Jack detection */
if (of_property_present(dev->of_node, "rockchip,hp-det-gpios")) {
rk_hp_jack_gpio.gpiod_dev = dev;
snd_soc_card_jack_new_pins(runtime->card, "Headphone Jack",
SND_JACK_HEADPHONE, &headphone_jack,
headphone_jack_pins,
ARRAY_SIZE(headphone_jack_pins));
snd_soc_jack_add_gpios(&headphone_jack, 1, &rk_hp_jack_gpio);
}
return 0;
}
static const struct snd_soc_ops rk_ops = {
.hw_params = rk_hw_params,
};
SND_SOC_DAILINK_DEFS(audio,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, NULL),
COMP_CODEC("hdmi-audio-codec.2.auto", "i2s-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
static struct snd_soc_dai_link rk_dailink = {
.name = "Codecs",
.stream_name = "Audio",
.init = rk_init,
.ops = &rk_ops,
/* Set codecs as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(audio),
};
static struct snd_soc_card snd_soc_card_rk = {
.name = "ROCKCHIP-I2S",
.dai_link = &rk_dailink,
.num_links = 1,
.num_aux_devs = 0,
.dapm_widgets = rk_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
.controls = rk_mc_controls,
.num_controls = ARRAY_SIZE(rk_mc_controls),
};
static int snd_rk_mc_probe(struct platform_device *pdev)
{
int ret;
struct snd_soc_card *card = &snd_soc_card_rk;
struct device_node *np = pdev->dev.of_node;
struct rk_drvdata *machine;
struct of_phandle_args args;
machine = devm_kzalloc(&pdev->dev, sizeof(struct rk_drvdata),
GFP_KERNEL);
if (!machine)
return -ENOMEM;
card->dev = &pdev->dev;
machine->gpio_hp_en = devm_gpiod_get_optional(&pdev->dev, "rockchip,hp-en", GPIOD_OUT_LOW);
if (IS_ERR(machine->gpio_hp_en))
return PTR_ERR(machine->gpio_hp_en);
gpiod_set_consumer_name(machine->gpio_hp_en, "hp_en");
ret = snd_soc_of_parse_card_name(card, "rockchip,model");
if (ret) {
dev_err(card->dev, "SoC parse card name failed %d\n", ret);
return ret;
}
rk_dailink.codecs[0].of_node = of_parse_phandle(np,
"rockchip,audio-codec",
0);
if (!rk_dailink.codecs[0].of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,audio-codec' missing or invalid\n");
return -EINVAL;
}
ret = of_parse_phandle_with_fixed_args(np, "rockchip,audio-codec",
0, 0, &args);
if (ret) {
dev_err(&pdev->dev,
"Unable to parse property 'rockchip,audio-codec'\n");
return ret;
}
ret = snd_soc_get_dai_name(&args, &rk_dailink.codecs[0].dai_name);
if (ret) {
dev_err(&pdev->dev, "Unable to get codec_dai_name\n");
return ret;
}
rk_dailink.cpus->of_node = of_parse_phandle(np, "rockchip,i2s-controller",
0);
if (!rk_dailink.cpus->of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,i2s-controller' missing or invalid\n");
return -EINVAL;
}
rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
ret = snd_soc_of_parse_audio_routing(card, "rockchip,routing");
if (ret) {
dev_err(&pdev->dev,
"Unable to parse 'rockchip,routing' property\n");
return ret;
}
snd_soc_card_set_drvdata(card, machine);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
return dev_err_probe(&pdev->dev, ret,
"Soc register card failed\n");
return 0;
}
static const struct of_device_id rockchip_sound_of_match[] = {
{ .compatible = "rockchip,rk3288-hdmi-analog", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
static struct platform_driver rockchip_sound_driver = {
.probe = snd_rk_mc_probe,
.driver = {
.name = DRV_NAME,
.pm = &snd_soc_pm_ops,
.of_match_table = rockchip_sound_of_match,
},
};
module_platform_driver(rockchip_sound_driver);
MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.com>");
MODULE_DESCRIPTION("Rockchip RK3288 machine ASoC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
@@ -0,0 +1,627 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA7219
*
* Copyright (c) 2016, ROCKCHIP CORPORATION. All rights reserved.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/spi/spi.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "rockchip_i2s.h"
#include "../codecs/da7219.h"
#include "../codecs/rt5514.h"
#define DRV_NAME "rk3399-gru-sound"
#define SOUND_FS 256
static unsigned int dmic_wakeup_delay;
static struct snd_soc_jack rockchip_sound_jack;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin rockchip_sound_jack_pins[] = {
{
.pin = "Headphones",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Line Out",
.mask = SND_JACK_LINEOUT,
},
};
static const struct snd_soc_dapm_widget rockchip_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_LINE("Line Out", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
SND_SOC_DAPM_LINE("HDMI", NULL),
};
static const struct snd_kcontrol_new rockchip_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphones"),
SOC_DAPM_PIN_SWITCH("Speakers"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Line Out"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
SOC_DAPM_PIN_SWITCH("HDMI"),
};
static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
unsigned int mclk;
int ret;
mclk = params_rate(params) * SOUND_FS;
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, 0);
if (ret) {
dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
__func__, mclk, ret);
return ret;
}
return 0;
}
static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
unsigned int mclk;
int ret;
mclk = params_rate(params) * SOUND_FS;
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret < 0) {
dev_err(rtd->card->dev, "Can't set cpu clock out %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, RT5514_SCLK_S_MCLK,
mclk, SND_SOC_CLOCK_IN);
if (ret) {
dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
__func__, params_rate(params) * 512, ret);
return ret;
}
/* Wait for DMIC stable */
msleep(dmic_wakeup_delay);
return 0;
}
static int rockchip_sound_da7219_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int mclk, ret;
/* in bypass mode, the mclk has to be one of the frequencies below */
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret < 0) {
dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(codec_dai->dev, "Can't set codec clock in %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
if (ret < 0) {
dev_err(codec_dai->dev, "Can't set pll sysclk mclk %d\n", ret);
return ret;
}
return 0;
}
static struct snd_soc_jack cdn_dp_card_jack;
static int rockchip_sound_cdndp_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_card *card = rtd->card;
int ret;
/* Enable jack detection. */
ret = snd_soc_card_jack_new(card, "DP Jack", SND_JACK_LINEOUT,
&cdn_dp_card_jack);
if (ret) {
dev_err(card->dev, "Can't create DP Jack %d\n", ret);
return ret;
}
return snd_soc_component_set_jack(component, &cdn_dp_card_jack, NULL);
}
static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_component *component = snd_soc_rtd_to_codec(rtd, 0)->component;
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int ret;
/* We need default MCLK and PLL settings for the accessory detection */
ret = snd_soc_dai_set_sysclk(codec_dai, 0, 12288000,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(codec_dai->dev, "Init can't set codec clock in %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_MCLK, 0, 0);
if (ret < 0) {
dev_err(codec_dai->dev, "Init can't set pll sysclk mclk %d\n", ret);
return ret;
}
/* Enable Headset and 4 Buttons Jack detection */
ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_LINEOUT |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
&rockchip_sound_jack,
rockchip_sound_jack_pins,
ARRAY_SIZE(rockchip_sound_jack_pins));
if (ret) {
dev_err(rtd->card->dev, "New Headset Jack failed! (%d)\n", ret);
return ret;
}
snd_jack_set_key(
rockchip_sound_jack.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
snd_jack_set_key(
rockchip_sound_jack.jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
snd_jack_set_key(
rockchip_sound_jack.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
snd_jack_set_key(
rockchip_sound_jack.jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
snd_soc_component_set_jack(component, &rockchip_sound_jack, NULL);
return 0;
}
static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
unsigned int mclk;
int ret;
mclk = params_rate(params) * SOUND_FS;
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), 0, mclk, 0);
if (ret) {
dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
__func__, mclk, ret);
return ret;
}
/* Wait for DMIC stable */
msleep(dmic_wakeup_delay);
return 0;
}
static int rockchip_sound_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
return snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
8000, 96000);
}
static const struct snd_soc_ops rockchip_sound_max98357a_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_max98357a_hw_params,
};
static const struct snd_soc_ops rockchip_sound_rt5514_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_rt5514_hw_params,
};
static const struct snd_soc_ops rockchip_sound_da7219_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_da7219_hw_params,
};
static const struct snd_soc_ops rockchip_sound_dmic_ops = {
.startup = rockchip_sound_startup,
.hw_params = rockchip_sound_dmic_hw_params,
};
static struct snd_soc_card rockchip_sound_card = {
.name = "rk3399-gru-sound",
.owner = THIS_MODULE,
.dapm_widgets = rockchip_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),
.controls = rockchip_controls,
.num_controls = ARRAY_SIZE(rockchip_controls),
};
enum {
DAILINK_CDNDP,
DAILINK_DA7219,
DAILINK_DMIC,
DAILINK_MAX98357A,
DAILINK_RT5514,
DAILINK_RT5514_DSP,
};
SND_SOC_DAILINK_DEFS(cdndp,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "spdif-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(da7219,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "da7219-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(dmic,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "dmic-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(max98357a,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(rt5514,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5514-aif1")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(rt5514_dsp,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
static const struct snd_soc_dai_link rockchip_dais[] = {
[DAILINK_CDNDP] = {
.name = "DP",
.stream_name = "DP PCM",
.init = rockchip_sound_cdndp_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(cdndp),
},
[DAILINK_DA7219] = {
.name = "DA7219",
.stream_name = "DA7219 PCM",
.init = rockchip_sound_da7219_init,
.ops = &rockchip_sound_da7219_ops,
/* set da7219 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(da7219),
},
[DAILINK_DMIC] = {
.name = "DMIC",
.stream_name = "DMIC PCM",
.ops = &rockchip_sound_dmic_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(dmic),
},
[DAILINK_MAX98357A] = {
.name = "MAX98357A",
.stream_name = "MAX98357A PCM",
.ops = &rockchip_sound_max98357a_ops,
/* set max98357a as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(max98357a),
},
[DAILINK_RT5514] = {
.name = "RT5514",
.stream_name = "RT5514 PCM",
.ops = &rockchip_sound_rt5514_ops,
/* set rt5514 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(rt5514),
},
/* RT5514 DSP for voice wakeup via spi bus */
[DAILINK_RT5514_DSP] = {
.name = "RT5514 DSP",
.stream_name = "Wake on Voice",
SND_SOC_DAILINK_REG(rt5514_dsp),
},
};
static const struct snd_soc_dapm_route rockchip_sound_cdndp_routes[] = {
/* Output */
{"HDMI", NULL, "TX"},
};
static const struct snd_soc_dapm_route rockchip_sound_da7219_routes[] = {
/* Output */
{"Headphones", NULL, "HPL"},
{"Headphones", NULL, "HPR"},
/* Input */
{"MIC", NULL, "Headset Mic"},
};
static const struct snd_soc_dapm_route rockchip_sound_dmic_routes[] = {
/* Input */
{"DMic", NULL, "Int Mic"},
};
static const struct snd_soc_dapm_route rockchip_sound_max98357a_routes[] = {
/* Output */
{"Speakers", NULL, "Speaker"},
};
static const struct snd_soc_dapm_route rockchip_sound_rt5514_routes[] = {
/* Input */
{"DMIC1L", NULL, "Int Mic"},
{"DMIC1R", NULL, "Int Mic"},
};
struct rockchip_sound_route {
const struct snd_soc_dapm_route *routes;
int num_routes;
};
static const struct rockchip_sound_route rockchip_routes[] = {
[DAILINK_CDNDP] = {
.routes = rockchip_sound_cdndp_routes,
.num_routes = ARRAY_SIZE(rockchip_sound_cdndp_routes),
},
[DAILINK_DA7219] = {
.routes = rockchip_sound_da7219_routes,
.num_routes = ARRAY_SIZE(rockchip_sound_da7219_routes),
},
[DAILINK_DMIC] = {
.routes = rockchip_sound_dmic_routes,
.num_routes = ARRAY_SIZE(rockchip_sound_dmic_routes),
},
[DAILINK_MAX98357A] = {
.routes = rockchip_sound_max98357a_routes,
.num_routes = ARRAY_SIZE(rockchip_sound_max98357a_routes),
},
[DAILINK_RT5514] = {
.routes = rockchip_sound_rt5514_routes,
.num_routes = ARRAY_SIZE(rockchip_sound_rt5514_routes),
},
[DAILINK_RT5514_DSP] = {},
};
struct dailink_match_data {
const char *compatible;
const struct bus_type *bus_type;
};
static const struct dailink_match_data dailink_match[] = {
[DAILINK_CDNDP] = {
.compatible = "rockchip,rk3399-cdn-dp",
},
[DAILINK_DA7219] = {
.compatible = "dlg,da7219",
},
[DAILINK_DMIC] = {
.compatible = "dmic-codec",
},
[DAILINK_MAX98357A] = {
.compatible = "maxim,max98357a",
},
[DAILINK_RT5514] = {
.compatible = "realtek,rt5514",
.bus_type = &i2c_bus_type,
},
[DAILINK_RT5514_DSP] = {
.compatible = "realtek,rt5514",
.bus_type = &spi_bus_type,
},
};
static int rockchip_sound_codec_node_match(struct device_node *np_codec)
{
struct device *dev;
int i;
for (i = 0; i < ARRAY_SIZE(dailink_match); i++) {
if (!of_device_is_compatible(np_codec,
dailink_match[i].compatible))
continue;
if (dailink_match[i].bus_type) {
dev = bus_find_device_by_of_node(dailink_match[i].bus_type,
np_codec);
if (!dev)
continue;
put_device(dev);
}
return i;
}
return -1;
}
static int rockchip_sound_of_parse_dais(struct device *dev,
struct snd_soc_card *card)
{
struct device_node *np_cpu, *np_cpu0, *np_cpu1;
struct device_node *np_codec;
struct snd_soc_dai_link *dai;
struct snd_soc_dapm_route *routes;
int i, index;
int num_routes;
card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais),
GFP_KERNEL);
if (!card->dai_link)
return -ENOMEM;
num_routes = 0;
for (i = 0; i < ARRAY_SIZE(rockchip_routes); i++)
num_routes += rockchip_routes[i].num_routes;
routes = devm_kcalloc(dev, num_routes, sizeof(*routes),
GFP_KERNEL);
if (!routes)
return -ENOMEM;
card->dapm_routes = routes;
np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0);
np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1);
card->num_dapm_routes = 0;
card->num_links = 0;
for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) {
np_codec = of_parse_phandle(dev->of_node,
"rockchip,codec", i);
if (!np_codec)
break;
if (!of_device_is_available(np_codec))
continue;
index = rockchip_sound_codec_node_match(np_codec);
if (index < 0)
continue;
switch (index) {
case DAILINK_CDNDP:
np_cpu = np_cpu1;
break;
case DAILINK_RT5514_DSP:
np_cpu = np_codec;
break;
default:
np_cpu = np_cpu0;
break;
}
if (!np_cpu) {
dev_err(dev, "Missing 'rockchip,cpu' for %s\n",
rockchip_dais[index].name);
return -EINVAL;
}
dai = &card->dai_link[card->num_links++];
*dai = rockchip_dais[index];
if (!dai->codecs->name)
dai->codecs->of_node = np_codec;
dai->platforms->of_node = np_cpu;
dai->cpus->of_node = np_cpu;
if (card->num_dapm_routes + rockchip_routes[index].num_routes >
num_routes) {
dev_err(dev, "Too many routes\n");
return -EINVAL;
}
memcpy(routes + card->num_dapm_routes,
rockchip_routes[index].routes,
rockchip_routes[index].num_routes * sizeof(*routes));
card->num_dapm_routes += rockchip_routes[index].num_routes;
}
return 0;
}
static int rockchip_sound_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &rockchip_sound_card;
int ret;
ret = rockchip_sound_of_parse_dais(&pdev->dev, card);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to parse dais: %d\n", ret);
return ret;
}
/* Set DMIC wakeup delay */
ret = device_property_read_u32(&pdev->dev, "dmic-wakeup-delay-ms",
&dmic_wakeup_delay);
if (ret) {
dmic_wakeup_delay = 0;
dev_dbg(&pdev->dev,
"no optional property 'dmic-wakeup-delay-ms' found, default: no delay\n");
}
card->dev = &pdev->dev;
return devm_snd_soc_register_card(&pdev->dev, card);
}
static const struct of_device_id rockchip_sound_of_match[] = {
{ .compatible = "rockchip,rk3399-gru-sound", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_sound_of_match);
static struct platform_driver rockchip_sound_driver = {
.probe = rockchip_sound_probe,
.driver = {
.name = DRV_NAME,
.of_match_table = rockchip_sound_of_match,
#ifdef CONFIG_PM
.pm = &snd_soc_pm_ops,
#endif
},
};
module_platform_driver(rockchip_sound_driver);
MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip ASoC Machine Driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
@@ -0,0 +1,865 @@
// SPDX-License-Identifier: GPL-2.0-only
/* sound/soc/rockchip/rockchip_i2s.c
*
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun <jay.xu@rock-chips.com>
*/
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/spinlock.h>
#include <sound/pcm_params.h>
#include <sound/dmaengine_pcm.h>
#include "rockchip_i2s.h"
#define DRV_NAME "rockchip-i2s"
struct rk_i2s_pins {
u32 reg_offset;
u32 shift;
};
struct rk_i2s_dev {
struct device *dev;
struct clk *hclk;
struct clk *mclk;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct regmap *regmap;
struct regmap *grf;
bool has_capture;
bool has_playback;
/*
* Used to indicate the tx/rx status.
* I2S controller hopes to start the tx and rx together,
* also to stop them when they are both try to stop.
*/
bool tx_start;
bool rx_start;
bool is_master_mode;
const struct rk_i2s_pins *pins;
unsigned int bclk_ratio;
spinlock_t lock; /* tx/rx lock */
struct pinctrl *pinctrl;
struct pinctrl_state *bclk_on;
struct pinctrl_state *bclk_off;
};
static int i2s_pinctrl_select_bclk_on(struct rk_i2s_dev *i2s)
{
int ret = 0;
if (!IS_ERR(i2s->pinctrl) && !IS_ERR_OR_NULL(i2s->bclk_on))
ret = pinctrl_select_state(i2s->pinctrl, i2s->bclk_on);
if (ret)
dev_err(i2s->dev, "bclk enable failed %d\n", ret);
return ret;
}
static int i2s_pinctrl_select_bclk_off(struct rk_i2s_dev *i2s)
{
int ret = 0;
if (!IS_ERR(i2s->pinctrl) && !IS_ERR_OR_NULL(i2s->bclk_off))
ret = pinctrl_select_state(i2s->pinctrl, i2s->bclk_off);
if (ret)
dev_err(i2s->dev, "bclk disable failed %d\n", ret);
return ret;
}
static int i2s_runtime_suspend(struct device *dev)
{
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
regcache_cache_only(i2s->regmap, true);
clk_disable_unprepare(i2s->mclk);
return 0;
}
static int i2s_runtime_resume(struct device *dev)
{
struct rk_i2s_dev *i2s = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(i2s->mclk);
if (ret) {
dev_err(i2s->dev, "clock enable failed %d\n", ret);
return ret;
}
regcache_cache_only(i2s->regmap, false);
regcache_mark_dirty(i2s->regmap);
ret = regcache_sync(i2s->regmap);
if (ret)
clk_disable_unprepare(i2s->mclk);
return ret;
}
static inline struct rk_i2s_dev *to_info(struct snd_soc_dai *dai)
{
return snd_soc_dai_get_drvdata(dai);
}
static int rockchip_snd_txctrl(struct rk_i2s_dev *i2s, int on)
{
unsigned int val = 0;
int ret = 0;
scoped_guard(spinlock, &i2s->lock) {
if (on) {
ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_TDE_ENABLE,
I2S_DMACR_TDE_ENABLE);
if (ret < 0)
break;
ret = regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
if (ret < 0)
break;
i2s->tx_start = true;
} else {
i2s->tx_start = false;
ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_TDE_ENABLE,
I2S_DMACR_TDE_DISABLE);
if (ret < 0)
break;
if (!i2s->rx_start) {
ret = regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP);
if (ret < 0)
break;
udelay(150);
ret = regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC | I2S_CLR_RXC,
I2S_CLR_TXC | I2S_CLR_RXC);
if (ret < 0)
break;
ret = regmap_read_poll_timeout_atomic(i2s->regmap,
I2S_CLR,
val,
val == 0,
20,
200);
if (ret < 0)
dev_warn(i2s->dev, "fail to clear: %d\n", ret);
}
}
}
if (ret < 0)
dev_err(i2s->dev, "lrclk update failed\n");
return ret;
}
static int rockchip_snd_rxctrl(struct rk_i2s_dev *i2s, int on)
{
unsigned int val = 0;
int ret = 0;
scoped_guard(spinlock, &i2s->lock) {
if (on) {
ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_RDE_ENABLE,
I2S_DMACR_RDE_ENABLE);
if (ret < 0)
break;
ret = regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
I2S_XFER_TXS_START | I2S_XFER_RXS_START);
if (ret < 0)
break;
i2s->rx_start = true;
} else {
i2s->rx_start = false;
ret = regmap_update_bits(i2s->regmap, I2S_DMACR,
I2S_DMACR_RDE_ENABLE,
I2S_DMACR_RDE_DISABLE);
if (ret < 0)
break;
if (!i2s->tx_start) {
ret = regmap_update_bits(i2s->regmap, I2S_XFER,
I2S_XFER_TXS_START | I2S_XFER_RXS_START,
I2S_XFER_TXS_STOP | I2S_XFER_RXS_STOP);
if (ret < 0)
break;
udelay(150);
ret = regmap_update_bits(i2s->regmap, I2S_CLR,
I2S_CLR_TXC | I2S_CLR_RXC,
I2S_CLR_TXC | I2S_CLR_RXC);
if (ret < 0)
break;
ret = regmap_read_poll_timeout_atomic(i2s->regmap,
I2S_CLR,
val,
val == 0,
20,
200);
if (ret < 0)
dev_warn(i2s->dev, "fail to clear: %d\n", ret);
}
}
}
if (ret < 0)
dev_err(i2s->dev, "lrclk update failed\n");
return ret;
}
static int rockchip_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct rk_i2s_dev *i2s = to_info(cpu_dai);
unsigned int mask = 0, val = 0;
int ret = 0;
pm_runtime_get_sync(cpu_dai->dev);
mask = I2S_CKR_MSS_MASK;
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_BP_FP:
/* Set source clock in Master mode */
val = I2S_CKR_MSS_MASTER;
i2s->is_master_mode = true;
break;
case SND_SOC_DAIFMT_BC_FC:
val = I2S_CKR_MSS_SLAVE;
i2s->is_master_mode = false;
break;
default:
ret = -EINVAL;
goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
mask = I2S_CKR_CKP_MASK | I2S_CKR_TLP_MASK | I2S_CKR_RLP_MASK;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
val = I2S_CKR_CKP_NORMAL |
I2S_CKR_TLP_NORMAL |
I2S_CKR_RLP_NORMAL;
break;
case SND_SOC_DAIFMT_NB_IF:
val = I2S_CKR_CKP_NORMAL |
I2S_CKR_TLP_INVERTED |
I2S_CKR_RLP_INVERTED;
break;
case SND_SOC_DAIFMT_IB_NF:
val = I2S_CKR_CKP_INVERTED |
I2S_CKR_TLP_NORMAL |
I2S_CKR_RLP_NORMAL;
break;
case SND_SOC_DAIFMT_IB_IF:
val = I2S_CKR_CKP_INVERTED |
I2S_CKR_TLP_INVERTED |
I2S_CKR_RLP_INVERTED;
break;
default:
ret = -EINVAL;
goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_CKR, mask, val);
mask = I2S_TXCR_IBM_MASK | I2S_TXCR_TFS_MASK | I2S_TXCR_PBM_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S_TXCR_IBM_RSJM;
break;
case SND_SOC_DAIFMT_LEFT_J:
val = I2S_TXCR_IBM_LSJM;
break;
case SND_SOC_DAIFMT_I2S:
val = I2S_TXCR_IBM_NORMAL;
break;
case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 bit mode */
val = I2S_TXCR_TFS_PCM | I2S_TXCR_PBM_MODE(1);
break;
case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */
val = I2S_TXCR_TFS_PCM;
break;
default:
ret = -EINVAL;
goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_TXCR, mask, val);
mask = I2S_RXCR_IBM_MASK | I2S_RXCR_TFS_MASK | I2S_RXCR_PBM_MASK;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_RIGHT_J:
val = I2S_RXCR_IBM_RSJM;
break;
case SND_SOC_DAIFMT_LEFT_J:
val = I2S_RXCR_IBM_LSJM;
break;
case SND_SOC_DAIFMT_I2S:
val = I2S_RXCR_IBM_NORMAL;
break;
case SND_SOC_DAIFMT_DSP_A: /* PCM delay 1 bit mode */
val = I2S_RXCR_TFS_PCM | I2S_RXCR_PBM_MODE(1);
break;
case SND_SOC_DAIFMT_DSP_B: /* PCM no delay mode */
val = I2S_RXCR_TFS_PCM;
break;
default:
ret = -EINVAL;
goto err_pm_put;
}
regmap_update_bits(i2s->regmap, I2S_RXCR, mask, val);
err_pm_put:
pm_runtime_put(cpu_dai->dev);
return ret;
}
static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = to_info(dai);
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
unsigned int val = 0;
unsigned int mclk_rate, bclk_rate, div_bclk, div_lrck;
if (i2s->is_master_mode) {
mclk_rate = clk_get_rate(i2s->mclk);
bclk_rate = i2s->bclk_ratio * params_rate(params);
if (!bclk_rate)
return -EINVAL;
div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
div_lrck = bclk_rate / params_rate(params);
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_MDIV_MASK,
I2S_CKR_MDIV(div_bclk));
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_TSD_MASK |
I2S_CKR_RSD_MASK,
I2S_CKR_TSD(div_lrck) |
I2S_CKR_RSD(div_lrck));
}
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
val |= I2S_TXCR_VDW(8);
break;
case SNDRV_PCM_FORMAT_S16_LE:
val |= I2S_TXCR_VDW(16);
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= I2S_TXCR_VDW(20);
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= I2S_TXCR_VDW(24);
break;
case SNDRV_PCM_FORMAT_S32_LE:
val |= I2S_TXCR_VDW(32);
break;
default:
return -EINVAL;
}
switch (params_channels(params)) {
case 8:
val |= I2S_CHN_8;
break;
case 6:
val |= I2S_CHN_6;
break;
case 4:
val |= I2S_CHN_4;
break;
case 2:
val |= I2S_CHN_2;
break;
default:
dev_err(i2s->dev, "invalid channel: %d\n",
params_channels(params));
return -EINVAL;
}
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
regmap_update_bits(i2s->regmap, I2S_RXCR,
I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
val);
else
regmap_update_bits(i2s->regmap, I2S_TXCR,
I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
val);
if (!IS_ERR(i2s->grf) && i2s->pins) {
regmap_read(i2s->regmap, I2S_TXCR, &val);
val &= I2S_TXCR_CSR_MASK;
switch (val) {
case I2S_CHN_4:
val = I2S_IO_4CH_OUT_6CH_IN;
break;
case I2S_CHN_6:
val = I2S_IO_6CH_OUT_4CH_IN;
break;
case I2S_CHN_8:
val = I2S_IO_8CH_OUT_2CH_IN;
break;
default:
val = I2S_IO_2CH_OUT_8CH_IN;
break;
}
val <<= i2s->pins->shift;
val |= (I2S_IO_DIRECTION_MASK << i2s->pins->shift) << 16;
regmap_write(i2s->grf, i2s->pins->reg_offset, val);
}
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
I2S_DMACR_TDL(16));
regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
I2S_DMACR_RDL(16));
val = I2S_CKR_TRCM_TXRX;
if (dai->driver->symmetric_rate && rtd->dai_link->symmetric_rate)
val = I2S_CKR_TRCM_TXONLY;
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_TRCM_MASK,
val);
return 0;
}
static int rockchip_i2s_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = to_info(dai);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
ret = rockchip_snd_rxctrl(i2s, 1);
else
ret = rockchip_snd_txctrl(i2s, 1);
if (ret < 0)
return ret;
i2s_pinctrl_select_bclk_on(i2s);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
if (!i2s->tx_start)
i2s_pinctrl_select_bclk_off(i2s);
ret = rockchip_snd_rxctrl(i2s, 0);
} else {
if (!i2s->rx_start)
i2s_pinctrl_select_bclk_off(i2s);
ret = rockchip_snd_txctrl(i2s, 0);
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int rockchip_i2s_set_bclk_ratio(struct snd_soc_dai *dai,
unsigned int ratio)
{
struct rk_i2s_dev *i2s = to_info(dai);
i2s->bclk_ratio = ratio;
return 0;
}
static int rockchip_i2s_set_sysclk(struct snd_soc_dai *cpu_dai, int clk_id,
unsigned int freq, int dir)
{
struct rk_i2s_dev *i2s = to_info(cpu_dai);
int ret;
if (freq == 0)
return 0;
ret = clk_set_rate(i2s->mclk, freq);
if (ret)
dev_err(i2s->dev, "Fail to set mclk %d\n", ret);
return ret;
}
static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
{
struct rk_i2s_dev *i2s = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_init_dma_data(dai,
i2s->has_playback ? &i2s->playback_dma_data : NULL,
i2s->has_capture ? &i2s->capture_dma_data : NULL);
return 0;
}
static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
.probe = rockchip_i2s_dai_probe,
.hw_params = rockchip_i2s_hw_params,
.set_bclk_ratio = rockchip_i2s_set_bclk_ratio,
.set_sysclk = rockchip_i2s_set_sysclk,
.set_fmt = rockchip_i2s_set_fmt,
.trigger = rockchip_i2s_trigger,
};
static struct snd_soc_dai_driver rockchip_i2s_dai = {
.ops = &rockchip_i2s_dai_ops,
.symmetric_rate = 1,
};
static const struct snd_soc_component_driver rockchip_i2s_component = {
.name = DRV_NAME,
.legacy_dai_naming = 1,
};
static bool rockchip_i2s_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case I2S_TXCR:
case I2S_RXCR:
case I2S_CKR:
case I2S_DMACR:
case I2S_INTCR:
case I2S_XFER:
case I2S_CLR:
case I2S_TXDR:
return true;
default:
return false;
}
}
static bool rockchip_i2s_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case I2S_TXCR:
case I2S_RXCR:
case I2S_CKR:
case I2S_DMACR:
case I2S_INTCR:
case I2S_XFER:
case I2S_CLR:
case I2S_TXDR:
case I2S_RXDR:
case I2S_FIFOLR:
case I2S_INTSR:
return true;
default:
return false;
}
}
static bool rockchip_i2s_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case I2S_INTSR:
case I2S_CLR:
case I2S_FIFOLR:
case I2S_TXDR:
case I2S_RXDR:
return true;
default:
return false;
}
}
static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case I2S_RXDR:
return true;
default:
return false;
}
}
static const struct reg_default rockchip_i2s_reg_defaults[] = {
{0x00, 0x0000000f},
{0x04, 0x0000000f},
{0x08, 0x00071f1f},
{0x10, 0x001f0000},
{0x14, 0x01f00000},
};
static const struct regmap_config rockchip_i2s_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = I2S_RXDR,
.reg_defaults = rockchip_i2s_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults),
.writeable_reg = rockchip_i2s_wr_reg,
.readable_reg = rockchip_i2s_rd_reg,
.volatile_reg = rockchip_i2s_volatile_reg,
.precious_reg = rockchip_i2s_precious_reg,
.cache_type = REGCACHE_FLAT,
};
static const struct rk_i2s_pins rk3399_i2s_pins = {
.reg_offset = 0xe220,
.shift = 11,
};
static const struct of_device_id rockchip_i2s_match[] __maybe_unused = {
{ .compatible = "rockchip,px30-i2s", },
{ .compatible = "rockchip,rk1808-i2s", },
{ .compatible = "rockchip,rk3036-i2s", },
{ .compatible = "rockchip,rk3066-i2s", },
{ .compatible = "rockchip,rk3128-i2s", },
{ .compatible = "rockchip,rk3188-i2s", },
{ .compatible = "rockchip,rk3228-i2s", },
{ .compatible = "rockchip,rk3288-i2s", },
{ .compatible = "rockchip,rk3308-i2s", },
{ .compatible = "rockchip,rk3328-i2s", },
{ .compatible = "rockchip,rk3366-i2s", },
{ .compatible = "rockchip,rk3368-i2s", },
{ .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
{ .compatible = "rockchip,rk3588-i2s", },
{ .compatible = "rockchip,rv1126-i2s", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_i2s_match);
static void rockchip_i2s_suspend(void *data)
{
struct device *dev = data;
if (!pm_runtime_status_suspended(dev))
i2s_runtime_suspend(dev);
}
static int rockchip_i2s_init_dai(struct rk_i2s_dev *i2s, struct resource *res,
struct snd_soc_dai_driver **dp)
{
struct device_node *node = i2s->dev->of_node;
struct snd_soc_dai_driver *dai;
struct property *dma_names;
const char *dma_name;
unsigned int val;
of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
if (!strcmp(dma_name, "tx"))
i2s->has_playback = true;
if (!strcmp(dma_name, "rx"))
i2s->has_capture = true;
}
dai = devm_kmemdup(i2s->dev, &rockchip_i2s_dai,
sizeof(*dai), GFP_KERNEL);
if (!dai)
return -ENOMEM;
if (i2s->has_playback) {
dai->playback.stream_name = "Playback";
dai->playback.channels_min = 2;
dai->playback.channels_max = 8;
dai->playback.rates = SNDRV_PCM_RATE_8000_192000;
dai->playback.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE;
i2s->playback_dma_data.addr = res->start + I2S_TXDR;
i2s->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
i2s->playback_dma_data.maxburst = 8;
if (!of_property_read_u32(node, "rockchip,playback-channels", &val)) {
if (val >= 2 && val <= 8)
dai->playback.channels_max = val;
}
}
if (i2s->has_capture) {
dai->capture.stream_name = "Capture";
dai->capture.channels_min = 2;
dai->capture.channels_max = 8;
dai->capture.rates = SNDRV_PCM_RATE_8000_192000;
dai->capture.formats = SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE;
i2s->capture_dma_data.addr = res->start + I2S_RXDR;
i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
i2s->capture_dma_data.maxburst = 8;
if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
if (val >= 2 && val <= 8)
dai->capture.channels_max = val;
}
}
if (dp)
*dp = dai;
return 0;
}
static int rockchip_i2s_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct rk_i2s_dev *i2s;
struct snd_soc_dai_driver *dai;
struct resource *res;
void __iomem *regs;
int ret;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
spin_lock_init(&i2s->lock);
i2s->dev = &pdev->dev;
i2s->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
if (!IS_ERR(i2s->grf)) {
i2s->pins = device_get_match_data(&pdev->dev);
if (!i2s->pins)
return -EINVAL;
}
/* try to prepare related clocks */
i2s->hclk = devm_clk_get_enabled(&pdev->dev, "i2s_hclk");
if (IS_ERR(i2s->hclk)) {
dev_err(&pdev->dev, "Can't retrieve i2s bus clock\n");
return PTR_ERR(i2s->hclk);
}
i2s->mclk = devm_clk_get(&pdev->dev, "i2s_clk");
if (IS_ERR(i2s->mclk)) {
dev_err(&pdev->dev, "Can't retrieve i2s master clock\n");
return PTR_ERR(i2s->mclk);
}
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&rockchip_i2s_regmap_config);
if (IS_ERR(i2s->regmap)) {
dev_err(&pdev->dev,
"Failed to initialise managed register map\n");
return PTR_ERR(i2s->regmap);
}
i2s->bclk_ratio = 64;
i2s->pinctrl = devm_pinctrl_get(&pdev->dev);
if (!IS_ERR(i2s->pinctrl)) {
i2s->bclk_on = pinctrl_lookup_state(i2s->pinctrl, "bclk_on");
if (!IS_ERR_OR_NULL(i2s->bclk_on)) {
i2s->bclk_off = pinctrl_lookup_state(i2s->pinctrl, "bclk_off");
if (IS_ERR_OR_NULL(i2s->bclk_off)) {
dev_err(&pdev->dev, "failed to find i2s bclk_off\n");
return -EINVAL;
}
}
} else {
dev_dbg(&pdev->dev, "failed to find i2s pinctrl\n");
}
i2s_pinctrl_select_bclk_off(i2s);
dev_set_drvdata(&pdev->dev, i2s);
ret = devm_add_action(&pdev->dev, rockchip_i2s_suspend, &pdev->dev);
if (ret)
return ret;
ret = devm_pm_runtime_enable(&pdev->dev);
if (ret)
return ret;
if (!pm_runtime_enabled(&pdev->dev)) {
ret = i2s_runtime_resume(&pdev->dev);
if (ret)
return ret;
}
ret = rockchip_i2s_init_dai(i2s, res, &dai);
if (ret)
return ret;
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_i2s_component,
dai, 1);
if (ret) {
dev_err(&pdev->dev, "Could not register DAI\n");
return ret;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "Could not register PCM\n");
return ret;
}
return 0;
}
static const struct dev_pm_ops rockchip_i2s_pm_ops = {
RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume, NULL)
};
static struct platform_driver rockchip_i2s_driver = {
.probe = rockchip_i2s_probe,
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(rockchip_i2s_match),
.pm = pm_ptr(&rockchip_i2s_pm_ops),
},
};
module_platform_driver(rockchip_i2s_driver);
MODULE_DESCRIPTION("ROCKCHIP IIS ASoC Interface");
MODULE_AUTHOR("jianqun <jay.xu@rock-chips.com>");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
@@ -0,0 +1,248 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* sound/soc/rockchip/rockchip_i2s.h
*
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun xu <jay.xu@rock-chips.com>
*/
#ifndef _ROCKCHIP_IIS_H
#define _ROCKCHIP_IIS_H
/*
* TXCR
* transmit operation control register
*/
#define I2S_TXCR_RCNT_SHIFT 17
#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT)
#define I2S_TXCR_CSR_SHIFT 15
#define I2S_TXCR_CSR(x) (x << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_HWT BIT(14)
#define I2S_TXCR_SJM_SHIFT 12
#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT)
#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT)
#define I2S_TXCR_FBM_SHIFT 11
#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT)
#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT)
#define I2S_TXCR_IBM_SHIFT 9
#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_PBM_SHIFT 7
#define I2S_TXCR_PBM_MODE(x) (x << I2S_TXCR_PBM_SHIFT)
#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT)
#define I2S_TXCR_TFS_SHIFT 5
#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_MASK (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_VDW_SHIFT 0
#define I2S_TXCR_VDW(x) ((x - 1) << I2S_TXCR_VDW_SHIFT)
#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
/*
* RXCR
* receive operation control register
*/
#define I2S_RXCR_CSR_SHIFT 15
#define I2S_RXCR_CSR(x) (x << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_HWT BIT(14)
#define I2S_RXCR_SJM_SHIFT 12
#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT)
#define I2S_RXCR_FBM_SHIFT 11
#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT)
#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT)
#define I2S_RXCR_IBM_SHIFT 9
#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_PBM_SHIFT 7
#define I2S_RXCR_PBM_MODE(x) (x << I2S_RXCR_PBM_SHIFT)
#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT)
#define I2S_RXCR_TFS_SHIFT 5
#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_MASK (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_VDW_SHIFT 0
#define I2S_RXCR_VDW(x) ((x - 1) << I2S_RXCR_VDW_SHIFT)
#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
/*
* CKR
* clock generation register
*/
#define I2S_CKR_TRCM_SHIFT 28
#define I2S_CKR_TRCM(x) (x << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_CKP_SHIFT 26
#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_RLP_SHIFT 25
#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_TLP_SHIFT 24
#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_MDIV_SHIFT 16
#define I2S_CKR_MDIV(x) ((x - 1) << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_RSD_SHIFT 8
#define I2S_CKR_RSD(x) ((x - 1) << I2S_CKR_RSD_SHIFT)
#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT)
#define I2S_CKR_TSD_SHIFT 0
#define I2S_CKR_TSD(x) ((x - 1) << I2S_CKR_TSD_SHIFT)
#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT)
/*
* FIFOLR
* FIFO level register
*/
#define I2S_FIFOLR_RFL_SHIFT 24
#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT)
#define I2S_FIFOLR_TFL3_SHIFT 18
#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT)
#define I2S_FIFOLR_TFL2_SHIFT 12
#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT)
#define I2S_FIFOLR_TFL1_SHIFT 6
#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT)
#define I2S_FIFOLR_TFL0_SHIFT 0
#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT)
/*
* DMACR
* DMA control register
*/
#define I2S_DMACR_RDE_SHIFT 24
#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDL_SHIFT 16
#define I2S_DMACR_RDL(x) ((x - 1) << I2S_DMACR_RDL_SHIFT)
#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT)
#define I2S_DMACR_TDE_SHIFT 8
#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDL_SHIFT 0
#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT)
#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT)
/*
* INTCR
* interrupt control register
*/
#define I2S_INTCR_RFT_SHIFT 20
#define I2S_INTCR_RFT(x) ((x - 1) << I2S_INTCR_RFT_SHIFT)
#define I2S_INTCR_RXOIC BIT(18)
#define I2S_INTCR_RXOIE_SHIFT 17
#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT)
#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT)
#define I2S_INTCR_RXFIE_SHIFT 16
#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT)
#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT)
#define I2S_INTCR_TFT_SHIFT 4
#define I2S_INTCR_TFT(x) ((x - 1) << I2S_INTCR_TFT_SHIFT)
#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT)
#define I2S_INTCR_TXUIC BIT(2)
#define I2S_INTCR_TXUIE_SHIFT 1
#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT)
#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT)
/*
* INTSR
* interrupt status register
*/
#define I2S_INTSR_TXEIE_SHIFT 0
#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT)
#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT)
#define I2S_INTSR_RXOI_SHIFT 17
#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT)
#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT)
#define I2S_INTSR_RXFI_SHIFT 16
#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT)
#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT)
#define I2S_INTSR_TXUI_SHIFT 1
#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT)
#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT)
#define I2S_INTSR_TXEI_SHIFT 0
#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT)
#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT)
/*
* XFER
* Transfer start register
*/
#define I2S_XFER_RXS_SHIFT 1
#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_TXS_SHIFT 0
#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT)
#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT)
/*
* CLR
* clear SCLK domain logic register
*/
#define I2S_CLR_RXC BIT(1)
#define I2S_CLR_TXC BIT(0)
/*
* TXDR
* Transimt FIFO data register, write only.
*/
#define I2S_TXDR_MASK (0xff)
/*
* RXDR
* Receive FIFO data register, write only.
*/
#define I2S_RXDR_MASK (0xff)
/* Clock divider id */
enum {
ROCKCHIP_DIV_MCLK = 0,
ROCKCHIP_DIV_BCLK,
};
/* channel select */
#define I2S_CSR_SHIFT 15
#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)
#define I2S_CKR (0x0008)
#define I2S_FIFOLR (0x000c)
#define I2S_DMACR (0x0010)
#define I2S_INTCR (0x0014)
#define I2S_INTSR (0x0018)
#define I2S_XFER (0x001c)
#define I2S_CLR (0x0020)
#define I2S_TXDR (0x0024)
#define I2S_RXDR (0x0028)
/* io direction cfg register */
#define I2S_IO_DIRECTION_MASK (7)
#define I2S_IO_8CH_OUT_2CH_IN (0)
#define I2S_IO_6CH_OUT_4CH_IN (4)
#define I2S_IO_4CH_OUT_6CH_IN (6)
#define I2S_IO_2CH_OUT_8CH_IN (7)
#endif /* _ROCKCHIP_IIS_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,400 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC Audio Layer - Rockchip I2S/TDM Controller driver
*
* Copyright (c) 2018 Rockchip Electronics Co. Ltd.
* Author: Sugar Zhang <sugar.zhang@rock-chips.com>
*
*/
#ifndef _ROCKCHIP_I2S_TDM_H
#define _ROCKCHIP_I2S_TDM_H
#include <linux/hw_bitfield.h>
/*
* TXCR
* transmit operation control register
*/
#define I2S_TXCR_PATH_SHIFT(x) (23 + (x) * 2)
#define I2S_TXCR_PATH_MASK(x) (0x3 << I2S_TXCR_PATH_SHIFT(x))
#define I2S_TXCR_PATH(x, v) ((v) << I2S_TXCR_PATH_SHIFT(x))
#define I2S_TXCR_RCNT_SHIFT 17
#define I2S_TXCR_RCNT_MASK (0x3f << I2S_TXCR_RCNT_SHIFT)
#define I2S_TXCR_CSR_SHIFT 15
#define I2S_TXCR_CSR(x) ((x) << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_CSR_MASK (3 << I2S_TXCR_CSR_SHIFT)
#define I2S_TXCR_HWT BIT(14)
#define I2S_TXCR_SJM_SHIFT 12
#define I2S_TXCR_SJM_R (0 << I2S_TXCR_SJM_SHIFT)
#define I2S_TXCR_SJM_L (1 << I2S_TXCR_SJM_SHIFT)
#define I2S_TXCR_FBM_SHIFT 11
#define I2S_TXCR_FBM_MSB (0 << I2S_TXCR_FBM_SHIFT)
#define I2S_TXCR_FBM_LSB (1 << I2S_TXCR_FBM_SHIFT)
#define I2S_TXCR_IBM_SHIFT 9
#define I2S_TXCR_IBM_NORMAL (0 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_LSJM (1 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_RSJM (2 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_IBM_MASK (3 << I2S_TXCR_IBM_SHIFT)
#define I2S_TXCR_PBM_SHIFT 7
#define I2S_TXCR_PBM_MODE(x) ((x) << I2S_TXCR_PBM_SHIFT)
#define I2S_TXCR_PBM_MASK (3 << I2S_TXCR_PBM_SHIFT)
#define I2S_TXCR_TFS_SHIFT 5
#define I2S_TXCR_TFS_I2S (0 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_PCM (1 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_TDM_PCM (2 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_TDM_I2S (3 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_TFS_MASK (3 << I2S_TXCR_TFS_SHIFT)
#define I2S_TXCR_VDW_SHIFT 0
#define I2S_TXCR_VDW(x) (((x) - 1) << I2S_TXCR_VDW_SHIFT)
#define I2S_TXCR_VDW_MASK (0x1f << I2S_TXCR_VDW_SHIFT)
/*
* RXCR
* receive operation control register
*/
#define I2S_RXCR_PATH_SHIFT(x) (17 + (x) * 2)
#define I2S_RXCR_PATH_MASK(x) (0x3 << I2S_RXCR_PATH_SHIFT(x))
#define I2S_RXCR_PATH(x, v) ((v) << I2S_RXCR_PATH_SHIFT(x))
#define I2S_RXCR_CSR_SHIFT 15
#define I2S_RXCR_CSR(x) ((x) << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_CSR_MASK (3 << I2S_RXCR_CSR_SHIFT)
#define I2S_RXCR_HWT BIT(14)
#define I2S_RXCR_SJM_SHIFT 12
#define I2S_RXCR_SJM_R (0 << I2S_RXCR_SJM_SHIFT)
#define I2S_RXCR_SJM_L (1 << I2S_RXCR_SJM_SHIFT)
#define I2S_RXCR_FBM_SHIFT 11
#define I2S_RXCR_FBM_MSB (0 << I2S_RXCR_FBM_SHIFT)
#define I2S_RXCR_FBM_LSB (1 << I2S_RXCR_FBM_SHIFT)
#define I2S_RXCR_IBM_SHIFT 9
#define I2S_RXCR_IBM_NORMAL (0 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_LSJM (1 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_RSJM (2 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_IBM_MASK (3 << I2S_RXCR_IBM_SHIFT)
#define I2S_RXCR_PBM_SHIFT 7
#define I2S_RXCR_PBM_MODE(x) ((x) << I2S_RXCR_PBM_SHIFT)
#define I2S_RXCR_PBM_MASK (3 << I2S_RXCR_PBM_SHIFT)
#define I2S_RXCR_TFS_SHIFT 5
#define I2S_RXCR_TFS_I2S (0 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_PCM (1 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_TDM_PCM (2 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_TDM_I2S (3 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_TFS_MASK (3 << I2S_RXCR_TFS_SHIFT)
#define I2S_RXCR_VDW_SHIFT 0
#define I2S_RXCR_VDW(x) (((x) - 1) << I2S_RXCR_VDW_SHIFT)
#define I2S_RXCR_VDW_MASK (0x1f << I2S_RXCR_VDW_SHIFT)
/*
* CKR
* clock generation register
*/
#define I2S_CKR_TRCM_SHIFT 28
#define I2S_CKR_TRCM(x) ((x) << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_TXRX (0 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_TXONLY (1 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_RXONLY (2 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_TRCM_MASK (3 << I2S_CKR_TRCM_SHIFT)
#define I2S_CKR_MSS_SHIFT 27
#define I2S_CKR_MSS_MASTER (0 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_SLAVE (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_MSS_MASK (1 << I2S_CKR_MSS_SHIFT)
#define I2S_CKR_CKP_SHIFT 26
#define I2S_CKR_CKP_NORMAL (0 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_INVERTED (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_CKP_MASK (1 << I2S_CKR_CKP_SHIFT)
#define I2S_CKR_RLP_SHIFT 25
#define I2S_CKR_RLP_NORMAL (0 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_RLP_INVERTED (1 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_RLP_MASK (1 << I2S_CKR_RLP_SHIFT)
#define I2S_CKR_TLP_SHIFT 24
#define I2S_CKR_TLP_NORMAL (0 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_TLP_INVERTED (1 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_TLP_MASK (1 << I2S_CKR_TLP_SHIFT)
#define I2S_CKR_MDIV_SHIFT 16
#define I2S_CKR_MDIV(x) (((x) - 1) << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_MDIV_MASK (0xff << I2S_CKR_MDIV_SHIFT)
#define I2S_CKR_RSD_SHIFT 8
#define I2S_CKR_RSD(x) (((x) - 1) << I2S_CKR_RSD_SHIFT)
#define I2S_CKR_RSD_MASK (0xff << I2S_CKR_RSD_SHIFT)
#define I2S_CKR_TSD_SHIFT 0
#define I2S_CKR_TSD(x) (((x) - 1) << I2S_CKR_TSD_SHIFT)
#define I2S_CKR_TSD_MASK (0xff << I2S_CKR_TSD_SHIFT)
/*
* FIFOLR
* FIFO level register
*/
#define I2S_FIFOLR_RFL_SHIFT 24
#define I2S_FIFOLR_RFL_MASK (0x3f << I2S_FIFOLR_RFL_SHIFT)
#define I2S_FIFOLR_TFL3_SHIFT 18
#define I2S_FIFOLR_TFL3_MASK (0x3f << I2S_FIFOLR_TFL3_SHIFT)
#define I2S_FIFOLR_TFL2_SHIFT 12
#define I2S_FIFOLR_TFL2_MASK (0x3f << I2S_FIFOLR_TFL2_SHIFT)
#define I2S_FIFOLR_TFL1_SHIFT 6
#define I2S_FIFOLR_TFL1_MASK (0x3f << I2S_FIFOLR_TFL1_SHIFT)
#define I2S_FIFOLR_TFL0_SHIFT 0
#define I2S_FIFOLR_TFL0_MASK (0x3f << I2S_FIFOLR_TFL0_SHIFT)
/*
* DMACR
* DMA control register
*/
#define I2S_DMACR_RDE_SHIFT 24
#define I2S_DMACR_RDE_DISABLE (0 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDE_ENABLE (1 << I2S_DMACR_RDE_SHIFT)
#define I2S_DMACR_RDL_SHIFT 16
#define I2S_DMACR_RDL(x) (((x) - 1) << I2S_DMACR_RDL_SHIFT)
#define I2S_DMACR_RDL_MASK (0x1f << I2S_DMACR_RDL_SHIFT)
#define I2S_DMACR_TDE_SHIFT 8
#define I2S_DMACR_TDE_DISABLE (0 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDE_ENABLE (1 << I2S_DMACR_TDE_SHIFT)
#define I2S_DMACR_TDL_SHIFT 0
#define I2S_DMACR_TDL(x) ((x) << I2S_DMACR_TDL_SHIFT)
#define I2S_DMACR_TDL_MASK (0x1f << I2S_DMACR_TDL_SHIFT)
/*
* INTCR
* interrupt control register
*/
#define I2S_INTCR_RFT_SHIFT 20
#define I2S_INTCR_RFT(x) (((x) - 1) << I2S_INTCR_RFT_SHIFT)
#define I2S_INTCR_RXOIC BIT(18)
#define I2S_INTCR_RXOIE_SHIFT 17
#define I2S_INTCR_RXOIE_DISABLE (0 << I2S_INTCR_RXOIE_SHIFT)
#define I2S_INTCR_RXOIE_ENABLE (1 << I2S_INTCR_RXOIE_SHIFT)
#define I2S_INTCR_RXFIE_SHIFT 16
#define I2S_INTCR_RXFIE_DISABLE (0 << I2S_INTCR_RXFIE_SHIFT)
#define I2S_INTCR_RXFIE_ENABLE (1 << I2S_INTCR_RXFIE_SHIFT)
#define I2S_INTCR_TFT_SHIFT 4
#define I2S_INTCR_TFT(x) (((x) - 1) << I2S_INTCR_TFT_SHIFT)
#define I2S_INTCR_TFT_MASK (0x1f << I2S_INTCR_TFT_SHIFT)
#define I2S_INTCR_TXUIC BIT(2)
#define I2S_INTCR_TXUIE_SHIFT 1
#define I2S_INTCR_TXUIE_DISABLE (0 << I2S_INTCR_TXUIE_SHIFT)
#define I2S_INTCR_TXUIE_ENABLE (1 << I2S_INTCR_TXUIE_SHIFT)
/*
* INTSR
* interrupt status register
*/
#define I2S_INTSR_TXEIE_SHIFT 0
#define I2S_INTSR_TXEIE_DISABLE (0 << I2S_INTSR_TXEIE_SHIFT)
#define I2S_INTSR_TXEIE_ENABLE (1 << I2S_INTSR_TXEIE_SHIFT)
#define I2S_INTSR_RXOI_SHIFT 17
#define I2S_INTSR_RXOI_INA (0 << I2S_INTSR_RXOI_SHIFT)
#define I2S_INTSR_RXOI_ACT (1 << I2S_INTSR_RXOI_SHIFT)
#define I2S_INTSR_RXFI_SHIFT 16
#define I2S_INTSR_RXFI_INA (0 << I2S_INTSR_RXFI_SHIFT)
#define I2S_INTSR_RXFI_ACT (1 << I2S_INTSR_RXFI_SHIFT)
#define I2S_INTSR_TXUI_SHIFT 1
#define I2S_INTSR_TXUI_INA (0 << I2S_INTSR_TXUI_SHIFT)
#define I2S_INTSR_TXUI_ACT (1 << I2S_INTSR_TXUI_SHIFT)
#define I2S_INTSR_TXEI_SHIFT 0
#define I2S_INTSR_TXEI_INA (0 << I2S_INTSR_TXEI_SHIFT)
#define I2S_INTSR_TXEI_ACT (1 << I2S_INTSR_TXEI_SHIFT)
/*
* XFER
* Transfer start register
*/
#define I2S_XFER_RXS_SHIFT 1
#define I2S_XFER_RXS_STOP (0 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_RXS_START (1 << I2S_XFER_RXS_SHIFT)
#define I2S_XFER_TXS_SHIFT 0
#define I2S_XFER_TXS_STOP (0 << I2S_XFER_TXS_SHIFT)
#define I2S_XFER_TXS_START (1 << I2S_XFER_TXS_SHIFT)
/*
* CLR
* clear SCLK domain logic register
*/
#define I2S_CLR_RXC BIT(1)
#define I2S_CLR_TXC BIT(0)
/*
* TXDR
* Transimt FIFO data register, write only.
*/
#define I2S_TXDR_MASK (0xff)
/*
* RXDR
* Receive FIFO data register, write only.
*/
#define I2S_RXDR_MASK (0xff)
/*
* TDM_CTRL
* TDM ctrl register
*/
#define TDM_FSYNC_WIDTH_SEL1_MSK GENMASK(20, 18)
#define TDM_FSYNC_WIDTH_SEL1(x) (((x) - 1) << 18)
#define TDM_FSYNC_WIDTH_SEL0_MSK BIT(17)
#define TDM_FSYNC_WIDTH_HALF_FRAME 0
#define TDM_FSYNC_WIDTH_ONE_FRAME BIT(17)
#define TDM_SHIFT_CTRL_MSK GENMASK(16, 14)
#define TDM_SHIFT_CTRL(x) ((x) << 14)
#define TDM_SLOT_BIT_WIDTH_MSK GENMASK(13, 9)
#define TDM_SLOT_BIT_WIDTH(x) (((x) - 1) << 9)
#define TDM_FRAME_WIDTH_MSK GENMASK(8, 0)
#define TDM_FRAME_WIDTH(x) (((x) - 1) << 0)
/*
* CLKDIV
* Mclk div register
*/
#define I2S_CLKDIV_TXM_SHIFT 0
#define I2S_CLKDIV_TXM(x) (((x) - 1) << I2S_CLKDIV_TXM_SHIFT)
#define I2S_CLKDIV_TXM_MASK (0xff << I2S_CLKDIV_TXM_SHIFT)
#define I2S_CLKDIV_RXM_SHIFT 8
#define I2S_CLKDIV_RXM(x) (((x) - 1) << I2S_CLKDIV_RXM_SHIFT)
#define I2S_CLKDIV_RXM_MASK (0xff << I2S_CLKDIV_RXM_SHIFT)
/* Clock divider id */
enum {
ROCKCHIP_DIV_MCLK = 0,
ROCKCHIP_DIV_BCLK,
};
/* channel select */
#define I2S_CSR_SHIFT 15
#define I2S_CHN_2 (0 << I2S_CSR_SHIFT)
#define I2S_CHN_4 (1 << I2S_CSR_SHIFT)
#define I2S_CHN_6 (2 << I2S_CSR_SHIFT)
#define I2S_CHN_8 (3 << I2S_CSR_SHIFT)
/* io direction cfg register */
#define I2S_IO_DIRECTION_MASK (7)
#define I2S_IO_8CH_OUT_2CH_IN (7)
#define I2S_IO_6CH_OUT_4CH_IN (3)
#define I2S_IO_4CH_OUT_6CH_IN (1)
#define I2S_IO_2CH_OUT_8CH_IN (0)
/* I2S REGS */
#define I2S_TXCR (0x0000)
#define I2S_RXCR (0x0004)
#define I2S_CKR (0x0008)
#define I2S_TXFIFOLR (0x000c)
#define I2S_DMACR (0x0010)
#define I2S_INTCR (0x0014)
#define I2S_INTSR (0x0018)
#define I2S_XFER (0x001c)
#define I2S_CLR (0x0020)
#define I2S_TXDR (0x0024)
#define I2S_RXDR (0x0028)
#define I2S_RXFIFOLR (0x002c)
#define I2S_TDM_TXCR (0x0030)
#define I2S_TDM_RXCR (0x0034)
#define I2S_CLKDIV (0x0038)
#define HIWORD_UPDATE(v, h, l) (FIELD_PREP_WM16_CONST(GENMASK((h), (l)), (v)))
/* PX30 GRF CONFIGS */
#define PX30_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 13, 12)
#define PX30_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 13, 12)
#define PX30_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5)
#define PX30_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5)
#define PX30_I2S0_CLK_TXONLY \
(PX30_I2S0_MCLK_OUT_SRC_FROM_TX | PX30_I2S0_CLK_IN_SRC_FROM_TX)
#define PX30_I2S0_CLK_RXONLY \
(PX30_I2S0_MCLK_OUT_SRC_FROM_RX | PX30_I2S0_CLK_IN_SRC_FROM_RX)
/* RK1808 GRF CONFIGS */
#define RK1808_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2)
#define RK1808_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2)
#define RK1808_I2S0_CLK_IN_SRC_FROM_TX HIWORD_UPDATE(1, 1, 0)
#define RK1808_I2S0_CLK_IN_SRC_FROM_RX HIWORD_UPDATE(2, 1, 0)
#define RK1808_I2S0_CLK_TXONLY \
(RK1808_I2S0_MCLK_OUT_SRC_FROM_TX | RK1808_I2S0_CLK_IN_SRC_FROM_TX)
#define RK1808_I2S0_CLK_RXONLY \
(RK1808_I2S0_MCLK_OUT_SRC_FROM_RX | RK1808_I2S0_CLK_IN_SRC_FROM_RX)
/* RK3308 GRF CONFIGS */
#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 10, 10)
#define RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 10, 10)
#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 9, 9)
#define RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 9, 9)
#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 8, 8)
#define RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 8, 8)
#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 2, 2)
#define RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 2, 2)
#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX HIWORD_UPDATE(1, 1, 1)
#define RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX HIWORD_UPDATE(0, 1, 1)
#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX HIWORD_UPDATE(1, 0, 0)
#define RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX HIWORD_UPDATE(0, 0, 0)
#define RK3308_I2S0_CLK_TXONLY \
(RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_TX | \
RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_TX | \
RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_TX)
#define RK3308_I2S0_CLK_RXONLY \
(RK3308_I2S0_8CH_MCLK_OUT_SRC_FROM_RX | \
RK3308_I2S0_8CH_CLK_IN_RX_SRC_FROM_RX | \
RK3308_I2S0_8CH_CLK_IN_TX_SRC_FROM_RX)
#define RK3308_I2S1_CLK_TXONLY \
(RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_TX | \
RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_TX | \
RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_TX)
#define RK3308_I2S1_CLK_RXONLY \
(RK3308_I2S1_8CH_MCLK_OUT_SRC_FROM_RX | \
RK3308_I2S1_8CH_CLK_IN_RX_SRC_FROM_RX | \
RK3308_I2S1_8CH_CLK_IN_TX_SRC_FROM_RX)
/* RK3568 GRF CONFIGS */
#define RK3568_I2S1_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 5, 5)
#define RK3568_I2S1_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 5, 5)
#define RK3568_I2S1_CLK_TXONLY \
RK3568_I2S1_MCLK_OUT_SRC_FROM_TX
#define RK3568_I2S1_CLK_RXONLY \
RK3568_I2S1_MCLK_OUT_SRC_FROM_RX
#define RK3568_I2S3_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(1, 15, 15)
#define RK3568_I2S3_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(0, 15, 15)
#define RK3568_I2S3_SCLK_SRC_FROM_TX HIWORD_UPDATE(1, 7, 7)
#define RK3568_I2S3_SCLK_SRC_FROM_RX HIWORD_UPDATE(0, 7, 7)
#define RK3568_I2S3_LRCK_SRC_FROM_TX HIWORD_UPDATE(1, 6, 6)
#define RK3568_I2S3_LRCK_SRC_FROM_RX HIWORD_UPDATE(0, 6, 6)
#define RK3568_I2S3_MCLK_TXONLY \
RK3568_I2S3_MCLK_OUT_SRC_FROM_TX
#define RK3568_I2S3_CLK_TXONLY \
(RK3568_I2S3_SCLK_SRC_FROM_TX | \
RK3568_I2S3_LRCK_SRC_FROM_TX)
#define RK3568_I2S3_MCLK_RXONLY \
RK3568_I2S3_MCLK_OUT_SRC_FROM_RX
#define RK3568_I2S3_CLK_RXONLY \
(RK3568_I2S3_SCLK_SRC_FROM_RX | \
RK3568_I2S3_LRCK_SRC_FROM_RX)
#define RK3568_I2S3_MCLK_IE HIWORD_UPDATE(0, 3, 3)
#define RK3568_I2S3_MCLK_OE HIWORD_UPDATE(1, 3, 3)
#define RK3568_I2S2_MCLK_IE HIWORD_UPDATE(0, 2, 2)
#define RK3568_I2S2_MCLK_OE HIWORD_UPDATE(1, 2, 2)
#define RK3568_I2S1_MCLK_TX_IE HIWORD_UPDATE(0, 1, 1)
#define RK3568_I2S1_MCLK_TX_OE HIWORD_UPDATE(1, 1, 1)
#define RK3568_I2S1_MCLK_RX_IE HIWORD_UPDATE(0, 0, 0)
#define RK3568_I2S1_MCLK_RX_OE HIWORD_UPDATE(1, 0, 0)
/* RV1126 GRF CONFIGS */
#define RV1126_I2S0_MCLK_OUT_SRC_FROM_TX HIWORD_UPDATE(0, 9, 9)
#define RV1126_I2S0_MCLK_OUT_SRC_FROM_RX HIWORD_UPDATE(1, 9, 9)
#define RV1126_I2S0_CLK_TXONLY \
RV1126_I2S0_MCLK_OUT_SRC_FROM_TX
#define RV1126_I2S0_CLK_RXONLY \
RV1126_I2S0_MCLK_OUT_SRC_FROM_RX
#endif /* _ROCKCHIP_I2S_TDM_H */
@@ -0,0 +1,469 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip machine ASoC driver for boards using a MAX90809 CODEC.
*
* Copyright (c) 2014, ROCKCHIP CORPORATION. All rights reserved.
*/
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "rockchip_i2s.h"
#include "../codecs/ts3a227e.h"
#define DRV_NAME "rockchip-snd-max98090"
static struct snd_soc_jack headset_jack;
/* Headset jack detection DAPM pins */
static struct snd_soc_jack_pin headset_jack_pins[] = {
{
.pin = "Headphone",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
#define RK_MAX98090_WIDGETS \
SND_SOC_DAPM_HP("Headphone", NULL), \
SND_SOC_DAPM_MIC("Headset Mic", NULL), \
SND_SOC_DAPM_MIC("Int Mic", NULL), \
SND_SOC_DAPM_SPK("Speaker", NULL)
#define RK_HDMI_WIDGETS \
SND_SOC_DAPM_LINE("HDMI", NULL)
static const struct snd_soc_dapm_widget rk_max98090_dapm_widgets[] = {
RK_MAX98090_WIDGETS,
};
static const struct snd_soc_dapm_widget rk_hdmi_dapm_widgets[] = {
RK_HDMI_WIDGETS,
};
static const struct snd_soc_dapm_widget rk_max98090_hdmi_dapm_widgets[] = {
RK_MAX98090_WIDGETS,
RK_HDMI_WIDGETS,
};
#define RK_MAX98090_AUDIO_MAP \
{"IN34", NULL, "Headset Mic"}, \
{"Headset Mic", NULL, "MICBIAS"}, \
{"DMICL", NULL, "Int Mic"}, \
{"Headphone", NULL, "HPL"}, \
{"Headphone", NULL, "HPR"}, \
{"Speaker", NULL, "SPKL"}, \
{"Speaker", NULL, "SPKR"}
#define RK_HDMI_AUDIO_MAP \
{"HDMI", NULL, "TX"}
static const struct snd_soc_dapm_route rk_max98090_audio_map[] = {
RK_MAX98090_AUDIO_MAP,
};
static const struct snd_soc_dapm_route rk_hdmi_audio_map[] = {
RK_HDMI_AUDIO_MAP,
};
static const struct snd_soc_dapm_route rk_max98090_hdmi_audio_map[] = {
RK_MAX98090_AUDIO_MAP,
RK_HDMI_AUDIO_MAP,
};
#define RK_MAX98090_CONTROLS \
SOC_DAPM_PIN_SWITCH("Headphone"), \
SOC_DAPM_PIN_SWITCH("Headset Mic"), \
SOC_DAPM_PIN_SWITCH("Int Mic"), \
SOC_DAPM_PIN_SWITCH("Speaker")
#define RK_HDMI_CONTROLS \
SOC_DAPM_PIN_SWITCH("HDMI")
static const struct snd_kcontrol_new rk_max98090_controls[] = {
RK_MAX98090_CONTROLS,
};
static const struct snd_kcontrol_new rk_hdmi_controls[] = {
RK_HDMI_CONTROLS,
};
static const struct snd_kcontrol_new rk_max98090_hdmi_controls[] = {
RK_MAX98090_CONTROLS,
RK_HDMI_CONTROLS,
};
static int rk_jack_event(struct notifier_block *nb, unsigned long event,
void *data)
{
struct snd_soc_jack *jack = (struct snd_soc_jack *)data;
struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(jack->card);
if (event & SND_JACK_MICROPHONE) {
snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
snd_soc_dapm_force_enable_pin(dapm, "SHDN");
} else {
snd_soc_dapm_disable_pin(dapm, "MICBIAS");
snd_soc_dapm_disable_pin(dapm, "SHDN");
}
snd_soc_dapm_sync(dapm);
return 0;
}
static struct notifier_block rk_jack_nb = {
.notifier_call = rk_jack_event,
};
static int rk_init(struct snd_soc_pcm_runtime *runtime)
{
/*
* The jack has already been created in the rk_98090_headset_init()
* function.
*/
snd_soc_jack_notifier_register(&headset_jack, &rk_jack_nb);
return 0;
}
static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int mclk;
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret) {
dev_err(cpu_dai->dev, "Can't set cpu dai clock %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
/* HDMI codec dai does not need to set sysclk. */
if (!strcmp(rtd->dai_link->name, "HDMI"))
return 0;
if (ret) {
dev_err(codec_dai->dev, "Can't set codec dai clock %d\n", ret);
return ret;
}
return ret;
}
static int rk_aif1_startup(struct snd_pcm_substream *substream)
{
/*
* Set period size to 240 because pl330 has issue
* dealing with larger period in stress testing.
*/
return snd_pcm_hw_constraint_minmax(substream->runtime,
SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 240, 240);
}
static const struct snd_soc_ops rk_aif1_ops = {
.hw_params = rk_aif1_hw_params,
.startup = rk_aif1_startup,
};
SND_SOC_DAILINK_DEFS(analog,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "HiFi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(hdmi,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "i2s-hifi")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
enum {
DAILINK_MAX98090,
DAILINK_HDMI,
};
static struct snd_soc_jack rk_hdmi_jack;
static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
struct snd_soc_component *component = snd_soc_rtd_to_codec(runtime, 0)->component;
int ret;
/* enable jack detection */
ret = snd_soc_card_jack_new(card, "HDMI Jack", SND_JACK_LINEOUT,
&rk_hdmi_jack);
if (ret) {
dev_err(card->dev, "Can't new HDMI Jack %d\n", ret);
return ret;
}
return snd_soc_component_set_jack(component, &rk_hdmi_jack, NULL);
}
/* max98090 dai_link */
static struct snd_soc_dai_link rk_max98090_dailinks[] = {
{
.name = "max98090",
.stream_name = "Analog",
.init = rk_init,
.ops = &rk_aif1_ops,
/* set max98090 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(analog),
},
};
/* HDMI codec dai_link */
static struct snd_soc_dai_link rk_hdmi_dailinks[] = {
{
.name = "HDMI",
.stream_name = "HDMI",
.init = rk_hdmi_init,
.ops = &rk_aif1_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(hdmi),
}
};
/* max98090 and HDMI codec dai_link */
static struct snd_soc_dai_link rk_max98090_hdmi_dailinks[] = {
[DAILINK_MAX98090] = {
.name = "max98090",
.stream_name = "Analog",
.init = rk_init,
.ops = &rk_aif1_ops,
/* set max98090 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(analog),
},
[DAILINK_HDMI] = {
.name = "HDMI",
.stream_name = "HDMI",
.init = rk_hdmi_init,
.ops = &rk_aif1_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(hdmi),
}
};
static int rk_98090_headset_init(struct snd_soc_component *component);
static struct snd_soc_aux_dev rk_98090_headset_dev = {
.dlc = COMP_EMPTY(),
.init = rk_98090_headset_init,
};
static struct snd_soc_card rockchip_max98090_card = {
.name = "ROCKCHIP-I2S",
.owner = THIS_MODULE,
.dai_link = rk_max98090_dailinks,
.num_links = ARRAY_SIZE(rk_max98090_dailinks),
.aux_dev = &rk_98090_headset_dev,
.num_aux_devs = 1,
.dapm_widgets = rk_max98090_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk_max98090_dapm_widgets),
.dapm_routes = rk_max98090_audio_map,
.num_dapm_routes = ARRAY_SIZE(rk_max98090_audio_map),
.controls = rk_max98090_controls,
.num_controls = ARRAY_SIZE(rk_max98090_controls),
};
static struct snd_soc_card rockchip_hdmi_card = {
.name = "ROCKCHIP-HDMI",
.owner = THIS_MODULE,
.dai_link = rk_hdmi_dailinks,
.num_links = ARRAY_SIZE(rk_hdmi_dailinks),
.dapm_widgets = rk_hdmi_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk_hdmi_dapm_widgets),
.dapm_routes = rk_hdmi_audio_map,
.num_dapm_routes = ARRAY_SIZE(rk_hdmi_audio_map),
.controls = rk_hdmi_controls,
.num_controls = ARRAY_SIZE(rk_hdmi_controls),
};
static struct snd_soc_card rockchip_max98090_hdmi_card = {
.name = "ROCKCHIP-MAX98090-HDMI",
.owner = THIS_MODULE,
.dai_link = rk_max98090_hdmi_dailinks,
.num_links = ARRAY_SIZE(rk_max98090_hdmi_dailinks),
.aux_dev = &rk_98090_headset_dev,
.num_aux_devs = 1,
.dapm_widgets = rk_max98090_hdmi_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk_max98090_hdmi_dapm_widgets),
.dapm_routes = rk_max98090_hdmi_audio_map,
.num_dapm_routes = ARRAY_SIZE(rk_max98090_hdmi_audio_map),
.controls = rk_max98090_hdmi_controls,
.num_controls = ARRAY_SIZE(rk_max98090_hdmi_controls),
};
static int rk_98090_headset_init(struct snd_soc_component *component)
{
int ret;
/* Enable Headset and 4 Buttons Jack detection */
ret = snd_soc_card_jack_new_pins(component->card, "Headset Jack",
SND_JACK_HEADSET |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
&headset_jack,
headset_jack_pins,
ARRAY_SIZE(headset_jack_pins));
if (ret)
return ret;
ret = ts3a227e_enable_jack_detect(component, &headset_jack);
return ret;
}
static int rk_parse_headset_from_of(struct device *dev, struct device_node *np)
{
rk_98090_headset_dev.dlc.of_node = of_parse_phandle(
np, "rockchip,headset-codec", 0);
if (!rk_98090_headset_dev.dlc.of_node) {
dev_err(dev,
"Property 'rockchip,headset-codec' missing/invalid\n");
return -EINVAL;
}
return 0;
}
static int snd_rk_mc_probe(struct platform_device *pdev)
{
int ret = 0;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
struct device_node *np = pdev->dev.of_node;
struct device_node *np_cpu;
struct device_node *np_audio, *np_hdmi;
/* Parse DTS for I2S controller. */
np_cpu = of_parse_phandle(np, "rockchip,i2s-controller", 0);
if (!np_cpu) {
dev_err(&pdev->dev,
"Property 'rockchip,i2s-controller missing or invalid\n");
return -EINVAL;
}
/*
* Find the card to use based on the presences of audio codec
* and hdmi codec in device property. Set their of_node accordingly.
*/
np_audio = of_parse_phandle(np, "rockchip,audio-codec", 0);
np_hdmi = of_parse_phandle(np, "rockchip,hdmi-codec", 0);
if (np_audio && np_hdmi) {
card = &rockchip_max98090_hdmi_card;
card->dai_link[DAILINK_MAX98090].codecs->of_node = np_audio;
card->dai_link[DAILINK_HDMI].codecs->of_node = np_hdmi;
card->dai_link[DAILINK_MAX98090].cpus->of_node = np_cpu;
card->dai_link[DAILINK_MAX98090].platforms->of_node = np_cpu;
card->dai_link[DAILINK_HDMI].cpus->of_node = np_cpu;
card->dai_link[DAILINK_HDMI].platforms->of_node = np_cpu;
} else if (np_audio) {
card = &rockchip_max98090_card;
card->dai_link[0].codecs->of_node = np_audio;
card->dai_link[0].cpus->of_node = np_cpu;
card->dai_link[0].platforms->of_node = np_cpu;
} else if (np_hdmi) {
card = &rockchip_hdmi_card;
card->dai_link[0].codecs->of_node = np_hdmi;
card->dai_link[0].cpus->of_node = np_cpu;
card->dai_link[0].platforms->of_node = np_cpu;
} else {
dev_err(dev, "At least one of codecs should be specified\n");
return -EINVAL;
}
card->dev = dev;
/* Parse headset detection codec. */
if (np_audio) {
ret = rk_parse_headset_from_of(dev, np);
if (ret)
return ret;
}
/* Parse card name. */
ret = snd_soc_of_parse_card_name(card, "rockchip,model");
if (ret) {
dev_err(&pdev->dev,
"Soc parse card name failed %d\n", ret);
return ret;
}
/* register the soc card */
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
"Soc register card failed %d\n", ret);
return ret;
}
return ret;
}
static const struct of_device_id rockchip_max98090_of_match[] = {
{ .compatible = "rockchip,rockchip-audio-max98090", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_max98090_of_match);
static struct platform_driver snd_rk_mc_driver = {
.probe = snd_rk_mc_probe,
.driver = {
.name = DRV_NAME,
.pm = &snd_soc_pm_ops,
.of_match_table = rockchip_max98090_of_match,
},
};
module_platform_driver(snd_rk_mc_driver);
MODULE_AUTHOR("jianqun <jay.xu@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip max98090 machine ASoC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
@@ -0,0 +1,720 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
*/
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/rational.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include "rockchip_pdm.h"
#define PDM_DMA_BURST_SIZE (8) /* size * width: 8*4 = 32 bytes */
#define PDM_SIGNOFF_CLK_RATE (100000000)
#define PDM_PATH_MAX (4)
enum rk_pdm_version {
RK_PDM_RK3229,
RK_PDM_RK3308,
RK_PDM_RV1126,
};
struct rk_pdm_dev {
struct device *dev;
struct clk *clk;
struct clk *hclk;
struct regmap *regmap;
struct snd_dmaengine_dai_dma_data capture_dma_data;
struct reset_control *reset;
enum rk_pdm_version version;
};
struct rk_pdm_clkref {
unsigned int sr;
unsigned int clk;
unsigned int clk_out;
};
struct rk_pdm_ds_ratio {
unsigned int ratio;
unsigned int sr;
};
static struct rk_pdm_clkref clkref[] = {
{ 8000, 40960000, 2048000 },
{ 11025, 56448000, 2822400 },
{ 12000, 61440000, 3072000 },
{ 8000, 98304000, 2048000 },
{ 12000, 98304000, 3072000 },
};
static struct rk_pdm_ds_ratio ds_ratio[] = {
{ 0, 192000 },
{ 0, 176400 },
{ 0, 128000 },
{ 1, 96000 },
{ 1, 88200 },
{ 1, 64000 },
{ 2, 48000 },
{ 2, 44100 },
{ 2, 32000 },
{ 3, 24000 },
{ 3, 22050 },
{ 3, 16000 },
{ 4, 12000 },
{ 4, 11025 },
{ 4, 8000 },
};
static unsigned int get_pdm_clk(struct rk_pdm_dev *pdm, unsigned int sr,
unsigned int *clk_src, unsigned int *clk_out)
{
unsigned int i, count, clk, div, rate;
clk = 0;
if (!sr)
return clk;
count = ARRAY_SIZE(clkref);
for (i = 0; i < count; i++) {
if (sr % clkref[i].sr)
continue;
div = sr / clkref[i].sr;
if ((div & (div - 1)) == 0) {
*clk_out = clkref[i].clk_out;
rate = clk_round_rate(pdm->clk, clkref[i].clk);
if (rate != clkref[i].clk)
continue;
clk = clkref[i].clk;
*clk_src = clkref[i].clk;
break;
}
}
if (!clk) {
clk = clk_round_rate(pdm->clk, PDM_SIGNOFF_CLK_RATE);
*clk_src = clk;
}
return clk;
}
static unsigned int get_pdm_ds_ratio(unsigned int sr)
{
unsigned int i, count, ratio;
ratio = 0;
if (!sr)
return ratio;
count = ARRAY_SIZE(ds_ratio);
for (i = 0; i < count; i++) {
if (sr == ds_ratio[i].sr)
ratio = ds_ratio[i].ratio;
}
return ratio;
}
static unsigned int get_pdm_cic_ratio(unsigned int clk)
{
switch (clk) {
case 4096000:
case 5644800:
case 6144000:
return 0;
case 2048000:
case 2822400:
case 3072000:
return 1;
case 1024000:
case 1411200:
case 1536000:
return 2;
default:
return 1;
}
}
static unsigned int samplerate_to_bit(unsigned int samplerate)
{
switch (samplerate) {
case 8000:
case 11025:
case 12000:
return 0;
case 16000:
case 22050:
case 24000:
return 1;
case 32000:
return 2;
case 44100:
case 48000:
return 3;
case 64000:
case 88200:
case 96000:
return 4;
case 128000:
case 176400:
case 192000:
return 5;
default:
return 1;
}
}
static inline struct rk_pdm_dev *to_info(struct snd_soc_dai *dai)
{
return snd_soc_dai_get_drvdata(dai);
}
static void rockchip_pdm_rxctrl(struct rk_pdm_dev *pdm, int on)
{
if (on) {
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
PDM_DMA_RD_MSK, PDM_DMA_RD_EN);
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
PDM_RX_MASK, PDM_RX_START);
} else {
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL,
PDM_DMA_RD_MSK, PDM_DMA_RD_DIS);
regmap_update_bits(pdm->regmap, PDM_SYSCONFIG,
PDM_RX_MASK | PDM_RX_CLR_MASK,
PDM_RX_STOP | PDM_RX_CLR_WR);
}
}
static int rockchip_pdm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
unsigned int val = 0;
unsigned int clk_rate, clk_div, samplerate;
unsigned int clk_src, clk_out = 0;
unsigned long m, n;
bool change;
int ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return 0;
samplerate = params_rate(params);
clk_rate = get_pdm_clk(pdm, samplerate, &clk_src, &clk_out);
if (!clk_rate)
return -EINVAL;
ret = clk_set_rate(pdm->clk, clk_src);
if (ret)
return -EINVAL;
if (pdm->version == RK_PDM_RK3308 ||
pdm->version == RK_PDM_RV1126) {
rational_best_approximation(clk_out, clk_src,
GENMASK(16 - 1, 0),
GENMASK(16 - 1, 0),
&m, &n);
val = (m << PDM_FD_NUMERATOR_SFT) |
(n << PDM_FD_DENOMINATOR_SFT);
regmap_update_bits_check(pdm->regmap, PDM_CTRL1,
PDM_FD_NUMERATOR_MSK |
PDM_FD_DENOMINATOR_MSK,
val, &change);
if (change) {
reset_control_assert(pdm->reset);
reset_control_deassert(pdm->reset);
rockchip_pdm_rxctrl(pdm, 0);
}
clk_div = n / m;
if (clk_div >= 40)
val = PDM_CLK_FD_RATIO_40;
else if (clk_div <= 35)
val = PDM_CLK_FD_RATIO_35;
else
return -EINVAL;
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL,
PDM_CLK_FD_RATIO_MSK,
val);
}
if (pdm->version == RK_PDM_RV1126) {
val = get_pdm_cic_ratio(clk_out);
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CIC_RATIO_MSK, val);
val = samplerate_to_bit(samplerate);
regmap_update_bits(pdm->regmap, PDM_CTRL0,
PDM_SAMPLERATE_MSK, PDM_SAMPLERATE(val));
} else {
val = get_pdm_ds_ratio(samplerate);
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_DS_RATIO_MSK, val);
}
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
PDM_HPF_CF_MSK, PDM_HPF_60HZ);
regmap_update_bits(pdm->regmap, PDM_HPF_CTRL,
PDM_HPF_LE | PDM_HPF_RE, PDM_HPF_LE | PDM_HPF_RE);
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, PDM_CLK_EN, PDM_CLK_EN);
if (pdm->version != RK_PDM_RK3229)
regmap_update_bits(pdm->regmap, PDM_CTRL0,
PDM_MODE_MSK, PDM_MODE_LJ);
val = 0;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
val |= PDM_VDW(8);
break;
case SNDRV_PCM_FORMAT_S16_LE:
val |= PDM_VDW(16);
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= PDM_VDW(20);
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= PDM_VDW(24);
break;
case SNDRV_PCM_FORMAT_S32_LE:
val |= PDM_VDW(32);
break;
default:
return -EINVAL;
}
switch (params_channels(params)) {
case 8:
val |= PDM_PATH3_EN;
fallthrough;
case 6:
val |= PDM_PATH2_EN;
fallthrough;
case 4:
val |= PDM_PATH1_EN;
fallthrough;
case 2:
val |= PDM_PATH0_EN;
break;
default:
dev_err(pdm->dev, "invalid channel: %d\n",
params_channels(params));
return -EINVAL;
}
regmap_update_bits(pdm->regmap, PDM_CTRL0,
PDM_PATH_MSK | PDM_VDW_MSK,
val);
/* all channels share the single FIFO */
regmap_update_bits(pdm->regmap, PDM_DMA_CTRL, PDM_DMA_RDL_MSK,
PDM_DMA_RDL(8 * params_channels(params)));
return 0;
}
static int rockchip_pdm_set_fmt(struct snd_soc_dai *cpu_dai,
unsigned int fmt)
{
struct rk_pdm_dev *pdm = to_info(cpu_dai);
unsigned int mask = 0, val = 0;
int ret;
mask = PDM_CKP_MSK;
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
val = PDM_CKP_NORMAL;
break;
case SND_SOC_DAIFMT_IB_NF:
val = PDM_CKP_INVERTED;
break;
default:
return -EINVAL;
}
ret = pm_runtime_resume_and_get(cpu_dai->dev);
if (ret)
return ret;
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, mask, val);
pm_runtime_put(cpu_dai->dev);
return 0;
}
static int rockchip_pdm_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
rockchip_pdm_rxctrl(pdm, 1);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
rockchip_pdm_rxctrl(pdm, 0);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
{
struct rk_pdm_dev *pdm = to_info(dai);
snd_soc_dai_dma_data_set_capture(dai, &pdm->capture_dma_data);
return 0;
}
static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
.probe = rockchip_pdm_dai_probe,
.set_fmt = rockchip_pdm_set_fmt,
.trigger = rockchip_pdm_trigger,
.hw_params = rockchip_pdm_hw_params,
};
#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000
#define ROCKCHIP_PDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver rockchip_pdm_dai = {
.capture = {
.stream_name = "Capture",
.channels_min = 2,
.channels_max = 8,
.rates = ROCKCHIP_PDM_RATES,
.formats = ROCKCHIP_PDM_FORMATS,
},
.ops = &rockchip_pdm_dai_ops,
.symmetric_rate = 1,
};
static const struct snd_soc_component_driver rockchip_pdm_component = {
.name = "rockchip-pdm",
.legacy_dai_naming = 1,
};
static int rockchip_pdm_runtime_suspend(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
clk_disable_unprepare(pdm->clk);
clk_disable_unprepare(pdm->hclk);
return 0;
}
static int rockchip_pdm_runtime_resume(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(pdm->hclk);
if (ret) {
dev_err(pdm->dev, "hclock enable failed %d\n", ret);
return ret;
}
ret = clk_prepare_enable(pdm->clk);
if (ret) {
clk_disable_unprepare(pdm->hclk);
dev_err(pdm->dev, "clock enable failed %d\n", ret);
return ret;
}
return 0;
}
static bool rockchip_pdm_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_SYSCONFIG:
case PDM_CTRL0:
case PDM_CTRL1:
case PDM_CLK_CTRL:
case PDM_HPF_CTRL:
case PDM_FIFO_CTRL:
case PDM_DMA_CTRL:
case PDM_INT_EN:
case PDM_INT_CLR:
case PDM_DATA_VALID:
return true;
default:
return false;
}
}
static bool rockchip_pdm_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_SYSCONFIG:
case PDM_CTRL0:
case PDM_CTRL1:
case PDM_CLK_CTRL:
case PDM_HPF_CTRL:
case PDM_FIFO_CTRL:
case PDM_DMA_CTRL:
case PDM_INT_EN:
case PDM_INT_CLR:
case PDM_INT_ST:
case PDM_DATA_VALID:
case PDM_RXFIFO_DATA:
case PDM_VERSION:
return true;
default:
return false;
}
}
static bool rockchip_pdm_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_SYSCONFIG:
case PDM_FIFO_CTRL:
case PDM_INT_CLR:
case PDM_INT_ST:
case PDM_RXFIFO_DATA:
return true;
default:
return false;
}
}
static bool rockchip_pdm_precious_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case PDM_RXFIFO_DATA:
return true;
default:
return false;
}
}
static const struct reg_default rockchip_pdm_reg_defaults[] = {
{ PDM_CTRL0, 0x78000017 },
{ PDM_CTRL1, 0x0bb8ea60 },
{ PDM_CLK_CTRL, 0x0000e401 },
{ PDM_DMA_CTRL, 0x0000001f },
};
static const struct regmap_config rockchip_pdm_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = PDM_VERSION,
.reg_defaults = rockchip_pdm_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(rockchip_pdm_reg_defaults),
.writeable_reg = rockchip_pdm_wr_reg,
.readable_reg = rockchip_pdm_rd_reg,
.volatile_reg = rockchip_pdm_volatile_reg,
.precious_reg = rockchip_pdm_precious_reg,
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id rockchip_pdm_match[] __maybe_unused = {
{ .compatible = "rockchip,pdm",
.data = (void *)RK_PDM_RK3229 },
{ .compatible = "rockchip,px30-pdm",
.data = (void *)RK_PDM_RK3308 },
{ .compatible = "rockchip,rk1808-pdm",
.data = (void *)RK_PDM_RK3308 },
{ .compatible = "rockchip,rk3308-pdm",
.data = (void *)RK_PDM_RK3308 },
{ .compatible = "rockchip,rk3568-pdm",
.data = (void *)RK_PDM_RV1126 },
{ .compatible = "rockchip,rv1126-pdm",
.data = (void *)RK_PDM_RV1126 },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_pdm_match);
static int rockchip_pdm_path_parse(struct rk_pdm_dev *pdm, struct device_node *node)
{
unsigned int path[PDM_PATH_MAX];
int cnt = 0, ret = 0, i = 0, val = 0, msk = 0;
cnt = of_count_phandle_with_args(node, "rockchip,path-map",
NULL);
if (cnt != PDM_PATH_MAX)
return cnt;
ret = of_property_read_u32_array(node, "rockchip,path-map",
path, cnt);
if (ret)
return ret;
for (i = 0; i < cnt; i++) {
if (path[i] >= PDM_PATH_MAX)
return -EINVAL;
msk |= PDM_PATH_MASK(i);
val |= PDM_PATH(i, path[i]);
}
regmap_update_bits(pdm->regmap, PDM_CLK_CTRL, msk, val);
return 0;
}
static int rockchip_pdm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct rk_pdm_dev *pdm;
struct resource *res;
void __iomem *regs;
int ret;
pdm = devm_kzalloc(&pdev->dev, sizeof(*pdm), GFP_KERNEL);
if (!pdm)
return -ENOMEM;
pdm->version = (unsigned long)device_get_match_data(&pdev->dev);
if (pdm->version == RK_PDM_RK3308) {
pdm->reset = devm_reset_control_get(&pdev->dev, "pdm-m");
if (IS_ERR(pdm->reset))
return PTR_ERR(pdm->reset);
}
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
pdm->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
&rockchip_pdm_regmap_config);
if (IS_ERR(pdm->regmap))
return PTR_ERR(pdm->regmap);
pdm->capture_dma_data.addr = res->start + PDM_RXFIFO_DATA;
pdm->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
pdm->capture_dma_data.maxburst = PDM_DMA_BURST_SIZE;
pdm->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, pdm);
pdm->clk = devm_clk_get(&pdev->dev, "pdm_clk");
if (IS_ERR(pdm->clk))
return PTR_ERR(pdm->clk);
pdm->hclk = devm_clk_get(&pdev->dev, "pdm_hclk");
if (IS_ERR(pdm->hclk))
return PTR_ERR(pdm->hclk);
ret = clk_prepare_enable(pdm->hclk);
if (ret)
return ret;
pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rockchip_pdm_runtime_resume(&pdev->dev);
if (ret)
goto err_pm_disable;
}
ret = devm_snd_soc_register_component(&pdev->dev,
&rockchip_pdm_component,
&rockchip_pdm_dai, 1);
if (ret) {
dev_err(&pdev->dev, "could not register dai: %d\n", ret);
goto err_suspend;
}
rockchip_pdm_rxctrl(pdm, 0);
ret = rockchip_pdm_path_parse(pdm, node);
if (ret != 0 && ret != -ENOENT)
goto err_suspend;
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret) {
dev_err(&pdev->dev, "could not register pcm: %d\n", ret);
goto err_suspend;
}
return 0;
err_suspend:
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_pdm_runtime_suspend(&pdev->dev);
err_pm_disable:
pm_runtime_disable(&pdev->dev);
clk_disable_unprepare(pdm->hclk);
return ret;
}
static void rockchip_pdm_remove(struct platform_device *pdev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(&pdev->dev);
pm_runtime_disable(&pdev->dev);
if (!pm_runtime_status_suspended(&pdev->dev))
rockchip_pdm_runtime_suspend(&pdev->dev);
clk_disable_unprepare(pdm->clk);
clk_disable_unprepare(pdm->hclk);
}
static int rockchip_pdm_suspend(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
regcache_mark_dirty(pdm->regmap);
return 0;
}
static int rockchip_pdm_resume(struct device *dev)
{
struct rk_pdm_dev *pdm = dev_get_drvdata(dev);
int ret;
ret = pm_runtime_resume_and_get(dev);
if (ret < 0)
return ret;
ret = regcache_sync(pdm->regmap);
pm_runtime_put(dev);
return ret;
}
static const struct dev_pm_ops rockchip_pdm_pm_ops = {
RUNTIME_PM_OPS(rockchip_pdm_runtime_suspend,
rockchip_pdm_runtime_resume, NULL)
SYSTEM_SLEEP_PM_OPS(rockchip_pdm_suspend, rockchip_pdm_resume)
};
static struct platform_driver rockchip_pdm_driver = {
.probe = rockchip_pdm_probe,
.remove = rockchip_pdm_remove,
.driver = {
.name = "rockchip-pdm",
.of_match_table = of_match_ptr(rockchip_pdm_match),
.pm = pm_ptr(&rockchip_pdm_pm_ops),
},
};
module_platform_driver(rockchip_pdm_driver);
MODULE_AUTHOR("Sugar <sugar.zhang@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip PDM Controller Driver");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,92 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Rockchip PDM ALSA SoC Digital Audio Interface(DAI) driver
*
* Copyright (C) 2017 Fuzhou Rockchip Electronics Co., Ltd
*/
#ifndef _ROCKCHIP_PDM_H
#define _ROCKCHIP_PDM_H
/* PDM REGS */
#define PDM_SYSCONFIG (0x0000)
#define PDM_CTRL0 (0x0004)
#define PDM_CTRL1 (0x0008)
#define PDM_CLK_CTRL (0x000c)
#define PDM_HPF_CTRL (0x0010)
#define PDM_FIFO_CTRL (0x0014)
#define PDM_DMA_CTRL (0x0018)
#define PDM_INT_EN (0x001c)
#define PDM_INT_CLR (0x0020)
#define PDM_INT_ST (0x0024)
#define PDM_RXFIFO_DATA (0x0030)
#define PDM_DATA_VALID (0x0054)
#define PDM_VERSION (0x0058)
/* PDM_SYSCONFIG */
#define PDM_RX_MASK (0x1 << 2)
#define PDM_RX_START (0x1 << 2)
#define PDM_RX_STOP (0x0 << 2)
#define PDM_RX_CLR_MASK (0x1 << 0)
#define PDM_RX_CLR_WR (0x1 << 0)
#define PDM_RX_CLR_DONE (0x0 << 0)
/* PDM CTRL0 */
#define PDM_PATH_MSK (0xf << 27)
#define PDM_MODE_MSK BIT(31)
#define PDM_MODE_RJ 0
#define PDM_MODE_LJ BIT(31)
#define PDM_PATH3_EN BIT(30)
#define PDM_PATH2_EN BIT(29)
#define PDM_PATH1_EN BIT(28)
#define PDM_PATH0_EN BIT(27)
#define PDM_HWT_EN BIT(26)
#define PDM_SAMPLERATE_MSK GENMASK(7, 5)
#define PDM_SAMPLERATE(x) ((x) << 5)
#define PDM_VDW_MSK (0x1f << 0)
#define PDM_VDW(X) ((X - 1) << 0)
/* PDM CTRL1 */
#define PDM_FD_NUMERATOR_SFT 16
#define PDM_FD_NUMERATOR_MSK GENMASK(31, 16)
#define PDM_FD_DENOMINATOR_SFT 0
#define PDM_FD_DENOMINATOR_MSK GENMASK(15, 0)
/* PDM CLK CTRL */
#define PDM_PATH_SHIFT(x) (8 + (x) * 2)
#define PDM_PATH_MASK(x) (0x3 << PDM_PATH_SHIFT(x))
#define PDM_PATH(x, v) ((v) << PDM_PATH_SHIFT(x))
#define PDM_CLK_FD_RATIO_MSK BIT(6)
#define PDM_CLK_FD_RATIO_40 (0X0 << 6)
#define PDM_CLK_FD_RATIO_35 BIT(6)
#define PDM_CLK_MSK BIT(5)
#define PDM_CLK_EN BIT(5)
#define PDM_CLK_DIS (0x0 << 5)
#define PDM_CKP_MSK BIT(3)
#define PDM_CKP_NORMAL (0x0 << 3)
#define PDM_CKP_INVERTED BIT(3)
#define PDM_DS_RATIO_MSK (0x7 << 0)
#define PDM_CLK_320FS (0x0 << 0)
#define PDM_CLK_640FS (0x1 << 0)
#define PDM_CLK_1280FS (0x2 << 0)
#define PDM_CLK_2560FS (0x3 << 0)
#define PDM_CLK_5120FS (0x4 << 0)
#define PDM_CIC_RATIO_MSK (0x3 << 0)
/* PDM HPF CTRL */
#define PDM_HPF_LE BIT(3)
#define PDM_HPF_RE BIT(2)
#define PDM_HPF_CF_MSK (0x3 << 0)
#define PDM_HPF_3P79HZ (0x0 << 0)
#define PDM_HPF_60HZ (0x1 << 0)
#define PDM_HPF_243HZ (0x2 << 0)
#define PDM_HPF_493HZ (0x3 << 0)
/* PDM DMA CTRL */
#define PDM_DMA_RD_MSK BIT(8)
#define PDM_DMA_RD_EN BIT(8)
#define PDM_DMA_RD_DIS (0x0 << 8)
#define PDM_DMA_RDL_MSK (0x7f << 0)
#define PDM_DMA_RDL(X) ((X - 1) << 0)
#endif /* _ROCKCHIP_PDM_H */
@@ -0,0 +1,249 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Rockchip machine ASoC driver for boards using a RT5645/RT5650 CODEC.
*
* Copyright (c) 2015, ROCKCHIP CORPORATION. All rights reserved.
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "rockchip_i2s.h"
#include "../codecs/rt5645.h"
#define DRV_NAME "rockchip-snd-rt5645"
static struct snd_soc_jack headset_jack;
static struct snd_soc_jack_pin headset_jack_pins[] = {
{
.pin = "Headphones",
.mask = SND_JACK_HEADPHONE,
},
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
};
static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speakers", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
};
static const struct snd_soc_dapm_route rk_audio_map[] = {
/* Input Lines */
{"DMIC L2", NULL, "Int Mic"},
{"DMIC R2", NULL, "Int Mic"},
{"RECMIXL", NULL, "Headset Mic"},
{"RECMIXR", NULL, "Headset Mic"},
/* Output Lines */
{"Headphones", NULL, "HPOR"},
{"Headphones", NULL, "HPOL"},
{"Speakers", NULL, "SPOL"},
{"Speakers", NULL, "SPOR"},
};
static const struct snd_kcontrol_new rk_mc_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphones"),
SOC_DAPM_PIN_SWITCH("Speakers"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
SOC_DAPM_PIN_SWITCH("Int Mic"),
};
static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
int ret = 0;
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
int mclk;
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret < 0) {
dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
return ret;
}
ret = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(codec_dai->dev, "Can't set codec clock %d\n", ret);
return ret;
}
return ret;
}
static int rk_init(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_card *card = runtime->card;
int ret;
/* Enable Headset and 4 Buttons Jack detection */
ret = snd_soc_card_jack_new_pins(card, "Headset Jack",
SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
SND_JACK_BTN_0 | SND_JACK_BTN_1 |
SND_JACK_BTN_2 | SND_JACK_BTN_3,
&headset_jack,
headset_jack_pins,
ARRAY_SIZE(headset_jack_pins));
if (ret) {
dev_err(card->dev, "New Headset Jack failed! (%d)\n", ret);
return ret;
}
return rt5645_set_jack_detect(snd_soc_rtd_to_codec(runtime, 0)->component,
&headset_jack,
&headset_jack,
&headset_jack);
}
static const struct snd_soc_ops rk_aif1_ops = {
.hw_params = rk_aif1_hw_params,
};
SND_SOC_DAILINK_DEFS(pcm,
DAILINK_COMP_ARRAY(COMP_EMPTY()),
DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "rt5645-aif1")),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
static struct snd_soc_dai_link rk_dailink = {
.name = "rt5645",
.stream_name = "rt5645 PCM",
.init = rk_init,
.ops = &rk_aif1_ops,
/* set rt5645 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBC_CFC,
SND_SOC_DAILINK_REG(pcm),
};
static struct snd_soc_card snd_soc_card_rk = {
.name = "I2S-RT5650",
.owner = THIS_MODULE,
.dai_link = &rk_dailink,
.num_links = 1,
.dapm_widgets = rk_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rk_dapm_widgets),
.dapm_routes = rk_audio_map,
.num_dapm_routes = ARRAY_SIZE(rk_audio_map),
.controls = rk_mc_controls,
.num_controls = ARRAY_SIZE(rk_mc_controls),
};
static int snd_rk_mc_probe(struct platform_device *pdev)
{
int ret = 0;
struct snd_soc_card *card = &snd_soc_card_rk;
struct device_node *np = pdev->dev.of_node;
/* register the soc card */
card->dev = &pdev->dev;
rk_dailink.codecs->of_node = of_parse_phandle(np,
"rockchip,audio-codec", 0);
if (!rk_dailink.codecs->of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,audio-codec' missing or invalid\n");
return -EINVAL;
}
rk_dailink.cpus->of_node = of_parse_phandle(np,
"rockchip,i2s-controller", 0);
if (!rk_dailink.cpus->of_node) {
dev_err(&pdev->dev,
"Property 'rockchip,i2s-controller' missing or invalid\n");
ret = -EINVAL;
goto put_codec_of_node;
}
rk_dailink.platforms->of_node = rk_dailink.cpus->of_node;
ret = snd_soc_of_parse_card_name(card, "rockchip,model");
if (ret) {
dev_err(&pdev->dev,
"Soc parse card name failed %d\n", ret);
goto put_cpu_of_node;
}
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
dev_err(&pdev->dev,
"Soc register card failed %d\n", ret);
goto put_cpu_of_node;
}
return ret;
put_cpu_of_node:
of_node_put(rk_dailink.cpus->of_node);
rk_dailink.cpus->of_node = NULL;
put_codec_of_node:
of_node_put(rk_dailink.codecs->of_node);
rk_dailink.codecs->of_node = NULL;
return ret;
}
static void snd_rk_mc_remove(struct platform_device *pdev)
{
of_node_put(rk_dailink.cpus->of_node);
rk_dailink.cpus->of_node = NULL;
of_node_put(rk_dailink.codecs->of_node);
rk_dailink.codecs->of_node = NULL;
}
static const struct of_device_id rockchip_rt5645_of_match[] = {
{ .compatible = "rockchip,rockchip-audio-rt5645", },
{},
};
MODULE_DEVICE_TABLE(of, rockchip_rt5645_of_match);
static struct platform_driver snd_rk_mc_driver = {
.probe = snd_rk_mc_probe,
.remove = snd_rk_mc_remove,
.driver = {
.name = DRV_NAME,
.pm = &snd_soc_pm_ops,
.of_match_table = rockchip_rt5645_of_match,
},
};
module_platform_driver(snd_rk_mc_driver);
MODULE_AUTHOR("Xing Zheng <zhengxing@rock-chips.com>");
MODULE_DESCRIPTION("Rockchip rt5645 machine ASoC driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,251 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* ALSA SoC Audio Layer - Rockchip SAI Controller driver
*
* Copyright (c) 2022 Rockchip Electronics Co. Ltd.
*/
#ifndef _ROCKCHIP_SAI_H
#define _ROCKCHIP_SAI_H
/* XCR Transmit / Receive Control Register */
#define SAI_XCR_START_SEL_MASK BIT(23)
#define SAI_XCR_START_SEL_CHAINED BIT(23)
#define SAI_XCR_START_SEL_STANDALONE 0
#define SAI_XCR_EDGE_SHIFT_MASK BIT(22)
#define SAI_XCR_EDGE_SHIFT_1 BIT(22)
#define SAI_XCR_EDGE_SHIFT_0 0
#define SAI_XCR_CSR_MASK GENMASK(21, 20)
#define SAI_XCR_CSR(x) ((x - 1) << 20)
#define SAI_XCR_CSR_V(v) ((((v) & SAI_XCR_CSR_MASK) >> 20) + 1)
#define SAI_XCR_SJM_MASK BIT(19)
#define SAI_XCR_SJM_L BIT(19)
#define SAI_XCR_SJM_R 0
#define SAI_XCR_FBM_MASK BIT(18)
#define SAI_XCR_FBM_LSB BIT(18)
#define SAI_XCR_FBM_MSB 0
#define SAI_XCR_SNB_MASK GENMASK(17, 11)
#define SAI_XCR_SNB(x) ((x - 1) << 11)
#define SAI_XCR_VDJ_MASK BIT(10)
#define SAI_XCR_VDJ_L BIT(10)
#define SAI_XCR_VDJ_R 0
#define SAI_XCR_SBW_MASK GENMASK(9, 5)
#define SAI_XCR_SBW(x) ((x - 1) << 5)
#define SAI_XCR_SBW_V(v) ((((v) & SAI_XCR_SBW_MASK) >> 5) + 1)
#define SAI_XCR_VDW_MASK GENMASK(4, 0)
#define SAI_XCR_VDW(x) ((x - 1) << 0)
/* FSCR Frame Sync Control Register */
#define SAI_FSCR_EDGE_MASK BIT(24)
#define SAI_FSCR_EDGE_DUAL BIT(24)
#define SAI_FSCR_EDGE_RISING 0
#define SAI_FSCR_FPW_MASK GENMASK(23, 12)
#define SAI_FSCR_FPW(x) ((x - 1) << 12)
#define SAI_FSCR_FW_MASK GENMASK(11, 0)
#define SAI_FSCR_FW(x) ((x - 1) << 0)
#define SAI_FSCR_FW_V(v) ((((v) & SAI_FSCR_FW_MASK) >> 0) + 1)
/* MONO_CR Mono Control Register */
#define SAI_MCR_RX_MONO_SLOT_MASK GENMASK(8, 2)
#define SAI_MCR_RX_MONO_SLOT_SEL(x) ((x - 1) << 2)
#define SAI_MCR_RX_MONO_MASK BIT(1)
#define SAI_MCR_RX_MONO_EN BIT(1)
#define SAI_MCR_RX_MONO_DIS 0
#define SAI_MCR_TX_MONO_MASK BIT(0)
#define SAI_MCR_TX_MONO_EN BIT(0)
#define SAI_MCR_TX_MONO_DIS 0
/* XFER Transfer Start Register */
#define SAI_XFER_RX_IDLE BIT(8)
#define SAI_XFER_TX_IDLE BIT(7)
#define SAI_XFER_FS_IDLE BIT(6)
/*
* Used for TX only (VERSION >= SAI_VER_2311)
*
* SCLK/FSYNC auto gated when TX FIFO empty.
*/
#define SAI_XFER_TX_AUTO_MASK BIT(6)
#define SAI_XFER_TX_AUTO_EN BIT(6)
#define SAI_XFER_TX_AUTO_DIS 0
#define SAI_XFER_RX_CNT_MASK BIT(5)
#define SAI_XFER_RX_CNT_EN BIT(5)
#define SAI_XFER_RX_CNT_DIS 0
#define SAI_XFER_TX_CNT_MASK BIT(4)
#define SAI_XFER_TX_CNT_EN BIT(4)
#define SAI_XFER_TX_CNT_DIS 0
#define SAI_XFER_RXS_MASK BIT(3)
#define SAI_XFER_RXS_EN BIT(3)
#define SAI_XFER_RXS_DIS 0
#define SAI_XFER_TXS_MASK BIT(2)
#define SAI_XFER_TXS_EN BIT(2)
#define SAI_XFER_TXS_DIS 0
#define SAI_XFER_FSS_MASK BIT(1)
#define SAI_XFER_FSS_EN BIT(1)
#define SAI_XFER_FSS_DIS 0
#define SAI_XFER_CLK_MASK BIT(0)
#define SAI_XFER_CLK_EN BIT(0)
#define SAI_XFER_CLK_DIS 0
/* CLR Clear Logic Register */
#define SAI_CLR_FCR BIT(3) /* TODO: what is this? */
#define SAI_CLR_FSC BIT(2)
#define SAI_CLR_RXC BIT(1)
#define SAI_CLR_TXC BIT(0)
/* CKR Clock Generation Register */
#define SAI_CKR_MDIV_MASK GENMASK(14, 3)
#define SAI_CKR_MDIV(x) ((x - 1) << 3)
#define SAI_CKR_MSS_MASK BIT(2)
#define SAI_CKR_MSS_SLAVE BIT(2)
#define SAI_CKR_MSS_MASTER 0
#define SAI_CKR_CKP_MASK BIT(1)
#define SAI_CKR_CKP_INVERTED BIT(1)
#define SAI_CKR_CKP_NORMAL 0
#define SAI_CKR_FSP_MASK BIT(0)
#define SAI_CKR_FSP_INVERTED BIT(0)
#define SAI_CKR_FSP_NORMAL 0
/* DMACR DMA Control Register */
#define SAI_DMACR_RDE_MASK BIT(24)
#define SAI_DMACR_RDE(x) ((x) << 24)
#define SAI_DMACR_RDL_MASK GENMASK(20, 16)
#define SAI_DMACR_RDL(x) ((x - 1) << 16)
#define SAI_DMACR_RDL_V(v) ((((v) & SAI_DMACR_RDL_MASK) >> 16) + 1)
#define SAI_DMACR_TDE_MASK BIT(8)
#define SAI_DMACR_TDE(x) ((x) << 8)
#define SAI_DMACR_TDL_MASK GENMASK(4, 0)
#define SAI_DMACR_TDL(x) ((x) << 0)
#define SAI_DMACR_TDL_V(v) (((v) & SAI_DMACR_TDL_MASK) >> 0)
/* INTCR Interrupt Ctrl Register */
#define SAI_INTCR_FSLOSTC BIT(28)
#define SAI_INTCR_FSLOST_MASK BIT(27)
#define SAI_INTCR_FSLOST(x) ((x) << 27)
#define SAI_INTCR_FSERRC BIT(26)
#define SAI_INTCR_FSERR_MASK BIT(25)
#define SAI_INTCR_FSERR(x) ((x) << 25)
#define SAI_INTCR_RXOIC BIT(18)
#define SAI_INTCR_RXOIE_MASK BIT(17)
#define SAI_INTCR_RXOIE(x) ((x) << 17)
#define SAI_INTCR_TXUIC BIT(2)
#define SAI_INTCR_TXUIE_MASK BIT(1)
#define SAI_INTCR_TXUIE(x) ((x) << 1)
/* INTSR Interrupt Status Register */
#define SAI_INTSR_FSLOSTI_INA 0
#define SAI_INTSR_FSLOSTI_ACT BIT(19)
#define SAI_INTSR_FSERRI_INA 0
#define SAI_INTSR_FSERRI_ACT BIT(18)
#define SAI_INTSR_RXOI_INA 0
#define SAI_INTSR_RXOI_ACT BIT(17)
#define SAI_INTSR_TXUI_INA 0
#define SAI_INTSR_TXUI_ACT BIT(1)
/* PATH_SEL: Transfer / Receive Path Select Register */
#define SAI_RX_PATH_SHIFT(x) (8 + (x) * 2)
#define SAI_RX_PATH_MASK(x) (0x3 << SAI_RX_PATH_SHIFT(x))
#define SAI_RX_PATH(x, v) ((v) << SAI_RX_PATH_SHIFT(x))
#define SAI_TX_PATH_SHIFT(x) (0 + (x) * 2)
#define SAI_TX_PATH_MASK(x) (0x3 << SAI_TX_PATH_SHIFT(x))
#define SAI_TX_PATH(x, v) ((v) << SAI_TX_PATH_SHIFT(x))
/* XSHIFT: Transfer / Receive Frame Sync Shift Register */
/*
* TX-ONLY: LEFT Direction Feature
* +------------------------------------------------+
* | DATA LEFTx (step: 0.5 cycle) | FSYNC Edge |
* +------------------------------------------------+
*/
#define SAI_XSHIFT_LEFT_MASK GENMASK(25, 24)
#define SAI_XSHIFT_LEFT(x) ((x) << 24)
/*
* +------------------------------------------------+
* | FSYNC Edge | DATA RIGHTx (step: 0.5 cycle) |
* +------------------------------------------------+
*/
#define SAI_XSHIFT_RIGHT_MASK GENMASK(23, 0)
#define SAI_XSHIFT_RIGHT(x) (x)
/* XFIFOLR: Transfer / Receive FIFO Level Register */
#define SAI_FIFOLR_XFL3_SHIFT 18
#define SAI_FIFOLR_XFL3_MASK GENMASK(23, 18)
#define SAI_FIFOLR_XFL2_SHIFT 12
#define SAI_FIFOLR_XFL2_MASK GENMASK(17, 12)
#define SAI_FIFOLR_XFL1_SHIFT 6
#define SAI_FIFOLR_XFL1_MASK GENMASK(11, 6)
#define SAI_FIFOLR_XFL0_SHIFT 0
#define SAI_FIFOLR_XFL0_MASK GENMASK(5, 0)
/* STATUS Status Register (VERSION >= SAI_VER_2307) */
#define SAI_STATUS_RX_IDLE BIT(3)
#define SAI_STATUS_TX_IDLE BIT(2)
#define SAI_STATUS_FS_IDLE BIT(1)
/* VERSION */
/*
* Updates:
*
* VERSION >= SAI_VER_2311
*
* Support Frame Sync xN (FSXN)
* Support Frame Sync Error Detect (FSE)
* Support Frame Sync Lost Detect (FSLOST)
* Support Force Clear (FCR)
* Support SAIn-Chained (e.g. SAI0-CLK-DATA + SAI3-DATA +...)
* Support Transmit Auto Gate Mode
* Support Timing Shift Left for TX
*
* Optimize SCLK/FSYNC Timing Alignment
*
* VERSION >= SAI_VER_2403
*
* Support Loopback LR Select (e.g. L:MIC R:LP)
*
*/
#define SAI_VER_2307 0x23073576
#define SAI_VER_2311 0x23112118
#define SAI_VER_2401 0x24013506
#define SAI_VER_2403 0x24031103
/* FS_TIMEOUT: Frame Sync Timeout Register */
#define SAI_FS_TIMEOUT_VAL_MASK GENMASK(31, 1)
#define SAI_FS_TIMEOUT_VAL(x) ((x) << 1)
#define SAI_FS_TIMEOUT_EN_MASK BIT(0)
#define SAI_FS_TIMEOUT_EN(x) ((x) << 0)
/* SAI Registers */
#define SAI_TXCR (0x0000)
#define SAI_FSCR (0x0004)
#define SAI_RXCR (0x0008)
#define SAI_MONO_CR (0x000c)
#define SAI_XFER (0x0010)
#define SAI_CLR (0x0014)
#define SAI_CKR (0x0018)
#define SAI_TXFIFOLR (0x001c)
#define SAI_RXFIFOLR (0x0020)
#define SAI_DMACR (0x0024)
#define SAI_INTCR (0x0028)
#define SAI_INTSR (0x002c)
#define SAI_TXDR (0x0030)
#define SAI_RXDR (0x0034)
#define SAI_PATH_SEL (0x0038)
#define SAI_TX_SLOT_MASK0 (0x003c)
#define SAI_TX_SLOT_MASK1 (0x0040)
#define SAI_TX_SLOT_MASK2 (0x0044)
#define SAI_TX_SLOT_MASK3 (0x0048)
#define SAI_RX_SLOT_MASK0 (0x004c)
#define SAI_RX_SLOT_MASK1 (0x0050)
#define SAI_RX_SLOT_MASK2 (0x0054)
#define SAI_RX_SLOT_MASK3 (0x0058)
#define SAI_TX_DATA_CNT (0x005c)
#define SAI_RX_DATA_CNT (0x0060)
#define SAI_TX_SHIFT (0x0064)
#define SAI_RX_SHIFT (0x0068)
#define SAI_STATUS (0x006c)
#define SAI_VERSION (0x0070)
#define SAI_FSXN (0x0074)
#define SAI_FS_TIMEOUT (0x0078)
#define SAI_LOOPBACK_LR (0x007c)
#endif /* _ROCKCHIP_SAI_H */
@@ -0,0 +1,450 @@
// SPDX-License-Identifier: GPL-2.0-only
/* sound/soc/rockchip/rk_spdif.c
*
* ALSA SoC Audio Layer - Rockchip I2S Controller driver
*
* Copyright (c) 2014 Rockchip Electronics Co. Ltd.
* Author: Jianqun <jay.xu@rock-chips.com>
* Copyright (c) 2015-2026 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
*/
#include <linux/bitfield.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/mfd/syscon.h>
#include <linux/regmap.h>
#include <sound/pcm_params.h>
#include <sound/pcm_iec958.h>
#include <sound/dmaengine_pcm.h>
#include "rockchip_spdif.h"
enum rk_spdif_type {
RK_SPDIF_RK3066,
RK_SPDIF_RK3188,
RK_SPDIF_RK3288,
RK_SPDIF_RK3366,
};
/*
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* CS0: | Mode | d | c | b | a |
* CS1: | Category Code |
* CS2: | Channel Number | Source Number |
* CS3: | Clock Accuracy | Sample Freq |
* CS4: | Ori Sample Freq | Word Length |
* CS5: | | CGMS-A |
* CS6~CS23: Reserved
*
* a: use of channel status block
* b: linear PCM identification: 0 for lpcm, 1 for nlpcm
* c: copyright information
* d: additional format information
*/
#define CS_BYTE 6
#define CS_FRAME(c) ((c) << 16 | (c))
#define RK3288_GRF_SOC_CON2 0x24c
struct rk_spdif_dev {
struct device *dev;
struct clk *mclk;
struct clk *hclk;
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct regmap *regmap;
};
static int rk_spdif_runtime_suspend(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
regcache_cache_only(spdif->regmap, true);
clk_disable_unprepare(spdif->mclk);
clk_disable_unprepare(spdif->hclk);
return 0;
}
static int rk_spdif_runtime_resume(struct device *dev)
{
struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(spdif->hclk);
if (ret) {
dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
return ret;
}
ret = clk_prepare_enable(spdif->mclk);
if (ret) {
clk_disable_unprepare(spdif->hclk);
dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
return ret;
}
regcache_cache_only(spdif->regmap, false);
regcache_mark_dirty(spdif->regmap);
ret = regcache_sync(spdif->regmap);
if (ret) {
regcache_cache_only(spdif->regmap, true);
clk_disable_unprepare(spdif->mclk);
clk_disable_unprepare(spdif->hclk);
}
return ret;
}
static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
unsigned int mclk_rate = clk_get_rate(spdif->mclk);
unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
int bmc, div, ret, i;
u16 *fc;
u8 cs[CS_BYTE];
ret = snd_pcm_create_iec958_consumer_hw_params(params, cs, sizeof(cs));
if (ret < 0)
return ret;
fc = (u16 *)cs;
for (i = 0; i < CS_BYTE / 2; i++)
regmap_write(spdif->regmap, SPDIF_CHNSRn(i), CS_FRAME(fc[i]));
regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CSE_MASK,
SPDIF_CFGR_CSE_EN);
/* bmc = 128fs */
bmc = 128 * params_rate(params);
div = DIV_ROUND_CLOSEST(mclk_rate, bmc);
val |= SPDIF_CFGR_CLK_DIV(div);
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
val |= SPDIF_CFGR_VDW_16;
val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S20_3LE:
val |= SPDIF_CFGR_VDW_20;
val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S24_LE:
val |= SPDIF_CFGR_VDW_24;
val |= SPDIF_CFGR_ADJ_RIGHT_J;
break;
case SNDRV_PCM_FORMAT_S32_LE:
val |= SPDIF_CFGR_VDW_24;
val |= SPDIF_CFGR_ADJ_LEFT_J;
break;
default:
return -EINVAL;
}
/*
* clear MCLK domain logic before setting Fmclk and Fsdo to ensure
* that switching between S16_LE and S32_LE audio does not result
* in accidential channels swap.
*/
regmap_update_bits(spdif->regmap, SPDIF_CFGR, SPDIF_CFGR_CLR_MASK,
SPDIF_CFGR_CLR_EN);
udelay(1);
ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
SPDIF_CFGR_CLK_DIV_MASK |
SPDIF_CFGR_HALFWORD_MASK |
SDPIF_CFGR_VDW_MASK |
SPDIF_CFGR_ADJ_MASK, val);
return ret;
}
static int rk_spdif_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
int ret;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
SPDIF_DMACR_TDE_MASK |
SPDIF_DMACR_TDL_MASK,
SPDIF_DMACR_TDE_ENABLE |
SPDIF_DMACR_TDL(16));
if (ret != 0)
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
SPDIF_XFER_TXS_MASK,
SPDIF_XFER_TXS_START);
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
SPDIF_DMACR_TDE_MASK,
SPDIF_DMACR_TDE_DISABLE);
if (ret != 0)
return ret;
ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
SPDIF_XFER_TXS_MASK,
SPDIF_XFER_TXS_STOP);
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
snd_soc_dai_dma_data_set_playback(dai, &spdif->playback_dma_data);
return 0;
}
static int rk_spdif_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
int ret;
if (!freq)
return 0;
ret = clk_set_rate(spdif->mclk, freq);
if (ret)
dev_err(spdif->dev, "Failed to set mclk: %d\n", ret);
return ret;
}
static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
.set_sysclk = rk_spdif_set_sysclk,
.probe = rk_spdif_dai_probe,
.hw_params = rk_spdif_hw_params,
.trigger = rk_spdif_trigger,
};
static struct snd_soc_dai_driver rk_spdif_dai = {
.playback = {
.stream_name = "Playback",
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE),
},
.ops = &rk_spdif_dai_ops,
};
static const struct snd_soc_component_driver rk_spdif_component = {
.name = "rockchip-spdif",
.legacy_dai_naming = 1,
};
static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIF_CFGR:
case SPDIF_DMACR:
case SPDIF_INTCR:
case SPDIF_XFER:
case SPDIF_SMPDR:
case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11):
case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11):
case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11):
return true;
default:
return false;
}
}
static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIF_CFGR:
case SPDIF_SDBLR:
case SPDIF_INTCR:
case SPDIF_INTSR:
case SPDIF_XFER:
case SPDIF_SMPDR:
case SPDIF_VLDFRn(0) ... SPDIF_VLDFRn(11):
case SPDIF_USRDRn(0) ... SPDIF_USRDRn(11):
case SPDIF_CHNSRn(0) ... SPDIF_CHNSRn(11):
return true;
default:
return false;
}
}
static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case SPDIF_INTSR:
case SPDIF_SDBLR:
case SPDIF_SMPDR:
return true;
default:
return false;
}
}
static const struct regmap_config rk_spdif_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.max_register = SPDIF_VERSION,
.writeable_reg = rk_spdif_wr_reg,
.readable_reg = rk_spdif_rd_reg,
.volatile_reg = rk_spdif_volatile_reg,
.cache_type = REGCACHE_FLAT,
};
static void rk_spdif_suspend(void *data)
{
struct device *dev = data;
if (!pm_runtime_status_suspended(dev))
rk_spdif_runtime_suspend(dev);
}
static int rk_spdif_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
enum rk_spdif_type spdif_type;
struct rk_spdif_dev *spdif;
struct resource *res;
void __iomem *regs;
int ret;
spdif_type = (uintptr_t) device_get_match_data(&pdev->dev);
if (spdif_type == RK_SPDIF_RK3288) {
struct regmap *grf;
grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
if (IS_ERR(grf))
return dev_err_probe(&pdev->dev, PTR_ERR(grf),
"rockchip_spdif missing 'rockchip,grf'\n");
/* Select the 8 channel SPDIF solution on RK3288 as
* the 2 channel one does not appear to work
*/
regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16);
}
spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
if (!spdif)
return -ENOMEM;
spdif->hclk = devm_clk_get(&pdev->dev, "hclk");
if (IS_ERR(spdif->hclk))
return PTR_ERR(spdif->hclk);
spdif->mclk = devm_clk_get(&pdev->dev, "mclk");
if (IS_ERR(spdif->mclk))
return PTR_ERR(spdif->mclk);
regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
if (IS_ERR(regs))
return PTR_ERR(regs);
spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs,
&rk_spdif_regmap_config);
if (IS_ERR(spdif->regmap))
return PTR_ERR(spdif->regmap);
spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR;
spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
spdif->playback_dma_data.maxburst = 4;
spdif->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, spdif);
ret = devm_add_action_or_reset(&pdev->dev, rk_spdif_suspend, &pdev->dev);
if (ret)
return ret;
devm_pm_runtime_enable(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
ret = rk_spdif_runtime_resume(&pdev->dev);
if (ret)
return ret;
}
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Could not register PCM\n");
ret = devm_snd_soc_register_component(&pdev->dev,
&rk_spdif_component,
&rk_spdif_dai, 1);
if (ret)
return dev_err_probe(&pdev->dev, ret, "Could not register DAI\n");
return 0;
}
static const struct dev_pm_ops rk_spdif_pm_ops = {
RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume, NULL)
};
static const struct of_device_id rk_spdif_match[] = {
{ .compatible = "rockchip,rk3066-spdif",
.data = (void *)RK_SPDIF_RK3066 },
{ .compatible = "rockchip,rk3188-spdif",
.data = (void *)RK_SPDIF_RK3188 },
{ .compatible = "rockchip,rk3228-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3288-spdif",
.data = (void *)RK_SPDIF_RK3288 },
{ .compatible = "rockchip,rk3328-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3366-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3368-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3399-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{ .compatible = "rockchip,rk3568-spdif",
.data = (void *)RK_SPDIF_RK3366 },
{},
};
MODULE_DEVICE_TABLE(of, rk_spdif_match);
static struct platform_driver rk_spdif_driver = {
.probe = rk_spdif_probe,
.driver = {
.name = "rockchip-spdif",
.of_match_table = rk_spdif_match,
.pm = pm_ptr(&rk_spdif_pm_ops),
},
};
module_platform_driver(rk_spdif_driver);
MODULE_ALIAS("platform:rockchip-spdif");
MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface");
MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.co.uk>");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
*
* Copyright (c) 2015-2026 Collabora Ltd.
* Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
*/
#ifndef _ROCKCHIP_SPDIF_H
#define _ROCKCHIP_SPDIF_H
/*
* CFGR
* transfer configuration register
*/
#define SPDIF_CFGR_CLK_DIV_MASK GENMASK(23, 16)
#define SPDIF_CFGR_CLK_DIV(x) FIELD_PREP(SPDIF_CFGR_CLK_DIV_MASK, x-1)
#define SPDIF_CFGR_CLR_MASK BIT(7)
#define SPDIF_CFGR_CLR_EN FIELD_PREP(SPDIF_CFGR_CLR_MASK, 1)
#define SPDIF_CFGR_CLR_DIS FIELD_PREP(SPDIF_CFGR_CLR_MASK, 0)
#define SPDIF_CFGR_CSE_MASK BIT(6)
#define SPDIF_CFGR_CSE_EN FIELD_PREP(SPDIF_CFGR_CSE_MASK, 1)
#define SPDIF_CFGR_CSE_DIS FIELD_PREP(SPDIF_CFGR_CSE_MASK, 0)
#define SPDIF_CFGR_ADJ_MASK BIT(3)
#define SPDIF_CFGR_ADJ_LEFT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 1)
#define SPDIF_CFGR_ADJ_RIGHT_J FIELD_PREP(SPDIF_CFGR_ADJ_MASK, 0)
#define SPDIF_CFGR_HALFWORD_MASK BIT(2)
#define SPDIF_CFGR_HALFWORD_DISABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 0)
#define SPDIF_CFGR_HALFWORD_ENABLE FIELD_PREP(SPDIF_CFGR_HALFWORD_MASK, 1)
#define SDPIF_CFGR_VDW_MASK GENMASK(1, 0)
#define SPDIF_CFGR_VDW(x) FIELD_PREP(SDPIF_CFGR_VDW_MASK, x)
#define SPDIF_CFGR_VDW_16 SPDIF_CFGR_VDW(0x0)
#define SPDIF_CFGR_VDW_20 SPDIF_CFGR_VDW(0x1)
#define SPDIF_CFGR_VDW_24 SPDIF_CFGR_VDW(0x2)
/*
* DMACR
* DMA control register
*/
#define SPDIF_DMACR_TDE_MASK BIT(5)
#define SPDIF_DMACR_TDE_DISABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 0)
#define SPDIF_DMACR_TDE_ENABLE FIELD_PREP(SPDIF_DMACR_TDE_MASK, 1)
#define SPDIF_DMACR_TDL_MASK GENMASK(4, 0)
#define SPDIF_DMACR_TDL(x) FIELD_PREP(SPDIF_DMACR_TDL_MASK, x)
/*
* XFER
* Transfer control register
*/
#define SPDIF_XFER_TXS_MASK BIT(0)
#define SPDIF_XFER_TXS_STOP FIELD_PREP(SPDIF_XFER_TXS_MASK, 0)
#define SPDIF_XFER_TXS_START FIELD_PREP(SPDIF_XFER_TXS_MASK, 1)
#define SPDIF_CFGR (0x0000)
#define SPDIF_SDBLR (0x0004)
#define SPDIF_DMACR (0x0008)
#define SPDIF_INTCR (0x000c)
#define SPDIF_INTSR (0x0010)
#define SPDIF_XFER (0x0018)
#define SPDIF_SMPDR (0x0020)
#define SPDIF_VLDFRn(x) (0x0060 + (x) * 4)
#define SPDIF_USRDRn(x) (0x0090 + (x) * 4)
#define SPDIF_CHNSRn(x) (0x00c0 + (x) * 4)
#define SPDIF_VERSION (0x01c0)
#endif /* _ROCKCHIP_SPDIF_H */