From 6b27e27729270a2478fdebea2db9c4f57bb4e8e6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 31 Oct 2018 19:31:43 +0000 Subject: ASoC: stm32: sai: fix less than zero comparison on unsigned int Currently, the comparison of div < 0 is always false because div is an unsigned int. Fix this by making div an int. Detected by CoverityScan, CID#1475309 ("Unsigned compared against 0") Fixes: 8307b2afd386 "(ASoC: stm32: sai: set sai as mclk clock provider") Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index ea05cc91aa05..506423d5861a 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -336,8 +336,7 @@ static int stm32_sai_mclk_set_rate(struct clk_hw *hw, unsigned long rate, { struct stm32_sai_mclk_data *mclk = to_mclk_data(hw); struct stm32_sai_sub_data *sai = mclk->sai_data; - unsigned int div; - int ret; + int div, ret; div = stm32_sai_get_clk_div(sai, parent_rate, rate); if (div < 0) -- cgit v1.2.3-58-ga151 From ff60005e4598ca9a79fa6d8378dd35a668ae4de7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 2 Nov 2018 16:00:18 +0100 Subject: ASoC: smd845: constify snd_soc_ops structure The snd_soc_ops structure can be const as it is only stored in the ops field of a snd_soc_dai_link structure and this field is const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 9effbecc571f..8d0cdff64377 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -171,7 +171,7 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) } } -static struct snd_soc_ops sdm845_be_ops = { +static const struct snd_soc_ops sdm845_be_ops = { .hw_params = sdm845_snd_hw_params, .startup = sdm845_snd_startup, .shutdown = sdm845_snd_shutdown, -- cgit v1.2.3-58-ga151 From 576ce4075bfa0f03e0e91a89eecc539b3b828b08 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Nov 2018 16:18:21 +0100 Subject: ASoC: wm97xx: fix uninitialized regmap pointer problem gcc notices that without either the ac97 bus or the pdata, we never initialize the regmap pointer, which leads to an uninitialized variable access: sound/soc/codecs/wm9712.c: In function 'wm9712_soc_probe': sound/soc/codecs/wm9712.c:666:2: error: 'regmap' may be used uninitialized in this function [-Werror=maybe-uninitialized] Since that configuration is invalid, it's better to return an error here. I tried to avoid adding complexity to the conditions, and turned the #ifdef into a regular if(IS_ENABLED()) check for readability. This in turn requires moving some header file declarations out of an #ifdef. The same code is used in three drivers, all of which I'm changing the same way. Fixes: 2ed1a8e0ce8d ("ASoC: wm9712: add ac97 new bus support") Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- include/sound/soc.h | 2 +- sound/soc/codecs/wm9705.c | 10 ++++------ sound/soc/codecs/wm9712.c | 10 ++++------ sound/soc/codecs/wm9713.c | 10 ++++------ 4 files changed, 13 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index f1dab1f4b194..5a8b84140f01 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -553,12 +553,12 @@ static inline void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, } #endif -#ifdef CONFIG_SND_SOC_AC97_BUS struct snd_ac97 *snd_soc_alloc_ac97_component(struct snd_soc_component *component); struct snd_ac97 *snd_soc_new_ac97_component(struct snd_soc_component *component, unsigned int id, unsigned int id_mask); void snd_soc_free_ac97_component(struct snd_ac97 *ac97); +#ifdef CONFIG_SND_SOC_AC97_BUS int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops); int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops, struct platform_device *pdev); diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c index ccdf088461b7..54c306707c02 100644 --- a/sound/soc/codecs/wm9705.c +++ b/sound/soc/codecs/wm9705.c @@ -325,8 +325,7 @@ static int wm9705_soc_probe(struct snd_soc_component *component) if (wm9705->mfd_pdata) { wm9705->ac97 = wm9705->mfd_pdata->ac97; regmap = wm9705->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { wm9705->ac97 = snd_soc_new_ac97_component(component, WM9705_VENDOR_ID, WM9705_VENDOR_ID_MASK); if (IS_ERR(wm9705->ac97)) { @@ -339,7 +338,8 @@ static int wm9705_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9705->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_set_drvdata(component, wm9705->ac97); @@ -350,14 +350,12 @@ static int wm9705_soc_probe(struct snd_soc_component *component) static void wm9705_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9705_priv *wm9705 = snd_soc_component_get_drvdata(component); - if (!wm9705->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9705->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9705->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9705 = { diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index e873baa9e778..01949eaba4fd 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -642,8 +642,7 @@ static int wm9712_soc_probe(struct snd_soc_component *component) if (wm9712->mfd_pdata) { wm9712->ac97 = wm9712->mfd_pdata->ac97; regmap = wm9712->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { int ret; wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID, @@ -660,7 +659,8 @@ static int wm9712_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9712->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_init_regmap(component, regmap); @@ -673,14 +673,12 @@ static int wm9712_soc_probe(struct snd_soc_component *component) static void wm9712_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); - if (!wm9712->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9712->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9712->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9712 = { diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 643863bb32e0..5a2fdf4f69bf 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1214,8 +1214,7 @@ static int wm9713_soc_probe(struct snd_soc_component *component) if (wm9713->mfd_pdata) { wm9713->ac97 = wm9713->mfd_pdata->ac97; regmap = wm9713->mfd_pdata->regmap; - } else { -#ifdef CONFIG_SND_SOC_AC97_BUS + } else if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS)) { wm9713->ac97 = snd_soc_new_ac97_component(component, WM9713_VENDOR_ID, WM9713_VENDOR_ID_MASK); if (IS_ERR(wm9713->ac97)) @@ -1225,7 +1224,8 @@ static int wm9713_soc_probe(struct snd_soc_component *component) snd_soc_free_ac97_component(wm9713->ac97); return PTR_ERR(regmap); } -#endif + } else { + return -ENXIO; } snd_soc_component_init_regmap(component, regmap); @@ -1238,14 +1238,12 @@ static int wm9713_soc_probe(struct snd_soc_component *component) static void wm9713_soc_remove(struct snd_soc_component *component) { -#ifdef CONFIG_SND_SOC_AC97_BUS struct wm9713_priv *wm9713 = snd_soc_component_get_drvdata(component); - if (!wm9713->mfd_pdata) { + if (IS_ENABLED(CONFIG_SND_SOC_AC97_BUS) && !wm9713->mfd_pdata) { snd_soc_component_exit_regmap(component); snd_soc_free_ac97_component(wm9713->ac97); } -#endif } static const struct snd_soc_component_driver soc_component_dev_wm9713 = { -- cgit v1.2.3-58-ga151 From 1539c7f23f256120f89f8b9ec53160790bce9ed2 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sat, 3 Nov 2018 22:21:22 +0100 Subject: ASoC: Intel: mrfld: fix uninitialized variable access Randconfig testing revealed a very old bug, with gcc-8: sound/soc/intel/atom/sst/sst_loader.c: In function 'sst_load_fw': sound/soc/intel/atom/sst/sst_loader.c:357:5: error: 'fw' may be used uninitialized in this function [-Werror=maybe-uninitialized] if (fw == NULL) { ^ sound/soc/intel/atom/sst/sst_loader.c:354:25: note: 'fw' was declared here const struct firmware *fw; We must check the return code of request_firmware() before we look at the pointer result that may be uninitialized when the function fails. Fixes: 9012c9544eea ("ASoC: Intel: mrfld - Add DSP load and management") Signed-off-by: Arnd Bergmann Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_loader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 27413ebae956..b8c456753f01 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -354,14 +354,14 @@ static int sst_request_fw(struct intel_sst_drv *sst) const struct firmware *fw; retval = request_firmware(&fw, sst->firmware_name, sst->dev); - if (fw == NULL) { - dev_err(sst->dev, "fw is returning as null\n"); - return -EINVAL; - } if (retval) { dev_err(sst->dev, "request fw failed %d\n", retval); return retval; } + if (fw == NULL) { + dev_err(sst->dev, "fw is returning as null\n"); + return -EINVAL; + } mutex_lock(&sst->sst_lock); retval = sst_cache_and_parse_fw(sst, fw); mutex_unlock(&sst->sst_lock); -- cgit v1.2.3-58-ga151 From 0b6277e6343e192aaa7d452ab933281eb0d420dc Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Sun, 4 Nov 2018 07:55:29 -0500 Subject: ASoC: nau8825: remove unnecessary unlikely() WARN_ON() already contains an unlikely(), so it's not necessary to use unlikely. Signed-off-by: Yangtao Li Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index b9fed99d8b5e..7bbcbf5f05c8 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -424,10 +424,8 @@ static u32 nau8825_xtalk_sidetone(u32 sig_org, u32 sig_cros) { u32 gain, sidetone; - if (unlikely(sig_org == 0) || unlikely(sig_cros == 0)) { - WARN_ON(1); + if (WARN_ON(sig_org == 0 || sig_cros == 0)) return 0; - } sig_org = nau8825_intlog10_dec3(sig_org); sig_cros = nau8825_intlog10_dec3(sig_cros); -- cgit v1.2.3-58-ga151 From 7dfcd9d727c3e69ecb51c0bb4d54b3d46de98e87 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 27 Oct 2018 15:34:43 +0200 Subject: ASoC: qdsp6: q6afe-dai: constify snd_soc_dai_ops structures The snd_soc_dai_ops structures are only stored in the ops field of a snd_soc_dai_driver structure, so make the snd_soc_dai_ops structures const as well. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe-dai.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 60ff4a2d3577..a07a4538d5cf 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -561,13 +561,13 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, }; -static struct snd_soc_dai_ops q6hdmi_ops = { +static const struct snd_soc_dai_ops q6hdmi_ops = { .prepare = q6afe_dai_prepare, .hw_params = q6hdmi_hw_params, .shutdown = q6afe_dai_shutdown, }; -static struct snd_soc_dai_ops q6i2s_ops = { +static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6afe_dai_prepare, .hw_params = q6i2s_hw_params, .set_fmt = q6i2s_set_fmt, @@ -575,14 +575,14 @@ static struct snd_soc_dai_ops q6i2s_ops = { .set_sysclk = q6afe_mi2s_set_sysclk, }; -static struct snd_soc_dai_ops q6slim_ops = { +static const struct snd_soc_dai_ops q6slim_ops = { .prepare = q6afe_dai_prepare, .hw_params = q6slim_hw_params, .shutdown = q6afe_dai_shutdown, .set_channel_map = q6slim_set_channel_map, }; -static struct snd_soc_dai_ops q6tdm_ops = { +static const struct snd_soc_dai_ops q6tdm_ops = { .prepare = q6afe_dai_prepare, .shutdown = q6afe_dai_shutdown, .set_sysclk = q6afe_mi2s_set_sysclk, -- cgit v1.2.3-58-ga151 From 704a9fc20b87f2929732cab0a1a04f28d4093085 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 27 Oct 2018 15:34:44 +0200 Subject: ASoC: codecs: constify snd_soc_dai_ops structures The snd_soc_dai_ops structures are only stored in the ops field of a snd_soc_dai_driver structure, so make the snd_soc_dai_ops structures const as well. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- sound/soc/codecs/ak4458.c | 2 +- sound/soc/codecs/ak5558.c | 2 +- sound/soc/codecs/hdac_hda.c | 2 +- sound/soc/codecs/tas6424.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 299ada4dfaa0..70d4c89bd6fc 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -456,7 +456,7 @@ static int ak4458_startup(struct snd_pcm_substream *substream, return ret; } -static struct snd_soc_dai_ops ak4458_dai_ops = { +static const struct snd_soc_dai_ops ak4458_dai_ops = { .startup = ak4458_startup, .hw_params = ak4458_hw_params, .set_fmt = ak4458_set_dai_fmt, diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 448bb90c9c8e..60f1f12c81ea 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -246,7 +246,7 @@ static int ak5558_startup(struct snd_pcm_substream *substream, &ak5558_rate_constraints); } -static struct snd_soc_dai_ops ak5558_dai_ops = { +static const struct snd_soc_dai_ops ak5558_dai_ops = { .startup = ak5558_startup, .hw_params = ak5558_hw_params, diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 2aaa83028e55..ffecdaaa8cf2 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -46,7 +46,7 @@ static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, struct snd_soc_dai *dai); -static struct snd_soc_dai_ops hdac_hda_dai_ops = { +static const struct snd_soc_dai_ops hdac_hda_dai_ops = { .startup = hdac_hda_dai_open, .shutdown = hdac_hda_dai_close, .prepare = hdac_hda_dai_prepare, diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index 36aebdb8f55c..aaba39295079 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -378,7 +378,7 @@ static struct snd_soc_component_driver soc_codec_dev_tas6424 = { .non_legacy_dai_naming = 1, }; -static struct snd_soc_dai_ops tas6424_speaker_dai_ops = { +static const struct snd_soc_dai_ops tas6424_speaker_dai_ops = { .hw_params = tas6424_hw_params, .set_fmt = tas6424_set_dai_fmt, .set_tdm_slot = tas6424_set_dai_tdm_slot, -- cgit v1.2.3-58-ga151 From de3636488cff3678bafaf2d024e64c003ed2beae Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 28 Oct 2018 06:56:08 +0100 Subject: ASoC: AMD: constify regulator_desc structure The regulator_desc structure can be const as it is only passed as the second argument of devm_regulator_register and the corresponding parameter is declared as const. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Mark Brown --- sound/soc/amd/acp-da7219-max98357a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c index 3f813ea5210a..a5daad973ce5 100644 --- a/sound/soc/amd/acp-da7219-max98357a.c +++ b/sound/soc/amd/acp-da7219-max98357a.c @@ -403,7 +403,7 @@ static struct regulator_config acp_da7219_cfg = { static struct regulator_ops acp_da7219_ops = { }; -static struct regulator_desc acp_da7219_desc = { +static const struct regulator_desc acp_da7219_desc = { .name = "reg-fixed-1.8V", .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, -- cgit v1.2.3-58-ga151 From 61f18dc78a6b70c409134d0f4d873e9253565e05 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 25 Oct 2018 16:48:24 +0300 Subject: ASoC: omap-mcbsp: On OMAP1 and OMAP2420 only 16bit format is supported For samples sizes of 20/24/32 bit the DRR2/DXR2 registers must be used to receive or transmit the most significant part of the 32bit sample. We can not trick the system by using the DRR2/DXR2 register offset and use 32bit element size since these (and other McBSP) registers are 16bit ones. We would need support for port_window in cyclic mode from the sDMA driver, but it is not straight forward as we would need to have the sDMA frame to cover the 32bit (DRR2+DRR1/DXR2+DXR1), but the frames must cover the ALSA period to be able to receive periodic interrupts. Since the 32bit samples are not working (DMA timeout), just remove it in case we have McBSP with reg_size of 2. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/omap-mcbsp.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index d0ebb6b9bfac..caa24c463b3e 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -862,6 +862,11 @@ static int asoc_mcbsp_probe(struct platform_device *pdev) if (ret) return ret; + if (mcbsp->pdata->reg_size == 2) { + omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE; + omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE; + } + ret = devm_snd_soc_register_component(&pdev->dev, &omap_mcbsp_component, &omap_mcbsp_dai, 1); -- cgit v1.2.3-58-ga151 From 0900d1e64815b3b8d4d04197aa135e77317176e1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Oct 2018 07:31:40 +0000 Subject: ASoC: pcm3168a: cleanup hw_constraint for LEFT_J/I2S hw constraint for LEFT_J/I2S are same, but had duplicated code. This code cleanup these. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 52cc950c9fd1..5f4327f5b725 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -504,6 +504,10 @@ static int pcm3168a_startup(struct snd_pcm_substream *substream, unsigned int fmt; unsigned int sample_min; unsigned int channel_max; + unsigned int channel_maxs[] = { + 6, /* rx */ + 8 /* tx */ + }; if (tx) fmt = pcm3168a->dac_fmt; @@ -528,18 +532,9 @@ static int pcm3168a_startup(struct snd_pcm_substream *substream, channel_max = 2; break; case PCM3168A_FMT_LEFT_J: - sample_min = 24; - if (tx) - channel_max = 8; - else - channel_max = 6; - break; case PCM3168A_FMT_I2S: sample_min = 24; - if (tx) - channel_max = 8; - else - channel_max = 6; + channel_max = channel_maxs[tx]; break; default: sample_min = 24; -- cgit v1.2.3-58-ga151 From 7e7fe06de376e9874f4399dac81d65ea9b0a9507 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Oct 2018 07:46:05 +0000 Subject: ASoC: rsnd: move .get_status under rsnd_mod_ops Each mod needs to have .get_status, but current driver is handling it under rsnd_mod, instead of rsnd_mod_ops. It is not any make sence. This patch moves it to rsnd_mod_ops, and tidyup its parameter order to align to other callback functions. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 2 +- sound/soc/sh/rcar/cmd.c | 11 +++-- sound/soc/sh/rcar/core.c | 10 ++-- sound/soc/sh/rcar/ctu.c | 3 +- sound/soc/sh/rcar/dma.c | 26 +++++----- sound/soc/sh/rcar/dvc.c | 3 +- sound/soc/sh/rcar/mix.c | 3 +- sound/soc/sh/rcar/rsnd.h | 13 ++--- sound/soc/sh/rcar/src.c | 24 ++++----- sound/soc/sh/rcar/ssi.c | 124 ++++++++++++++++++++++++----------------------- sound/soc/sh/rcar/ssiu.c | 37 +++++++------- 11 files changed, 129 insertions(+), 127 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 28327dd2c6cb..6768a66588eb 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -613,7 +613,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv) return -ENOMEM; ret = rsnd_mod_init(priv, &adg->mod, &adg_ops, - NULL, NULL, 0, 0); + NULL, 0, 0); if (ret) return ret; diff --git a/sound/soc/sh/rcar/cmd.c b/sound/soc/sh/rcar/cmd.c index cc191cd5fb82..e6bb6a9a0684 100644 --- a/sound/soc/sh/rcar/cmd.c +++ b/sound/soc/sh/rcar/cmd.c @@ -116,10 +116,11 @@ static int rsnd_cmd_stop(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_cmd_ops = { - .name = CMD_NAME, - .init = rsnd_cmd_init, - .start = rsnd_cmd_start, - .stop = rsnd_cmd_stop, + .name = CMD_NAME, + .init = rsnd_cmd_init, + .start = rsnd_cmd_start, + .stop = rsnd_cmd_stop, + .get_status = rsnd_mod_get_status, }; static struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id) @@ -162,7 +163,7 @@ int rsnd_cmd_probe(struct rsnd_priv *priv) for_each_rsnd_cmd(cmd, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(cmd), &rsnd_cmd_ops, NULL, - rsnd_mod_get_status, RSND_MOD_CMD, i); + RSND_MOD_CMD, i); if (ret) return ret; } diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index f930f51b686f..5373eba5d572 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -137,8 +137,8 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } -u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, +u32 *rsnd_mod_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, enum rsnd_mod_type type) { return &mod->status; @@ -148,9 +148,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, - u32* (*get_status)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type), enum rsnd_mod_type type, int id) { @@ -164,7 +161,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, mod->type = type; mod->clk = clk; mod->priv = priv; - mod->get_status = get_status; return ret; } @@ -472,7 +468,7 @@ static int rsnd_status_update(u32 *status, enum rsnd_mod_type *types = rsnd_mod_sequence[is_play]; \ for_each_rsnd_mod_arrays(i, mod, io, types, RSND_MOD_MAX) { \ int tmp = 0; \ - u32 *status = mod->get_status(io, mod, types[i]); \ + u32 *status = mod->ops->get_status(mod, io, types[i]); \ int func_call = rsnd_status_update(status, \ __rsnd_mod_shift_##fn, \ __rsnd_mod_add_##fn, \ diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index ad702377a6c3..28058474075b 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -341,6 +341,7 @@ static struct rsnd_mod_ops rsnd_ctu_ops = { .quit = rsnd_ctu_quit, .hw_params = rsnd_ctu_hw_params, .pcm_new = rsnd_ctu_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) @@ -404,7 +405,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops, - clk, rsnd_mod_get_status, RSND_MOD_CTU, i); + clk, RSND_MOD_CTU, i); if (ret) { of_node_put(np); goto rsnd_ctu_probe_done; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 6d1947515dc8..e5c30eec1190 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -289,12 +289,13 @@ static int rsnd_dmaen_pointer(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_dmaen_ops = { - .name = "audmac", - .prepare = rsnd_dmaen_prepare, - .cleanup = rsnd_dmaen_cleanup, - .start = rsnd_dmaen_start, - .stop = rsnd_dmaen_stop, - .pointer= rsnd_dmaen_pointer, + .name = "audmac", + .prepare = rsnd_dmaen_prepare, + .cleanup = rsnd_dmaen_cleanup, + .start = rsnd_dmaen_start, + .stop = rsnd_dmaen_stop, + .pointer = rsnd_dmaen_pointer, + .get_status = rsnd_mod_get_status, }; /* @@ -477,10 +478,11 @@ static int rsnd_dmapp_attach(struct rsnd_dai_stream *io, } static struct rsnd_mod_ops rsnd_dmapp_ops = { - .name = "audmac-pp", - .start = rsnd_dmapp_start, - .stop = rsnd_dmapp_stop, - .quit = rsnd_dmapp_stop, + .name = "audmac-pp", + .start = rsnd_dmapp_start, + .stop = rsnd_dmapp_stop, + .quit = rsnd_dmapp_stop, + .get_status = rsnd_mod_get_status, }; /* @@ -756,7 +758,7 @@ static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, *dma_mod = rsnd_mod_get(dma); ret = rsnd_mod_init(priv, *dma_mod, ops, NULL, - rsnd_mod_get_status, type, dma_id); + type, dma_id); if (ret < 0) return ret; @@ -823,5 +825,5 @@ int rsnd_dma_probe(struct rsnd_priv *priv) priv->dma = dmac; /* dummy mem mod for debug */ - return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, NULL, 0, 0); + return rsnd_mod_init(NULL, &mem, &mem_ops, NULL, 0, 0); } diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 2b16e0ce6bc5..d65f24bec991 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -306,6 +306,7 @@ static struct rsnd_mod_ops rsnd_dvc_ops = { .init = rsnd_dvc_init, .quit = rsnd_dvc_quit, .pcm_new = rsnd_dvc_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id) @@ -365,7 +366,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops, - clk, rsnd_mod_get_status, RSND_MOD_DVC, i); + clk, RSND_MOD_DVC, i); if (ret) { of_node_put(np); goto rsnd_dvc_probe_done; diff --git a/sound/soc/sh/rcar/mix.c b/sound/soc/sh/rcar/mix.c index 8e3b57eaa708..a3e0370f5704 100644 --- a/sound/soc/sh/rcar/mix.c +++ b/sound/soc/sh/rcar/mix.c @@ -256,6 +256,7 @@ static struct rsnd_mod_ops rsnd_mix_ops = { .init = rsnd_mix_init, .quit = rsnd_mix_quit, .pcm_new = rsnd_mix_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id) @@ -315,7 +316,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops, - clk, rsnd_mod_get_status, RSND_MOD_MIX, i); + clk, RSND_MOD_MIX, i); if (ret) { of_node_put(np); goto rsnd_mix_probe_done; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4464d1d0a042..d25fb5c14c2d 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -301,6 +301,9 @@ struct rsnd_mod_ops { int (*cleanup)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv); + u32 *(*get_status)(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type); }; struct rsnd_dai_stream; @@ -310,9 +313,6 @@ struct rsnd_mod { struct rsnd_mod_ops *ops; struct rsnd_priv *priv; struct clk *clk; - u32 *(*get_status)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type); u32 status; }; /* @@ -385,9 +385,6 @@ int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, struct clk *clk, - u32* (*get_status)(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type), enum rsnd_mod_type type, int id); void rsnd_mod_quit(struct rsnd_mod *mod); @@ -396,8 +393,8 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, void rsnd_mod_interrupt(struct rsnd_mod *mod, void (*callback)(struct rsnd_mod *mod, struct rsnd_dai_stream *io)); -u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, +u32 *rsnd_mod_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, enum rsnd_mod_type type); struct rsnd_mod *rsnd_mod_next(int *iterator, struct rsnd_dai_stream *io, diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index cd38a43b976f..7de7afd6a3a6 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -527,16 +527,17 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_src_ops = { - .name = SRC_NAME, - .dma_req = rsnd_src_dma_req, - .probe = rsnd_src_probe_, - .init = rsnd_src_init, - .quit = rsnd_src_quit, - .start = rsnd_src_start, - .stop = rsnd_src_stop, - .irq = rsnd_src_irq, - .hw_params = rsnd_src_hw_params, - .pcm_new = rsnd_src_pcm_new, + .name = SRC_NAME, + .dma_req = rsnd_src_dma_req, + .probe = rsnd_src_probe_, + .init = rsnd_src_init, + .quit = rsnd_src_quit, + .start = rsnd_src_start, + .stop = rsnd_src_stop, + .irq = rsnd_src_irq, + .hw_params = rsnd_src_hw_params, + .pcm_new = rsnd_src_pcm_new, + .get_status = rsnd_mod_get_status, }; struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) @@ -605,8 +606,7 @@ int rsnd_src_probe(struct rsnd_priv *priv) } ret = rsnd_mod_init(priv, rsnd_mod_get(src), - &rsnd_src_ops, clk, rsnd_mod_get_status, - RSND_MOD_SRC, i); + &rsnd_src_ops, clk, RSND_MOD_SRC, i); if (ret) { of_node_put(np); goto rsnd_src_probe_done; diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index fcb4df23248c..cb66986ff776 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -681,6 +681,41 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) return IRQ_HANDLED; } +static u32 *rsnd_ssi_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + /* + * SSIP (= SSI parent) needs to be special, otherwise, + * 2nd SSI might doesn't start. see also rsnd_mod_call() + * + * We can't include parent SSI status on SSI, because we don't know + * how many SSI requests parent SSI. Thus, it is localed on "io" now. + * ex) trouble case + * Playback: SSI0 + * Capture : SSI1 (needs SSI0) + * + * 1) start Capture -> SSI0/SSI1 are started. + * 2) start Playback -> SSI0 doesn't work, because it is already + * marked as "started" on 1) + * + * OTOH, using each mod's status is good for MUX case. + * It doesn't need to start in 2nd start + * ex) + * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 + * | + * IO-1: SRC1 -> CTU2 -+ + * + * 1) start IO-0 -> start SSI0 + * 2) start IO-1 -> SSI0 doesn't need to start, because it is + * already started on 1) + */ + if (type == RSND_MOD_SSIP) + return &io->parent_ssi_status; + + return rsnd_mod_get_status(mod, io, type); +} + /* * SSI PIO */ @@ -876,18 +911,19 @@ static int rsnd_ssi_prepare(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_ssi_pio_ops = { - .name = SSI_NAME, - .probe = rsnd_ssi_common_probe, - .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_pio_init, - .quit = rsnd_ssi_quit, - .start = rsnd_ssi_start, - .stop = rsnd_ssi_stop, - .irq = rsnd_ssi_irq, - .pointer = rsnd_ssi_pio_pointer, - .pcm_new = rsnd_ssi_pcm_new, - .hw_params = rsnd_ssi_hw_params, - .prepare = rsnd_ssi_prepare, + .name = SSI_NAME, + .probe = rsnd_ssi_common_probe, + .remove = rsnd_ssi_common_remove, + .init = rsnd_ssi_pio_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pointer = rsnd_ssi_pio_pointer, + .pcm_new = rsnd_ssi_pcm_new, + .hw_params = rsnd_ssi_hw_params, + .prepare = rsnd_ssi_prepare, + .get_status = rsnd_ssi_get_status, }; static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, @@ -951,19 +987,20 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, } static struct rsnd_mod_ops rsnd_ssi_dma_ops = { - .name = SSI_NAME, - .dma_req = rsnd_ssi_dma_req, - .probe = rsnd_ssi_dma_probe, - .remove = rsnd_ssi_common_remove, - .init = rsnd_ssi_init, - .quit = rsnd_ssi_quit, - .start = rsnd_ssi_start, - .stop = rsnd_ssi_stop, - .irq = rsnd_ssi_irq, - .pcm_new = rsnd_ssi_pcm_new, - .fallback = rsnd_ssi_fallback, - .hw_params = rsnd_ssi_hw_params, - .prepare = rsnd_ssi_prepare, + .name = SSI_NAME, + .dma_req = rsnd_ssi_dma_req, + .probe = rsnd_ssi_dma_probe, + .remove = rsnd_ssi_common_remove, + .init = rsnd_ssi_init, + .quit = rsnd_ssi_quit, + .start = rsnd_ssi_start, + .stop = rsnd_ssi_stop, + .irq = rsnd_ssi_irq, + .pcm_new = rsnd_ssi_pcm_new, + .fallback = rsnd_ssi_fallback, + .hw_params = rsnd_ssi_hw_params, + .prepare = rsnd_ssi_prepare, + .get_status = rsnd_ssi_get_status, }; int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) @@ -1091,41 +1128,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod) return !!(rsnd_flags_has(rsnd_mod_to_ssi(mod), RSND_SSI_CLK_PIN_SHARE)); } -static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type) -{ - /* - * SSIP (= SSI parent) needs to be special, otherwise, - * 2nd SSI might doesn't start. see also rsnd_mod_call() - * - * We can't include parent SSI status on SSI, because we don't know - * how many SSI requests parent SSI. Thus, it is localed on "io" now. - * ex) trouble case - * Playback: SSI0 - * Capture : SSI1 (needs SSI0) - * - * 1) start Capture -> SSI0/SSI1 are started. - * 2) start Playback -> SSI0 doesn't work, because it is already - * marked as "started" on 1) - * - * OTOH, using each mod's status is good for MUX case. - * It doesn't need to start in 2nd start - * ex) - * IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0 - * | - * IO-1: SRC1 -> CTU2 -+ - * - * 1) start IO-0 -> start SSI0 - * 2) start IO-1 -> SSI0 doesn't need to start, because it is - * already started on 1) - */ - if (type == RSND_MOD_SSIP) - return &io->parent_ssi_status; - - return rsnd_mod_get_status(io, mod, type); -} - int rsnd_ssi_probe(struct rsnd_priv *priv) { struct device_node *node; @@ -1192,7 +1194,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv) ops = &rsnd_ssi_dma_ops; ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk, - rsnd_ssi_get_status, RSND_MOD_SSI, i); + RSND_MOD_SSI, i); if (ret) { of_node_put(np); goto rsnd_ssi_probe_done; diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 39b67643b5dc..ebcb826b849f 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -22,6 +22,16 @@ struct rsnd_ssiu { ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \ i++) +static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod, + struct rsnd_dai_stream *io, + enum rsnd_mod_type type) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + int busif = rsnd_ssi_get_busif(io); + + return &ssiu->busif_status[busif]; +} + static int rsnd_ssiu_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -115,8 +125,9 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_ssiu_ops_gen1 = { - .name = SSIU_NAME, - .init = rsnd_ssiu_init, + .name = SSIU_NAME, + .init = rsnd_ssiu_init, + .get_status = rsnd_ssiu_get_status, }; static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, @@ -279,10 +290,11 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, } static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = { - .name = SSIU_NAME, - .init = rsnd_ssiu_init_gen2, - .start = rsnd_ssiu_start_gen2, - .stop = rsnd_ssiu_stop_gen2, + .name = SSIU_NAME, + .init = rsnd_ssiu_init_gen2, + .start = rsnd_ssiu_start_gen2, + .stop = rsnd_ssiu_stop_gen2, + .get_status = rsnd_ssiu_get_status, }; static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) @@ -304,16 +316,6 @@ int rsnd_ssiu_attach(struct rsnd_dai_stream *io, return rsnd_dai_connect(mod, io, mod->type); } -static u32 *rsnd_ssiu_get_status(struct rsnd_dai_stream *io, - struct rsnd_mod *mod, - enum rsnd_mod_type type) -{ - struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_ssi_get_busif(io); - - return &ssiu->busif_status[busif]; -} - int rsnd_ssiu_probe(struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); @@ -337,8 +339,7 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) for_each_rsnd_ssiu(ssiu, priv, i) { ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), - ops, NULL, rsnd_ssiu_get_status, - RSND_MOD_SSIU, i); + ops, NULL, RSND_MOD_SSIU, i); if (ret) return ret; } -- cgit v1.2.3-58-ga151 From c16015f36cc128244c910152663de45c3b99f551 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Oct 2018 07:47:18 +0000 Subject: ASoC: rsnd: add .get_id/.get_id_sub ID for CTU and SSIU are confusable. 1 CTU has 4 sub nodes. This means, CTU0 has CTU01 - CTU03, CTU1 has CTU10 - CTU13. SSIU is more confusable. Gen2 SSIU has BUSIF0-3, Gen3 SSIU has BUSIF0-7, but not for all SSIU. In rsnd driver, each mod drivers are assuming rsnd_mod_id() returns main device ID (In CTU case CTU0-1, SSIU case SSIU0-9), not serial number. This patch adds new .id/.id_sub to handling more detail ID. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 21 +++++++++++++++++++++ sound/soc/sh/rcar/ctu.c | 20 ++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 6 +++++- 3 files changed, 46 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 5373eba5d572..82d2234840c5 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -144,6 +144,27 @@ u32 *rsnd_mod_get_status(struct rsnd_mod *mod, return &mod->status; } +int rsnd_mod_id_raw(struct rsnd_mod *mod) +{ + return mod->id; +} + +int rsnd_mod_id(struct rsnd_mod *mod) +{ + if ((mod)->ops->id) + return (mod)->ops->id(mod); + + return rsnd_mod_id_raw(mod); +} + +int rsnd_mod_id_sub(struct rsnd_mod *mod) +{ + if ((mod)->ops->id_sub) + return (mod)->ops->id_sub(mod); + + return 0; +} + int rsnd_mod_init(struct rsnd_priv *priv, struct rsnd_mod *mod, struct rsnd_mod_ops *ops, diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 28058474075b..6a948b1fd9ab 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -334,6 +334,24 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, return ret; } +static int rsnd_ctu_id(struct rsnd_mod *mod) +{ + /* + * ctu00: -> 0, ctu01: -> 0, ctu02: -> 0, ctu03: -> 0 + * ctu10: -> 1, ctu11: -> 1, ctu12: -> 1, ctu13: -> 1 + */ + return mod->id / 4; +} + +static int rsnd_ctu_id_sub(struct rsnd_mod *mod) +{ + /* + * ctu00: -> 0, ctu01: -> 1, ctu02: -> 2, ctu03: -> 3 + * ctu10: -> 0, ctu11: -> 1, ctu12: -> 2, ctu13: -> 3 + */ + return mod->id % 4; +} + static struct rsnd_mod_ops rsnd_ctu_ops = { .name = CTU_NAME, .probe = rsnd_ctu_probe_, @@ -342,6 +360,8 @@ static struct rsnd_mod_ops rsnd_ctu_ops = { .hw_params = rsnd_ctu_hw_params, .pcm_new = rsnd_ctu_pcm_new, .get_status = rsnd_mod_get_status, + .id = rsnd_ctu_id, + .id_sub = rsnd_ctu_id_sub, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index d25fb5c14c2d..fdf007aeac27 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -304,6 +304,8 @@ struct rsnd_mod_ops { u32 *(*get_status)(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type); + int (*id)(struct rsnd_mod *mod); + int (*id_sub)(struct rsnd_mod *mod); }; struct rsnd_dai_stream; @@ -376,7 +378,6 @@ struct rsnd_mod { #define rsnd_mod_to_priv(mod) ((mod)->priv) #define rsnd_mod_name(mod) ((mod)->ops->name) -#define rsnd_mod_id(mod) ((mod)->id) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) @@ -396,6 +397,9 @@ void rsnd_mod_interrupt(struct rsnd_mod *mod, u32 *rsnd_mod_get_status(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type); +int rsnd_mod_id(struct rsnd_mod *mod); +int rsnd_mod_id_raw(struct rsnd_mod *mod); +int rsnd_mod_id_sub(struct rsnd_mod *mod); struct rsnd_mod *rsnd_mod_next(int *iterator, struct rsnd_dai_stream *io, enum rsnd_mod_type *array, -- cgit v1.2.3-58-ga151 From c0ea089dbad47a41ae30ad290766d7a6571c9802 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 30 Oct 2018 07:47:50 +0000 Subject: ASoC: rsnd: rsnd_mod_name() handles both name and ID Current rsnd driver is using "%s[%d]" for mod name and ID, but, this ID portion might confusable. For example currently, CTU ID is 0 to 7, but using 00 to 13 (= 00, 01, 02, 03, 10, 11, 12, 13) is very best matching to datasheet. In the future, we will support BUSIFn, but it will be more complicated numbering. To avoid future confusable code, this patch modify rsnd_mod_name() to return understandable name. To avoid using pointless memory, it uses static char and snprintf, thus, rsnd_mod_name() user should use it immediately, and shouldn't keep its pointer. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 41 ++++++++++++++++++++++++++++++++--------- sound/soc/sh/rcar/dma.c | 21 ++++++++------------- sound/soc/sh/rcar/gen.c | 12 ++++++------ sound/soc/sh/rcar/rsnd.h | 2 +- sound/soc/sh/rcar/src.c | 5 ++--- sound/soc/sh/rcar/ssi.c | 23 ++++++++--------------- 6 files changed, 57 insertions(+), 47 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 82d2234840c5..5e100122391a 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -123,8 +123,8 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type) struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); - dev_warn(dev, "%s[%d] is not your expected module\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_warn(dev, "%s is not your expected module\n", + rsnd_mod_name(mod)); } } @@ -137,6 +137,30 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } +#define MOD_NAME_SIZE 16 +char *rsnd_mod_name(struct rsnd_mod *mod) +{ + static char name[MOD_NAME_SIZE]; + + /* + * Let's use same char to avoid pointlessness memory + * Thus, rsnd_mod_name() should be used immediately + * Don't keep pointer + */ + if ((mod)->ops->id_sub) { + snprintf(name, MOD_NAME_SIZE, "%s[%d%d]", + mod->ops->name, + rsnd_mod_id(mod), + rsnd_mod_id_sub(mod)); + } else { + snprintf(name, MOD_NAME_SIZE, "%s[%d]", + mod->ops->name, + rsnd_mod_id(mod)); + } + + return name; +} + u32 *rsnd_mod_get_status(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type) @@ -494,15 +518,14 @@ static int rsnd_status_update(u32 *status, __rsnd_mod_shift_##fn, \ __rsnd_mod_add_##fn, \ __rsnd_mod_call_##fn); \ - rsnd_dbg_dai_call(dev, "%s[%d]\t0x%08x %s\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), *status, \ + rsnd_dbg_dai_call(dev, "%s\t0x%08x %s\n", \ + rsnd_mod_name(mod), *status, \ (func_call && (mod)->ops->fn) ? #fn : ""); \ if (func_call && (mod)->ops->fn) \ tmp = (mod)->ops->fn(mod, io, param); \ if (tmp && (tmp != -EPROBE_DEFER)) \ - dev_err(dev, "%s[%d] : %s error %d\n", \ - rsnd_mod_name(mod), rsnd_mod_id(mod), \ - #fn, tmp); \ + dev_err(dev, "%s : %s error %d\n", \ + rsnd_mod_name(mod), #fn, tmp); \ ret |= tmp; \ } \ ret; \ @@ -529,8 +552,8 @@ int rsnd_dai_connect(struct rsnd_mod *mod, io->mod[type] = mod; - dev_dbg(dev, "%s[%d] is connected to io (%s)\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "%s is connected to io (%s)\n", + rsnd_mod_name(mod), rsnd_io_is_play(io) ? "Playback" : "Capture"); return 0; diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index e5c30eec1190..5daa6c932fce 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -174,8 +174,8 @@ static int rsnd_dmaen_start(struct rsnd_mod *mod, cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - dev_dbg(dev, "%s[%d] %pad -> %pad\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "%s %pad -> %pad\n", + rsnd_mod_name(mod), &cfg.src_addr, &cfg.dst_addr); ret = dmaengine_slave_config(dmaen->chan, &cfg); @@ -369,8 +369,7 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, if ((!entry) || (size <= id)) { struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); - dev_err(dev, "unknown connection (%s[%d])\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_err(dev, "unknown connection (%s)\n", rsnd_mod_name(mod)); /* use non-prohibited SRS number as error */ return 0x00; /* SSI00 */ @@ -692,12 +691,10 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, *mod_to = mod[1]; } - dev_dbg(dev, "module connection (this is %s[%d])\n", - rsnd_mod_name(this), rsnd_mod_id(this)); + dev_dbg(dev, "module connection (this is %s)\n", rsnd_mod_name(this)); for (i = 0; i <= idx; i++) { - dev_dbg(dev, " %s[%d]%s\n", + dev_dbg(dev, " %s%s\n", rsnd_mod_name(mod[i] ? mod[i] : &mem), - rsnd_mod_id (mod[i] ? mod[i] : &mem), (mod[i] == *mod_from) ? " from" : (mod[i] == *mod_to) ? " to" : ""); } @@ -762,12 +759,10 @@ static int rsnd_dma_alloc(struct rsnd_dai_stream *io, struct rsnd_mod *mod, if (ret < 0) return ret; - dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n", - rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod), + dev_dbg(dev, "%s %s -> %s\n", + rsnd_mod_name(*dma_mod), rsnd_mod_name(mod_from ? mod_from : &mem), - rsnd_mod_id (mod_from ? mod_from : &mem), - rsnd_mod_name(mod_to ? mod_to : &mem), - rsnd_mod_id (mod_to ? mod_to : &mem)); + rsnd_mod_name(mod_to ? mod_to : &mem)); ret = attach(io, dma, mod_from, mod_to); if (ret < 0) diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 1f7881cc16b2..ca639404f2cd 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -83,8 +83,8 @@ u32 rsnd_read(struct rsnd_priv *priv, regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); - dev_dbg(dev, "r %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "r %s - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_reg_name(gen, reg), reg, val); return val; @@ -102,8 +102,8 @@ void rsnd_write(struct rsnd_priv *priv, regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); - dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "w %s - %-18s (%4d) : %08x\n", + rsnd_mod_name(mod), rsnd_reg_name(gen, reg), reg, data); } @@ -119,8 +119,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, regmap_fields_force_update_bits(gen->regs[reg], rsnd_mod_id(mod), mask, data); - dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), + dev_dbg(dev, "b %s - %-18s (%4d) : %08x/%08x\n", + rsnd_mod_name(mod), rsnd_reg_name(gen, reg), reg, data, mask); } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index fdf007aeac27..28bd90ab5a99 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -377,7 +377,6 @@ struct rsnd_mod { #define __rsnd_mod_call_pointer 0 #define rsnd_mod_to_priv(mod) ((mod)->priv) -#define rsnd_mod_name(mod) ((mod)->ops->name) #define rsnd_mod_power_on(mod) clk_enable((mod)->clk) #define rsnd_mod_power_off(mod) clk_disable((mod)->clk) #define rsnd_mod_get(ip) (&(ip)->mod) @@ -400,6 +399,7 @@ u32 *rsnd_mod_get_status(struct rsnd_mod *mod, int rsnd_mod_id(struct rsnd_mod *mod); int rsnd_mod_id_raw(struct rsnd_mod *mod); int rsnd_mod_id_sub(struct rsnd_mod *mod); +char *rsnd_mod_name(struct rsnd_mod *mod); struct rsnd_mod *rsnd_mod_next(int *iterator, struct rsnd_dai_stream *io, enum rsnd_mod_type *array, diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index 7de7afd6a3a6..bdc05952f75f 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -349,9 +349,8 @@ static bool rsnd_src_error_occurred(struct rsnd_mod *mod) status0 = rsnd_mod_read(mod, SCU_SYS_STATUS0); status1 = rsnd_mod_read(mod, SCU_SYS_STATUS1); if ((status0 & val0) || (status1 & val1)) { - rsnd_dbg_irq_status(dev, "%s[%d] err status : 0x%08x, 0x%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), - status0, status1); + rsnd_dbg_irq_status(dev, "%s err status : 0x%08x, 0x%08x\n", + rsnd_mod_name(mod), status0, status1); ret = true; } diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index cb66986ff776..a032819044f7 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -181,8 +181,7 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod, udelay(5); } - dev_warn(dev, "%s[%d] status check failed\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_warn(dev, "%s status check failed\n", rsnd_mod_name(mod)); } static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io) @@ -346,9 +345,7 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, ssi->rate = rate; ssi->chan = chan; - dev_dbg(dev, "%s[%d] outputs %u Hz\n", - rsnd_mod_name(mod), - rsnd_mod_id(mod), rate); + dev_dbg(dev, "%s outputs %u Hz\n", rsnd_mod_name(mod), rate); return 0; } @@ -494,8 +491,7 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod, return 0; if (!ssi->usrcnt) { - dev_err(dev, "%s[%d] usrcnt error\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_err(dev, "%s usrcnt error\n", rsnd_mod_name(mod)); return -EIO; } @@ -654,8 +650,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod, /* DMA only */ if (is_dma && (status & (UIRQ | OIRQ))) { - rsnd_dbg_irq_status(dev, "%s[%d] err status : 0x%08x\n", - rsnd_mod_name(mod), rsnd_mod_id(mod), status); + rsnd_dbg_irq_status(dev, "%s err status : 0x%08x\n", + rsnd_mod_name(mod), status); stop = true; } @@ -964,8 +960,7 @@ static int rsnd_ssi_fallback(struct rsnd_mod *mod, */ mod->ops = &rsnd_ssi_pio_ops; - dev_info(dev, "%s[%d] fallback to PIO mode\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_info(dev, "%s fallback to PIO mode\n", rsnd_mod_name(mod)); return 0; } @@ -1085,15 +1080,13 @@ static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, /* HDMI0 */ if (strstr(remote_node->full_name, "hdmi@fead0000")) { rsnd_flags_set(ssi, RSND_SSI_HDMI0); - dev_dbg(dev, "%s[%d] connected to HDMI0\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_dbg(dev, "%s connected to HDMI0\n", rsnd_mod_name(mod)); } /* HDMI1 */ if (strstr(remote_node->full_name, "hdmi@feae0000")) { rsnd_flags_set(ssi, RSND_SSI_HDMI1); - dev_dbg(dev, "%s[%d] connected to HDMI1\n", - rsnd_mod_name(mod), rsnd_mod_id(mod)); + dev_dbg(dev, "%s connected to HDMI1\n", rsnd_mod_name(mod)); } } -- cgit v1.2.3-58-ga151 From 39dfdf00c7a56b2a8c01c596f6522f089428b1c7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 6 Nov 2018 17:39:19 +0800 Subject: ASoC: ak5558: Remove redundant snd_soc_component_read32 calls snd_soc_component_update_bits() will only update the mask bits, so remove the redundant snd_soc_component_read32(). Signed-off-by: Axel Lin Reviewed-by: Daniel Baluta Signed-off-by: Mark Brown --- sound/soc/codecs/ak5558.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 60f1f12c81ea..8179512129d3 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -130,16 +130,12 @@ static int ak5558_hw_params(struct snd_pcm_substream *substream, u8 bits; int pcm_width = max(params_physical_width(params), ak5558->slot_width); - /* set master/slave audio interface */ - bits = snd_soc_component_read32(component, AK5558_02_CONTROL1); - bits &= ~AK5558_BITS; - switch (pcm_width) { case 16: - bits |= AK5558_DIF_24BIT_MODE; + bits = AK5558_DIF_24BIT_MODE; break; case 32: - bits |= AK5558_DIF_32BIT_MODE; + bits = AK5558_DIF_32BIT_MODE; break; default: return -EINVAL; @@ -168,18 +164,15 @@ static int ak5558_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) } /* set master/slave audio interface */ - format = snd_soc_component_read32(component, AK5558_02_CONTROL1); - format &= ~AK5558_DIF; - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: - format |= AK5558_DIF_I2S_MODE; + format = AK5558_DIF_I2S_MODE; break; case SND_SOC_DAIFMT_LEFT_J: - format |= AK5558_DIF_MSB_MODE; + format = AK5558_DIF_MSB_MODE; break; case SND_SOC_DAIFMT_DSP_B: - format |= AK5558_DIF_MSB_MODE; + format = AK5558_DIF_MSB_MODE; break; default: return -EINVAL; -- cgit v1.2.3-58-ga151 From 223bc10b84970fd772c105b550beeef3ac3502be Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 04:33:02 +0000 Subject: ASoC: pcm3168a: remove read-only status register from snd_kcontrol_new "DACn Zero Flag" and "ADCn Overflow Flag" are read-only status register. These information are needed for driver not for user. Let's remove these from snd_kcontrol_new. There is no detail explanation for these register on datasheet, but, judging from its behavior, it seems these register needs clock to read. Otherwise, I2C returns error. Therefor, amixer command will be failed under non working timing. Without this patch, user often fail amixer command. This patch is tested on R-Car H3 ulcb-kf board, SSI3/4 TDM sound. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3168a.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index 5f4327f5b725..115f21658222 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -133,10 +133,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { SOC_DOUBLE("DAC2 Invert Switch", PCM3168A_DAC_INV, 2, 3, 1, 0), SOC_DOUBLE("DAC3 Invert Switch", PCM3168A_DAC_INV, 4, 5, 1, 0), SOC_DOUBLE("DAC4 Invert Switch", PCM3168A_DAC_INV, 6, 7, 1, 0), - SOC_DOUBLE_STS("DAC1 Zero Flag", PCM3168A_DAC_ZERO, 0, 1, 1, 0), - SOC_DOUBLE_STS("DAC2 Zero Flag", PCM3168A_DAC_ZERO, 2, 3, 1, 0), - SOC_DOUBLE_STS("DAC3 Zero Flag", PCM3168A_DAC_ZERO, 4, 5, 1, 0), - SOC_DOUBLE_STS("DAC4 Zero Flag", PCM3168A_DAC_ZERO, 6, 7, 1, 0), SOC_ENUM("DAC Volume Control Type", pcm3168a_dac_volume_type), SOC_ENUM("DAC Volume Rate Multiplier", pcm3168a_dac_att_mult), SOC_ENUM("DAC De-Emphasis", pcm3168a_dac_demp), @@ -176,9 +172,6 @@ static const struct snd_kcontrol_new pcm3168a_snd_controls[] = { SOC_DOUBLE("ADC1 Mute Switch", PCM3168A_ADC_MUTE, 0, 1, 1, 0), SOC_DOUBLE("ADC2 Mute Switch", PCM3168A_ADC_MUTE, 2, 3, 1, 0), SOC_DOUBLE("ADC3 Mute Switch", PCM3168A_ADC_MUTE, 4, 5, 1, 0), - SOC_DOUBLE_STS("ADC1 Overflow Flag", PCM3168A_ADC_OV, 0, 1, 1, 0), - SOC_DOUBLE_STS("ADC2 Overflow Flag", PCM3168A_ADC_OV, 2, 3, 1, 0), - SOC_DOUBLE_STS("ADC3 Overflow Flag", PCM3168A_ADC_OV, 4, 5, 1, 0), SOC_ENUM("ADC Volume Control Type", pcm3168a_adc_volume_type), SOC_ENUM("ADC Volume Rate Multiplier", pcm3168a_adc_att_mult), SOC_ENUM("ADC Overflow Flag Polarity", pcm3168a_adc_ov_pol), -- cgit v1.2.3-58-ga151 From 2c2596f3ab25008adbc12059321cadbf5264e7bb Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 5 Nov 2018 15:10:11 +0200 Subject: ASoC: omap: Remove unused machine driver for AM3517-evm The audio support is done via simple-audio-card via DT, the custom driver is no longer needed. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/omap/Kconfig | 9 --- sound/soc/omap/Makefile | 2 - sound/soc/omap/am3517evm.c | 141 --------------------------------------------- 3 files changed, 152 deletions(-) delete mode 100644 sound/soc/omap/am3517evm.c (limited to 'sound') diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index 6dccea6fdaeb..3d418482d242 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig @@ -78,15 +78,6 @@ config SND_OMAP_SOC_OSK5912 help Say Y if you want to add support for SoC audio on osk5912. -config SND_OMAP_SOC_AM3517EVM - tristate "SoC Audio support for OMAP3517 / AM3517 EVM" - depends on SND_SDMA_SOC && MACH_OMAP3517EVM && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC23_I2C - help - Say Y if you want to add support for SoC audio on the OMAP3517 / AM3517 - EVM. - config SND_OMAP_SOC_OMAP_TWL4030 tristate "SoC Audio support for TI SoC based boards with twl4030 codec" depends on TWL4030_CORE && SND_SDMA_SOC diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index 53eba3413485..af50cdd6eea9 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -17,7 +17,6 @@ snd-soc-n810-objs := n810.o snd-soc-rx51-objs := rx51.o snd-soc-ams-delta-objs := ams-delta.o snd-soc-osk5912-objs := osk5912.o -snd-soc-am3517evm-objs := am3517evm.o snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o snd-soc-omap-twl4030-objs := omap-twl4030.o snd-soc-omap3pandora-objs := omap3pandora.o @@ -26,7 +25,6 @@ obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o -obj-$(CONFIG_SND_OMAP_SOC_AM3517EVM) += snd-soc-am3517evm.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c deleted file mode 100644 index d5651026ec10..000000000000 --- a/sound/soc/omap/am3517evm.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - * am3517evm.c -- ALSA SoC support for OMAP3517 / AM3517 EVM - * - * Author: Anuj Aggarwal - * - * Based on sound/soc/omap/beagle.c by Steve Sakoman - * - * Copyright (C) 2009 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "omap-mcbsp.h" - -#include "../codecs/tlv320aic23.h" - -#define CODEC_CLOCK 12000000 - -static int am3517evm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int ret; - - /* Set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - CODEC_CLOCK, SND_SOC_CLOCK_IN); - if (ret < 0) - printk(KERN_ERR "can't set codec system clock\n"); - - return ret; -} - -static const struct snd_soc_ops am3517evm_ops = { - .hw_params = am3517evm_hw_params, -}; - -/* am3517evm machine dapm widgets */ -static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { - SND_SOC_DAPM_HP("Line Out", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), - SND_SOC_DAPM_MIC("Mic In", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* Line Out connected to LLOUT, RLOUT */ - {"Line Out", NULL, "LOUT"}, - {"Line Out", NULL, "ROUT"}, - - {"LLINEIN", NULL, "Line In"}, - {"RLINEIN", NULL, "Line In"}, - - {"MICIN", NULL, "Mic In"}, -}; - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link am3517evm_dai = { - .name = "TLV320AIC23", - .stream_name = "AIC23", - .cpu_dai_name = "omap-mcbsp.1", - .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "omap-mcbsp.1", - .codec_name = "tlv320aic23-codec.2-001a", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &am3517evm_ops, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_am3517evm = { - .name = "am3517evm", - .owner = THIS_MODULE, - .dai_link = &am3517evm_dai, - .num_links = 1, - - .dapm_widgets = tlv320aic23_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static struct platform_device *am3517evm_snd_device; - -static int __init am3517evm_soc_init(void) -{ - int ret; - - if (!machine_is_omap3517evm()) - return -ENODEV; - pr_info("OMAP3517 / AM3517 EVM SoC init\n"); - - am3517evm_snd_device = platform_device_alloc("soc-audio", -1); - if (!am3517evm_snd_device) { - printk(KERN_ERR "Platform device allocation failed\n"); - return -ENOMEM; - } - - platform_set_drvdata(am3517evm_snd_device, &snd_soc_am3517evm); - - ret = platform_device_add(am3517evm_snd_device); - if (ret) - goto err1; - - return 0; - -err1: - printk(KERN_ERR "Unable to add platform device\n"); - platform_device_put(am3517evm_snd_device); - - return ret; -} - -static void __exit am3517evm_soc_exit(void) -{ - platform_device_unregister(am3517evm_snd_device); -} - -module_init(am3517evm_soc_init); -module_exit(am3517evm_soc_exit); - -MODULE_AUTHOR("Anuj Aggarwal "); -MODULE_DESCRIPTION("ALSA SoC OMAP3517 / AM3517 EVM"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 25540f68c871d0ed3771b552eb640a1130880ab6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 2 Nov 2018 12:20:39 +0100 Subject: ASoC: pxa: change ac97 dependencies Enabling both the old AC97_BUS code and the new AC97_BUS_COMPAT causes problems because both modules provide an exported snd_ac97_reset() function. I had tried to fix the problem of having both coexist earlier, but my patch only prevented them from being built-in. This is because of a special Kconfig feature that lets a symbol have a dependency on another one being disabled, but still allow both to be loadable modules. Changing the dependency to =n avoids that problem, now we can only build the new driver if the old one is completely disabled. If we could figure out a way to let rename one of the reset functions and have each driver link to exactly the old or the compat code, that would also work, but I could not find if that's possible. Fixes: bec5ecdf41d4 ("ASoC: pxa: avoid AC97_BUS build warning") Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 943b44de1464..6075fc32e6b9 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -79,7 +79,7 @@ config SND_PXA2XX_SOC_TOSA tristate "SoC AC97 Audio support for Tosa" depends on SND_PXA2XX_SOC && MACH_TOSA depends on MFD_TC6393XB - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -89,7 +89,7 @@ config SND_PXA2XX_SOC_TOSA config SND_PXA2XX_SOC_E740 tristate "SoC AC97 Audio support for e740" depends on SND_PXA2XX_SOC && MACH_E740 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_SOC_WM9705 select SND_PXA2XX_SOC_AC97 help @@ -99,7 +99,7 @@ config SND_PXA2XX_SOC_E740 config SND_PXA2XX_SOC_E750 tristate "SoC AC97 Audio support for e750" depends on SND_PXA2XX_SOC && MACH_E750 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_SOC_WM9705 select SND_PXA2XX_SOC_AC97 help @@ -109,7 +109,7 @@ config SND_PXA2XX_SOC_E750 config SND_PXA2XX_SOC_E800 tristate "SoC AC97 Audio support for e800" depends on SND_PXA2XX_SOC && MACH_E800 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_SOC_WM9712 select SND_PXA2XX_SOC_AC97 help @@ -120,7 +120,7 @@ config SND_PXA2XX_SOC_EM_X270 tristate "SoC Audio support for CompuLab EM-x270, eXeda and CM-X300" depends on SND_PXA2XX_SOC && (MACH_EM_X270 || MACH_EXEDA || \ MACH_CM_X300) - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -131,7 +131,7 @@ config SND_PXA2XX_SOC_PALM27X bool "SoC Audio support for Palm T|X, T5, E2 and LifeDrive" depends on SND_PXA2XX_SOC && (MACH_PALMLD || MACH_PALMTX || \ MACH_PALMT5 || MACH_PALMTE2) - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9712 help @@ -161,7 +161,7 @@ config SND_SOC_TTC_DKB config SND_SOC_ZYLONITE tristate "SoC Audio support for Marvell Zylonite" depends on SND_PXA2XX_SOC && MACH_ZYLONITE - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_PXA_SOC_SSP select SND_SOC_WM9713 @@ -201,7 +201,7 @@ config SND_PXA2XX_SOC_MAGICIAN config SND_PXA2XX_SOC_MIOA701 tristate "SoC Audio support for MIO A701" depends on SND_PXA2XX_SOC && MACH_MIOA701 - depends on !AC97_BUS + depends on AC97_BUS=n select SND_PXA2XX_SOC_AC97 select SND_SOC_WM9713 help -- cgit v1.2.3-58-ga151 From 842bb5135f1016151235413726e4956210a79664 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Nov 2018 16:34:49 -0500 Subject: ASoC: Intel: use standard interface for Hdaudio machine driver Don't rely on internal Skylake-specific data structures, use generic interface to let other drivers use the same machine driver as is, e.g. SOF to support HDaudio codecs and HDMI outputs. Tested on LeafHill CRB board, no regression seen with this change. Reviewed-by: Andy Shevchenko Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/skl_hda_dsp_generic.c | 20 ++++++++++---------- sound/soc/intel/skylake/skl.c | 10 +++------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index b415dd4c85f5..b6f287fa9505 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -12,8 +12,8 @@ #include #include #include +#include #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #include "skl_hda_dsp_common.h" static const struct snd_soc_dapm_widget skl_hda_widgets[] = { @@ -101,17 +101,17 @@ static struct snd_soc_card hda_soc_card = { #define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) #define IDISP_CODEC_MASK 0x4 -static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) +static int skl_hda_fill_card_info(struct snd_soc_acpi_mach_params *mach_params) { struct snd_soc_card *card = &hda_soc_card; struct snd_soc_dai_link *dai_link; u32 codec_count, codec_mask; int i, num_links, num_route; - codec_mask = pdata->codec_mask; + codec_mask = mach_params->codec_mask; codec_count = hweight_long(codec_mask); - if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { + if (codec_count == 1 && codec_mask & IDISP_CODEC_MASK) { num_links = IDISP_DAI_COUNT; num_route = IDISP_ROUTE_COUNT; } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { @@ -127,14 +127,14 @@ static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) card->num_dapm_routes = num_route; for_each_card_prelinks(card, i, dai_link) - dai_link->platform_name = pdata->platform; + dai_link->platform_name = mach_params->platform; return 0; } static int skl_hda_audio_probe(struct platform_device *pdev) { - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; struct skl_hda_private *ctx; int ret; @@ -146,11 +146,11 @@ static int skl_hda_audio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - pdata = dev_get_drvdata(&pdev->dev); - if (!pdata) + mach = dev_get_drvdata(&pdev->dev); + if (!mach) return -EINVAL; - ret = skl_hda_fill_card_info(pdata); + ret = skl_hda_fill_card_info(&mach->mach_params); if (ret < 0) { dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); return ret; @@ -158,7 +158,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) ctx->pcm_count = hda_soc_card.num_links; ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */ - ctx->platform_name = pdata->platform; + ctx->platform_name = mach->mach_params.platform; hda_soc_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&hda_soc_card, ctx); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 29225623b4b4..ebab04b51bc1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -525,7 +525,6 @@ static int skl_machine_device_register(struct skl *skl) { struct snd_soc_acpi_mach *mach = skl->mach; struct hdac_bus *bus = skl_to_bus(skl); - struct skl_machine_pdata *pdata; struct platform_device *pdev; int ret; @@ -542,12 +541,9 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } - if (mach->pdata) { - pdata = (struct skl_machine_pdata *)mach->pdata; - pdata->platform = dev_name(bus->dev); - pdata->codec_mask = bus->codec_mask; - dev_set_drvdata(&pdev->dev, mach->pdata); - } + mach->mach_params.platform = dev_name(bus->dev); + mach->mach_params.codec_mask = bus->codec_mask; + dev_set_drvdata(&pdev->dev, mach); skl->i2s_dev = pdev; -- cgit v1.2.3-58-ga151 From 3ee1cd4f81e15f51638db80fb9f1371b3bdf05ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Nov 2018 16:34:50 -0500 Subject: ASoC: Intel: use standard interface for Atom machine drivers Don't rely on internal Atom/SST-specific data structures, use generic interface to let other drivers use the same machine drivers as is, e.g. SOF to support BYT-CR devices Tested-by: Hans de Goede Reviewed-by: Andy Shevchenko Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_acpi.c | 4 ++++ sound/soc/intel/boards/bytcr_rt5640.c | 6 +----- sound/soc/intel/boards/bytcr_rt5651.c | 6 +----- sound/soc/intel/boards/cht_bsw_rt5645.c | 6 +----- 4 files changed, 7 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index c90b04cc071d..ac542535b9d5 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -341,6 +341,10 @@ static int sst_acpi_probe(struct platform_device *pdev) byt_rvp_platform_data.res_info = &bytcr_res_info; } + /* update machine parameters */ + mach->mach_params.acpi_ipc_irq_index = + pdata->res_info->acpi_ipc_irq_index; + plat_dev = platform_device_register_data(dev, pdata->platform, -1, NULL, 0); if (IS_ERR(plat_dev)) { diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 8587bd3d1cc1..09591144ea7d 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -1152,10 +1151,7 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { - struct sst_platform_info *p_info = mach->pdata; - const struct sst_res_info *res_info = p_info->res_info; - - if (res_info->acpi_ipc_irq_index == 0) + if (mach->mach_params.acpi_ipc_irq_index == 0) is_bytcr = true; } diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c index c44298130720..e528995668b7 100644 --- a/sound/soc/intel/boards/bytcr_rt5651.c +++ b/sound/soc/intel/boards/bytcr_rt5651.c @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -920,10 +919,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (x86_match_cpu(baytrail_cpu_ids)) { - struct sst_platform_info *p_info = mach->pdata; - const struct sst_res_info *res_info = p_info->res_info; - - if (res_info->acpi_ipc_irq_index == 0) + if (mach->mach_params.acpi_ipc_irq_index == 0) is_bytcr = true; } diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c index f5a5ea6a093c..250a356a0cbf 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5645.c +++ b/sound/soc/intel/boards/cht_bsw_rt5645.c @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -585,10 +584,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) * (will be overridden if DMI quirk is detected) */ if (is_valleyview()) { - struct sst_platform_info *p_info = mach->pdata; - const struct sst_res_info *res_info = p_info->res_info; - - if (res_info->acpi_ipc_irq_index == 0) + if (mach->mach_params.acpi_ipc_irq_index == 0) is_bytcr = true; } -- cgit v1.2.3-58-ga151 From 48bf41a2be51ef3f67b60f85bf75cecfb57884ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Nov 2018 16:34:51 -0500 Subject: ASoC: Intel: boards: fix Skylake typo s/skylaye/skylake Reviewed-by: Andy Shevchenko Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/kbl_da7219_max98927.c | 6 +++--- sound/soc/intel/boards/kbl_rt5663_max98927.c | 4 ++-- sound/soc/intel/boards/skl_nau88l25_max98357a.c | 4 ++-- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 3fa1c3ca6d37..3bc78e3a91ee 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -441,7 +441,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) } -static struct snd_soc_ops skylaye_refcap_ops = { +static struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; @@ -525,7 +525,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", @@ -736,7 +736,7 @@ static struct snd_soc_dai_link kabylake_max98927_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 99e1320c485f..6ea969c0a5fb 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -586,7 +586,7 @@ static int kabylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static struct snd_soc_ops skylaye_refcap_ops = { +static struct snd_soc_ops skylake_refcap_ops = { .startup = kabylake_refcap_startup, }; @@ -655,7 +655,7 @@ static struct snd_soc_dai_link kabylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [KBL_DPCM_AUDIO_DMIC_CP] = { .name = "Kbl Audio DMIC cap", diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index d31482b8c9bb..552958ce736d 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -400,7 +400,7 @@ static int skylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static const struct snd_soc_ops skylaye_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = skylake_refcap_startup, }; @@ -447,7 +447,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index e877bb60beb1..f985b30a1d0e 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -449,7 +449,7 @@ static int skylake_refcap_startup(struct snd_pcm_substream *substream) &constraints_16000); } -static const struct snd_soc_ops skylaye_refcap_ops = { +static const struct snd_soc_ops skylake_refcap_ops = { .startup = skylake_refcap_startup, }; @@ -496,7 +496,7 @@ static struct snd_soc_dai_link skylake_dais[] = { .dpcm_capture = 1, .nonatomic = 1, .dynamic = 1, - .ops = &skylaye_refcap_ops, + .ops = &skylake_refcap_ops, }, [SKL_DPCM_AUDIO_DMIC_CP] = { .name = "Skl Audio DMIC cap", -- cgit v1.2.3-58-ga151 From 972b0d456e645ea8fd3fdc70f95f0e41c27c0870 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Nov 2018 16:34:52 -0500 Subject: ASoC: Intel: remove GFP_ATOMIC, use GFP_KERNEL GFP_ATOMIC is not required on any Intel drivers, use GFP_KERNEL instead. A first cleanup was merged in April but missed a number occurrences and new ones were added by copy/paste inertia. While we are at it, make checkpatch happy with a sizeof(*msg) Reviewed-by: Andy Shevchenko Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_pvt.c | 4 ++-- sound/soc/intel/boards/cht_bsw_rt5672.c | 2 +- sound/soc/intel/boards/glk_rt5682_max98357a.c | 2 +- sound/soc/intel/boards/kbl_da7219_max98927.c | 2 +- sound/soc/intel/boards/skl_hda_dsp_generic.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index af93244b4868..00a37a09dc9b 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -166,11 +166,11 @@ int sst_create_ipc_msg(struct ipc_post **arg, bool large) { struct ipc_post *msg; - msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC); + msg = kzalloc(sizeof(*msg), GFP_KERNEL); if (!msg) return -ENOMEM; if (large) { - msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC); + msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_KERNEL); if (!msg->mailbox_data) { kfree(msg); return -ENOMEM; diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c index 51f0d45d6f8f..9de64f447e7b 100644 --- a/sound/soc/intel/boards/cht_bsw_rt5672.c +++ b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -403,7 +403,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev) const char *i2c_name; int i; - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC); + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; diff --git a/sound/soc/intel/boards/glk_rt5682_max98357a.c b/sound/soc/intel/boards/glk_rt5682_max98357a.c index c4b94e2617c5..c74c4f17316f 100644 --- a/sound/soc/intel/boards/glk_rt5682_max98357a.c +++ b/sound/soc/intel/boards/glk_rt5682_max98357a.c @@ -603,7 +603,7 @@ static int geminilake_audio_probe(struct platform_device *pdev) { struct glk_card_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 3bc78e3a91ee..58eb0fe69978 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -935,7 +935,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index b6f287fa9505..15c502d6774d 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -140,7 +140,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) dev_dbg(&pdev->dev, "%s: entry\n", __func__); - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; -- cgit v1.2.3-58-ga151 From 97bb91ae2f3325fd06ea2c28fb8b5b4e023b4b5d Mon Sep 17 00:00:00 2001 From: Bard liao Date: Thu, 1 Nov 2018 16:34:53 -0500 Subject: ASoC: Intel: common: add SOF information for APL RVP Add firmware/topology information for APL RVP Reviewed-by: Andy Shevchenko Signed-off-by: Bard liao Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index f39386e540d3..2756fa4ab552 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -19,6 +19,9 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { .id = "INT343A", .drv_name = "bxt_alc298s_i2s", .fw_filename = "intel/dsp_fw_bxtn.bin", + .sof_fw_filename = "intel/sof-apl.ri", + .sof_tplg_filename = "intel/sof-apl-rt298.tplg", + .asoc_plat_name = "0000:00:0e.0", }, { .id = "DLGS7219", -- cgit v1.2.3-58-ga151 From 935ff8007f5efd24e995d26ebf875ee2c787465e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 1 Nov 2018 16:34:54 -0500 Subject: ASoC: Intel: common: add quirk for APL RVP boards For some reason the RVP/LeafHill SSDT exposes an INT34C3 ID which is used on other boards to point to the TDF8532 amplifier. Yay BIOS. Add a DMI-quirk to ignore this ID and check for other valid machine driver descriptors. Reviewed-by: Andy Shevchenko Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-bxt-match.c | 33 +++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c index 2756fa4ab552..61dedc103b19 100644 --- a/sound/soc/intel/common/soc-acpi-intel-bxt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-bxt-match.c @@ -6,9 +6,41 @@ * */ +#include #include #include +enum { + APL_RVP, +}; + +static const struct dmi_system_id apl_table[] = { + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Intel Corp."), + DMI_MATCH(DMI_BOARD_NAME, "Apollolake RVP1A"), + }, + .driver_data = (void *)(APL_RVP), + }, + {} +}; + +static struct snd_soc_acpi_mach *apl_quirk(void *arg) +{ + struct snd_soc_acpi_mach *mach = arg; + const struct dmi_system_id *dmi_id; + unsigned long apl_machine_id; + + dmi_id = dmi_first_match(apl_table); + if (dmi_id) { + apl_machine_id = (unsigned long)dmi_id->driver_data; + if (apl_machine_id == APL_RVP) + return NULL; + } + + return mach; +} + static struct snd_soc_acpi_codecs bxt_codecs = { .num_codecs = 1, .codecs = {"MX98357A"} @@ -50,6 +82,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[] = { { .id = "INT34C3", .drv_name = "bxt_tdf8532", + .machine_quirk = apl_quirk, .sof_fw_filename = "intel/sof-apl.ri", .sof_tplg_filename = "intel/sof-apl-tdf8532.tplg", .asoc_plat_name = "0000:00:0e.0", -- cgit v1.2.3-58-ga151 From 39ee0b3401feac5932dca6a98d538438aa5f6236 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 04:36:27 +0000 Subject: ASoC: audio-graph-card: remove endpoint bidirectional check DTC commit df536831d02c ("checks: add graph binding checks") is checking endpoint bidirectional, and it is upstreamed to linux by commit 50aafd60898a ("scripts/dtc: Update to upstream version v1.4.6-21-g84e414b0b5bc"). Let's remove own bidirectional check Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 25c819e402e1..26eb6ff9b9f3 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -167,16 +167,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); - struct device_node *rcpu_ep = of_graph_get_remote_endpoint(codec_ep); int ret; - if (rcpu_ep != cpu_ep) { - dev_err(dev, "remote-endpoint mismatch (%s/%s/%s)\n", - cpu_ep->name, codec_ep->name, rcpu_ep->name); - ret = -EINVAL; - goto dai_link_of_err; - } - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) @@ -228,7 +220,6 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, dai_link_of_err: of_node_put(cpu_ep); - of_node_put(rcpu_ep); of_node_put(codec_ep); return ret; -- cgit v1.2.3-58-ga151 From 1a115f3a5418459aabc10628eb25dff1bae64bca Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 04:36:41 +0000 Subject: ASoC: audio-graph-scu-card: remove endpoint bidirectional check DTC commit df536831d02c ("checks: add graph binding checks") is checking endpoint bidirectional, and it is upstreamed to linux by commit 50aafd60898a ("scripts/dtc: Update to upstream version v1.4.6-21-g84e414b0b5bc"). Let's remove own bidirectional check Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index b83bb31021a9..ba7b0cf028b7 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -186,7 +186,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device_node *cpu_port; struct device_node *cpu_ep; struct device_node *codec_ep; - struct device_node *rcpu_ep; struct device_node *codec_port; struct device_node *codec_port_old; unsigned int daifmt = 0; @@ -217,22 +216,13 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) cpu_port = it.node; cpu_ep = of_get_next_child(cpu_port, NULL); codec_ep = of_graph_get_remote_endpoint(cpu_ep); - rcpu_ep = of_graph_get_remote_endpoint(codec_ep); of_node_put(cpu_ep); of_node_put(codec_ep); - of_node_put(rcpu_ep); if (!codec_ep) continue; - if (rcpu_ep != cpu_ep) { - dev_err(dev, "remote-endpoint missmatch (%s/%s/%s)\n", - cpu_ep->name, codec_ep->name, rcpu_ep->name); - ret = -EINVAL; - goto parse_of_err; - } - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &daifmt); if (ret < 0) { -- cgit v1.2.3-58-ga151 From 5049a6e7316cdbf4370d5479e9628d8f5e906635 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 04:36:55 +0000 Subject: ASoC: audio-graph-scu-card: remove error check which never happen Current driver is checking situation that can not happen. This patch removes over-kill check Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index ba7b0cf028b7..2ba35bdd3b38 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -250,9 +250,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) of_node_put(codec_port); if (codec) { - if (!codec_port) - continue; - if (codec_port_old == codec_port) continue; @@ -308,11 +305,7 @@ static int asoc_graph_get_dais_count(struct device *dev) of_node_put(codec_ep); of_node_put(codec_port); - if (cpu_ep) - count++; - - if (!codec_port) - continue; + count++; if (codec_port_old == codec_port) continue; -- cgit v1.2.3-58-ga151 From c8b222bf110ccb54d787f695fb3eb2fb6b723903 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:18:58 +0000 Subject: ASoC: rsnd: remove endpoint bidirectional check DTC commit df536831d02c ("checks: add graph binding checks") is checking endpoint bidirectional, and it is upstreamed to linux by commit 50aafd60898a ("scripts/dtc: Update to upstream version v1.4.6-21-g84e414b0b5bc"). Let's remove own bidirectional check Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index a032819044f7..10a53e2adcad 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -1095,11 +1095,7 @@ void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, int dai_i) { struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); - struct device_node *remote_ep; - - remote_ep = of_graph_get_remote_endpoint(endpoint); - if (!remote_ep) - return; + struct device_node *remote_ep = of_graph_get_remote_endpoint(endpoint); __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep); __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep); -- cgit v1.2.3-58-ga151 From a607280293b4b804fa080c75412b256ec37ce04c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:19:19 +0000 Subject: ASoC: rsnd: rename rsnd_runtime_is_ssi_xxx() Current rsnd driver has rsnd_runtime_is_ssi_xxx() functions, but it is not only related to SSI, thus, it is misunderstandable. This patch renames it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 6 +++--- sound/soc/sh/rcar/rsnd.h | 4 ++-- sound/soc/sh/rcar/ssi.c | 4 ++-- sound/soc/sh/rcar/ssiu.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 5e100122391a..cbe846e4438f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -287,7 +287,7 @@ int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, rsnd_runtime_channel_original_with_params(io, params); /* Use Multi SSI */ - if (rsnd_runtime_is_ssi_multi(io)) + if (rsnd_runtime_is_multi_ssi(io)) chan /= rsnd_rdai_ssi_lane_get(rdai); /* TDM Extend Mode needs 8ch */ @@ -297,7 +297,7 @@ int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, return chan; } -int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) +int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); int lane = rsnd_rdai_ssi_lane_get(rdai); @@ -308,7 +308,7 @@ int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io) return (chan > 2) && (lane > 1); } -int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io) +int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io) { return rsnd_runtime_channel_for_ssi(io) >= 6; } diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 28bd90ab5a99..4b547c6f62a2 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -431,8 +431,8 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, rsnd_runtime_channel_for_ssi_with_params(io, NULL) int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, struct snd_pcm_hw_params *params); -int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io); -int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io); +int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io); +int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); /* * DT diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 10a53e2adcad..f5bda1617b02 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -223,7 +223,7 @@ static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io) u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io) { - if (rsnd_runtime_is_ssi_multi(io)) + if (rsnd_runtime_is_multi_ssi(io)) return rsnd_ssi_multi_slaves(io); return 0; @@ -383,7 +383,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, u32 wsr = ssi->wsr; int is_tdm; - is_tdm = rsnd_runtime_is_ssi_tdm(io); + is_tdm = rsnd_runtime_is_tdm(io); cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai); diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index ebcb826b849f..645340ee4222 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -145,7 +145,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, ssiu->usrcnt++; - if (rsnd_runtime_is_ssi_tdm(io)) { + if (rsnd_runtime_is_tdm(io)) { /* * TDM Extend Mode * see -- cgit v1.2.3-58-ga151 From 9328882e9dc6b3ae6d11eaf6dbdeb45dfbb2d38f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:19:33 +0000 Subject: ASoC: rsnd: use defined io_playback/io_capture rdai->playback/rdai->capture are defined as io_playback/io_capture on __rsnd_dai_probe(). Let's use it instead of original one. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index cbe846e4438f..cb592191bc86 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1091,24 +1091,24 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, drv->name = rdai->name; drv->ops = &rsnd_soc_dai_ops; - snprintf(rdai->playback.name, RSND_DAI_NAME_SIZE, + snprintf(io_playback->name, RSND_DAI_NAME_SIZE, "DAI%d Playback", dai_i); drv->playback.rates = RSND_RATES; drv->playback.formats = RSND_FMTS; drv->playback.channels_min = 2; drv->playback.channels_max = 8; - drv->playback.stream_name = rdai->playback.name; + drv->playback.stream_name = io_playback->name; - snprintf(rdai->capture.name, RSND_DAI_NAME_SIZE, + snprintf(io_capture->name, RSND_DAI_NAME_SIZE, "DAI%d Capture", dai_i); drv->capture.rates = RSND_RATES; drv->capture.formats = RSND_FMTS; drv->capture.channels_min = 2; drv->capture.channels_max = 8; - drv->capture.stream_name = rdai->capture.name; + drv->capture.stream_name = io_capture->name; - rdai->playback.rdai = rdai; - rdai->capture.rdai = rdai; + io_playback->rdai = rdai; + io_capture->rdai = rdai; rsnd_rdai_channels_set(rdai, 2); /* default 2ch */ rsnd_rdai_ssi_lane_set(rdai, 1); /* default 1lane */ rsnd_rdai_width_set(rdai, 32); /* default 32bit width */ -- cgit v1.2.3-58-ga151 From beed78aeeb10214798b1fd5f1dd05e0b7199d65f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:19:48 +0000 Subject: ASoC: rsnd: move HDMI information from ssi.c to core.c Current driver is supporting HDMI output, and its information are handled under ssi.c. But, it is stream information. Let's move it from ssi.c to core.c. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 30 +++++++++++++++++++++++- sound/soc/sh/rcar/rsnd.h | 13 +++++------ sound/soc/sh/rcar/ssi.c | 60 +----------------------------------------------- sound/soc/sh/rcar/ssiu.c | 13 ++++------- 4 files changed, 41 insertions(+), 75 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index cb592191bc86..1758fecaed4c 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1005,6 +1005,29 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .prepare = rsnd_soc_dai_prepare, }; +static void rsnd_parse_connect_graph(struct rsnd_priv *priv, + struct rsnd_dai_stream *io, + struct device_node *endpoint) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *remote_node = of_graph_get_remote_port_parent(endpoint); + + if (!rsnd_io_to_mod_ssi(io)) + return; + + /* HDMI0 */ + if (strstr(remote_node->full_name, "hdmi@fead0000")) { + rsnd_flags_set(io, RSND_STREAM_HDMI0); + dev_dbg(dev, "%s connected to HDMI0\n", io->name); + } + + /* HDMI1 */ + if (strstr(remote_node->full_name, "hdmi@feae0000")) { + rsnd_flags_set(io, RSND_STREAM_HDMI1); + dev_dbg(dev, "%s connected to HDMI1\n", io->name); + } +} + void rsnd_parse_connect_common(struct rsnd_dai *rdai, struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id), struct device_node *node, @@ -1177,7 +1200,12 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) if (is_graph) { for_each_endpoint_of_node(dai_node, dai_np) { __rsnd_dai_probe(priv, dai_np, dai_i); - rsnd_ssi_parse_hdmi_connection(priv, dai_np, dai_i); + if (rsnd_is_gen3(priv)) { + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + + rsnd_parse_connect_graph(priv, &rdai->playback, dai_np); + rsnd_parse_connect_graph(priv, &rdai->capture, dai_np); + } dai_i++; } } else { diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4b547c6f62a2..9aeb5478fe02 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -458,7 +458,13 @@ struct rsnd_dai_stream { struct rsnd_dai *rdai; struct device *dmac_dev; /* for IPMMU */ u32 parent_ssi_status; + u32 flags; }; + +/* flags */ +#define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */ +#define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */ + #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) #define rsnd_io_to_mod_ssiu(io) rsnd_io_to_mod((io), RSND_MOD_SSIU) @@ -718,13 +724,6 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); int rsnd_ssi_get_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); -#define RSND_SSI_HDMI_PORT0 0xf0 -#define RSND_SSI_HDMI_PORT1 0xf1 -int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io); -void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, - struct device_node *endpoint, - int dai_i); - #define rsnd_ssi_is_pin_sharing(io) \ __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io)) int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index f5bda1617b02..e91d3c942ea0 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -99,9 +99,7 @@ struct rsnd_ssi { /* flags */ #define RSND_SSI_CLK_PIN_SHARE (1 << 0) #define RSND_SSI_NO_BUSIF (1 << 1) /* SSI+DMA without BUSIF */ -#define RSND_SSI_HDMI0 (1 << 2) /* for HDMI0 */ -#define RSND_SSI_HDMI1 (1 << 3) /* for HDMI1 */ -#define RSND_SSI_PROBED (1 << 4) +#define RSND_SSI_PROBED (1 << 2) #define for_each_rsnd_ssi(pos, priv, i) \ for (i = 0; \ @@ -119,20 +117,6 @@ struct rsnd_ssi { (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) #define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod)) -int rsnd_ssi_hdmi_port(struct rsnd_dai_stream *io) -{ - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - - if (rsnd_flags_has(ssi, RSND_SSI_HDMI0)) - return RSND_SSI_HDMI_PORT0; - - if (rsnd_flags_has(ssi, RSND_SSI_HDMI1)) - return RSND_SSI_HDMI_PORT1; - - return 0; -} - int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); @@ -1059,48 +1043,6 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai, of_node_put(node); } -static void __rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, - struct rsnd_dai_stream *io, - struct device_node *remote_ep) -{ - struct device *dev = rsnd_priv_to_dev(priv); - struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); - struct rsnd_ssi *ssi; - struct device_node *remote_node = of_graph_get_port_parent(remote_ep); - - /* support Gen3 only */ - if (!rsnd_is_gen3(priv)) - return; - - if (!mod) - return; - - ssi = rsnd_mod_to_ssi(mod); - - /* HDMI0 */ - if (strstr(remote_node->full_name, "hdmi@fead0000")) { - rsnd_flags_set(ssi, RSND_SSI_HDMI0); - dev_dbg(dev, "%s connected to HDMI0\n", rsnd_mod_name(mod)); - } - - /* HDMI1 */ - if (strstr(remote_node->full_name, "hdmi@feae0000")) { - rsnd_flags_set(ssi, RSND_SSI_HDMI1); - dev_dbg(dev, "%s connected to HDMI1\n", rsnd_mod_name(mod)); - } -} - -void rsnd_ssi_parse_hdmi_connection(struct rsnd_priv *priv, - struct device_node *endpoint, - int dai_i) -{ - struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); - struct device_node *remote_ep = of_graph_get_remote_endpoint(endpoint); - - __rsnd_ssi_parse_hdmi_connection(priv, &rdai->playback, remote_ep); - __rsnd_ssi_parse_hdmi_connection(priv, &rdai->capture, remote_ep); -} - struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) { if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv))) diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 645340ee4222..4f591008cfe5 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -135,7 +135,8 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int hdmi = rsnd_ssi_hdmi_port(io); + u32 has_hdmi0 = rsnd_flags_has(io, RSND_STREAM_HDMI0); + u32 has_hdmi1 = rsnd_flags_has(io, RSND_STREAM_HDMI1); int ret; u32 mode = 0; @@ -212,7 +213,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, } } - if (hdmi) { + if (has_hdmi0 || has_hdmi1) { enum rsnd_mod_type rsnd_ssi_array[] = { RSND_MOD_SSIM1, RSND_MOD_SSIM2, @@ -238,14 +239,10 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, rsnd_mod_id(pos) << shift; } - switch (hdmi) { - case RSND_SSI_HDMI_PORT0: + if (has_hdmi0) rsnd_mod_write(mod, HDMI0_SEL, val); - break; - case RSND_SSI_HDMI_PORT1: + if (has_hdmi1) rsnd_mod_write(mod, HDMI1_SEL, val); - break; - } } return 0; -- cgit v1.2.3-58-ga151 From c2aaaa57d44a195b71fe1e9ff0c6a80ac017495d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:20:10 +0000 Subject: ASoC: rsnd: handle DPCM converted rate/chan under core converted rate/chan are handled each rated module, but it will be used other module too. For examle, converted channel is currently used for CTU, but, it will be used for TDM Split mode, too. This patch move/merge SRC/CTU hw_param under core.c and handles converted rate/chan under rsnd_dai_stream. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 34 +++++++++++++++++++++++++++++++++- sound/soc/sh/rcar/ctu.c | 45 --------------------------------------------- sound/soc/sh/rcar/rsnd.h | 5 ++++- sound/soc/sh/rcar/src.c | 40 ++-------------------------------------- 4 files changed, 39 insertions(+), 85 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 1758fecaed4c..fcc5c2e9d191 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -269,7 +269,7 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io); if (ctu_mod) { - u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod); + u32 converted_chan = rsnd_io_converted_chan(io); if (converted_chan) return converted_chan; @@ -1225,8 +1225,40 @@ static int rsnd_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); + struct snd_soc_pcm_runtime *fe = substream->private_data; int ret; + /* + * rsnd assumes that it might be used under DPCM if user want to use + * channel / rate convert. Then, rsnd should be FE. + * And then, this function will be called *after* BE settings. + * this means, each BE already has fixuped hw_params. + * see + * dpcm_fe_dai_hw_params() + * dpcm_be_dai_hw_params() + */ + io->converted_rate = 0; + io->converted_chan = 0; + if (fe->dai_link->dynamic) { + struct rsnd_priv *priv = rsnd_io_to_priv(io); + struct device *dev = rsnd_priv_to_dev(priv); + struct snd_soc_dpcm *dpcm; + struct snd_pcm_hw_params *be_params; + int stream = substream->stream; + + for_each_dpcm_be(fe, stream, dpcm) { + be_params = &dpcm->hw_params; + if (params_channels(hw_params) != params_channels(be_params)) + io->converted_chan = params_channels(be_params); + if (params_rate(hw_params) != params_rate(be_params)) + io->converted_rate = params_rate(be_params); + } + if (io->converted_chan) + dev_dbg(dev, "convert channels = %d\n", io->converted_chan); + if (io->converted_rate) + dev_dbg(dev, "convert rate = %d\n", io->converted_rate); + } + ret = rsnd_dai_call(hw_params, io, substream, hw_params); if (ret) return ret; diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 6a948b1fd9ab..4dda7d968894 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -107,13 +107,6 @@ static void rsnd_ctu_halt(struct rsnd_mod *mod) rsnd_mod_write(mod, CTU_SWRSR, 0); } -int rsnd_ctu_converted_channel(struct rsnd_mod *mod) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - - return ctu->channels; -} - static int rsnd_ctu_probe_(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) @@ -233,43 +226,6 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod, return 0; } -static int rsnd_ctu_hw_params(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *fe_params) -{ - struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); - struct snd_soc_pcm_runtime *fe = substream->private_data; - - /* - * CTU assumes that it is used under DPCM if user want to use - * channel transfer. Then, CTU should be FE. - * And then, this function will be called *after* BE settings. - * this means, each BE already has fixuped hw_params. - * see - * dpcm_fe_dai_hw_params() - * dpcm_be_dai_hw_params() - */ - ctu->channels = 0; - if (fe->dai_link->dynamic) { - struct rsnd_priv *priv = rsnd_mod_to_priv(mod); - struct device *dev = rsnd_priv_to_dev(priv); - struct snd_soc_dpcm *dpcm; - struct snd_pcm_hw_params *be_params; - int stream = substream->stream; - - for_each_dpcm_be(fe, stream, dpcm) { - be_params = &dpcm->hw_params; - if (params_channels(fe_params) != params_channels(be_params)) - ctu->channels = params_channels(be_params); - } - - dev_dbg(dev, "CTU convert channels %d\n", ctu->channels); - } - - return 0; -} - static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct snd_soc_pcm_runtime *rtd) @@ -357,7 +313,6 @@ static struct rsnd_mod_ops rsnd_ctu_ops = { .probe = rsnd_ctu_probe_, .init = rsnd_ctu_init, .quit = rsnd_ctu_quit, - .hw_params = rsnd_ctu_hw_params, .pcm_new = rsnd_ctu_pcm_new, .get_status = rsnd_mod_get_status, .id = rsnd_ctu_id, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 9aeb5478fe02..7e54edcc2ce8 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -457,6 +457,8 @@ struct rsnd_dai_stream { struct rsnd_mod *dma; struct rsnd_dai *rdai; struct device *dmac_dev; /* for IPMMU */ + u32 converted_rate; /* converted sampling rate */ + int converted_chan; /* converted channels */ u32 parent_ssi_status; u32 flags; }; @@ -479,6 +481,8 @@ struct rsnd_dai_stream { #define rsnd_io_is_play(io) (&rsnd_io_to_rdai(io)->playback == io) #define rsnd_io_to_runtime(io) ((io)->substream ? \ (io)->substream->runtime : NULL) +#define rsnd_io_converted_rate(io) ((io)->converted_rate) +#define rsnd_io_converted_chan(io) ((io)->converted_chan) int rsnd_io_is_working(struct rsnd_dai_stream *io); struct rsnd_dai { @@ -767,7 +771,6 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, */ int rsnd_ctu_probe(struct rsnd_priv *priv); void rsnd_ctu_remove(struct rsnd_priv *priv); -int rsnd_ctu_converted_channel(struct rsnd_mod *mod); struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id); #define rsnd_ctu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_CTU) #define rsnd_parse_connect_ctu(rdai, playback, capture) \ diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index bdc05952f75f..50348a2c9203 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c @@ -25,7 +25,6 @@ struct rsnd_src { struct rsnd_mod *dma; struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ struct rsnd_kctrl_cfg_s sync; /* sync convert */ - u32 convert_rate; /* sampling rate convert */ int irq; }; @@ -89,12 +88,12 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io, return 0; if (!rsnd_src_sync_is_enabled(mod)) - return src->convert_rate; + return rsnd_io_converted_rate(io); convert_rate = src->sync.val; if (!convert_rate) - convert_rate = src->convert_rate; + convert_rate = rsnd_io_converted_rate(io); if (!convert_rate) convert_rate = runtime->rate; @@ -135,40 +134,6 @@ unsigned int rsnd_src_get_rate(struct rsnd_priv *priv, return rate; } -static int rsnd_src_hw_params(struct rsnd_mod *mod, - struct rsnd_dai_stream *io, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *fe_params) -{ - struct rsnd_src *src = rsnd_mod_to_src(mod); - struct snd_soc_pcm_runtime *fe = substream->private_data; - - /* - * SRC assumes that it is used under DPCM if user want to use - * sampling rate convert. Then, SRC should be FE. - * And then, this function will be called *after* BE settings. - * this means, each BE already has fixuped hw_params. - * see - * dpcm_fe_dai_hw_params() - * dpcm_be_dai_hw_params() - */ - src->convert_rate = 0; - if (fe->dai_link->dynamic) { - int stream = substream->stream; - struct snd_soc_dpcm *dpcm; - struct snd_pcm_hw_params *be_params; - - for_each_dpcm_be(fe, stream, dpcm) { - be_params = &dpcm->hw_params; - - if (params_rate(fe_params) != params_rate(be_params)) - src->convert_rate = params_rate(be_params); - } - } - - return 0; -} - static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { @@ -534,7 +499,6 @@ static struct rsnd_mod_ops rsnd_src_ops = { .start = rsnd_src_start, .stop = rsnd_src_stop, .irq = rsnd_src_irq, - .hw_params = rsnd_src_hw_params, .pcm_new = rsnd_src_pcm_new, .get_status = rsnd_mod_get_status, }; -- cgit v1.2.3-58-ga151 From 4e7788fb8018afbb2e34e9d59b59baea30f5b045 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:21:08 +0000 Subject: ASoC: rsnd: add SSIU BUSIF support Gen2 has BUSIF0-3, Gen3 has BUSIF0-7 on some SSIU. Current driver is assuming it is using BUSIF0 as default. Thus, SSI is attaching SSIU (with BUSIF0) by using rsnd_ssiu_attach(). But, TDM split mode also needs other BUSIF to use it. This patch adds missing SSIU BUSIFx support. BUSIF is handled by SSIU instead of SSI anymore. Thus, its settings no longer needed on SSI node on DT. This patch removes its settings from Document, but driver is still keeping compatibility. Thus, old DT style is still working. But, to avoid confusing, it doesn't indicate old compatibility things on Document. New SoC should have SSIU on DT from this patch. 1) old style DT is still supported (= no rcar_sound,ssiu node on DT) 2) If ssiu is not indicated on playback/capture, BUSIF0 will be used as default playback = <&ssi3>; /* ssiu30 will be selected */ 3) you can select own ssiu playback = <&ssi32 &ssi3>; /* ssiu32 will be selected */ Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 1 + sound/soc/sh/rcar/dma.c | 37 ++++++++-- sound/soc/sh/rcar/rsnd.h | 6 +- sound/soc/sh/rcar/ssi.c | 22 +++--- sound/soc/sh/rcar/ssiu.c | 178 +++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 215 insertions(+), 29 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index fcc5c2e9d191..ff621615a924 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1144,6 +1144,7 @@ static void __rsnd_dai_probe(struct rsnd_priv *priv, break; rsnd_parse_connect_ssi(rdai, playback, capture); + rsnd_parse_connect_ssiu(rdai, playback, capture); rsnd_parse_connect_src(rdai, playback, capture); rsnd_parse_connect_ctu(rdai, playback, capture); rsnd_parse_connect_mix(rdai, playback, capture); diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c index 5daa6c932fce..0324a5c39619 100644 --- a/sound/soc/sh/rcar/dma.c +++ b/sound/soc/sh/rcar/dma.c @@ -218,7 +218,7 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, int i = 0; for_each_child_of_node(of_node, np) { - if (i == rsnd_mod_id(mod) && (!chan)) + if (i == rsnd_mod_id_raw(mod) && (!chan)) chan = of_dma_request_slave_channel(np, name); i++; } @@ -344,14 +344,16 @@ static u32 rsnd_dmapp_get_id(struct rsnd_dai_stream *io, struct rsnd_mod *mod) { struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); const u8 *entry = NULL; int id = 255; int size = 0; - if (mod == ssi) { - int busif = rsnd_ssi_get_busif(io); + if ((mod == ssi) || + (mod == ssiu)) { + int busif = rsnd_mod_id_sub(ssiu); entry = gen2_id_table_ssiu; size = ARRAY_SIZE(gen2_id_table_ssiu); @@ -530,13 +532,14 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io, struct device *dev = rsnd_priv_to_dev(priv); phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); - int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); + int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod) || + !!(rsnd_io_to_mod_ssiu(io) == mod); int use_src = !!rsnd_io_to_mod_src(io); int use_cmd = !!rsnd_io_to_mod_dvc(io) || !!rsnd_io_to_mod_mix(io) || !!rsnd_io_to_mod_ctu(io); int id = rsnd_mod_id(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(rsnd_io_to_mod_ssiu(io)); struct dma_addr { dma_addr_t out_addr; dma_addr_t in_addr; @@ -620,7 +623,7 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, struct rsnd_mod **mod_from, struct rsnd_mod **mod_to) { - struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *ssi; struct rsnd_mod *src = rsnd_io_to_mod_src(io); struct rsnd_mod *ctu = rsnd_io_to_mod_ctu(io); struct rsnd_mod *mix = rsnd_io_to_mod_mix(io); @@ -631,6 +634,28 @@ static void rsnd_dma_of_path(struct rsnd_mod *this, struct device *dev = rsnd_priv_to_dev(priv); int nr, i, idx; + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssiu_dma_req() + * rsnd_ssi_dma_req() + */ + if (rsnd_ssiu_of_node(priv)) { + struct rsnd_mod *ssiu = rsnd_io_to_mod_ssiu(io); + + /* use SSIU */ + ssi = ssiu; + if (this == rsnd_io_to_mod_ssi(io)) + this = ssiu; + } else { + /* keep compatible, use SSI */ + ssi = rsnd_io_to_mod_ssi(io); + } + if (!ssi) return; diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 7e54edcc2ce8..7b1e7fb1968c 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -441,6 +441,7 @@ int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, node) #define RSND_NODE_DAI "rcar_sound,dai" #define RSND_NODE_SSI "rcar_sound,ssi" +#define RSND_NODE_SSIU "rcar_sound,ssiu" #define RSND_NODE_SRC "rcar_sound,src" #define RSND_NODE_CTU "rcar_sound,ctu" #define RSND_NODE_MIX "rcar_sound,mix" @@ -725,7 +726,6 @@ void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); -int rsnd_ssi_get_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); #define rsnd_ssi_is_pin_sharing(io) \ @@ -746,6 +746,10 @@ int rsnd_ssiu_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod); int rsnd_ssiu_probe(struct rsnd_priv *priv); void rsnd_ssiu_remove(struct rsnd_priv *priv); +void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture); +#define rsnd_ssiu_of_node(priv) rsnd_parse_of_node(priv, RSND_NODE_SSIU) /* * R-Car SRC diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index e91d3c942ea0..940a7ac69af1 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -134,11 +134,6 @@ int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) return use_busif; } -int rsnd_ssi_get_busif(struct rsnd_dai_stream *io) -{ - return 0; /* BUSIF0 only for now */ -} - static void rsnd_ssi_status_clear(struct rsnd_mod *mod) { rsnd_mod_write(mod, SSISR, 0); @@ -745,7 +740,7 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, { struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); - int ret; + int ret = 0; /* * SSIP/SSIU/IRQ are not needed on @@ -759,10 +754,6 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod, * see rsnd_ssi_pcm_new() */ - ret = rsnd_ssiu_attach(io, mod); - if (ret < 0) - return ret; - /* * SSI might be called again as PIO fallback * It is easy to manual handling for IRQ request/free @@ -956,6 +947,17 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io, int is_play = rsnd_io_is_play(io); char *name; + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssiu_dma_req() + * rsnd_dma_of_path() + */ + if (rsnd_ssi_use_busif(io)) name = is_play ? "rxu" : "txu"; else diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 4f591008cfe5..0609a0c5f9f9 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -12,6 +12,8 @@ struct rsnd_ssiu { struct rsnd_mod mod; u32 busif_status[8]; /* for BUSIF0 - BUSIF7 */ unsigned int usrcnt; + int id; + int id_sub; }; #define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr) @@ -22,12 +24,29 @@ struct rsnd_ssiu { ((pos) = ((struct rsnd_ssiu *)(priv)->ssiu + i)); \ i++) +/* + * SSI Gen2 Gen3 + * 0 BUSIF0-3 BUSIF0-7 + * 1 BUSIF0-3 BUSIF0-7 + * 2 BUSIF0-3 BUSIF0-7 + * 3 BUSIF0 BUSIF0-7 + * 4 BUSIF0 BUSIF0-7 + * 5 BUSIF0 BUSIF0 + * 6 BUSIF0 BUSIF0 + * 7 BUSIF0 BUSIF0 + * 8 BUSIF0 BUSIF0 + * 9 BUSIF0-3 BUSIF0-7 + * total 22 52 + */ +static const int gen2_id[] = { 0, 4, 8, 12, 13, 14, 15, 16, 17, 18 }; +static const int gen3_id[] = { 0, 8, 16, 24, 32, 40, 41, 42, 43, 44 }; + static u32 *rsnd_ssiu_get_status(struct rsnd_mod *mod, struct rsnd_dai_stream *io, enum rsnd_mod_type type) { struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); return &ssiu->busif_status[busif]; } @@ -159,7 +178,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, if (rsnd_ssi_use_busif(io)) { int id = rsnd_mod_id(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); /* * FIXME @@ -252,7 +271,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod, struct rsnd_dai_stream *io, struct rsnd_priv *priv) { - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); if (!rsnd_ssi_use_busif(io)) return 0; @@ -270,7 +289,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, struct rsnd_priv *priv) { struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); - int busif = rsnd_ssi_get_busif(io); + int busif = rsnd_mod_id_sub(mod); if (!rsnd_ssi_use_busif(io)) return 0; @@ -286,8 +305,49 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod, return 0; } +static int rsnd_ssiu_id(struct rsnd_mod *mod) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + + /* see rsnd_ssiu_probe() */ + return ssiu->id; +} + +static int rsnd_ssiu_id_sub(struct rsnd_mod *mod) +{ + struct rsnd_ssiu *ssiu = rsnd_mod_to_ssiu(mod); + + /* see rsnd_ssiu_probe() */ + return ssiu->id_sub; +} + +static struct dma_chan *rsnd_ssiu_dma_req(struct rsnd_dai_stream *io, + struct rsnd_mod *mod) +{ + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); + int is_play = rsnd_io_is_play(io); + char *name; + + /* + * It should use "rcar_sound,ssiu" on DT. + * But, we need to keep compatibility for old version. + * + * If it has "rcar_sound.ssiu", it will be used. + * If not, "rcar_sound.ssi" will be used. + * see + * rsnd_ssi_dma_req() + * rsnd_dma_of_path() + */ + + name = is_play ? "rx" : "tx"; + + return rsnd_dma_request_channel(rsnd_ssiu_of_node(priv), + mod, name); +} + static struct rsnd_mod_ops rsnd_ssiu_ops_gen2 = { .name = SSIU_NAME, + .dma_req = rsnd_ssiu_dma_req, .init = rsnd_ssiu_init_gen2, .start = rsnd_ssiu_start_gen2, .stop = rsnd_ssiu_stop_gen2, @@ -302,26 +362,83 @@ static struct rsnd_mod *rsnd_ssiu_mod_get(struct rsnd_priv *priv, int id) return rsnd_mod_get((struct rsnd_ssiu *)(priv->ssiu) + id); } -int rsnd_ssiu_attach(struct rsnd_dai_stream *io, - struct rsnd_mod *ssi_mod) +static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv, + struct rsnd_dai_stream *io) +{ + struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io); + struct rsnd_mod *mod; + struct rsnd_ssiu *ssiu; + int i; + + if (!ssi_mod) + return; + + /* select BUSIF0 */ + for_each_rsnd_ssiu(ssiu, priv, i) { + mod = rsnd_mod_get(ssiu); + + if ((rsnd_mod_id(ssi_mod) == rsnd_ssiu_id(mod)) && + (rsnd_mod_id_sub(mod) == 0)) + rsnd_dai_connect(mod, io, mod->type); + } +} + +void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai, + struct device_node *playback, + struct device_node *capture) { - struct rsnd_priv *priv = rsnd_io_to_priv(io); - struct rsnd_mod *mod = rsnd_ssiu_mod_get(priv, rsnd_mod_id(ssi_mod)); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device_node *node = rsnd_ssiu_of_node(priv); + struct device_node *np; + struct rsnd_mod *mod; + struct rsnd_dai_stream *io_p = &rdai->playback; + struct rsnd_dai_stream *io_c = &rdai->capture; + int i; - rsnd_mod_confirm_ssi(ssi_mod); + /* use rcar_sound,ssiu if exist */ + if (node) { + i = 0; + for_each_child_of_node(node, np) { + mod = rsnd_ssiu_mod_get(priv, i); + if (np == playback) + rsnd_dai_connect(mod, io_p, mod->type); + if (np == capture) + rsnd_dai_connect(mod, io_c, mod->type); + i++; + } - return rsnd_dai_connect(mod, io, mod->type); + of_node_put(node); + } + + /* Keep DT compatibility */ + if (!rsnd_io_to_mod_ssiu(io_p)) + rsnd_parse_connect_ssiu_compatible(priv, io_p); + if (!rsnd_io_to_mod_ssiu(io_c)) + rsnd_parse_connect_ssiu_compatible(priv, io_c); } int rsnd_ssiu_probe(struct rsnd_priv *priv) { struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *node; struct rsnd_ssiu *ssiu; struct rsnd_mod_ops *ops; + const int *list = NULL; int i, nr, ret; - /* same number to SSI */ - nr = priv->ssi_nr; + /* + * Keep DT compatibility. + * if it has "rcar_sound,ssiu", use it. + * if not, use "rcar_sound,ssi" + * see + * rsnd_ssiu_bufsif_to_id() + */ + node = rsnd_ssiu_of_node(priv); + if (node) + nr = of_get_child_count(node); + else + nr = priv->ssi_nr; + ssiu = devm_kcalloc(dev, nr, sizeof(*ssiu), GFP_KERNEL); if (!ssiu) return -ENOMEM; @@ -334,7 +451,44 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv) else ops = &rsnd_ssiu_ops_gen2; + /* Keep compatibility */ + nr = 0; + if ((node) && + (ops == &rsnd_ssiu_ops_gen2)) { + ops->id = rsnd_ssiu_id; + ops->id_sub = rsnd_ssiu_id_sub; + + if (rsnd_is_gen2(priv)) { + list = gen2_id; + nr = ARRAY_SIZE(gen2_id); + } else if (rsnd_is_gen3(priv)) { + list = gen3_id; + nr = ARRAY_SIZE(gen3_id); + } else { + dev_err(dev, "unknown SSIU\n"); + return -ENODEV; + } + } + for_each_rsnd_ssiu(ssiu, priv, i) { + if (node) { + int j; + + /* + * see + * rsnd_ssiu_get_id() + * rsnd_ssiu_get_id_sub() + */ + for (j = 0; j < nr; j++) { + if (list[j] > i) + break; + ssiu->id = j; + ssiu->id_sub = i - list[ssiu->id]; + } + } else { + ssiu->id = i; + } + ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu), ops, NULL, RSND_MOD_SSIU, i); if (ret) -- cgit v1.2.3-58-ga151 From f69f452243e4e16d7a3d0a14e14126b10933d545 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 6 Nov 2018 05:21:46 +0000 Subject: ASoC: rsnd: add TDM Split mode support This patch adds TDM Split mode support. rsnd driver is assuming audio-graph-scu-card is used for Sound Card. This is very simple sample DT settings to use it. sound_card: sound { compatible = "audio-graph-scu-card"; ... convert-channels = <8>; /* TDM Split */ dais = <&rsnd_port0 /* playback ch1/ch2 */ &rsnd_port1 /* playback ch3/ch4 */ &rsnd_port2 /* playback ch5/ch6 */ &rsnd_port3 /* playback ch7/ch8 */ >; }; audio-codec { ... port { codec_0: endpoint@1 { remote-endpoint = <&rsnd_ep0>; }; codec_1: endpoint@2 { remote-endpoint = <&rsnd_ep1>; }; codec_2: endpoint@3 { remote-endpoint = <&rsnd_ep2>; }; codec_3: endpoint@4 { remote-endpoint = <&rsnd_ep3>; }; }; }; &rcar_sound { ... ports { rsnd_port0: port@0 { rsnd_ep0: endpoint { remote-endpoint = <&codec_0>; ... playback = <&ssiu30 &ssi3>; }; }; rsnd_port1: port@1 { rsnd_ep1: endpoint { remote-endpoint = <&codec_1>; ... playback = <&ssiu31 &ssi3>; }; }; rsnd_port2: port@2 { rsnd_ep2: endpoint { remote-endpoint = <&codec_2>; ... playback = <&ssiu32 &ssi3>; }; }; rsnd_port3: port@3 { rsnd_ep3: endpoint { remote-endpoint = <&codec_3>; ... playback = <&ssiu33 &ssi3>; }; }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 29 +++++++++++++++++++++++++++++ sound/soc/sh/rcar/rsnd.h | 2 ++ sound/soc/sh/rcar/ssi.c | 27 +++++++++++++++++++++++---- sound/soc/sh/rcar/ssiu.c | 21 +++++++++++++-------- 4 files changed, 67 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index ff621615a924..12f559e0463f 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -271,6 +271,19 @@ int rsnd_runtime_channel_after_ctu_with_params(struct rsnd_dai_stream *io, if (ctu_mod) { u32 converted_chan = rsnd_io_converted_chan(io); + /* + * !! Note !! + * + * converted_chan will be used for CTU, + * or TDM Split mode. + * User shouldn't use CTU with TDM Split mode. + */ + if (rsnd_runtime_is_tdm_split(io)) { + struct device *dev = rsnd_priv_to_dev(rsnd_io_to_priv(io)); + + dev_err(dev, "CTU and TDM Split should be used\n"); + } + if (converted_chan) return converted_chan; } @@ -313,6 +326,11 @@ int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io) return rsnd_runtime_channel_for_ssi(io) >= 6; } +int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io) +{ + return !!rsnd_flags_has(io, RSND_STREAM_TDM_SPLIT); +} + /* * ADINR function */ @@ -790,6 +808,7 @@ static int rsnd_soc_set_dai_tdm_slot(struct snd_soc_dai *dai, switch (slots) { case 2: + /* TDM Split Mode */ case 6: case 8: /* TDM Extend Mode */ @@ -1010,6 +1029,7 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv, struct device_node *endpoint) { struct device *dev = rsnd_priv_to_dev(priv); + struct device_node *remote_port = of_graph_get_remote_port(endpoint); struct device_node *remote_node = of_graph_get_remote_port_parent(endpoint); if (!rsnd_io_to_mod_ssi(io)) @@ -1026,6 +1046,15 @@ static void rsnd_parse_connect_graph(struct rsnd_priv *priv, rsnd_flags_set(io, RSND_STREAM_HDMI1); dev_dbg(dev, "%s connected to HDMI1\n", io->name); } + + /* + * This driver assumes that it is TDM Split mode + * if remote node has multi endpoint + */ + if (of_get_child_count(remote_port) > 1) { + rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); + dev_dbg(dev, "%s is part of TDM Split\n", io->name); + } } void rsnd_parse_connect_common(struct rsnd_dai *rdai, diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 7b1e7fb1968c..64c3a3bbcbb9 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -433,6 +433,7 @@ int rsnd_runtime_channel_for_ssi_with_params(struct rsnd_dai_stream *io, struct snd_pcm_hw_params *params); int rsnd_runtime_is_multi_ssi(struct rsnd_dai_stream *io); int rsnd_runtime_is_tdm(struct rsnd_dai_stream *io); +int rsnd_runtime_is_tdm_split(struct rsnd_dai_stream *io); /* * DT @@ -467,6 +468,7 @@ struct rsnd_dai_stream { /* flags */ #define RSND_STREAM_HDMI0 (1 << 0) /* for HDMI0 */ #define RSND_STREAM_HDMI1 (1 << 1) /* for HDMI1 */ +#define RSND_STREAM_TDM_SPLIT (1 << 2) /* for TDM split mode */ #define rsnd_io_to_mod(io, i) ((i) < RSND_MOD_MAX ? (io)->mod[(i)] : NULL) #define rsnd_io_to_mod_ssi(io) rsnd_io_to_mod((io), RSND_MOD_SSI) diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 940a7ac69af1..26fc4a5da0e8 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -298,6 +298,9 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, return 0; } + if (rsnd_runtime_is_tdm_split(io)) + chan = rsnd_io_converted_chan(io); + main_rate = rsnd_ssi_clk_query(rdai, rate, chan, &idx); if (!main_rate) { dev_err(dev, "unsupported clock rate\n"); @@ -360,9 +363,11 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, u32 cr_own = ssi->cr_own; u32 cr_mode = ssi->cr_mode; u32 wsr = ssi->wsr; - int is_tdm; + int width; + int is_tdm, is_tdm_split; - is_tdm = rsnd_runtime_is_tdm(io); + is_tdm = rsnd_runtime_is_tdm(io); + is_tdm_split = rsnd_runtime_is_tdm_split(io); cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai); @@ -381,7 +386,7 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, * rsnd_ssiu_init_gen2() */ wsr = ssi->wsr; - if (is_tdm) { + if (is_tdm || is_tdm_split) { wsr |= WS_MODE; cr_own |= CHNL_8; } @@ -397,7 +402,18 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, cr_own |= TRMD; cr_own &= ~DWL_MASK; - switch (snd_pcm_format_width(runtime->format)) { + width = snd_pcm_format_width(runtime->format); + if (is_tdm_split) { + /* + * The SWL and DWL bits in SSICR should be fixed at 32-bit + * setting when TDM split mode. + * see datasheet + * Operation :: TDM Format Split Function (TDM Split Mode) + */ + width = 32; + } + + switch (width) { case 8: cr_own |= DWL_8; break; @@ -407,6 +423,9 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, case 24: cr_own |= DWL_24; break; + case 32: + cr_own |= DWL_32; + break; } if (rsnd_ssi_is_dma_mode(mod)) { diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 0609a0c5f9f9..650b14e99b05 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -16,6 +16,10 @@ struct rsnd_ssiu { int id_sub; }; +/* SSI_MODE */ +#define TDM_EXT (1 << 0) +#define TDM_SPLIT (1 << 8) + #define rsnd_ssiu_nr(priv) ((priv)->ssiu_nr) #define rsnd_mod_to_ssiu(_mod) container_of((_mod), struct rsnd_ssiu, mod) #define for_each_rsnd_ssiu(pos, priv, i) \ @@ -165,14 +169,15 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, ssiu->usrcnt++; - if (rsnd_runtime_is_tdm(io)) { - /* - * TDM Extend Mode - * see - * rsnd_ssi_config_init() - */ - mode = 0x1; - } + /* + * TDM Extend/Split Mode + * see + * rsnd_ssi_config_init() + */ + if (rsnd_runtime_is_tdm(io)) + mode = TDM_EXT; + else if (rsnd_runtime_is_tdm_split(io)) + mode = TDM_SPLIT; rsnd_mod_write(mod, SSI_MODE, mode); -- cgit v1.2.3-58-ga151 From d3ff06b03c106171cdc7a718c4de724ce6ae346d Mon Sep 17 00:00:00 2001 From: Yangtao Li Date: Mon, 5 Nov 2018 09:37:30 -0500 Subject: ASoC: fsl_ssi: Change to use DEFINE_SHOW_ATTRIBUTE macro Use DEFINE_SHOW_ATTRIBUTE macro to simplify the code. Signed-off-by: Yangtao Li Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_ssi_dbg.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_ssi_dbg.c b/sound/soc/fsl/fsl_ssi_dbg.c index 1255dfe19eef..6f6294149476 100644 --- a/sound/soc/fsl/fsl_ssi_dbg.c +++ b/sound/soc/fsl/fsl_ssi_dbg.c @@ -124,17 +124,7 @@ static int fsl_ssi_stats_show(struct seq_file *s, void *unused) return 0; } -static int fsl_ssi_stats_open(struct inode *inode, struct file *file) -{ - return single_open(file, fsl_ssi_stats_show, inode->i_private); -} - -static const struct file_operations fsl_ssi_stats_ops = { - .open = fsl_ssi_stats_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +DEFINE_SHOW_ATTRIBUTE(fsl_ssi_stats); int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) { @@ -144,7 +134,7 @@ int fsl_ssi_debugfs_create(struct fsl_ssi_dbg *ssi_dbg, struct device *dev) ssi_dbg->dbg_stats = debugfs_create_file("stats", 0444, ssi_dbg->dbg_dir, ssi_dbg, - &fsl_ssi_stats_ops); + &fsl_ssi_stats_fops); if (!ssi_dbg->dbg_stats) { debugfs_remove(ssi_dbg->dbg_dir); return -ENOMEM; -- cgit v1.2.3-58-ga151 From 0246c661b6f0051ef7bfbfff01d8ef7fd0359372 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 9 Nov 2018 04:15:46 +0000 Subject: ASoC: rsnd: use ring buffer for rsnd_mod_name() commit c0ea089dbad4 ("ASoC: rsnd: rsnd_mod_name() handles both name and ID") merged "name" and "ID" on rsnd_mod_name() to handle sub-ID (= for CTU/BUSIF). Then, it decided to share static char to avoid pointless memory. But, it doesn't work correctry in below case, because last called name will be used. dev_xxx(dev, "%s is connected to %s\n", rsnd_mod_name(mod_a), /* ssiu[00] */ rsnd_mod_name(mod_b)); /* ssi[0] */ -> rcar_sound ec500000.sound: ssi[0] is connected to ssi[0] ~~~~~~ ~~~~~~ We still don't want to have pointless memory, so let's use ring buffer. 16byte x 5 is very enough for this purpose. dev_xxx(dev, "%s is connected to %s\n", rsnd_mod_name(mod_a), /* ssiu[00] */ rsnd_mod_name(mod_b)); /* ssi[0] */ -> rcar_sound ec500000.sound: ssiu[00] is connected to ssi[0] ~~~~~~~~ ~~~~~~ Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 12f559e0463f..56469ac4bc8c 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -137,10 +137,17 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io, return mod->ops->dma_req(io, mod); } +#define MOD_NAME_NUM 5 #define MOD_NAME_SIZE 16 char *rsnd_mod_name(struct rsnd_mod *mod) { - static char name[MOD_NAME_SIZE]; + static char names[MOD_NAME_NUM][MOD_NAME_SIZE]; + static int num; + char *name = names[num]; + + num++; + if (num >= MOD_NAME_NUM) + num = 0; /* * Let's use same char to avoid pointlessness memory -- cgit v1.2.3-58-ga151 From dd443a7c0b00edb9aa471a18e4238b0199b3a3ad Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:53 +0200 Subject: ASoC: omap-mcbsp: Clean up dma_data addr initialization code Make the omap_mcbsp_dma_reg_params() a bit more intuitive to read for the first glance by using SNDRV_PCM_STREAM_PLAYBACK/CAPTURE and to group the outermost if case by stream direction. While there, fix the outdated comment for the function. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/mcbsp.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 79d4dc785e5c..d6784f2887e4 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -227,28 +227,27 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp, /** * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register - * @id - mcbsp id - * @stream - indicates the direction of data flow (rx or tx) + * @mcbsp: omap_mcbsp struct for the McBSP instance + * @stream: Stream direction (playback/capture) * * Returns the address of mcbsp data transmit register or data receive register - * to be used by DMA for transferring/receiving data based on the value of - * @stream for the requested mcbsp given by @id + * to be used by DMA for transferring/receiving data */ static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, unsigned int stream) { int data_reg; - if (mcbsp->pdata->reg_size == 2) { - if (stream) - data_reg = OMAP_MCBSP_REG_DRR1; - else + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcbsp->pdata->reg_size == 2) data_reg = OMAP_MCBSP_REG_DXR1; - } else { - if (stream) - data_reg = OMAP_MCBSP_REG_DRR; else data_reg = OMAP_MCBSP_REG_DXR; + } else { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DRR; } return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; @@ -1034,10 +1033,12 @@ int omap_mcbsp_init(struct platform_device *pdev) mcbsp->dma_data[1].filter_data = "rx"; } - mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, 0); + mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_PLAYBACK); mcbsp->dma_data[0].maxburst = 4; - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, 1); + mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_CAPTURE); mcbsp->dma_data[1].maxburst = 4; mcbsp->fclk = clk_get(&pdev->dev, "fck"); -- cgit v1.2.3-58-ga151 From c9ece9c29e263a4bda60b141b7b9dece4256b7dd Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:54 +0200 Subject: ASoC: omap-mcbsp: Skip dma_data.maxburst initialization It is configured runtime so no need to initialize it. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/mcbsp.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index d6784f2887e4..8c3cedfcb878 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -1035,11 +1035,8 @@ int omap_mcbsp_init(struct platform_device *pdev) mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, SNDRV_PCM_STREAM_PLAYBACK); - mcbsp->dma_data[0].maxburst = 4; - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, SNDRV_PCM_STREAM_CAPTURE); - mcbsp->dma_data[1].maxburst = 4; mcbsp->fclk = clk_get(&pdev->dev, "fck"); if (IS_ERR(mcbsp->fclk)) { -- cgit v1.2.3-58-ga151 From d63a7625a6dff8f75604b952de600d24b58b8b54 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:55 +0200 Subject: ASoC: omap-mcbsp: Clean up the interrupt handlers The parameter name of dev_id is leftover from the old times when we passed numeric ID as data for the interrupt handlers. The mcbsp_rx and mcbsp_tx is misleading as they are pointers to the mcbsp struct. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/mcbsp.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 8c3cedfcb878..fca0b0e9186b 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -110,9 +110,9 @@ static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) dev_dbg(mcbsp->dev, "***********************\n"); } -static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id) +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) { - struct omap_mcbsp *mcbsp = dev_id; + struct omap_mcbsp *mcbsp = data; u16 irqst; irqst = MCBSP_READ(mcbsp, IRQST); @@ -151,37 +151,37 @@ static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) { - struct omap_mcbsp *mcbsp_tx = dev_id; + struct omap_mcbsp *mcbsp = data; u16 irqst_spcr2; - irqst_spcr2 = MCBSP_READ(mcbsp_tx, SPCR2); - dev_dbg(mcbsp_tx->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); + dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); if (irqst_spcr2 & XSYNC_ERR) { - dev_err(mcbsp_tx->dev, "TX Frame Sync Error! : 0x%x\n", + dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", irqst_spcr2); /* Writing zero to XSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp_tx, SPCR2, MCBSP_READ_CACHE(mcbsp_tx, SPCR2)); + MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); } return IRQ_HANDLED; } -static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id) +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) { - struct omap_mcbsp *mcbsp_rx = dev_id; + struct omap_mcbsp *mcbsp = data; u16 irqst_spcr1; - irqst_spcr1 = MCBSP_READ(mcbsp_rx, SPCR1); - dev_dbg(mcbsp_rx->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); + dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); if (irqst_spcr1 & RSYNC_ERR) { - dev_err(mcbsp_rx->dev, "RX Frame Sync Error! : 0x%x\n", + dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", irqst_spcr1); /* Writing zero to RSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp_rx, SPCR1, MCBSP_READ_CACHE(mcbsp_rx, SPCR1)); + MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); } return IRQ_HANDLED; -- cgit v1.2.3-58-ga151 From 59d177f65f50df07f5f0b461c968d2fab7597af1 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:56 +0200 Subject: ASoC: omap-mcbsp: Simplify the mcbsp_start/_stop function parameters We either start/stop TX or RX, never both. Move the tx/rx direction selection within the functions. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/mcbsp.c | 8 ++++++-- sound/soc/omap/mcbsp.h | 4 ++-- sound/soc/omap/omap-mcbsp.c | 9 ++++----- 3 files changed, 12 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index fca0b0e9186b..7ff22561f00f 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -637,8 +637,10 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp) * If no transmitter or receiver is active prior calling, then sample-rate * generator and frame sync are started. */ -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx) +void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) { + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; int enable_srg = 0; u16 w; @@ -694,8 +696,10 @@ void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx) omap_mcbsp_dump_reg(mcbsp); } -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx) +void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) { + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; int idle; u16 w; diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h index 46ae1269a698..92472c6ef358 100644 --- a/sound/soc/omap/mcbsp.h +++ b/sound/soc/omap/mcbsp.h @@ -339,8 +339,8 @@ u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); int omap_mcbsp_request(struct omap_mcbsp *mcbsp); void omap_mcbsp_free(struct omap_mcbsp *mcbsp); -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int tx, int rx); -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int tx, int rx); +void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream); +void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream); /* McBSP functional clock source changing function */ int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index caa24c463b3e..a18b7ecc3a2e 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -201,27 +201,26 @@ static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - int err = 0, play = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: mcbsp->active++; - omap_mcbsp_start(mcbsp, play, !play); + omap_mcbsp_start(mcbsp, substream->stream); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - omap_mcbsp_stop(mcbsp, play, !play); + omap_mcbsp_stop(mcbsp, substream->stream); mcbsp->active--; break; default: - err = -EINVAL; + return -EINVAL; } - return err; + return 0; } static snd_pcm_sframes_t omap_mcbsp_dai_delay( -- cgit v1.2.3-58-ga151 From be51c576e8495fd2cffbababad6de7e0a0a562ba Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:57 +0200 Subject: ASoC: omap-mcbsp: Move out the FIFO check from set_threshold and get_delay Check if the McBSP have FIFO in the omap_mcbsp_set_threshold() and omap_mcbsp_dai_delay() delay function to skip calling the lower layer if it is not needed. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/mcbsp.c | 12 ------------ sound/soc/omap/omap-mcbsp.c | 8 ++++++++ 2 files changed, 8 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c index 7ff22561f00f..b19168f5c110 100644 --- a/sound/soc/omap/mcbsp.c +++ b/sound/soc/omap/mcbsp.c @@ -450,9 +450,6 @@ int omap_st_is_enabled(struct omap_mcbsp *mcbsp) */ void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) { - if (mcbsp->pdata->buffer_size == 0) - return; - if (threshold && threshold <= mcbsp->max_tx_thres) MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); } @@ -464,9 +461,6 @@ void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) */ void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) { - if (mcbsp->pdata->buffer_size == 0) - return; - if (threshold && threshold <= mcbsp->max_rx_thres) MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); } @@ -478,9 +472,6 @@ u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) { u16 buffstat; - if (mcbsp->pdata->buffer_size == 0) - return 0; - /* Returns the number of free locations in the buffer */ buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); @@ -496,9 +487,6 @@ u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) { u16 buffstat, threshold; - if (mcbsp->pdata->buffer_size == 0) - return 0; - /* Returns the number of used locations in the buffer */ buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); /* RX threshold */ diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index a18b7ecc3a2e..69a6b8ad6d42 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -71,6 +71,10 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); int words; + /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return; + /* * Configure McBSP threshold based on either: * packet_size, when the sDMA is in packet mode, or based on the @@ -233,6 +237,10 @@ static snd_pcm_sframes_t omap_mcbsp_dai_delay( u16 fifo_use; snd_pcm_sframes_t delay; + /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return 0; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) fifo_use = omap_mcbsp_get_tx_delay(mcbsp); else -- cgit v1.2.3-58-ga151 From 9c34d023dc35a4b3d005d94db512742dfcdc8f82 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:58 +0200 Subject: ASoC: omap-mcbsp: Re-arrange files for core McBSP and Sidetone function split The mcbsp.c was copied a while back from arch/arm/plat-omap/mcbsp.c and it contained a mix of McBSP and McBSP sidetone functions. Create new file structure with the following split: omap-mcbsp.c - McBSP related functions omap-mcbsp-st.c - McBSP sidetone functionality omap-mcbsp-priv.h - Private header for internal use omap-mcbsp.h - Header for user drivers I have tried to do the code move with minimal code change, cleanup patches can be based on the new structure. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/Makefile | 2 +- sound/soc/omap/mcbsp.c | 1094 -------------------------------------- sound/soc/omap/mcbsp.h | 358 ------------- sound/soc/omap/omap-mcbsp-priv.h | 324 +++++++++++ sound/soc/omap/omap-mcbsp-st.c | 516 ++++++++++++++++++ sound/soc/omap/omap-mcbsp.c | 838 ++++++++++++++++++++++++----- sound/soc/omap/omap-mcbsp.h | 8 +- 7 files changed, 1547 insertions(+), 1593 deletions(-) delete mode 100644 sound/soc/omap/mcbsp.c delete mode 100644 sound/soc/omap/mcbsp.h create mode 100644 sound/soc/omap/omap-mcbsp-priv.h create mode 100644 sound/soc/omap/omap-mcbsp-st.c (limited to 'sound') diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile index af50cdd6eea9..d005338dd13c 100644 --- a/sound/soc/omap/Makefile +++ b/sound/soc/omap/Makefile @@ -2,7 +2,7 @@ # OMAP Platform Support snd-soc-sdma-objs := sdma-pcm.o snd-soc-omap-dmic-objs := omap-dmic.o -snd-soc-omap-mcbsp-objs := omap-mcbsp.o mcbsp.o +snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o snd-soc-omap-mcpdm-objs := omap-mcpdm.o snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c deleted file mode 100644 index b19168f5c110..000000000000 --- a/sound/soc/omap/mcbsp.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* - * sound/soc/omap/mcbsp.c - * - * Copyright (C) 2004 Nokia Corporation - * Author: Samuel Ortiz - * - * Contact: Jarkko Nikula - * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Multichannel mode not supported. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "mcbsp.h" - -static void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; - writew_relaxed((u16)val, addr); - } else { - ((u32 *)mcbsp->reg_cache)[reg] = val; - writel_relaxed(val, addr); - } -} - -static int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, bool from_cache) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - return !from_cache ? readw_relaxed(addr) : - ((u16 *)mcbsp->reg_cache)[reg]; - } else { - return !from_cache ? readl_relaxed(addr) : - ((u32 *)mcbsp->reg_cache)[reg]; - } -} - -static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - writel_relaxed(val, mcbsp->st_data->io_base_st + reg); -} - -static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) -{ - return readl_relaxed(mcbsp->st_data->io_base_st + reg); -} - -#define MCBSP_READ(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) -#define MCBSP_WRITE(mcbsp, reg, val) \ - omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) -#define MCBSP_READ_CACHE(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) - -#define MCBSP_ST_READ(mcbsp, reg) \ - omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) -#define MCBSP_ST_WRITE(mcbsp, reg, val) \ - omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) - -static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) -{ - dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); - dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", - MCBSP_READ(mcbsp, DRR2)); - dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", - MCBSP_READ(mcbsp, DRR1)); - dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", - MCBSP_READ(mcbsp, DXR2)); - dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", - MCBSP_READ(mcbsp, DXR1)); - dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", - MCBSP_READ(mcbsp, SPCR2)); - dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", - MCBSP_READ(mcbsp, SPCR1)); - dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", - MCBSP_READ(mcbsp, RCR2)); - dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", - MCBSP_READ(mcbsp, RCR1)); - dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", - MCBSP_READ(mcbsp, XCR2)); - dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", - MCBSP_READ(mcbsp, XCR1)); - dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", - MCBSP_READ(mcbsp, SRGR2)); - dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", - MCBSP_READ(mcbsp, SRGR1)); - dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", - MCBSP_READ(mcbsp, PCR0)); - dev_dbg(mcbsp->dev, "***********************\n"); -} - -static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst; - - irqst = MCBSP_READ(mcbsp, IRQST); - dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); - - if (irqst & RSYNCERREN) - dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); - if (irqst & RFSREN) - dev_dbg(mcbsp->dev, "RX Frame Sync\n"); - if (irqst & REOFEN) - dev_dbg(mcbsp->dev, "RX End Of Frame\n"); - if (irqst & RRDYEN) - dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); - if (irqst & RUNDFLEN) - dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); - if (irqst & ROVFLEN) - dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); - - if (irqst & XSYNCERREN) - dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); - if (irqst & XFSXEN) - dev_dbg(mcbsp->dev, "TX Frame Sync\n"); - if (irqst & XEOFEN) - dev_dbg(mcbsp->dev, "TX End Of Frame\n"); - if (irqst & XRDYEN) - dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); - if (irqst & XUNDFLEN) - dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); - if (irqst & XOVFLEN) - dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); - if (irqst & XEMPTYEOFEN) - dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); - - MCBSP_WRITE(mcbsp, IRQST, irqst); - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst_spcr2; - - irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); - dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); - - if (irqst_spcr2 & XSYNC_ERR) { - dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", - irqst_spcr2); - /* Writing zero to XSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); - } - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst_spcr1; - - irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); - dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); - - if (irqst_spcr1 & RSYNC_ERR) { - dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", - irqst_spcr1); - /* Writing zero to RSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); - } - - return IRQ_HANDLED; -} - -/* - * omap_mcbsp_config simply write a config to the - * appropriate McBSP. - * You either call this function or set the McBSP registers - * by yourself before calling omap_mcbsp_start(). - */ -void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config) -{ - dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", - mcbsp->id, mcbsp->phys_base); - - /* We write the given config */ - MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); - MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); - MCBSP_WRITE(mcbsp, RCR2, config->rcr2); - MCBSP_WRITE(mcbsp, RCR1, config->rcr1); - MCBSP_WRITE(mcbsp, XCR2, config->xcr2); - MCBSP_WRITE(mcbsp, XCR1, config->xcr1); - MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); - MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); - MCBSP_WRITE(mcbsp, MCR2, config->mcr2); - MCBSP_WRITE(mcbsp, MCR1, config->mcr1); - MCBSP_WRITE(mcbsp, PCR0, config->pcr0); - if (mcbsp->pdata->has_ccr) { - MCBSP_WRITE(mcbsp, XCCR, config->xccr); - MCBSP_WRITE(mcbsp, RCCR, config->rccr); - } - /* Enable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); - - /* Enable TX/RX sync error interrupts by default */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | - RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); -} - -/** - * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register - * @mcbsp: omap_mcbsp struct for the McBSP instance - * @stream: Stream direction (playback/capture) - * - * Returns the address of mcbsp data transmit register or data receive register - * to be used by DMA for transferring/receiving data - */ -static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, - unsigned int stream) -{ - int data_reg; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (mcbsp->pdata->reg_size == 2) - data_reg = OMAP_MCBSP_REG_DXR1; - else - data_reg = OMAP_MCBSP_REG_DXR; - } else { - if (mcbsp->pdata->reg_size == 2) - data_reg = OMAP_MCBSP_REG_DRR1; - else - data_reg = OMAP_MCBSP_REG_DRR; - } - - return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; -} - -static void omap_st_on(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); - - /* Disable Sidetone clock auto-gating for normal operation */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); - - /* Enable McBSP Sidetone */ - w = MCBSP_READ(mcbsp, SSELCR); - MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); - - /* Enable Sidetone from Sidetone Core */ - w = MCBSP_ST_READ(mcbsp, SSELCR); - MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); -} - -static void omap_st_off(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - w = MCBSP_ST_READ(mcbsp, SSELCR); - MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); - - w = MCBSP_READ(mcbsp, SSELCR); - MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); - - /* Enable Sidetone clock auto-gating to reduce power consumption */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); -} - -static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) -{ - u16 val, i; - - val = MCBSP_ST_READ(mcbsp, SSELCR); - - if (val & ST_COEFFWREN) - MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); - - MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); - - for (i = 0; i < 128; i++) - MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); - - i = 0; - - val = MCBSP_ST_READ(mcbsp, SSELCR); - while (!(val & ST_COEFFWRDONE) && (++i < 1000)) - val = MCBSP_ST_READ(mcbsp, SSELCR); - - MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); - - if (i == 1000) - dev_err(mcbsp->dev, "McBSP FIR load error!\n"); -} - -static void omap_st_chgain(struct omap_mcbsp *mcbsp) -{ - u16 w; - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - w = MCBSP_ST_READ(mcbsp, SSELCR); - - MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | \ - ST_CH1GAIN(st_data->ch1gain)); -} - -int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENOENT; - - spin_lock_irq(&mcbsp->lock); - if (channel == 0) - st_data->ch0gain = chgain; - else if (channel == 1) - st_data->ch1gain = chgain; - else - ret = -EINVAL; - - if (st_data->enabled) - omap_st_chgain(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENOENT; - - spin_lock_irq(&mcbsp->lock); - if (channel == 0) - *chgain = st_data->ch0gain; - else if (channel == 1) - *chgain = st_data->ch1gain; - else - ret = -EINVAL; - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -static int omap_st_start(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->enabled && !st_data->running) { - omap_st_fir_write(mcbsp, st_data->taps); - omap_st_chgain(mcbsp); - - if (!mcbsp->free) { - omap_st_on(mcbsp); - st_data->running = 1; - } - } - - return 0; -} - -int omap_st_enable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - st_data->enabled = 1; - omap_st_start(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return 0; -} - -static int omap_st_stop(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->running) { - if (!mcbsp->free) { - omap_st_off(mcbsp); - st_data->running = 0; - } - } - - return 0; -} - -int omap_st_disable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - omap_st_stop(mcbsp); - st_data->enabled = 0; - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -int omap_st_is_enabled(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - return st_data->enabled; -} - -/* - * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. - * The threshold parameter is 1 based, and it is converted (threshold - 1) - * for the THRSH2 register. - */ -void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (threshold && threshold <= mcbsp->max_tx_thres) - MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); -} - -/* - * omap_mcbsp_set_rx_threshold configures the receive threshold in words. - * The threshold parameter is 1 based, and it is converted (threshold - 1) - * for the THRSH1 register. - */ -void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (threshold && threshold <= mcbsp->max_rx_thres) - MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); -} - -/* - * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO - */ -u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat; - - /* Returns the number of free locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); - - /* Number of slots are different in McBSP ports */ - return mcbsp->pdata->buffer_size - buffstat; -} - -/* - * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO - * to reach the threshold value (when the DMA will be triggered to read it) - */ -u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat, threshold; - - /* Returns the number of used locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); - /* RX threshold */ - threshold = MCBSP_READ(mcbsp, THRSH1); - - /* Return the number of location till we reach the threshold limit */ - if (threshold <= buffstat) - return 0; - else - return threshold - buffstat; -} - -int omap_mcbsp_request(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - int err; - - reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); - if (!reg_cache) { - return -ENOMEM; - } - - spin_lock(&mcbsp->lock); - if (!mcbsp->free) { - dev_err(mcbsp->dev, "McBSP%d is currently in use\n", - mcbsp->id); - err = -EBUSY; - goto err_kfree; - } - - mcbsp->free = false; - mcbsp->reg_cache = reg_cache; - spin_unlock(&mcbsp->lock); - - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) - mcbsp->pdata->ops->request(mcbsp->id - 1); - - /* - * Make sure that transmitter, receiver and sample-rate generator are - * not running before activating IRQs. - */ - MCBSP_WRITE(mcbsp, SPCR1, 0); - MCBSP_WRITE(mcbsp, SPCR2, 0); - - if (mcbsp->irq) { - err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, - "McBSP", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request IRQ\n"); - goto err_clk_disable; - } - } else { - err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, - "McBSP TX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); - goto err_clk_disable; - } - - err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP RX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); - goto err_free_irq; - } - } - - return 0; -err_free_irq: - free_irq(mcbsp->tx_irq, (void *)mcbsp); -err_clk_disable: - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - spin_lock(&mcbsp->lock); - mcbsp->free = true; - mcbsp->reg_cache = NULL; -err_kfree: - spin_unlock(&mcbsp->lock); - kfree(reg_cache); - - return err; -} - -void omap_mcbsp_free(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - /* Disable interrupt requests */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, 0); - - if (mcbsp->irq) { - free_irq(mcbsp->irq, (void *)mcbsp); - } else { - free_irq(mcbsp->rx_irq, (void *)mcbsp); - free_irq(mcbsp->tx_irq, (void *)mcbsp); - } - - reg_cache = mcbsp->reg_cache; - - /* - * Select CLKS source from internal source unconditionally before - * marking the McBSP port as free. - * If the external clock source via MCBSP_CLKS pin has been selected the - * system will refuse to enter idle if the CLKS pin source is not reset - * back to internal source. - */ - if (!mcbsp_omap1()) - omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); - - spin_lock(&mcbsp->lock); - if (mcbsp->free) - dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); - else - mcbsp->free = true; - mcbsp->reg_cache = NULL; - spin_unlock(&mcbsp->lock); - - kfree(reg_cache); -} - -/* - * Here we start the McBSP, by enabling transmitter, receiver or both. - * If no transmitter or receiver is active prior calling, then sample-rate - * generator and frame sync are started. - */ -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) -{ - int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); - int rx = !tx; - int enable_srg = 0; - u16 w; - - if (mcbsp->st_data) - omap_st_start(mcbsp); - - /* Only enable SRG, if McBSP is master */ - w = MCBSP_READ_CACHE(mcbsp, PCR0); - if (w & (FSXM | FSRM | CLKXM | CLKRM)) - enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (enable_srg) { - /* Start the sample generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); - } - - /* Enable transmitter and receiver */ - tx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | tx); - - rx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w | rx); - - /* - * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec - * REVISIT: 100us may give enough time for two CLKSRG, however - * due to some unknown PM related, clock gating etc. reason it - * is now at 500us. - */ - udelay(500); - - if (enable_srg) { - /* Start frame sync */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); - } - - if (mcbsp->pdata->has_ccr) { - /* Release the transmitter and receiver */ - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w &= ~(tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w &= ~(rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - - /* Dump McBSP Regs */ - omap_mcbsp_dump_reg(mcbsp); -} - -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) -{ - int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); - int rx = !tx; - int idle; - u16 w; - - /* Reset transmitter */ - tx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w |= (tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); - - /* Reset receiver */ - rx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w |= (rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); - - idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (idle) { - /* Reset the sample rate generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); - } - - if (mcbsp->st_data) - omap_st_stop(mcbsp); -} - -int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) -{ - struct clk *fck_src; - const char *src; - int r; - - if (fck_src_id == MCBSP_CLKS_PAD_SRC) - src = "pad_fck"; - else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) - src = "prcm_fck"; - else - return -EINVAL; - - fck_src = clk_get(mcbsp->dev, src); - if (IS_ERR(fck_src)) { - dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); - return -EINVAL; - } - - pm_runtime_put_sync(mcbsp->dev); - - r = clk_set_parent(mcbsp->fclk, fck_src); - if (r) { - dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", - src); - clk_put(fck_src); - return r; - } - - pm_runtime_get_sync(mcbsp->dev); - - clk_put(fck_src); - - return 0; - -} - -#define max_thres(m) (mcbsp->pdata->buffer_size) -#define valid_threshold(m, val) ((val) <= max_thres(m)) -#define THRESHOLD_PROP_BUILDER(prop) \ -static ssize_t prop##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ - \ - return sprintf(buf, "%u\n", mcbsp->prop); \ -} \ - \ -static ssize_t prop##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t size) \ -{ \ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ - unsigned long val; \ - int status; \ - \ - status = kstrtoul(buf, 0, &val); \ - if (status) \ - return status; \ - \ - if (!valid_threshold(mcbsp, val)) \ - return -EDOM; \ - \ - mcbsp->prop = val; \ - return size; \ -} \ - \ -static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store); - -THRESHOLD_PROP_BUILDER(max_tx_thres); -THRESHOLD_PROP_BUILDER(max_rx_thres); - -static const char *dma_op_modes[] = { - "element", "threshold", -}; - -static ssize_t dma_op_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - int dma_op_mode, i = 0; - ssize_t len = 0; - const char * const *s; - - dma_op_mode = mcbsp->dma_op_mode; - - for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { - if (dma_op_mode == i) - len += sprintf(buf + len, "[%s] ", *s); - else - len += sprintf(buf + len, "%s ", *s); - } - len += sprintf(buf + len, "\n"); - - return len; -} - -static ssize_t dma_op_mode_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - int i; - - i = sysfs_match_string(dma_op_modes, buf); - if (i < 0) - return i; - - spin_lock_irq(&mcbsp->lock); - if (!mcbsp->free) { - size = -EBUSY; - goto unlock; - } - mcbsp->dma_op_mode = i; - -unlock: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(dma_op_mode); - -static const struct attribute *additional_attrs[] = { - &dev_attr_max_tx_thres.attr, - &dev_attr_max_rx_thres.attr, - &dev_attr_dma_op_mode.attr, - NULL, -}; - -static const struct attribute_group additional_attr_group = { - .attrs = (struct attribute **)additional_attrs, -}; - -static ssize_t st_taps_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - ssize_t status = 0; - int i; - - spin_lock_irq(&mcbsp->lock); - for (i = 0; i < st_data->nr_taps; i++) - status += sprintf(&buf[status], (i ? ", %d" : "%d"), - st_data->taps[i]); - if (i) - status += sprintf(&buf[status], "\n"); - spin_unlock_irq(&mcbsp->lock); - - return status; -} - -static ssize_t st_taps_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int val, tmp, status, i = 0; - - spin_lock_irq(&mcbsp->lock); - memset(st_data->taps, 0, sizeof(st_data->taps)); - st_data->nr_taps = 0; - - do { - status = sscanf(buf, "%d%n", &val, &tmp); - if (status < 0 || status == 0) { - size = -EINVAL; - goto out; - } - if (val < -32768 || val > 32767) { - size = -EINVAL; - goto out; - } - st_data->taps[i++] = val; - buf += tmp; - if (*buf != ',') - break; - buf++; - } while (1); - - st_data->nr_taps = i; - -out: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(st_taps); - -static const struct attribute *sidetone_attrs[] = { - &dev_attr_st_taps.attr, - NULL, -}; - -static const struct attribute_group sidetone_attr_group = { - .attrs = (struct attribute **)sidetone_attrs, -}; - -static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res) -{ - struct omap_mcbsp_st_data *st_data; - int err; - - st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); - if (!st_data) - return -ENOMEM; - - st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); - if (IS_ERR(st_data->mcbsp_iclk)) { - dev_warn(mcbsp->dev, - "Failed to get ick, sidetone might be broken\n"); - st_data->mcbsp_iclk = NULL; - } - - st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, - resource_size(res)); - if (!st_data->io_base_st) - return -ENOMEM; - - err = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); - if (err) - return err; - - mcbsp->st_data = st_data; - return 0; -} - -/* - * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. - * 730 has only 2 McBSP, and both of them are MPU peripherals. - */ -int omap_mcbsp_init(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - struct resource *res; - int ret = 0; - - spin_lock_init(&mcbsp->lock); - mcbsp->free = true; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mcbsp->io_base)) - return PTR_ERR(mcbsp->io_base); - - mcbsp->phys_base = res->start; - mcbsp->reg_cache_size = resource_size(res); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (!res) - mcbsp->phys_dma_base = mcbsp->phys_base; - else - mcbsp->phys_dma_base = res->start; - - /* - * OMAP1, 2 uses two interrupt lines: TX, RX - * OMAP2430, OMAP3 SoC have combined IRQ line as well. - * OMAP4 and newer SoC only have the combined IRQ line. - * Use the combined IRQ if available since it gives better debugging - * possibilities. - */ - mcbsp->irq = platform_get_irq_byname(pdev, "common"); - if (mcbsp->irq == -ENXIO) { - mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); - - if (mcbsp->tx_irq == -ENXIO) { - mcbsp->irq = platform_get_irq(pdev, 0); - mcbsp->tx_irq = 0; - } else { - mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); - mcbsp->irq = 0; - } - } - - if (!pdev->dev.of_node) { - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (!res) { - dev_err(&pdev->dev, "invalid tx DMA channel\n"); - return -ENODEV; - } - mcbsp->dma_req[0] = res->start; - mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (!res) { - dev_err(&pdev->dev, "invalid rx DMA channel\n"); - return -ENODEV; - } - mcbsp->dma_req[1] = res->start; - mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; - } else { - mcbsp->dma_data[0].filter_data = "tx"; - mcbsp->dma_data[1].filter_data = "rx"; - } - - mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, - SNDRV_PCM_STREAM_PLAYBACK); - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, - SNDRV_PCM_STREAM_CAPTURE); - - mcbsp->fclk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(mcbsp->fclk)) { - ret = PTR_ERR(mcbsp->fclk); - dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); - return ret; - } - - mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; - if (mcbsp->pdata->buffer_size) { - /* - * Initially configure the maximum thresholds to a safe value. - * The McBSP FIFO usage with these values should not go under - * 16 locations. - * If the whole FIFO without safety buffer is used, than there - * is a possibility that the DMA will be not able to push the - * new data on time, causing channel shifts in runtime. - */ - mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; - mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; - - ret = sysfs_create_group(&mcbsp->dev->kobj, - &additional_attr_group); - if (ret) { - dev_err(mcbsp->dev, - "Unable to create additional controls\n"); - goto err_thres; - } - } else { - mcbsp->max_tx_thres = -EINVAL; - mcbsp->max_rx_thres = -EINVAL; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); - if (res) { - ret = omap_st_add(mcbsp, res); - if (ret) { - dev_err(mcbsp->dev, - "Unable to create sidetone controls\n"); - goto err_st; - } - } - - return 0; - -err_st: - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); -err_thres: - clk_put(mcbsp->fclk); - return ret; -} - -void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp) -{ - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); - - if (mcbsp->st_data) { - sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); - clk_put(mcbsp->st_data->mcbsp_iclk); - } -} diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h deleted file mode 100644 index 92472c6ef358..000000000000 --- a/sound/soc/omap/mcbsp.h +++ /dev/null @@ -1,358 +0,0 @@ -/* - * sound/soc/omap/mcbsp.h - * - * OMAP Multi-Channel Buffered Serial Port - * - * Contact: Jarkko Nikula - * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ -#ifndef __ASOC_MCBSP_H -#define __ASOC_MCBSP_H - -#ifdef CONFIG_ARCH_OMAP1 -#define mcbsp_omap1() 1 -#else -#define mcbsp_omap1() 0 -#endif - -#include - -/* McBSP register numbers. Register address offset = num * reg_step */ -enum { - /* Common registers */ - OMAP_MCBSP_REG_SPCR2 = 4, - OMAP_MCBSP_REG_SPCR1, - OMAP_MCBSP_REG_RCR2, - OMAP_MCBSP_REG_RCR1, - OMAP_MCBSP_REG_XCR2, - OMAP_MCBSP_REG_XCR1, - OMAP_MCBSP_REG_SRGR2, - OMAP_MCBSP_REG_SRGR1, - OMAP_MCBSP_REG_MCR2, - OMAP_MCBSP_REG_MCR1, - OMAP_MCBSP_REG_RCERA, - OMAP_MCBSP_REG_RCERB, - OMAP_MCBSP_REG_XCERA, - OMAP_MCBSP_REG_XCERB, - OMAP_MCBSP_REG_PCR0, - OMAP_MCBSP_REG_RCERC, - OMAP_MCBSP_REG_RCERD, - OMAP_MCBSP_REG_XCERC, - OMAP_MCBSP_REG_XCERD, - OMAP_MCBSP_REG_RCERE, - OMAP_MCBSP_REG_RCERF, - OMAP_MCBSP_REG_XCERE, - OMAP_MCBSP_REG_XCERF, - OMAP_MCBSP_REG_RCERG, - OMAP_MCBSP_REG_RCERH, - OMAP_MCBSP_REG_XCERG, - OMAP_MCBSP_REG_XCERH, - - /* OMAP1-OMAP2420 registers */ - OMAP_MCBSP_REG_DRR2 = 0, - OMAP_MCBSP_REG_DRR1, - OMAP_MCBSP_REG_DXR2, - OMAP_MCBSP_REG_DXR1, - - /* OMAP2430 and onwards */ - OMAP_MCBSP_REG_DRR = 0, - OMAP_MCBSP_REG_DXR = 2, - OMAP_MCBSP_REG_SYSCON = 35, - OMAP_MCBSP_REG_THRSH2, - OMAP_MCBSP_REG_THRSH1, - OMAP_MCBSP_REG_IRQST = 40, - OMAP_MCBSP_REG_IRQEN, - OMAP_MCBSP_REG_WAKEUPEN, - OMAP_MCBSP_REG_XCCR, - OMAP_MCBSP_REG_RCCR, - OMAP_MCBSP_REG_XBUFFSTAT, - OMAP_MCBSP_REG_RBUFFSTAT, - OMAP_MCBSP_REG_SSELCR, -}; - -/* OMAP3 sidetone control registers */ -#define OMAP_ST_REG_REV 0x00 -#define OMAP_ST_REG_SYSCONFIG 0x10 -#define OMAP_ST_REG_IRQSTATUS 0x18 -#define OMAP_ST_REG_IRQENABLE 0x1C -#define OMAP_ST_REG_SGAINCR 0x24 -#define OMAP_ST_REG_SFIRCR 0x28 -#define OMAP_ST_REG_SSELCR 0x2C - -/************************** McBSP SPCR1 bit definitions ***********************/ -#define RRST BIT(0) -#define RRDY BIT(1) -#define RFULL BIT(2) -#define RSYNC_ERR BIT(3) -#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ -#define ABIS BIT(6) -#define DXENA BIT(7) -#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */ -#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */ -#define ALB BIT(15) -#define DLB BIT(15) - -/************************** McBSP SPCR2 bit definitions ***********************/ -#define XRST BIT(0) -#define XRDY BIT(1) -#define XEMPTY BIT(2) -#define XSYNC_ERR BIT(3) -#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ -#define GRST BIT(6) -#define FRST BIT(7) -#define SOFT BIT(8) -#define FREE BIT(9) - -/************************** McBSP PCR bit definitions *************************/ -#define CLKRP BIT(0) -#define CLKXP BIT(1) -#define FSRP BIT(2) -#define FSXP BIT(3) -#define DR_STAT BIT(4) -#define DX_STAT BIT(5) -#define CLKS_STAT BIT(6) -#define SCLKME BIT(7) -#define CLKRM BIT(8) -#define CLKXM BIT(9) -#define FSRM BIT(10) -#define FSXM BIT(11) -#define RIOEN BIT(12) -#define XIOEN BIT(13) -#define IDLE_EN BIT(14) - -/************************** McBSP RCR1 bit definitions ************************/ -#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ - -/************************** McBSP XCR1 bit definitions ************************/ -#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ - -/*************************** McBSP RCR2 bit definitions ***********************/ -#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ -#define RFIG BIT(2) -#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ -#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ -#define RPHASE BIT(15) - -/*************************** McBSP XCR2 bit definitions ***********************/ -#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ -#define XFIG BIT(2) -#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ -#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ -#define XPHASE BIT(15) - -/************************* McBSP SRGR1 bit definitions ************************/ -#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */ -#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */ - -/************************* McBSP SRGR2 bit definitions ************************/ -#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */ -#define FSGM BIT(12) -#define CLKSM BIT(13) -#define CLKSP BIT(14) -#define GSYNC BIT(15) - -/************************* McBSP MCR1 bit definitions *************************/ -#define RMCM BIT(0) -#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ -#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ -#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ - -/************************* McBSP MCR2 bit definitions *************************/ -#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */ -#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ -#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ -#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ - -/*********************** McBSP XCCR bit definitions *************************/ -#define XDISABLE BIT(0) -#define XDMAEN BIT(3) -#define DILB BIT(5) -#define XFULL_CYCLE BIT(11) -#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */ -#define PPCONNECT BIT(14) -#define EXTCLKGATE BIT(15) - -/********************** McBSP RCCR bit definitions *************************/ -#define RDISABLE BIT(0) -#define RDMAEN BIT(3) -#define RFULL_CYCLE BIT(11) - -/********************** McBSP SYSCONFIG bit definitions ********************/ -#define SOFTRST BIT(1) -#define ENAWAKEUP BIT(2) -#define SIDLEMODE(value) (((value) & 0x3) << 3) -#define CLOCKACTIVITY(value) (((value) & 0x3) << 8) - -/********************** McBSP SSELCR bit definitions ***********************/ -#define SIDETONEEN BIT(10) - -/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ -#define ST_AUTOIDLE BIT(0) - -/********************** McBSP Sidetone SGAINCR bit definitions *************/ -#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ -#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ - -/********************** McBSP Sidetone SFIRCR bit definitions **************/ -#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ - -/********************** McBSP Sidetone SSELCR bit definitions **************/ -#define ST_SIDETONEEN BIT(0) -#define ST_COEFFWREN BIT(1) -#define ST_COEFFWRDONE BIT(2) - -/********************** McBSP DMA operating modes **************************/ -#define MCBSP_DMA_MODE_ELEMENT 0 -#define MCBSP_DMA_MODE_THRESHOLD 1 - -/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/ -#define RSYNCERREN BIT(0) -#define RFSREN BIT(1) -#define REOFEN BIT(2) -#define RRDYEN BIT(3) -#define RUNDFLEN BIT(4) -#define ROVFLEN BIT(5) -#define XSYNCERREN BIT(7) -#define XFSXEN BIT(8) -#define XEOFEN BIT(9) -#define XRDYEN BIT(10) -#define XUNDFLEN BIT(11) -#define XOVFLEN BIT(12) -#define XEMPTYEOFEN BIT(14) - -/* Clock signal muxing options */ -#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */ -#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */ -#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */ -#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */ - -/* McBSP functional clock sources */ -#define MCBSP_CLKS_PRCM_SRC 0 -#define MCBSP_CLKS_PAD_SRC 1 - -/* we don't do multichannel for now */ -struct omap_mcbsp_reg_cfg { - u16 spcr2; - u16 spcr1; - u16 rcr2; - u16 rcr1; - u16 xcr2; - u16 xcr1; - u16 srgr2; - u16 srgr1; - u16 mcr2; - u16 mcr1; - u16 pcr0; - u16 rcerc; - u16 rcerd; - u16 xcerc; - u16 xcerd; - u16 rcere; - u16 rcerf; - u16 xcere; - u16 xcerf; - u16 rcerg; - u16 rcerh; - u16 xcerg; - u16 xcerh; - u16 xccr; - u16 rccr; -}; - -struct omap_mcbsp_st_data { - void __iomem *io_base_st; - struct clk *mcbsp_iclk; - bool running; - bool enabled; - s16 taps[128]; /* Sidetone filter coefficients */ - int nr_taps; /* Number of filter coefficients in use */ - s16 ch0gain; - s16 ch1gain; -}; - -struct omap_mcbsp { - struct device *dev; - struct clk *fclk; - spinlock_t lock; - unsigned long phys_base; - unsigned long phys_dma_base; - void __iomem *io_base; - u8 id; - /* - * Flags indicating is the bus already activated and configured by - * another substream - */ - int active; - int configured; - u8 free; - - int irq; - int rx_irq; - int tx_irq; - - /* Protect the field .free, while checking if the mcbsp is in use */ - struct omap_mcbsp_platform_data *pdata; - struct omap_mcbsp_st_data *st_data; - struct omap_mcbsp_reg_cfg cfg_regs; - struct snd_dmaengine_dai_dma_data dma_data[2]; - unsigned int dma_req[2]; - int dma_op_mode; - u16 max_tx_thres; - u16 max_rx_thres; - void *reg_cache; - int reg_cache_size; - - unsigned int fmt; - unsigned int in_freq; - unsigned int latency[2]; - int clk_div; - int wlen; - - struct pm_qos_request pm_qos_req; -}; - -void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config); -void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); -void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold); -u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp); -u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp); -int omap_mcbsp_get_dma_op_mode(struct omap_mcbsp *mcbsp); -int omap_mcbsp_request(struct omap_mcbsp *mcbsp); -void omap_mcbsp_free(struct omap_mcbsp *mcbsp); -void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream); -void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream); - -/* McBSP functional clock source changing function */ -int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id); - -/* Sidetone specific API */ -int omap_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, s16 chgain); -int omap_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, s16 *chgain); -int omap_st_enable(struct omap_mcbsp *mcbsp); -int omap_st_disable(struct omap_mcbsp *mcbsp); -int omap_st_is_enabled(struct omap_mcbsp *mcbsp); - -int omap_mcbsp_init(struct platform_device *pdev); -void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp); - -#endif /* __ASOC_MCBSP_H */ diff --git a/sound/soc/omap/omap-mcbsp-priv.h b/sound/soc/omap/omap-mcbsp-priv.h new file mode 100644 index 000000000000..7865cda4bf0a --- /dev/null +++ b/sound/soc/omap/omap-mcbsp-priv.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * OMAP Multi-Channel Buffered Serial Port + * + * Contact: Jarkko Nikula + * Peter Ujfalusi + */ + +#ifndef __OMAP_MCBSP_PRIV_H__ +#define __OMAP_MCBSP_PRIV_H__ + +#include + +#ifdef CONFIG_ARCH_OMAP1 +#define mcbsp_omap1() 1 +#else +#define mcbsp_omap1() 0 +#endif + +/* McBSP register numbers. Register address offset = num * reg_step */ +enum { + /* Common registers */ + OMAP_MCBSP_REG_SPCR2 = 4, + OMAP_MCBSP_REG_SPCR1, + OMAP_MCBSP_REG_RCR2, + OMAP_MCBSP_REG_RCR1, + OMAP_MCBSP_REG_XCR2, + OMAP_MCBSP_REG_XCR1, + OMAP_MCBSP_REG_SRGR2, + OMAP_MCBSP_REG_SRGR1, + OMAP_MCBSP_REG_MCR2, + OMAP_MCBSP_REG_MCR1, + OMAP_MCBSP_REG_RCERA, + OMAP_MCBSP_REG_RCERB, + OMAP_MCBSP_REG_XCERA, + OMAP_MCBSP_REG_XCERB, + OMAP_MCBSP_REG_PCR0, + OMAP_MCBSP_REG_RCERC, + OMAP_MCBSP_REG_RCERD, + OMAP_MCBSP_REG_XCERC, + OMAP_MCBSP_REG_XCERD, + OMAP_MCBSP_REG_RCERE, + OMAP_MCBSP_REG_RCERF, + OMAP_MCBSP_REG_XCERE, + OMAP_MCBSP_REG_XCERF, + OMAP_MCBSP_REG_RCERG, + OMAP_MCBSP_REG_RCERH, + OMAP_MCBSP_REG_XCERG, + OMAP_MCBSP_REG_XCERH, + + /* OMAP1-OMAP2420 registers */ + OMAP_MCBSP_REG_DRR2 = 0, + OMAP_MCBSP_REG_DRR1, + OMAP_MCBSP_REG_DXR2, + OMAP_MCBSP_REG_DXR1, + + /* OMAP2430 and onwards */ + OMAP_MCBSP_REG_DRR = 0, + OMAP_MCBSP_REG_DXR = 2, + OMAP_MCBSP_REG_SYSCON = 35, + OMAP_MCBSP_REG_THRSH2, + OMAP_MCBSP_REG_THRSH1, + OMAP_MCBSP_REG_IRQST = 40, + OMAP_MCBSP_REG_IRQEN, + OMAP_MCBSP_REG_WAKEUPEN, + OMAP_MCBSP_REG_XCCR, + OMAP_MCBSP_REG_RCCR, + OMAP_MCBSP_REG_XBUFFSTAT, + OMAP_MCBSP_REG_RBUFFSTAT, + OMAP_MCBSP_REG_SSELCR, +}; + +/************************** McBSP SPCR1 bit definitions ***********************/ +#define RRST BIT(0) +#define RRDY BIT(1) +#define RFULL BIT(2) +#define RSYNC_ERR BIT(3) +#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ +#define ABIS BIT(6) +#define DXENA BIT(7) +#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */ +#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */ +#define ALB BIT(15) +#define DLB BIT(15) + +/************************** McBSP SPCR2 bit definitions ***********************/ +#define XRST BIT(0) +#define XRDY BIT(1) +#define XEMPTY BIT(2) +#define XSYNC_ERR BIT(3) +#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ +#define GRST BIT(6) +#define FRST BIT(7) +#define SOFT BIT(8) +#define FREE BIT(9) + +/************************** McBSP PCR bit definitions *************************/ +#define CLKRP BIT(0) +#define CLKXP BIT(1) +#define FSRP BIT(2) +#define FSXP BIT(3) +#define DR_STAT BIT(4) +#define DX_STAT BIT(5) +#define CLKS_STAT BIT(6) +#define SCLKME BIT(7) +#define CLKRM BIT(8) +#define CLKXM BIT(9) +#define FSRM BIT(10) +#define FSXM BIT(11) +#define RIOEN BIT(12) +#define XIOEN BIT(13) +#define IDLE_EN BIT(14) + +/************************** McBSP RCR1 bit definitions ************************/ +#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ + +/************************** McBSP XCR1 bit definitions ************************/ +#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ + +/*************************** McBSP RCR2 bit definitions ***********************/ +#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ +#define RFIG BIT(2) +#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ +#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ +#define RPHASE BIT(15) + +/*************************** McBSP XCR2 bit definitions ***********************/ +#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ +#define XFIG BIT(2) +#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ +#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ +#define XPHASE BIT(15) + +/************************* McBSP SRGR1 bit definitions ************************/ +#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */ +#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */ + +/************************* McBSP SRGR2 bit definitions ************************/ +#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */ +#define FSGM BIT(12) +#define CLKSM BIT(13) +#define CLKSP BIT(14) +#define GSYNC BIT(15) + +/************************* McBSP MCR1 bit definitions *************************/ +#define RMCM BIT(0) +#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ +#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ +#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ + +/************************* McBSP MCR2 bit definitions *************************/ +#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */ +#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ +#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ +#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ + +/*********************** McBSP XCCR bit definitions *************************/ +#define XDISABLE BIT(0) +#define XDMAEN BIT(3) +#define DILB BIT(5) +#define XFULL_CYCLE BIT(11) +#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */ +#define PPCONNECT BIT(14) +#define EXTCLKGATE BIT(15) + +/********************** McBSP RCCR bit definitions *************************/ +#define RDISABLE BIT(0) +#define RDMAEN BIT(3) +#define RFULL_CYCLE BIT(11) + +/********************** McBSP SYSCONFIG bit definitions ********************/ +#define SOFTRST BIT(1) +#define ENAWAKEUP BIT(2) +#define SIDLEMODE(value) (((value) & 0x3) << 3) +#define CLOCKACTIVITY(value) (((value) & 0x3) << 8) + +/********************** McBSP DMA operating modes **************************/ +#define MCBSP_DMA_MODE_ELEMENT 0 +#define MCBSP_DMA_MODE_THRESHOLD 1 + +/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/ +#define RSYNCERREN BIT(0) +#define RFSREN BIT(1) +#define REOFEN BIT(2) +#define RRDYEN BIT(3) +#define RUNDFLEN BIT(4) +#define ROVFLEN BIT(5) +#define XSYNCERREN BIT(7) +#define XFSXEN BIT(8) +#define XEOFEN BIT(9) +#define XRDYEN BIT(10) +#define XUNDFLEN BIT(11) +#define XOVFLEN BIT(12) +#define XEMPTYEOFEN BIT(14) + +/* Clock signal muxing options */ +#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */ +#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */ +#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */ +#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */ + +/* McBSP functional clock sources */ +#define MCBSP_CLKS_PRCM_SRC 0 +#define MCBSP_CLKS_PAD_SRC 1 + +/* we don't do multichannel for now */ +struct omap_mcbsp_reg_cfg { + u16 spcr2; + u16 spcr1; + u16 rcr2; + u16 rcr1; + u16 xcr2; + u16 xcr1; + u16 srgr2; + u16 srgr1; + u16 mcr2; + u16 mcr1; + u16 pcr0; + u16 rcerc; + u16 rcerd; + u16 xcerc; + u16 xcerd; + u16 rcere; + u16 rcerf; + u16 xcere; + u16 xcerf; + u16 rcerg; + u16 rcerh; + u16 xcerg; + u16 xcerh; + u16 xccr; + u16 rccr; +}; + +struct omap_mcbsp_st_data; + +struct omap_mcbsp { + struct device *dev; + struct clk *fclk; + spinlock_t lock; + unsigned long phys_base; + unsigned long phys_dma_base; + void __iomem *io_base; + u8 id; + /* + * Flags indicating is the bus already activated and configured by + * another substream + */ + int active; + int configured; + u8 free; + + int irq; + int rx_irq; + int tx_irq; + + /* Protect the field .free, while checking if the mcbsp is in use */ + struct omap_mcbsp_platform_data *pdata; + struct omap_mcbsp_st_data *st_data; + struct omap_mcbsp_reg_cfg cfg_regs; + struct snd_dmaengine_dai_dma_data dma_data[2]; + unsigned int dma_req[2]; + int dma_op_mode; + u16 max_tx_thres; + u16 max_rx_thres; + void *reg_cache; + int reg_cache_size; + + unsigned int fmt; + unsigned int in_freq; + unsigned int latency[2]; + int clk_div; + int wlen; + + struct pm_qos_request pm_qos_req; +}; + +static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; + writew_relaxed((u16)val, addr); + } else { + ((u32 *)mcbsp->reg_cache)[reg] = val; + writel_relaxed(val, addr); + } +} + +static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, + bool from_cache) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + return !from_cache ? readw_relaxed(addr) : + ((u16 *)mcbsp->reg_cache)[reg]; + } else { + return !from_cache ? readl_relaxed(addr) : + ((u32 *)mcbsp->reg_cache)[reg]; + } +} + +#define MCBSP_READ(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) +#define MCBSP_WRITE(mcbsp, reg, val) \ + omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) +#define MCBSP_READ_CACHE(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) + + +/* Sidetone specific API */ +int omap_mcbsp_st_init(struct platform_device *pdev); +void omap_mcbsp_st_cleanup(struct platform_device *pdev); + +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp); + +#endif /* __OMAP_MCBSP_PRIV_H__ */ diff --git a/sound/soc/omap/omap-mcbsp-st.c b/sound/soc/omap/omap-mcbsp-st.c new file mode 100644 index 000000000000..1a3fe854e856 --- /dev/null +++ b/sound/soc/omap/omap-mcbsp-st.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * McBSP Sidetone support + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz + * + * Contact: Jarkko Nikula + * Peter Ujfalusi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "omap-mcbsp.h" +#include "omap-mcbsp-priv.h" + +/* OMAP3 sidetone control registers */ +#define OMAP_ST_REG_REV 0x00 +#define OMAP_ST_REG_SYSCONFIG 0x10 +#define OMAP_ST_REG_IRQSTATUS 0x18 +#define OMAP_ST_REG_IRQENABLE 0x1C +#define OMAP_ST_REG_SGAINCR 0x24 +#define OMAP_ST_REG_SFIRCR 0x28 +#define OMAP_ST_REG_SSELCR 0x2C + +/********************** McBSP SSELCR bit definitions ***********************/ +#define SIDETONEEN BIT(10) + +/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ +#define ST_AUTOIDLE BIT(0) + +/********************** McBSP Sidetone SGAINCR bit definitions *************/ +#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ +#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ + +/********************** McBSP Sidetone SFIRCR bit definitions **************/ +#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ + +/********************** McBSP Sidetone SSELCR bit definitions **************/ +#define ST_SIDETONEEN BIT(0) +#define ST_COEFFWREN BIT(1) +#define ST_COEFFWRDONE BIT(2) + +struct omap_mcbsp_st_data { + void __iomem *io_base_st; + struct clk *mcbsp_iclk; + bool running; + bool enabled; + s16 taps[128]; /* Sidetone filter coefficients */ + int nr_taps; /* Number of filter coefficients in use */ + s16 ch0gain; + s16 ch1gain; +}; + +static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + writel_relaxed(val, mcbsp->st_data->io_base_st + reg); +} + +static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) +{ + return readl_relaxed(mcbsp->st_data->io_base_st + reg); +} + +#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) +#define MCBSP_ST_WRITE(mcbsp, reg, val) \ + omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) + +static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); + + /* Disable Sidetone clock auto-gating for normal operation */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); + + /* Enable McBSP Sidetone */ + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); + + /* Enable Sidetone from Sidetone Core */ + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); +} + +static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); + + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); + + /* Enable Sidetone clock auto-gating to reduce power consumption */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); +} + +static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) +{ + u16 val, i; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + + if (val & ST_COEFFWREN) + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); + + for (i = 0; i < 128; i++) + MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); + + i = 0; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + while (!(val & ST_COEFFWRDONE) && (++i < 1000)) + val = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + if (i == 1000) + dev_err(mcbsp->dev, "McBSP FIR load error!\n"); +} + +static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) +{ + u16 w; + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | + ST_CH1GAIN(st_data->ch1gain)); +} + +static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + st_data->ch0gain = chgain; + else if (channel == 1) + st_data->ch1gain = chgain; + else + ret = -EINVAL; + + if (st_data->enabled) + omap_mcbsp_st_chgain(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 *chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + *chgain = st_data->ch0gain; + else if (channel == 1) + *chgain = st_data->ch1gain; + else + ret = -EINVAL; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + st_data->enabled = 1; + omap_mcbsp_st_start(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return 0; +} + +static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + omap_mcbsp_st_stop(mcbsp); + st_data->enabled = 0; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + return st_data->enabled; +} + +static ssize_t st_taps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + ssize_t status = 0; + int i; + + spin_lock_irq(&mcbsp->lock); + for (i = 0; i < st_data->nr_taps; i++) + status += sprintf(&buf[status], (i ? ", %d" : "%d"), + st_data->taps[i]); + if (i) + status += sprintf(&buf[status], "\n"); + spin_unlock_irq(&mcbsp->lock); + + return status; +} + +static ssize_t st_taps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int val, tmp, status, i = 0; + + spin_lock_irq(&mcbsp->lock); + memset(st_data->taps, 0, sizeof(st_data->taps)); + st_data->nr_taps = 0; + + do { + status = sscanf(buf, "%d%n", &val, &tmp); + if (status < 0 || status == 0) { + size = -EINVAL; + goto out; + } + if (val < -32768 || val > 32767) { + size = -EINVAL; + goto out; + } + st_data->taps[i++] = val; + buf += tmp; + if (*buf != ',') + break; + buf++; + } while (1); + + st_data->nr_taps = i; + +out: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(st_taps); + +static const struct attribute *sidetone_attrs[] = { + &dev_attr_st_taps.attr, + NULL, +}; + +static const struct attribute_group sidetone_attr_group = { + .attrs = (struct attribute **)sidetone_attrs, +}; + +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->enabled && !st_data->running) { + omap_mcbsp_st_fir_write(mcbsp, st_data->taps); + omap_mcbsp_st_chgain(mcbsp); + + if (!mcbsp->free) { + omap_mcbsp_st_on(mcbsp); + st_data->running = 1; + } + } + + return 0; +} + +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->running) { + if (!mcbsp->free) { + omap_mcbsp_st_off(mcbsp); + st_data->running = 0; + } + } + + return 0; +} + +int omap_mcbsp_st_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct omap_mcbsp_st_data *st_data; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); + if (!res) + return 0; + + st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); + if (!st_data) + return -ENOMEM; + + st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); + if (IS_ERR(st_data->mcbsp_iclk)) { + dev_warn(mcbsp->dev, + "Failed to get ick, sidetone might be broken\n"); + st_data->mcbsp_iclk = NULL; + } + + st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, + resource_size(res)); + if (!st_data->io_base_st) + return -ENOMEM; + + ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); + if (ret) + return ret; + + mcbsp->st_data = st_data; + + return 0; +} + +void omap_mcbsp_st_cleanup(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + + if (mcbsp->st_data) { + sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); + clk_put(mcbsp->st_data->mcbsp_iclk); + } +} + +static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int max = mc->max; + int min = mc->min; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = min; + uinfo->value.integer.max = max; + return 0; +} + +#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ +static int \ +omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + struct soc_mixer_control *mc = \ + (struct soc_mixer_control *)kc->private_value; \ + int max = mc->max; \ + int min = mc->min; \ + int val = uc->value.integer.value[0]; \ + \ + if (val < min || val > max) \ + return -EINVAL; \ + \ + /* OMAP McBSP implementation uses index values 0..4 */ \ + return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ +} \ + \ +static int \ +omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + s16 chgain; \ + \ + if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ + return -EAGAIN; \ + \ + uc->value.integer.value[0] = chgain; \ + return 0; \ +} + +OMAP_MCBSP_ST_CHANNEL_VOLUME(0) +OMAP_MCBSP_ST_CHANNEL_VOLUME(1) + +static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + u8 value = ucontrol->value.integer.value[0]; + + if (value == omap_mcbsp_st_is_enabled(mcbsp)) + return 0; + + if (value) + omap_mcbsp_st_enable(mcbsp); + else + omap_mcbsp_st_disable(mcbsp); + + return 1; +} + +static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); + return 0; +} + +#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = omap_mcbsp_st_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.min = xmin, .max = xmax} } + +#define OMAP_MCBSP_ST_CONTROLS(port) \ +static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ +SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ + omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch0_volume, \ + omap_mcbsp_set_st_ch0_volume), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch1_volume, \ + omap_mcbsp_set_st_ch1_volume), \ +} + +OMAP_MCBSP_ST_CONTROLS(2); +OMAP_MCBSP_ST_CONTROLS(3); + +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + if (!mcbsp->st_data) { + dev_warn(mcbsp->dev, "No sidetone data for port\n"); + return 0; + } + + switch (port_id) { + case 2: /* McBSP 2 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp2_st_controls, + ARRAY_SIZE(omap_mcbsp2_st_controls)); + case 3: /* McBSP 3 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp3_st_controls, + ARRAY_SIZE(omap_mcbsp3_st_controls)); + default: + dev_err(mcbsp->dev, "Port %d not supported\n", port_id); + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 69a6b8ad6d42..e48fad11a0cc 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -35,21 +35,12 @@ #include #include -#include -#include "mcbsp.h" +#include "omap-mcbsp-priv.h" #include "omap-mcbsp.h" #include "sdma-pcm.h" #define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) -#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = omap_mcbsp_st_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long) &(struct soc_mixer_control) \ - {.min = xmin, .max = xmax} } - enum { OMAP_MCBSP_WORD_8 = 0, OMAP_MCBSP_WORD_12, @@ -59,6 +50,702 @@ enum { OMAP_MCBSP_WORD_32, }; +static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) +{ + dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); + dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); + dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); + dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); + dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); + dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); + dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); + dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); + dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); + dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); + dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); + dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); + dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); + dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); + dev_dbg(mcbsp->dev, "***********************\n"); +} + +static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) +{ + struct clk *fck_src; + const char *src; + int r; + + if (fck_src_id == MCBSP_CLKS_PAD_SRC) + src = "pad_fck"; + else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) + src = "prcm_fck"; + else + return -EINVAL; + + fck_src = clk_get(mcbsp->dev, src); + if (IS_ERR(fck_src)) { + dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); + return -EINVAL; + } + + pm_runtime_put_sync(mcbsp->dev); + + r = clk_set_parent(mcbsp->fclk, fck_src); + if (r) { + dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", + src); + clk_put(fck_src); + return r; + } + + pm_runtime_get_sync(mcbsp->dev); + + clk_put(fck_src); + + return 0; +} + +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst; + + irqst = MCBSP_READ(mcbsp, IRQST); + dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); + + if (irqst & RSYNCERREN) + dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); + if (irqst & RFSREN) + dev_dbg(mcbsp->dev, "RX Frame Sync\n"); + if (irqst & REOFEN) + dev_dbg(mcbsp->dev, "RX End Of Frame\n"); + if (irqst & RRDYEN) + dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); + if (irqst & RUNDFLEN) + dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); + if (irqst & ROVFLEN) + dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); + + if (irqst & XSYNCERREN) + dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); + if (irqst & XFSXEN) + dev_dbg(mcbsp->dev, "TX Frame Sync\n"); + if (irqst & XEOFEN) + dev_dbg(mcbsp->dev, "TX End Of Frame\n"); + if (irqst & XRDYEN) + dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); + if (irqst & XUNDFLEN) + dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); + if (irqst & XOVFLEN) + dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); + if (irqst & XEMPTYEOFEN) + dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); + + MCBSP_WRITE(mcbsp, IRQST, irqst); + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr2; + + irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); + dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + + if (irqst_spcr2 & XSYNC_ERR) { + dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", + irqst_spcr2); + /* Writing zero to XSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr1; + + irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); + dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + + if (irqst_spcr1 & RSYNC_ERR) { + dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", + irqst_spcr1); + /* Writing zero to RSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); + } + + return IRQ_HANDLED; +} + +/* + * omap_mcbsp_config simply write a config to the + * appropriate McBSP. + * You either call this function or set the McBSP registers + * by yourself before calling omap_mcbsp_start(). + */ +static void omap_mcbsp_config(struct omap_mcbsp *mcbsp, + const struct omap_mcbsp_reg_cfg *config) +{ + dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", + mcbsp->id, mcbsp->phys_base); + + /* We write the given config */ + MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); + MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); + MCBSP_WRITE(mcbsp, RCR2, config->rcr2); + MCBSP_WRITE(mcbsp, RCR1, config->rcr1); + MCBSP_WRITE(mcbsp, XCR2, config->xcr2); + MCBSP_WRITE(mcbsp, XCR1, config->xcr1); + MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); + MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); + MCBSP_WRITE(mcbsp, MCR2, config->mcr2); + MCBSP_WRITE(mcbsp, MCR1, config->mcr1); + MCBSP_WRITE(mcbsp, PCR0, config->pcr0); + if (mcbsp->pdata->has_ccr) { + MCBSP_WRITE(mcbsp, XCCR, config->xccr); + MCBSP_WRITE(mcbsp, RCCR, config->rccr); + } + /* Enable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); + + /* Enable TX/RX sync error interrupts by default */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | + RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); +} + +/** + * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register + * @mcbsp: omap_mcbsp struct for the McBSP instance + * @stream: Stream direction (playback/capture) + * + * Returns the address of mcbsp data transmit register or data receive register + * to be used by DMA for transferring/receiving data + */ +static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, + unsigned int stream) +{ + int data_reg; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DXR1; + else + data_reg = OMAP_MCBSP_REG_DXR; + } else { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DRR; + } + + return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; +} + +/* + * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH2 register. + */ +static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_tx_thres) + MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); +} + +/* + * omap_mcbsp_set_rx_threshold configures the receive threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH1 register. + */ +static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_rx_thres) + MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); +} + +/* + * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO + */ +static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat; + + /* Returns the number of free locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); + + /* Number of slots are different in McBSP ports */ + return mcbsp->pdata->buffer_size - buffstat; +} + +/* + * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO + * to reach the threshold value (when the DMA will be triggered to read it) + */ +static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat, threshold; + + /* Returns the number of used locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); + /* RX threshold */ + threshold = MCBSP_READ(mcbsp, THRSH1); + + /* Return the number of location till we reach the threshold limit */ + if (threshold <= buffstat) + return 0; + else + return threshold - buffstat; +} + +static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + int err; + + reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); + if (!reg_cache) + return -ENOMEM; + + spin_lock(&mcbsp->lock); + if (!mcbsp->free) { + dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); + err = -EBUSY; + goto err_kfree; + } + + mcbsp->free = false; + mcbsp->reg_cache = reg_cache; + spin_unlock(&mcbsp->lock); + + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) + mcbsp->pdata->ops->request(mcbsp->id - 1); + + /* + * Make sure that transmitter, receiver and sample-rate generator are + * not running before activating IRQs. + */ + MCBSP_WRITE(mcbsp, SPCR1, 0); + MCBSP_WRITE(mcbsp, SPCR2, 0); + + if (mcbsp->irq) { + err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, + "McBSP", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request IRQ\n"); + goto err_clk_disable; + } + } else { + err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP TX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); + goto err_clk_disable; + } + + err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP RX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); + goto err_free_irq; + } + } + + return 0; +err_free_irq: + free_irq(mcbsp->tx_irq, (void *)mcbsp); +err_clk_disable: + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + spin_lock(&mcbsp->lock); + mcbsp->free = true; + mcbsp->reg_cache = NULL; +err_kfree: + spin_unlock(&mcbsp->lock); + kfree(reg_cache); + + return err; +} + +static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + + if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + /* Disable interrupt requests */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, 0); + + if (mcbsp->irq) { + free_irq(mcbsp->irq, (void *)mcbsp); + } else { + free_irq(mcbsp->rx_irq, (void *)mcbsp); + free_irq(mcbsp->tx_irq, (void *)mcbsp); + } + + reg_cache = mcbsp->reg_cache; + + /* + * Select CLKS source from internal source unconditionally before + * marking the McBSP port as free. + * If the external clock source via MCBSP_CLKS pin has been selected the + * system will refuse to enter idle if the CLKS pin source is not reset + * back to internal source. + */ + if (!mcbsp_omap1()) + omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); + + spin_lock(&mcbsp->lock); + if (mcbsp->free) + dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); + else + mcbsp->free = true; + mcbsp->reg_cache = NULL; + spin_unlock(&mcbsp->lock); + + kfree(reg_cache); +} + +/* + * Here we start the McBSP, by enabling transmitter, receiver or both. + * If no transmitter or receiver is active prior calling, then sample-rate + * generator and frame sync are started. + */ +static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int enable_srg = 0; + u16 w; + + if (mcbsp->st_data) + omap_mcbsp_st_start(mcbsp); + + /* Only enable SRG, if McBSP is master */ + w = MCBSP_READ_CACHE(mcbsp, PCR0); + if (w & (FSXM | FSRM | CLKXM | CLKRM)) + enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (enable_srg) { + /* Start the sample generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); + } + + /* Enable transmitter and receiver */ + tx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | tx); + + rx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w | rx); + + /* + * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec + * REVISIT: 100us may give enough time for two CLKSRG, however + * due to some unknown PM related, clock gating etc. reason it + * is now at 500us. + */ + udelay(500); + + if (enable_srg) { + /* Start frame sync */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); + } + + if (mcbsp->pdata->has_ccr) { + /* Release the transmitter and receiver */ + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w &= ~(tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w &= ~(rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(mcbsp); +} + +static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int idle; + u16 w; + + /* Reset transmitter */ + tx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w |= (tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); + + /* Reset receiver */ + rx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w |= (rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); + + idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (idle) { + /* Reset the sample rate generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); + } + + if (mcbsp->st_data) + omap_mcbsp_st_stop(mcbsp); +} + +#define max_thres(m) (mcbsp->pdata->buffer_size) +#define valid_threshold(m, val) ((val) <= max_thres(m)) +#define THRESHOLD_PROP_BUILDER(prop) \ +static ssize_t prop##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%u\n", mcbsp->prop); \ +} \ + \ +static ssize_t prop##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + unsigned long val; \ + int status; \ + \ + status = kstrtoul(buf, 0, &val); \ + if (status) \ + return status; \ + \ + if (!valid_threshold(mcbsp, val)) \ + return -EDOM; \ + \ + mcbsp->prop = val; \ + return size; \ +} \ + \ +static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store) + +THRESHOLD_PROP_BUILDER(max_tx_thres); +THRESHOLD_PROP_BUILDER(max_rx_thres); + +static const char * const dma_op_modes[] = { + "element", "threshold", +}; + +static ssize_t dma_op_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int dma_op_mode, i = 0; + ssize_t len = 0; + const char * const *s; + + dma_op_mode = mcbsp->dma_op_mode; + + for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { + if (dma_op_mode == i) + len += sprintf(buf + len, "[%s] ", *s); + else + len += sprintf(buf + len, "%s ", *s); + } + len += sprintf(buf + len, "\n"); + + return len; +} + +static ssize_t dma_op_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int i; + + i = sysfs_match_string(dma_op_modes, buf); + if (i < 0) + return i; + + spin_lock_irq(&mcbsp->lock); + if (!mcbsp->free) { + size = -EBUSY; + goto unlock; + } + mcbsp->dma_op_mode = i; + +unlock: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(dma_op_mode); + +static const struct attribute *additional_attrs[] = { + &dev_attr_max_tx_thres.attr, + &dev_attr_max_rx_thres.attr, + &dev_attr_dma_op_mode.attr, + NULL, +}; + +static const struct attribute_group additional_attr_group = { + .attrs = (struct attribute **)additional_attrs, +}; + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +static int omap_mcbsp_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct resource *res; + int ret = 0; + + spin_lock_init(&mcbsp->lock); + mcbsp->free = true; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcbsp->io_base)) + return PTR_ERR(mcbsp->io_base); + + mcbsp->phys_base = res->start; + mcbsp->reg_cache_size = resource_size(res); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) + mcbsp->phys_dma_base = mcbsp->phys_base; + else + mcbsp->phys_dma_base = res->start; + + /* + * OMAP1, 2 uses two interrupt lines: TX, RX + * OMAP2430, OMAP3 SoC have combined IRQ line as well. + * OMAP4 and newer SoC only have the combined IRQ line. + * Use the combined IRQ if available since it gives better debugging + * possibilities. + */ + mcbsp->irq = platform_get_irq_byname(pdev, "common"); + if (mcbsp->irq == -ENXIO) { + mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); + + if (mcbsp->tx_irq == -ENXIO) { + mcbsp->irq = platform_get_irq(pdev, 0); + mcbsp->tx_irq = 0; + } else { + mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); + mcbsp->irq = 0; + } + } + + if (!pdev->dev.of_node) { + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!res) { + dev_err(&pdev->dev, "invalid tx DMA channel\n"); + return -ENODEV; + } + mcbsp->dma_req[0] = res->start; + mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!res) { + dev_err(&pdev->dev, "invalid rx DMA channel\n"); + return -ENODEV; + } + mcbsp->dma_req[1] = res->start; + mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; + } else { + mcbsp->dma_data[0].filter_data = "tx"; + mcbsp->dma_data[1].filter_data = "rx"; + } + + mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_PLAYBACK); + mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_CAPTURE); + + mcbsp->fclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(mcbsp->fclk)) { + ret = PTR_ERR(mcbsp->fclk); + dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); + return ret; + } + + mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; + if (mcbsp->pdata->buffer_size) { + /* + * Initially configure the maximum thresholds to a safe value. + * The McBSP FIFO usage with these values should not go under + * 16 locations. + * If the whole FIFO without safety buffer is used, than there + * is a possibility that the DMA will be not able to push the + * new data on time, causing channel shifts in runtime. + */ + mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; + mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; + + ret = sysfs_create_group(&mcbsp->dev->kobj, + &additional_attr_group); + if (ret) { + dev_err(mcbsp->dev, + "Unable to create additional controls\n"); + goto err_thres; + } + } else { + mcbsp->max_tx_thres = -EINVAL; + mcbsp->max_rx_thres = -EINVAL; + } + + ret = omap_mcbsp_st_init(pdev); + if (ret) + goto err_st; + + return 0; + +err_st: + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); +err_thres: + clk_put(mcbsp->fclk); + return ret; +} + /* * Stream DMA parameters. DMA request line and port address are set runtime * since they are different between OMAP1 and later OMAPs @@ -656,132 +1343,6 @@ static const struct snd_soc_component_driver omap_mcbsp_component = { .name = "omap-mcbsp", }; -static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - int min = mc->min; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = min; - uinfo->value.integer.max = max; - return 0; -} - -#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ -static int \ -omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - struct soc_mixer_control *mc = \ - (struct soc_mixer_control *)kc->private_value; \ - int max = mc->max; \ - int min = mc->min; \ - int val = uc->value.integer.value[0]; \ - \ - if (val < min || val > max) \ - return -EINVAL; \ - \ - /* OMAP McBSP implementation uses index values 0..4 */ \ - return omap_st_set_chgain(mcbsp, channel, val); \ -} \ - \ -static int \ -omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - s16 chgain; \ - \ - if (omap_st_get_chgain(mcbsp, channel, &chgain)) \ - return -EAGAIN; \ - \ - uc->value.integer.value[0] = chgain; \ - return 0; \ -} - -OMAP_MCBSP_ST_CHANNEL_VOLUME(0) -OMAP_MCBSP_ST_CHANNEL_VOLUME(1) - -static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - u8 value = ucontrol->value.integer.value[0]; - - if (value == omap_st_is_enabled(mcbsp)) - return 0; - - if (value) - omap_st_enable(mcbsp); - else - omap_st_disable(mcbsp); - - return 1; -} - -static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - ucontrol->value.integer.value[0] = omap_st_is_enabled(mcbsp); - return 0; -} - -#define OMAP_MCBSP_ST_CONTROLS(port) \ -static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ -SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ - omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch0_volume, \ - omap_mcbsp_set_st_ch0_volume), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch1_volume, \ - omap_mcbsp_set_st_ch1_volume), \ -} - -OMAP_MCBSP_ST_CONTROLS(2); -OMAP_MCBSP_ST_CONTROLS(3); - -int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) -{ - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - if (!mcbsp->st_data) { - dev_warn(mcbsp->dev, "No sidetone data for port\n"); - return 0; - } - - switch (port_id) { - case 2: /* McBSP 2 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp2_st_controls, - ARRAY_SIZE(omap_mcbsp2_st_controls)); - case 3: /* McBSP 3 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp3_st_controls, - ARRAY_SIZE(omap_mcbsp3_st_controls)); - default: - dev_err(mcbsp->dev, "Port %d not supported\n", port_id); - break; - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); - static struct omap_mcbsp_platform_data omap2420_pdata = { .reg_step = 4, .reg_size = 2, @@ -893,7 +1454,10 @@ static int asoc_mcbsp_remove(struct platform_device *pdev) if (pm_qos_request_active(&mcbsp->pm_qos_req)) pm_qos_remove_request(&mcbsp->pm_qos_req); - omap_mcbsp_cleanup(mcbsp); + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); + + omap_mcbsp_st_cleanup(pdev); clk_put(mcbsp->fclk); diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h index 2e3369c27be3..7911d24898c9 100644 --- a/sound/soc/omap/omap-mcbsp.h +++ b/sound/soc/omap/omap-mcbsp.h @@ -22,8 +22,10 @@ * */ -#ifndef __OMAP_I2S_H__ -#define __OMAP_I2S_H__ +#ifndef __OMAP_MCBSP_H__ +#define __OMAP_MCBSP_H__ + +#include /* Source clocks for McBSP sample rate generator */ enum omap_mcbsp_clksrg_clk { @@ -41,4 +43,4 @@ enum omap_mcbsp_div { int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id); -#endif +#endif /* __OMAP_MCBSP_H__ */ -- cgit v1.2.3-58-ga151 From 465d85b3976a15c9e0e205ff50be64efa11b7af3 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:29:59 +0200 Subject: ASoC: omap-mcbsp: Remove redundant check for mcbsp->pdata The driver will not probe if the pdata is not provided or created. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/omap-mcbsp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index e48fad11a0cc..8cab1151d3bb 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -324,7 +324,7 @@ static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) mcbsp->reg_cache = reg_cache; spin_unlock(&mcbsp->lock); - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->request) + if(mcbsp->pdata->ops && mcbsp->pdata->ops->request) mcbsp->pdata->ops->request(mcbsp->id - 1); /* @@ -361,7 +361,7 @@ static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) err_free_irq: free_irq(mcbsp->tx_irq, (void *)mcbsp); err_clk_disable: - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) mcbsp->pdata->ops->free(mcbsp->id - 1); /* Disable wakeup behavior */ @@ -382,7 +382,7 @@ static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) { void *reg_cache; - if (mcbsp->pdata && mcbsp->pdata->ops && mcbsp->pdata->ops->free) + if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) mcbsp->pdata->ops->free(mcbsp->id - 1); /* Disable wakeup behavior */ -- cgit v1.2.3-58-ga151 From f15c19af929d380d0acb2a8d8277d688351bd5de Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Nov 2018 09:30:00 +0200 Subject: ASoC: omap-mcbsp: No need to initialize max_xx_thres when it is not used Initializing to -EINVAL is not correct as the variables are unsigned and if buffer_size is 0 then they are not used anyway. Signed-off-by: Peter Ujfalusi Acked-by: Jarkko Nikula Tested-by: Jarkko Nikula Signed-off-by: Mark Brown --- sound/soc/omap/omap-mcbsp.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c index 8cab1151d3bb..e0ef4774c710 100644 --- a/sound/soc/omap/omap-mcbsp.c +++ b/sound/soc/omap/omap-mcbsp.c @@ -727,9 +727,6 @@ static int omap_mcbsp_init(struct platform_device *pdev) "Unable to create additional controls\n"); goto err_thres; } - } else { - mcbsp->max_tx_thres = -EINVAL; - mcbsp->max_rx_thres = -EINVAL; } ret = omap_mcbsp_st_init(pdev); -- cgit v1.2.3-58-ga151 From dcad34f818f2a23b9a51c7c2a21e167fff441e52 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 12 Nov 2018 13:36:39 +0000 Subject: ASoC: wm_adsp: Factor out common init code Factor out the duplicated initialization statements from wm_adsp1_init() and wm_adsp2_init() into new function wm_adsp_common_init(). The entire content of wm_adsp1_init() is the common code but it is convenient to retain this exported function to hide what we currently treat as common init (which might change in the future) and also make clear the difference between an ADSP1 entry point and common code. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index a53dc174bbf0..0ed44e5c58f7 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2416,7 +2416,7 @@ static int wm_adsp_create_name(struct wm_adsp *dsp) return 0; } -int wm_adsp1_init(struct wm_adsp *dsp) +static int wm_adsp_common_init(struct wm_adsp *dsp) { int ret; @@ -2425,11 +2425,17 @@ int wm_adsp1_init(struct wm_adsp *dsp) return ret; INIT_LIST_HEAD(&dsp->alg_regions); + INIT_LIST_HEAD(&dsp->ctl_list); mutex_init(&dsp->pwr_lock); return 0; } + +int wm_adsp1_init(struct wm_adsp *dsp) +{ + return wm_adsp_common_init(dsp); +} EXPORT_SYMBOL_GPL(wm_adsp1_init); int wm_adsp1_event(struct snd_soc_dapm_widget *w, @@ -2914,7 +2920,7 @@ int wm_adsp2_init(struct wm_adsp *dsp) { int ret; - ret = wm_adsp_create_name(dsp); + ret = wm_adsp_common_init(dsp); if (ret) return ret; @@ -2936,12 +2942,8 @@ int wm_adsp2_init(struct wm_adsp *dsp) break; } - INIT_LIST_HEAD(&dsp->alg_regions); - INIT_LIST_HEAD(&dsp->ctl_list); INIT_WORK(&dsp->boot_work, wm_adsp2_boot_work); - mutex_init(&dsp->pwr_lock); - return 0; } EXPORT_SYMBOL_GPL(wm_adsp2_init); -- cgit v1.2.3-58-ga151 From 019033c854a20e10f691f6cc0e897df8817d9521 Mon Sep 17 00:00:00 2001 From: Bard liao Date: Sun, 11 Nov 2018 05:18:46 +0800 Subject: ASoC: Intel: hdac_hdmi: add Icelake support Add Icelake device id. Also, Icelake's pin2port mapping table is complicated. So we use a mapping table to do the pin2port mapping. Signed-off-by: Bard liao Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 63 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 54 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 4e9854889a95..fac397326515 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -121,8 +121,16 @@ struct hdac_hdmi_dai_port_map { struct hdac_hdmi_cvt *cvt; }; +/* + * pin to port mapping table where the value indicate the pin number and + * the index indicate the port number with 1 base. + */ +static const int icl_pin2port_map[] = {0x4, 0x6, 0x8, 0xa, 0xb}; + struct hdac_hdmi_drv_data { unsigned int vendor_nid; + const int *port_map; /* pin to port mapping table */ + int port_num; }; struct hdac_hdmi_priv { @@ -1329,11 +1337,12 @@ static int hdac_hdmi_add_pin(struct hdac_device *hdev, hda_nid_t nid) return 0; } -#define INTEL_VENDOR_NID 0x08 -#define INTEL_GLK_VENDOR_NID 0x0b +#define INTEL_VENDOR_NID_0x2 0x02 +#define INTEL_VENDOR_NID_0x8 0x08 +#define INTEL_VENDOR_NID_0xb 0x0b #define INTEL_GET_VENDOR_VERB 0xf81 #define INTEL_SET_VENDOR_VERB 0x781 -#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ +#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */ #define INTEL_EN_ALL_PIN_CVTS 0x01 /* enable 2nd & 3rd pins and convertors */ static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdev) @@ -1538,7 +1547,26 @@ free_widgets: static int hdac_hdmi_pin2port(void *aptr, int pin) { - return pin - 4; /* map NID 0x05 -> port #1 */ + struct hdac_device *hdev = aptr; + struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); + const int *map = hdmi->drv_data->port_map; + int i; + + if (!hdmi->drv_data->port_num) + return pin - 4; /* map NID 0x05 -> port #1 */ + + /* + * looking for the pin number in the mapping table and return + * the index which indicate the port number + */ + for (i = 0; i < hdmi->drv_data->port_num; i++) { + if (pin == map[i]) + return i + 1; + } + + /* return -1 if pin number exceeds our expectation */ + dev_err(&hdev->dev, "Can't find the port for pin %d\n", pin); + return -1; } static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) @@ -1549,9 +1577,18 @@ static void hdac_hdmi_eld_notify_cb(void *aptr, int port, int pipe) struct hdac_hdmi_port *hport = NULL; struct snd_soc_component *component = hdmi->component; int i; - - /* Don't know how this mapping is derived */ - hda_nid_t pin_nid = port + 0x04; + hda_nid_t pin_nid; + + if (!hdmi->drv_data->port_num) { + /* for legacy platforms */ + pin_nid = port + 0x04; + } else if (port < hdmi->drv_data->port_num) { + /* get pin number from the pin2port mapping table */ + pin_nid = hdmi->drv_data->port_map[port - 1]; + } else { + dev_err(&hdev->dev, "Can't find the pin for port %d\n", port); + return; + } dev_dbg(&hdev->dev, "%s: for pin:%d port=%d\n", __func__, pin_nid, pipe); @@ -1973,12 +2010,18 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) return port->eld.info.spk_alloc; } +static struct hdac_hdmi_drv_data intel_icl_drv_data = { + .vendor_nid = INTEL_VENDOR_NID_0x2, + .port_map = icl_pin2port_map, + .port_num = ARRAY_SIZE(icl_pin2port_map), +}; + static struct hdac_hdmi_drv_data intel_glk_drv_data = { - .vendor_nid = INTEL_GLK_VENDOR_NID, + .vendor_nid = INTEL_VENDOR_NID_0xb, }; static struct hdac_hdmi_drv_data intel_drv_data = { - .vendor_nid = INTEL_VENDOR_NID, + .vendor_nid = INTEL_VENDOR_NID_0x8, }; static int hdac_hdmi_dev_probe(struct hdac_device *hdev) @@ -2259,6 +2302,8 @@ static const struct hda_device_id hdmi_list[] = { &intel_glk_drv_data), HDA_CODEC_EXT_ENTRY(0x8086280d, 0x100000, "Geminilake HDMI", &intel_glk_drv_data), + HDA_CODEC_EXT_ENTRY(0x8086280f, 0x100000, "Icelake HDMI", + &intel_icl_drv_data), {} }; -- cgit v1.2.3-58-ga151 From b1299acd0d63de314f29f6d108e9c8fd086ed373 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Mon, 12 Nov 2018 11:04:52 +0530 Subject: ASoC: AMD: add ACP 3.x IP register header ACP 3.x is a new audio block in raven. Added register header of the same. Signed-off-by: Maruthi Bayyavarapu Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/chip_offset_byte.h | 655 +++++++++++++++++++++++++++++++++ 1 file changed, 655 insertions(+) create mode 100644 sound/soc/amd/raven/chip_offset_byte.h (limited to 'sound') diff --git a/sound/soc/amd/raven/chip_offset_byte.h b/sound/soc/amd/raven/chip_offset_byte.h new file mode 100644 index 000000000000..fceb7bb40297 --- /dev/null +++ b/sound/soc/amd/raven/chip_offset_byte.h @@ -0,0 +1,655 @@ +/* + * ACP 3.0 Register documentation + * + * Copyright (C) 2016 Advanced Micro Devices, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR + * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _acp_ip_OFFSET_HEADER +#define _acp_ip_OFFSET_HEADER +// Registers from ACP_DMA block + +#define mmACP_DMA_CNTL_0 0x1240000 +#define mmACP_DMA_CNTL_1 0x1240004 +#define mmACP_DMA_CNTL_2 0x1240008 +#define mmACP_DMA_CNTL_3 0x124000C +#define mmACP_DMA_CNTL_4 0x1240010 +#define mmACP_DMA_CNTL_5 0x1240014 +#define mmACP_DMA_CNTL_6 0x1240018 +#define mmACP_DMA_CNTL_7 0x124001C +#define mmACP_DMA_DSCR_STRT_IDX_0 0x1240020 +#define mmACP_DMA_DSCR_STRT_IDX_1 0x1240024 +#define mmACP_DMA_DSCR_STRT_IDX_2 0x1240028 +#define mmACP_DMA_DSCR_STRT_IDX_3 0x124002C +#define mmACP_DMA_DSCR_STRT_IDX_4 0x1240030 +#define mmACP_DMA_DSCR_STRT_IDX_5 0x1240034 +#define mmACP_DMA_DSCR_STRT_IDX_6 0x1240038 +#define mmACP_DMA_DSCR_STRT_IDX_7 0x124003C +#define mmACP_DMA_DSCR_CNT_0 0x1240040 +#define mmACP_DMA_DSCR_CNT_1 0x1240044 +#define mmACP_DMA_DSCR_CNT_2 0x1240048 +#define mmACP_DMA_DSCR_CNT_3 0x124004C +#define mmACP_DMA_DSCR_CNT_4 0x1240050 +#define mmACP_DMA_DSCR_CNT_5 0x1240054 +#define mmACP_DMA_DSCR_CNT_6 0x1240058 +#define mmACP_DMA_DSCR_CNT_7 0x124005C +#define mmACP_DMA_PRIO_0 0x1240060 +#define mmACP_DMA_PRIO_1 0x1240064 +#define mmACP_DMA_PRIO_2 0x1240068 +#define mmACP_DMA_PRIO_3 0x124006C +#define mmACP_DMA_PRIO_4 0x1240070 +#define mmACP_DMA_PRIO_5 0x1240074 +#define mmACP_DMA_PRIO_6 0x1240078 +#define mmACP_DMA_PRIO_7 0x124007C +#define mmACP_DMA_CUR_DSCR_0 0x1240080 +#define mmACP_DMA_CUR_DSCR_1 0x1240084 +#define mmACP_DMA_CUR_DSCR_2 0x1240088 +#define mmACP_DMA_CUR_DSCR_3 0x124008C +#define mmACP_DMA_CUR_DSCR_4 0x1240090 +#define mmACP_DMA_CUR_DSCR_5 0x1240094 +#define mmACP_DMA_CUR_DSCR_6 0x1240098 +#define mmACP_DMA_CUR_DSCR_7 0x124009C +#define mmACP_DMA_CUR_TRANS_CNT_0 0x12400A0 +#define mmACP_DMA_CUR_TRANS_CNT_1 0x12400A4 +#define mmACP_DMA_CUR_TRANS_CNT_2 0x12400A8 +#define mmACP_DMA_CUR_TRANS_CNT_3 0x12400AC +#define mmACP_DMA_CUR_TRANS_CNT_4 0x12400B0 +#define mmACP_DMA_CUR_TRANS_CNT_5 0x12400B4 +#define mmACP_DMA_CUR_TRANS_CNT_6 0x12400B8 +#define mmACP_DMA_CUR_TRANS_CNT_7 0x12400BC +#define mmACP_DMA_ERR_STS_0 0x12400C0 +#define mmACP_DMA_ERR_STS_1 0x12400C4 +#define mmACP_DMA_ERR_STS_2 0x12400C8 +#define mmACP_DMA_ERR_STS_3 0x12400CC +#define mmACP_DMA_ERR_STS_4 0x12400D0 +#define mmACP_DMA_ERR_STS_5 0x12400D4 +#define mmACP_DMA_ERR_STS_6 0x12400D8 +#define mmACP_DMA_ERR_STS_7 0x12400DC +#define mmACP_DMA_DESC_BASE_ADDR 0x12400E0 +#define mmACP_DMA_DESC_MAX_NUM_DSCR 0x12400E4 +#define mmACP_DMA_CH_STS 0x12400E8 +#define mmACP_DMA_CH_GROUP 0x12400EC +#define mmACP_DMA_CH_RST_STS 0x12400F0 + + +// Registers from ACP_AXI2AXIATU block + +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1 0x1240C00 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1 0x1240C04 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_2 0x1240C08 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_2 0x1240C0C +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_3 0x1240C10 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_3 0x1240C14 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_4 0x1240C18 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_4 0x1240C1C +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_5 0x1240C20 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_5 0x1240C24 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_6 0x1240C28 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_6 0x1240C2C +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_7 0x1240C30 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_7 0x1240C34 +#define mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_8 0x1240C38 +#define mmACPAXI2AXI_ATU_BASE_ADDR_GRP_8 0x1240C3C +#define mmACPAXI2AXI_ATU_CTRL 0x1240C40 + + +// Registers from ACP_CLKRST block + +#define mmACP_SOFT_RESET 0x1241000 +#define mmACP_CONTROL 0x1241004 +#define mmACP_STATUS 0x1241008 +#define mmACP_DSP0_OCD_HALT_ON_RST 0x124100C +#define mmACP_DYNAMIC_CG_MASTER_CONTROL 0x1241010 + + +// Registers from ACP_MISC block + +#define mmACP_EXTERNAL_INTR_ENB 0x1241800 +#define mmACP_EXTERNAL_INTR_CNTL 0x1241804 +#define mmACP_EXTERNAL_INTR_STAT 0x1241808 +#define mmACP_DSP0_INTR_CNTL 0x124180C +#define mmACP_DSP0_INTR_STAT 0x1241810 +#define mmACP_DSP_SW_INTR_CNTL 0x1241814 +#define mmACP_DSP_SW_INTR_STAT 0x1241818 +#define mmACP_SW_INTR_TRIG 0x124181C +#define mmACP_SMU_MAILBOX 0x1241820 +#define mmDSP_INTERRUPT_ROUTING_CTRL 0x1241824 +#define mmACP_DSP0_WATCHDOG_TIMER_CNTL 0x1241828 +#define mmACP_DSP0_EXT_TIMER1_CNTL 0x124182C +#define mmACP_DSP0_EXT_TIMER2_CNTL 0x1241830 +#define mmACP_DSP0_EXT_TIMER3_CNTL 0x1241834 +#define mmACP_DSP0_EXT_TIMER4_CNTL 0x1241838 +#define mmACP_DSP0_EXT_TIMER5_CNTL 0x124183C +#define mmACP_DSP0_EXT_TIMER6_CNTL 0x1241840 +#define mmACP_DSP0_EXT_TIMER1_CURR_VALUE 0x1241844 +#define mmACP_DSP0_EXT_TIMER2_CURR_VALUE 0x1241848 +#define mmACP_DSP0_EXT_TIMER3_CURR_VALUE 0x124184C +#define mmACP_DSP0_EXT_TIMER4_CURR_VALUE 0x1241850 +#define mmACP_DSP0_EXT_TIMER5_CURR_VALUE 0x1241854 +#define mmACP_DSP0_EXT_TIMER6_CURR_VALUE 0x1241858 +#define mmACP_FW_STATUS 0x124185C +#define mmACP_TIMER 0x1241874 +#define mmACP_TIMER_CNTL 0x1241878 +#define mmACP_PGMEM_CTRL 0x12418C0 +#define mmACP_ERROR_STATUS 0x12418C4 +#define mmACP_SW_I2S_ERROR_REASON 0x12418C8 +#define mmACP_MEM_PG_STS 0x12418CC + + +// Registers from ACP_PGFSM block + +#define mmACP_I2S_PIN_CONFIG 0x1241400 +#define mmACP_PAD_PULLUP_PULLDOWN_CTRL 0x1241404 +#define mmACP_PAD_DRIVE_STRENGTH_CTRL 0x1241408 +#define mmACP_SW_PAD_KEEPER_EN 0x124140C +#define mmACP_SW_WAKE_EN 0x1241410 +#define mmACP_I2S_WAKE_EN 0x1241414 +#define mmACP_PME_EN 0x1241418 +#define mmACP_PGFSM_CONTROL 0x124141C +#define mmACP_PGFSM_STATUS 0x1241420 + + +// Registers from ACP_SCRATCH block + +#define mmACP_SCRATCH_REG_0 0x1250000 +#define mmACP_SCRATCH_REG_1 0x1250004 +#define mmACP_SCRATCH_REG_2 0x1250008 +#define mmACP_SCRATCH_REG_3 0x125000C +#define mmACP_SCRATCH_REG_4 0x1250010 +#define mmACP_SCRATCH_REG_5 0x1250014 +#define mmACP_SCRATCH_REG_6 0x1250018 +#define mmACP_SCRATCH_REG_7 0x125001C +#define mmACP_SCRATCH_REG_8 0x1250020 +#define mmACP_SCRATCH_REG_9 0x1250024 +#define mmACP_SCRATCH_REG_10 0x1250028 +#define mmACP_SCRATCH_REG_11 0x125002C +#define mmACP_SCRATCH_REG_12 0x1250030 +#define mmACP_SCRATCH_REG_13 0x1250034 +#define mmACP_SCRATCH_REG_14 0x1250038 +#define mmACP_SCRATCH_REG_15 0x125003C +#define mmACP_SCRATCH_REG_16 0x1250040 +#define mmACP_SCRATCH_REG_17 0x1250044 +#define mmACP_SCRATCH_REG_18 0x1250048 +#define mmACP_SCRATCH_REG_19 0x125004C +#define mmACP_SCRATCH_REG_20 0x1250050 +#define mmACP_SCRATCH_REG_21 0x1250054 +#define mmACP_SCRATCH_REG_22 0x1250058 +#define mmACP_SCRATCH_REG_23 0x125005C +#define mmACP_SCRATCH_REG_24 0x1250060 +#define mmACP_SCRATCH_REG_25 0x1250064 +#define mmACP_SCRATCH_REG_26 0x1250068 +#define mmACP_SCRATCH_REG_27 0x125006C +#define mmACP_SCRATCH_REG_28 0x1250070 +#define mmACP_SCRATCH_REG_29 0x1250074 +#define mmACP_SCRATCH_REG_30 0x1250078 +#define mmACP_SCRATCH_REG_31 0x125007C +#define mmACP_SCRATCH_REG_32 0x1250080 +#define mmACP_SCRATCH_REG_33 0x1250084 +#define mmACP_SCRATCH_REG_34 0x1250088 +#define mmACP_SCRATCH_REG_35 0x125008C +#define mmACP_SCRATCH_REG_36 0x1250090 +#define mmACP_SCRATCH_REG_37 0x1250094 +#define mmACP_SCRATCH_REG_38 0x1250098 +#define mmACP_SCRATCH_REG_39 0x125009C +#define mmACP_SCRATCH_REG_40 0x12500A0 +#define mmACP_SCRATCH_REG_41 0x12500A4 +#define mmACP_SCRATCH_REG_42 0x12500A8 +#define mmACP_SCRATCH_REG_43 0x12500AC +#define mmACP_SCRATCH_REG_44 0x12500B0 +#define mmACP_SCRATCH_REG_45 0x12500B4 +#define mmACP_SCRATCH_REG_46 0x12500B8 +#define mmACP_SCRATCH_REG_47 0x12500BC +#define mmACP_SCRATCH_REG_48 0x12500C0 +#define mmACP_SCRATCH_REG_49 0x12500C4 +#define mmACP_SCRATCH_REG_50 0x12500C8 +#define mmACP_SCRATCH_REG_51 0x12500CC +#define mmACP_SCRATCH_REG_52 0x12500D0 +#define mmACP_SCRATCH_REG_53 0x12500D4 +#define mmACP_SCRATCH_REG_54 0x12500D8 +#define mmACP_SCRATCH_REG_55 0x12500DC +#define mmACP_SCRATCH_REG_56 0x12500E0 +#define mmACP_SCRATCH_REG_57 0x12500E4 +#define mmACP_SCRATCH_REG_58 0x12500E8 +#define mmACP_SCRATCH_REG_59 0x12500EC +#define mmACP_SCRATCH_REG_60 0x12500F0 +#define mmACP_SCRATCH_REG_61 0x12500F4 +#define mmACP_SCRATCH_REG_62 0x12500F8 +#define mmACP_SCRATCH_REG_63 0x12500FC +#define mmACP_SCRATCH_REG_64 0x1250100 +#define mmACP_SCRATCH_REG_65 0x1250104 +#define mmACP_SCRATCH_REG_66 0x1250108 +#define mmACP_SCRATCH_REG_67 0x125010C +#define mmACP_SCRATCH_REG_68 0x1250110 +#define mmACP_SCRATCH_REG_69 0x1250114 +#define mmACP_SCRATCH_REG_70 0x1250118 +#define mmACP_SCRATCH_REG_71 0x125011C +#define mmACP_SCRATCH_REG_72 0x1250120 +#define mmACP_SCRATCH_REG_73 0x1250124 +#define mmACP_SCRATCH_REG_74 0x1250128 +#define mmACP_SCRATCH_REG_75 0x125012C +#define mmACP_SCRATCH_REG_76 0x1250130 +#define mmACP_SCRATCH_REG_77 0x1250134 +#define mmACP_SCRATCH_REG_78 0x1250138 +#define mmACP_SCRATCH_REG_79 0x125013C +#define mmACP_SCRATCH_REG_80 0x1250140 +#define mmACP_SCRATCH_REG_81 0x1250144 +#define mmACP_SCRATCH_REG_82 0x1250148 +#define mmACP_SCRATCH_REG_83 0x125014C +#define mmACP_SCRATCH_REG_84 0x1250150 +#define mmACP_SCRATCH_REG_85 0x1250154 +#define mmACP_SCRATCH_REG_86 0x1250158 +#define mmACP_SCRATCH_REG_87 0x125015C +#define mmACP_SCRATCH_REG_88 0x1250160 +#define mmACP_SCRATCH_REG_89 0x1250164 +#define mmACP_SCRATCH_REG_90 0x1250168 +#define mmACP_SCRATCH_REG_91 0x125016C +#define mmACP_SCRATCH_REG_92 0x1250170 +#define mmACP_SCRATCH_REG_93 0x1250174 +#define mmACP_SCRATCH_REG_94 0x1250178 +#define mmACP_SCRATCH_REG_95 0x125017C +#define mmACP_SCRATCH_REG_96 0x1250180 +#define mmACP_SCRATCH_REG_97 0x1250184 +#define mmACP_SCRATCH_REG_98 0x1250188 +#define mmACP_SCRATCH_REG_99 0x125018C +#define mmACP_SCRATCH_REG_100 0x1250190 +#define mmACP_SCRATCH_REG_101 0x1250194 +#define mmACP_SCRATCH_REG_102 0x1250198 +#define mmACP_SCRATCH_REG_103 0x125019C +#define mmACP_SCRATCH_REG_104 0x12501A0 +#define mmACP_SCRATCH_REG_105 0x12501A4 +#define mmACP_SCRATCH_REG_106 0x12501A8 +#define mmACP_SCRATCH_REG_107 0x12501AC +#define mmACP_SCRATCH_REG_108 0x12501B0 +#define mmACP_SCRATCH_REG_109 0x12501B4 +#define mmACP_SCRATCH_REG_110 0x12501B8 +#define mmACP_SCRATCH_REG_111 0x12501BC +#define mmACP_SCRATCH_REG_112 0x12501C0 +#define mmACP_SCRATCH_REG_113 0x12501C4 +#define mmACP_SCRATCH_REG_114 0x12501C8 +#define mmACP_SCRATCH_REG_115 0x12501CC +#define mmACP_SCRATCH_REG_116 0x12501D0 +#define mmACP_SCRATCH_REG_117 0x12501D4 +#define mmACP_SCRATCH_REG_118 0x12501D8 +#define mmACP_SCRATCH_REG_119 0x12501DC +#define mmACP_SCRATCH_REG_120 0x12501E0 +#define mmACP_SCRATCH_REG_121 0x12501E4 +#define mmACP_SCRATCH_REG_122 0x12501E8 +#define mmACP_SCRATCH_REG_123 0x12501EC +#define mmACP_SCRATCH_REG_124 0x12501F0 +#define mmACP_SCRATCH_REG_125 0x12501F4 +#define mmACP_SCRATCH_REG_126 0x12501F8 +#define mmACP_SCRATCH_REG_127 0x12501FC +#define mmACP_SCRATCH_REG_128 0x1250200 + + +// Registers from ACP_SW_ACLK block + +#define mmSW_CORB_Base_Address 0x1243200 +#define mmSW_CORB_Write_Pointer 0x1243204 +#define mmSW_CORB_Read_Pointer 0x1243208 +#define mmSW_CORB_Control 0x124320C +#define mmSW_CORB_Size 0x1243214 +#define mmSW_RIRB_Base_Address 0x1243218 +#define mmSW_RIRB_Write_Pointer 0x124321C +#define mmSW_RIRB_Response_Interrupt_Count 0x1243220 +#define mmSW_RIRB_Control 0x1243224 +#define mmSW_RIRB_Size 0x1243228 +#define mmSW_RIRB_FIFO_MIN_THDL 0x124322C +#define mmSW_imm_cmd_UPPER_WORD 0x1243230 +#define mmSW_imm_cmd_LOWER_QWORD 0x1243234 +#define mmSW_imm_resp_UPPER_WORD 0x1243238 +#define mmSW_imm_resp_LOWER_QWORD 0x124323C +#define mmSW_imm_cmd_sts 0x1243240 +#define mmSW_BRA_BASE_ADDRESS 0x1243244 +#define mmSW_BRA_TRANSFER_SIZE 0x1243248 +#define mmSW_BRA_DMA_BUSY 0x124324C +#define mmSW_BRA_RESP 0x1243250 +#define mmSW_BRA_RESP_FRAME_ADDR 0x1243254 +#define mmSW_BRA_CURRENT_TRANSFER_SIZE 0x1243258 +#define mmSW_STATE_CHANGE_STATUS_0TO7 0x124325C +#define mmSW_STATE_CHANGE_STATUS_8TO11 0x1243260 +#define mmSW_STATE_CHANGE_STATUS_MASK_0to7 0x1243264 +#define mmSW_STATE_CHANGE_STATUS_MASK_8to11 0x1243268 +#define mmSW_CLK_FREQUENCY_CTRL 0x124326C +#define mmSW_ERROR_INTR_MASK 0x1243270 +#define mmSW_PHY_TEST_MODE_DATA_OFF 0x1243274 + + +// Registers from ACP_SW_SWCLK block + +#define mmACP_SW_EN 0x1243000 +#define mmACP_SW_EN_STATUS 0x1243004 +#define mmACP_SW_FRAMESIZE 0x1243008 +#define mmACP_SW_SSP_Counter 0x124300C +#define mmACP_SW_Audio_TX_EN 0x1243010 +#define mmACP_SW_Audio_TX_EN_STATUS 0x1243014 +#define mmACP_SW_Audio_TX_Frame_Format 0x1243018 +#define mmACP_SW_Audio_TX_SampleInterval 0x124301C +#define mmACP_SW_Audio_TX_Hctrl_DP0 0x1243020 +#define mmACP_SW_Audio_TX_Hctrl_DP1 0x1243024 +#define mmACP_SW_Audio_TX_Hctrl_DP2 0x1243028 +#define mmACP_SW_Audio_TX_Hctrl_DP3 0x124302C +#define mmACP_SW_Audio_TX_offset_DP0 0x1243030 +#define mmACP_SW_Audio_TX_offset_DP1 0x1243034 +#define mmACP_SW_Audio_TX_offset_DP2 0x1243038 +#define mmACP_SW_Audio_TX_offset_DP3 0x124303C +#define mmACP_SW_Audio_TX_Channel_Enable_DP0 0x1243040 +#define mmACP_SW_Audio_TX_Channel_Enable_DP1 0x1243044 +#define mmACP_SW_Audio_TX_Channel_Enable_DP2 0x1243048 +#define mmACP_SW_Audio_TX_Channel_Enable_DP3 0x124304C +#define mmACP_SW_BT_TX_EN 0x1243050 +#define mmACP_SW_BT_TX_EN_STATUS 0x1243054 +#define mmACP_SW_BT_TX_Frame_Format 0x1243058 +#define mmACP_SW_BT_TX_SampleInterval 0x124305C +#define mmACP_SW_BT_TX_Hctrl 0x1243060 +#define mmACP_SW_BT_TX_offset 0x1243064 +#define mmACP_SW_BT_TX_Channel_Enable_DP0 0x1243068 +#define mmACP_SW_Headset_TX_EN 0x124306C +#define mmACP_SW_Headset_TX_EN_STATUS 0x1243070 +#define mmACP_SW_Headset_TX_Frame_Format 0x1243074 +#define mmACP_SW_Headset_TX_SampleInterval 0x1243078 +#define mmACP_SW_Headset_TX_Hctrl 0x124307C +#define mmACP_SW_Headset_TX_offset 0x1243080 +#define mmACP_SW_Headset_TX_Channel_Enable_DP0 0x1243084 +#define mmACP_SW_Audio_RX_EN 0x1243088 +#define mmACP_SW_Audio_RX_EN_STATUS 0x124308C +#define mmACP_SW_Audio_RX_Frame_Format 0x1243090 +#define mmACP_SW_Audio_RX_SampleInterval 0x1243094 +#define mmACP_SW_Audio_RX_Hctrl_DP0 0x1243098 +#define mmACP_SW_Audio_RX_Hctrl_DP1 0x124309C +#define mmACP_SW_Audio_RX_Hctrl_DP2 0x1243100 +#define mmACP_SW_Audio_RX_Hctrl_DP3 0x1243104 +#define mmACP_SW_Audio_RX_offset_DP0 0x1243108 +#define mmACP_SW_Audio_RX_offset_DP1 0x124310C +#define mmACP_SW_Audio_RX_offset_DP2 0x1243110 +#define mmACP_SW_Audio_RX_offset_DP3 0x1243114 +#define mmACP_SW_Audio_RX_Channel_Enable_DP0 0x1243118 +#define mmACP_SW_Audio_RX_Channel_Enable_DP1 0x124311C +#define mmACP_SW_Audio_RX_Channel_Enable_DP2 0x1243120 +#define mmACP_SW_Audio_RX_Channel_Enable_DP3 0x1243124 +#define mmACP_SW_BT_RX_EN 0x1243128 +#define mmACP_SW_BT_RX_EN_STATUS 0x124312C +#define mmACP_SW_BT_RX_Frame_Format 0x1243130 +#define mmACP_SW_BT_RX_SampleInterval 0x1243134 +#define mmACP_SW_BT_RX_Hctrl 0x1243138 +#define mmACP_SW_BT_RX_offset 0x124313C +#define mmACP_SW_BT_RX_Channel_Enable_DP0 0x1243140 +#define mmACP_SW_Headset_RX_EN 0x1243144 +#define mmACP_SW_Headset_RX_EN_STATUS 0x1243148 +#define mmACP_SW_Headset_RX_Frame_Format 0x124314C +#define mmACP_SW_Headset_RX_SampleInterval 0x1243150 +#define mmACP_SW_Headset_RX_Hctrl 0x1243154 +#define mmACP_SW_Headset_RX_offset 0x1243158 +#define mmACP_SW_Headset_RX_Channel_Enable_DP0 0x124315C +#define mmACP_SW_BPT_PORT_EN 0x1243160 +#define mmACP_SW_BPT_PORT_EN_STATUS 0x1243164 +#define mmACP_SW_BPT_PORT_Frame_Format 0x1243168 +#define mmACP_SW_BPT_PORT_SampleInterval 0x124316C +#define mmACP_SW_BPT_PORT_Hctrl 0x1243170 +#define mmACP_SW_BPT_PORT_offset 0x1243174 +#define mmACP_SW_BPT_PORT_Channel_Enable 0x1243178 +#define mmACP_SW_BPT_PORT_First_byte_addr 0x124317C +#define mmACP_SW_CLK_RESUME_CTRL 0x1243180 +#define mmACP_SW_CLK_RESUME_Delay_Cntr 0x1243184 +#define mmACP_SW_BUS_RESET_CTRL 0x1243188 +#define mmACP_SW_PRBS_ERR_STATUS 0x124318C + + +// Registers from ACP_AUDIO_BUFFERS block + +#define mmACP_I2S_RX_RINGBUFADDR 0x1242000 +#define mmACP_I2S_RX_RINGBUFSIZE 0x1242004 +#define mmACP_I2S_RX_LINKPOSITIONCNTR 0x1242008 +#define mmACP_I2S_RX_FIFOADDR 0x124200C +#define mmACP_I2S_RX_FIFOSIZE 0x1242010 +#define mmACP_I2S_RX_DMA_SIZE 0x1242014 +#define mmACP_I2S_RX_LINEARPOSITIONCNTR_HIGH 0x1242018 +#define mmACP_I2S_RX_LINEARPOSITIONCNTR_LOW 0x124201C +#define mmACP_I2S_RX_INTR_WATERMARK_SIZE 0x1242020 +#define mmACP_I2S_TX_RINGBUFADDR 0x1242024 +#define mmACP_I2S_TX_RINGBUFSIZE 0x1242028 +#define mmACP_I2S_TX_LINKPOSITIONCNTR 0x124202C +#define mmACP_I2S_TX_FIFOADDR 0x1242030 +#define mmACP_I2S_TX_FIFOSIZE 0x1242034 +#define mmACP_I2S_TX_DMA_SIZE 0x1242038 +#define mmACP_I2S_TX_LINEARPOSITIONCNTR_HIGH 0x124203C +#define mmACP_I2S_TX_LINEARPOSITIONCNTR_LOW 0x1242040 +#define mmACP_I2S_TX_INTR_WATERMARK_SIZE 0x1242044 +#define mmACP_BT_RX_RINGBUFADDR 0x1242048 +#define mmACP_BT_RX_RINGBUFSIZE 0x124204C +#define mmACP_BT_RX_LINKPOSITIONCNTR 0x1242050 +#define mmACP_BT_RX_FIFOADDR 0x1242054 +#define mmACP_BT_RX_FIFOSIZE 0x1242058 +#define mmACP_BT_RX_DMA_SIZE 0x124205C +#define mmACP_BT_RX_LINEARPOSITIONCNTR_HIGH 0x1242060 +#define mmACP_BT_RX_LINEARPOSITIONCNTR_LOW 0x1242064 +#define mmACP_BT_RX_INTR_WATERMARK_SIZE 0x1242068 +#define mmACP_BT_TX_RINGBUFADDR 0x124206C +#define mmACP_BT_TX_RINGBUFSIZE 0x1242070 +#define mmACP_BT_TX_LINKPOSITIONCNTR 0x1242074 +#define mmACP_BT_TX_FIFOADDR 0x1242078 +#define mmACP_BT_TX_FIFOSIZE 0x124207C +#define mmACP_BT_TX_DMA_SIZE 0x1242080 +#define mmACP_BT_TX_LINEARPOSITIONCNTR_HIGH 0x1242084 +#define mmACP_BT_TX_LINEARPOSITIONCNTR_LOW 0x1242088 +#define mmACP_BT_TX_INTR_WATERMARK_SIZE 0x124208C +#define mmACP_HS_RX_RINGBUFADDR 0x1242090 +#define mmACP_HS_RX_RINGBUFSIZE 0x1242094 +#define mmACP_HS_RX_LINKPOSITIONCNTR 0x1242098 +#define mmACP_HS_RX_FIFOADDR 0x124209C +#define mmACP_HS_RX_FIFOSIZE 0x12420A0 +#define mmACP_HS_RX_DMA_SIZE 0x12420A4 +#define mmACP_HS_RX_LINEARPOSITIONCNTR_HIGH 0x12420A8 +#define mmACP_HS_RX_LINEARPOSITIONCNTR_LOW 0x12420AC +#define mmACP_HS_RX_INTR_WATERMARK_SIZE 0x12420B0 +#define mmACP_HS_TX_RINGBUFADDR 0x12420B4 +#define mmACP_HS_TX_RINGBUFSIZE 0x12420B8 +#define mmACP_HS_TX_LINKPOSITIONCNTR 0x12420BC +#define mmACP_HS_TX_FIFOADDR 0x12420C0 +#define mmACP_HS_TX_FIFOSIZE 0x12420C4 +#define mmACP_HS_TX_DMA_SIZE 0x12420C8 +#define mmACP_HS_TX_LINEARPOSITIONCNTR_HIGH 0x12420CC +#define mmACP_HS_TX_LINEARPOSITIONCNTR_LOW 0x12420D0 +#define mmACP_HS_TX_INTR_WATERMARK_SIZE 0x12420D4 + + +// Registers from ACP_I2S_TDM block + +#define mmACP_I2STDM_IER 0x1242400 +#define mmACP_I2STDM_IRER 0x1242404 +#define mmACP_I2STDM_RXFRMT 0x1242408 +#define mmACP_I2STDM_ITER 0x124240C +#define mmACP_I2STDM_TXFRMT 0x1242410 + + +// Registers from ACP_BT_TDM block + +#define mmACP_BTTDM_IER 0x1242800 +#define mmACP_BTTDM_IRER 0x1242804 +#define mmACP_BTTDM_RXFRMT 0x1242808 +#define mmACP_BTTDM_ITER 0x124280C +#define mmACP_BTTDM_TXFRMT 0x1242810 + + +// Registers from AZALIA_IP block + +#define mmAudio_Az_Global_Capabilities 0x1200000 +#define mmAudio_Az_Minor_Version 0x1200002 +#define mmAudio_Az_Major_Version 0x1200003 +#define mmAudio_Az_Output_Payload_Capability 0x1200004 +#define mmAudio_Az_Input_Payload_Capability 0x1200006 +#define mmAudio_Az_Global_Control 0x1200008 +#define mmAudio_Az_Wake_Enable 0x120000C +#define mmAudio_Az_State_Change_Status 0x120000E +#define mmAudio_Az_Global_Status 0x1200010 +#define mmAudio_Az_Linked_List_Capability_Header 0x1200014 +#define mmAudio_Az_Output_Stream_Payload_Capability 0x1200018 +#define mmAudio_Az_Input_Stream_Payload_Capability 0x120001A +#define mmAudio_Az_Interrupt_Control 0x1200020 +#define mmAudio_Az_Interrupt_Status 0x1200024 +#define mmAudio_Az_Wall_Clock_Counter 0x1200030 +#define mmAudio_Az_Stream_Synchronization 0x1200038 +#define mmAudio_Az_CORB_Lower_Base_Address 0x1200040 +#define mmAudio_Az_CORB_Upper_Base_Address 0x1200044 +#define mmAudio_Az_CORB_Write_Pointer 0x1200048 +#define mmAudio_Az_CORB_Read_Pointer 0x120004A +#define mmAudio_Az_CORB_Control 0x120004C +#define mmAudio_Az_CORB_Status 0x120004D +#define mmAudio_Az_CORB_Size 0x120004E +#define mmAudio_Az_RIRB_Lower_Base_Address 0x1200050 +#define mmAudio_Az_RIRB_Upper_Base_Address 0x1200054 +#define mmAudio_Az_RIRB_Write_Pointer 0x1200058 +#define mmAudio_Az_RIRB_Response_Interrupt_Count 0x120005A +#define mmAudio_Az_RIRB_Control 0x120005C +#define mmAudio_Az_RIRB_Status 0x120005D +#define mmAudio_Az_RIRB_Size 0x120005E +#define mmAudio_Az_Immediate_Command_Output_Interface 0x1200060 +#define mmAudio_Az_Immediate_Response_Input_Interface 0x1200064 +#define mmAudio_Az_Immediate_Command_Status 0x1200068 +#define mmAudio_Az_DPLBASE 0x1200070 +#define mmAudio_Az_DPUBASE 0x1200074 +#define mmAudio_Az_Input_SD0CTL_and_STS 0x1200080 +#define mmAudio_Az_Input_SD0LPIB 0x1200084 +#define mmAudio_Az_Input_SD0CBL 0x1200088 +#define mmAudio_Az_Input_SD0LVI 0x120008C +#define mmAudio_Az_Input_SD0FIFOS 0x1200090 +#define mmAudio_Az_Input_SD0FMT 0x1200092 +#define mmAudio_Az_Input_SD0BDPL 0x1200098 +#define mmAudio_Az_Input_SD0BDPU 0x120009C +#define mmAudio_Az_Input_SD1CTL_and_STS 0x12000A0 +#define mmAudio_Az_Input_SD1LPIB 0x12000A4 +#define mmAudio_Az_Input_SD1CBL 0x12000A8 +#define mmAudio_Az_Input_SD1LVI 0x12000AC +#define mmAudio_Az_Input_SD1FIFOS 0x12000B0 +#define mmAudio_Az_Input_SD1FMT 0x12000B2 +#define mmAudio_Az_Input_SD1BDPL 0x12000B8 +#define mmAudio_Az_Input_SD1BDPU 0x12000BC +#define mmAudio_Az_Input_SD2CTL_and_STS 0x12000C0 +#define mmAudio_Az_Input_SD2LPIB 0x12000C4 +#define mmAudio_Az_Input_SD2CBL 0x12000C8 +#define mmAudio_Az_Input_SD2LVI 0x12000CC +#define mmAudio_Az_Input_SD2FIFOS 0x12000D0 +#define mmAudio_Az_Input_SD2FMT 0x12000D2 +#define mmAudio_Az_Input_SD2BDPL 0x12000D8 +#define mmAudio_Az_Input_SD2BDPU 0x12000DC +#define mmAudio_Az_Input_SD3CTL_and_STS 0x12000E0 +#define mmAudio_Az_Input_SD3LPIB 0x12000E4 +#define mmAudio_Az_Input_SD3CBL 0x12000E8 +#define mmAudio_Az_Input_SD3LVI 0x12000EC +#define mmAudio_Az_Input_SD3FIFOS 0x12000F0 +#define mmAudio_Az_Input_SD3FMT 0x12000F2 +#define mmAudio_Az_Input_SD3BDPL 0x12000F8 +#define mmAudio_Az_Input_SD3BDPU 0x12000FC +#define mmAudio_Az_Output_SD0CTL_and_STS 0x1200100 +#define mmAudio_Az_Output_SD0LPIB 0x1200104 +#define mmAudio_Az_Output_SD0CBL 0x1200108 +#define mmAudio_Az_Output_SD0LVI 0x120010C +#define mmAudio_Az_Output_SD0FIFOS 0x1200110 +#define mmAudio_Az_Output_SD0FMT 0x1200112 +#define mmAudio_Az_Output_SD0BDPL 0x1200118 +#define mmAudio_Az_Output_SD0BDPU 0x120011C +#define mmAudio_Az_Output_SD1CTL_and_STS 0x1200120 +#define mmAudio_Az_Output_SD1LPIB 0x1200124 +#define mmAudio_Az_Output_SD1CBL 0x1200128 +#define mmAudio_Az_Output_SD1LVI 0x120012C +#define mmAudio_Az_Output_SD1FIFOS 0x1200130 +#define mmAudio_Az_Output_SD1FMT 0x1200132 +#define mmAudio_Az_Output_SD1BDPL 0x1200138 +#define mmAudio_Az_Output_SD1BDPU 0x120013C +#define mmAudio_Az_Output_SD2CTL_and_STS 0x1200140 +#define mmAudio_Az_Output_SD2LPIB 0x1200144 +#define mmAudio_Az_Output_SD2CBL 0x1200148 +#define mmAudio_Az_Output_SD2LVI 0x120014C +#define mmAudio_Az_Output_SD2FIFOS 0x1200150 +#define mmAudio_Az_Output_SD2FMT 0x1200152 +#define mmAudio_Az_Output_SD2BDPL 0x1200158 +#define mmAudio_Az_Output_SD2BDPU 0x120015C +#define mmAudio_Az_Output_SD3CTL_and_STS 0x1200160 +#define mmAudio_Az_Output_SD3LPIB 0x1200164 +#define mmAudio_Az_Output_SD3CBL 0x1200168 +#define mmAudio_Az_Output_SD3LVI 0x120016C +#define mmAudio_Az_Output_SD3FIFOS 0x1200170 +#define mmAudio_Az_Output_SD3FMT 0x1200172 +#define mmAudio_Az_Output_SD3BDPL 0x1200178 +#define mmAudio_Az_Output_SD3BDPU 0x120017C +#define mmAudioAZ_Misc_Control_Register_1 0x1200180 +#define mmAudioAZ_Misc_Control_Register_2 0x1200182 +#define mmAudioAZ_Misc_Control_Register_3 0x1200183 +#define mmAudio_AZ_Multiple_Links_Capability_Header 0x1200200 +#define mmAudio_AZ_Multiple_Links_Capability_Declaration 0x1200204 +#define mmAudio_AZ_Link0_Capabilities 0x1200240 +#define mmAudio_AZ_Link0_Control 0x1200244 +#define mmAudio_AZ_Link0_Output_Stream_ID 0x1200248 +#define mmAudio_AZ_Link0_SDI_Identifier 0x120024C +#define mmAudio_AZ_Link0_Per_Stream_Overhead 0x1200250 +#define mmAudio_AZ_Link0_Wall_Frame_Counter 0x1200258 +#define mmAudio_AZ_Link0_Output_Payload_Capability_L 0x1200260 +#define mmAudio_AZ_Link0_Output_Payload_Capability_U 0x1200264 +#define mmAudio_AZ_Link0_Input_Payload_Capability_L 0x1200270 +#define mmAudio_AZ_Link0_Input_Payload_Capability_U 0x1200274 +#define mmAudio_Az_Input_SD0LICBA 0x1202084 +#define mmAudio_Az_Input_SD1LICBA 0x12020A4 +#define mmAudio_Az_Input_SD2LICBA 0x12020C4 +#define mmAudio_Az_Input_SD3LICBA 0x12020E4 +#define mmAudio_Az_Output_SD0LICBA 0x1202104 +#define mmAudio_Az_Output_SD1LICBA 0x1202124 +#define mmAudio_Az_Output_SD2LICBA 0x1202144 +#define mmAudio_Az_Output_SD3LICBA 0x1202164 +#define mmAUDIO_AZ_POWER_MANAGEMENT_CONTROL 0x1204000 +#define mmAUDIO_AZ_IOC_SOFTRST_CONTROL 0x1204004 +#define mmAUDIO_AZ_IOC_CLKGATE_CONTROL 0x1204008 + + +// Registers from ACP_AZALIA block + +#define mmACP_AZ_PAGE0_LBASE_ADDR 0x1243800 +#define mmACP_AZ_PAGE0_UBASE_ADDR 0x1243804 +#define mmACP_AZ_PAGE0_PGEN_SIZE 0x1243808 +#define mmACP_AZ_PAGE0_OFFSET 0x124380C +#define mmACP_AZ_PAGE1_LBASE_ADDR 0x1243810 +#define mmACP_AZ_PAGE1_UBASE_ADDR 0x1243814 +#define mmACP_AZ_PAGE1_PGEN_SIZE 0x1243818 +#define mmACP_AZ_PAGE1_OFFSET 0x124381C +#define mmACP_AZ_PAGE2_LBASE_ADDR 0x1243820 +#define mmACP_AZ_PAGE2_UBASE_ADDR 0x1243824 +#define mmACP_AZ_PAGE2_PGEN_SIZE 0x1243828 +#define mmACP_AZ_PAGE2_OFFSET 0x124382C +#define mmACP_AZ_PAGE3_LBASE_ADDR 0x1243830 +#define mmACP_AZ_PAGE3_UBASE_ADDR 0x1243834 +#define mmACP_AZ_PAGE3_PGEN_SIZE 0x1243838 +#define mmACP_AZ_PAGE3_OFFSET 0x124383C +#define mmACP_AZ_PAGE4_LBASE_ADDR 0x1243840 +#define mmACP_AZ_PAGE4_UBASE_ADDR 0x1243844 +#define mmACP_AZ_PAGE4_PGEN_SIZE 0x1243848 +#define mmACP_AZ_PAGE4_OFFSET 0x124384C +#define mmACP_AZ_PAGE5_LBASE_ADDR 0x1243850 +#define mmACP_AZ_PAGE5_UBASE_ADDR 0x1243854 +#define mmACP_AZ_PAGE5_PGEN_SIZE 0x1243858 +#define mmACP_AZ_PAGE5_OFFSET 0x124385C +#define mmACP_AZ_PAGE6_LBASE_ADDR 0x1243860 +#define mmACP_AZ_PAGE6_UBASE_ADDR 0x1243864 +#define mmACP_AZ_PAGE6_PGEN_SIZE 0x1243868 +#define mmACP_AZ_PAGE6_OFFSET 0x124386C +#define mmACP_AZ_PAGE7_LBASE_ADDR 0x1243870 +#define mmACP_AZ_PAGE7_UBASE_ADDR 0x1243874 +#define mmACP_AZ_PAGE7_PGEN_SIZE 0x1243878 +#define mmACP_AZ_PAGE7_OFFSET 0x124387C + + +#endif -- cgit v1.2.3-58-ga151 From e30d9128def6ca0715a89b9a3133a94521dc0b51 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Mon, 12 Nov 2018 11:04:53 +0530 Subject: ASoC: AMD: add ACP3.0 PCI driver ACP 3.0 is a PCI audio device. This patch adds PCI driver to bind to this device and get PCI resources. Signed-off-by: Maruthi Bayyavarapu Signed-off-by: Sanju R Mehta Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x.h | 13 ++++++ sound/soc/amd/raven/pci-acp3x.c | 97 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 sound/soc/amd/raven/acp3x.h create mode 100644 sound/soc/amd/raven/pci-acp3x.c (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h new file mode 100644 index 000000000000..e9b4df0c6726 --- /dev/null +++ b/sound/soc/amd/raven/acp3x.h @@ -0,0 +1,13 @@ +#include "chip_offset_byte.h" + +#define ACP3x_PHY_BASE_ADDRESS 0x1240000 + +static inline u32 rv_readl(void __iomem *base_addr) +{ + return readl(base_addr - ACP3x_PHY_BASE_ADDRESS); +} + +static inline void rv_writel(u32 val, void __iomem *base_addr) +{ + writel(val, base_addr - ACP3x_PHY_BASE_ADDRESS); +} diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c new file mode 100644 index 000000000000..27588ed066c9 --- /dev/null +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -0,0 +1,97 @@ +/* + * AMD ALSA SoC PCM Driver + * + * Copyright 2016 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include + +#include "acp3x.h" + +struct acp3x_dev_data { + void __iomem *acp3x_base; +}; + +static int snd_acp3x_probe(struct pci_dev *pci, + const struct pci_device_id *pci_id) +{ + int ret; + u32 addr; + struct acp3x_dev_data *adata; + + if (pci_enable_device(pci)) { + dev_err(&pci->dev, "pci_enable_device failed\n"); + return -ENODEV; + } + + ret = pci_request_regions(pci, "AMD ACP3x audio"); + if (ret < 0) { + dev_err(&pci->dev, "pci_request_regions failed\n"); + goto disable_pci; + } + + adata = devm_kzalloc(&pci->dev, sizeof(struct acp3x_dev_data), + GFP_KERNEL); + if (!adata) { + ret = -ENOMEM; + goto release_regions; + } + + addr = pci_resource_start(pci, 0); + adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0)); + if (!adata->acp3x_base) { + ret = -ENOMEM; + goto release_regions; + } + pci_set_master(pci); + pci_set_drvdata(pci, adata); + return 0; + +release_regions: + pci_release_regions(pci); +disable_pci: + pci_disable_device(pci); + + return ret; +} + +static void snd_acp3x_remove(struct pci_dev *pci) +{ + struct acp3x_dev_data *adata = pci_get_drvdata(pci); + + iounmap(adata->acp3x_base); + pci_release_regions(pci); + pci_disable_device(pci); +} + +static const struct pci_device_id snd_acp3x_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x15e2), + .class = PCI_CLASS_MULTIMEDIA_OTHER << 8, + .class_mask = 0xffffff }, + { 0, }, +}; +MODULE_DEVICE_TABLE(pci, snd_acp3x_ids); + +static struct pci_driver acp3x_driver = { + .name = KBUILD_MODNAME, + .id_table = snd_acp3x_ids, + .probe = snd_acp3x_probe, + .remove = snd_acp3x_remove, +}; + +module_pci_driver(acp3x_driver); + +MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); +MODULE_DESCRIPTION("AMD ACP3x PCI driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 7894a7e7ea3de6c1fd5b2b43f3a73106c94ef584 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:04:54 +0530 Subject: ASoC: amd: create ACP3x PCM platform device ACP 3x IP has I2S controller device as one of IP blocks. Create a platform device for it, so that the PCM platform driver can be bound to this device. Pass PCI resources like MMIO, irq to the platform device. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x.h | 4 +++ sound/soc/amd/raven/pci-acp3x.c | 70 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index e9b4df0c6726..83b1ed8f6060 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -1,6 +1,10 @@ #include "chip_offset_byte.h" #define ACP3x_PHY_BASE_ADDRESS 0x1240000 +#define ACP3x_I2S_MODE 0 +#define ACP3x_REG_START 0x1240000 +#define ACP3x_REG_END 0x1250200 +#define I2S_MODE 0x04 static inline u32 rv_readl(void __iomem *base_addr) { diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index 27588ed066c9..0700162b0ed1 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -16,19 +16,26 @@ #include #include #include +#include +#include #include "acp3x.h" struct acp3x_dev_data { void __iomem *acp3x_base; + bool acp3x_audio_mode; + struct resource *res; + struct platform_device *pdev; }; static int snd_acp3x_probe(struct pci_dev *pci, const struct pci_device_id *pci_id) { int ret; - u32 addr; + u32 addr, val; struct acp3x_dev_data *adata; + struct platform_device_info pdevinfo; + unsigned int irqflags; if (pci_enable_device(pci)) { dev_err(&pci->dev, "pci_enable_device failed\n"); @@ -48,6 +55,15 @@ static int snd_acp3x_probe(struct pci_dev *pci, goto release_regions; } + /* check for msi interrupt support */ + ret = pci_enable_msi(pci); + if (ret) + /* msi is not enabled */ + irqflags = IRQF_SHARED; + else + /* msi is enabled */ + irqflags = 0; + addr = pci_resource_start(pci, 0); adata->acp3x_base = ioremap(addr, pci_resource_len(pci, 0)); if (!adata->acp3x_base) { @@ -56,8 +72,57 @@ static int snd_acp3x_probe(struct pci_dev *pci, } pci_set_master(pci); pci_set_drvdata(pci, adata); + + val = rv_readl(adata->acp3x_base + mmACP_I2S_PIN_CONFIG); + switch (val) { + case I2S_MODE: + adata->res = devm_kzalloc(&pci->dev, + sizeof(struct resource) * 2, + GFP_KERNEL); + if (!adata->res) { + ret = -ENOMEM; + goto unmap_mmio; + } + + adata->res[0].name = "acp3x_i2s_iomem"; + adata->res[0].flags = IORESOURCE_MEM; + adata->res[0].start = addr; + adata->res[0].end = addr + (ACP3x_REG_END - ACP3x_REG_START); + + adata->res[1].name = "acp3x_i2s_irq"; + adata->res[1].flags = IORESOURCE_IRQ; + adata->res[1].start = pci->irq; + adata->res[1].end = pci->irq; + + adata->acp3x_audio_mode = ACP3x_I2S_MODE; + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = "acp3x_rv_i2s"; + pdevinfo.id = 0; + pdevinfo.parent = &pci->dev; + pdevinfo.num_res = 2; + pdevinfo.res = adata->res; + pdevinfo.data = &irqflags; + pdevinfo.size_data = sizeof(irqflags); + + adata->pdev = platform_device_register_full(&pdevinfo); + if (!adata->pdev) { + dev_err(&pci->dev, "cannot register %s device\n", + pdevinfo.name); + ret = -ENODEV; + goto unmap_mmio; + } + break; + default: + dev_err(&pci->dev, "Inavlid ACP audio mode : %d\n", val); + ret = -ENODEV; + goto unmap_mmio; + } return 0; +unmap_mmio: + pci_disable_msi(pci); + iounmap(adata->acp3x_base); release_regions: pci_release_regions(pci); disable_pci: @@ -70,7 +135,10 @@ static void snd_acp3x_remove(struct pci_dev *pci) { struct acp3x_dev_data *adata = pci_get_drvdata(pci); + platform_device_unregister(adata->pdev); iounmap(adata->acp3x_base); + + pci_disable_msi(pci); pci_release_regions(pci); pci_disable_device(pci); } -- cgit v1.2.3-58-ga151 From ac289c7ec0bc9cf426036a8adbe36f72be53d0ae Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:04:55 +0530 Subject: ASoC: amd: add ACP3x PCM platform driver PCM platform driver binds to the platform device created by ACP3x PCI device. PCM driver registers ALSA DMA and CPU DAI components with ASoC framework. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 263 ++++++++++++++++++++++++++++++++++++ sound/soc/amd/raven/acp3x.h | 7 + 2 files changed, 270 insertions(+) create mode 100644 sound/soc/amd/raven/acp3x-pcm-dma.c (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c new file mode 100644 index 000000000000..3e806f74c6f7 --- /dev/null +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -0,0 +1,263 @@ +/* + * AMD ALSA SoC PCM Driver + * + * Copyright 2016 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "acp3x.h" + +#define DRV_NAME "acp3x-i2s-audio" + +struct i2s_dev_data { + void __iomem *acp3x_base; + struct snd_pcm_substream *play_stream; + struct snd_pcm_substream *capture_stream; +}; + +static int acp3x_power_on(void __iomem *acp3x_base, bool on) +{ + u16 val, mask; + u32 timeout; + + if (on == true) { + val = 1; + mask = ACP3x_POWER_ON; + } else { + val = 0; + mask = ACP3x_POWER_OFF; + } + + rv_writel(val, acp3x_base + mmACP_PGFSM_CONTROL); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_PGFSM_STATUS); + if ((val & ACP3x_POWER_OFF_IN_PROGRESS) == mask) + break; + if (timeout > 100) { + pr_err("ACP3x power state change failure\n"); + return -ENODEV; + } + timeout++; + cpu_relax(); + } + return 0; +} + +static int acp3x_reset(void __iomem *acp3x_base) +{ + u32 val, timeout; + + rv_writel(1, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if ((val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) || + timeout > 100) { + if (val & ACP3x_SOFT_RESET__SoftResetAudDone_MASK) + break; + return -ENODEV; + } + timeout++; + cpu_relax(); + } + + rv_writel(0, acp3x_base + mmACP_SOFT_RESET); + timeout = 0; + while (true) { + val = rv_readl(acp3x_base + mmACP_SOFT_RESET); + if (!val || timeout > 100) { + if (!val) + break; + return -ENODEV; + } + timeout++; + cpu_relax(); + } + return 0; +} + +static int acp3x_init(void __iomem *acp3x_base) +{ + int ret; + + /* power on */ + ret = acp3x_power_on(acp3x_base, true); + if (ret) { + pr_err("ACP3x power on failed\n"); + return ret; + } + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + return 0; +} + +static int acp3x_deinit(void __iomem *acp3x_base) +{ + int ret; + + /* Reset */ + ret = acp3x_reset(acp3x_base); + if (ret) { + pr_err("ACP3x reset failed\n"); + return ret; + } + /* power off */ + ret = acp3x_power_on(acp3x_base, false); + if (ret) { + pr_err("ACP3x power off failed\n"); + return ret; + } + return 0; +} + +static struct snd_pcm_ops acp3x_dma_ops = { + .open = NULL, + .close = NULL, + .ioctl = NULL, + .hw_params = NULL, + .hw_free = NULL, + .pointer = NULL, + .mmap = NULL, +}; + +struct snd_soc_dai_ops acp3x_dai_i2s_ops = { + .hw_params = NULL, + .trigger = NULL, + .set_fmt = NULL, +}; + +static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { + .playback = { + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + + .rate_min = 8000, + .rate_max = 96000, + }, + .capture = { + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rate_min = 8000, + .rate_max = 48000, + }, + .ops = &acp3x_dai_i2s_ops, +}; + +static const struct snd_soc_component_driver acp3x_i2s_component = { + .name = DRV_NAME, + .ops = &acp3x_dma_ops, + .pcm_new = acp3x_dma_new, +}; + +static int acp3x_audio_probe(struct platform_device *pdev) +{ + int status; + struct resource *res; + struct i2s_dev_data *adata; + unsigned int irqflags; + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "platform_data not retrieved\n"); + return -ENODEV; + } + irqflags = *((unsigned int *)(pdev->dev.platform_data)); + + adata = devm_kzalloc(&pdev->dev, sizeof(struct i2s_dev_data), + GFP_KERNEL); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + return -ENODEV; + } + + adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, + resource_size(res)); + + adata->play_stream = NULL; + adata->capture_stream = NULL; + + dev_set_drvdata(&pdev->dev, adata); + /* Initialize ACP */ + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + status = devm_snd_soc_register_component(&pdev->dev, + &acp3x_i2s_component, + &acp3x_i2s_dai_driver, 1); + if (status) { + dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); + goto dev_err; + } + + return 0; +dev_err: + status = acp3x_deinit(adata->acp3x_base); + if (status) + dev_err(&pdev->dev, "ACP de-init failed\n"); + else + dev_info(&pdev->dev, "ACP de-initialized\n"); + /*ignore device status and return driver probe error*/ + return -ENODEV; +} + +static int acp3x_audio_remove(struct platform_device *pdev) +{ + int ret; + struct i2s_dev_data *adata = dev_get_drvdata(&pdev->dev); + + ret = acp3x_deinit(adata->acp3x_base); + if (ret) + dev_err(&pdev->dev, "ACP de-init failed\n"); + else + dev_info(&pdev->dev, "ACP de-initialized\n"); + + return 0; +} + +static struct platform_driver acp3x_dma_driver = { + .probe = acp3x_audio_probe, + .remove = acp3x_audio_remove, + .driver = { + .name = "acp3x_rv_i2s", + }, +}; + +module_platform_driver(acp3x_dma_driver); + +MODULE_AUTHOR("Maruthi.Bayyavarapu@amd.com"); +MODULE_AUTHOR("Vijendar.Mukunda@amd.com"); +MODULE_DESCRIPTION("AMD ACP 3.x PCM Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index 83b1ed8f6060..f3d3e5128afc 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -5,6 +5,13 @@ #define ACP3x_REG_START 0x1240000 #define ACP3x_REG_END 0x1250200 #define I2S_MODE 0x04 +#define BT_TX_THRESHOLD 26 +#define BT_RX_THRESHOLD 25 +#define ACP3x_POWER_ON 0x00 +#define ACP3x_POWER_ON_IN_PROGRESS 0x01 +#define ACP3x_POWER_OFF 0x02 +#define ACP3x_POWER_OFF_IN_PROGRESS 0x03 +#define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001 static inline u32 rv_readl(void __iomem *base_addr) { -- cgit v1.2.3-58-ga151 From 32feac95f646aebaafdaf12a610898b6e5db54ae Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:04:56 +0530 Subject: ASoC: amd: Interrupt handler changes for ACP3x DMA driver Whenever audio data equal to the I2S FIFO watermark level are produced/consumed, interrupt is generated. Acknowledge the interrupt. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 3e806f74c6f7..94f915afd1b3 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -27,6 +27,7 @@ #define DRV_NAME "acp3x-i2s-audio" struct i2s_dev_data { + unsigned int i2s_irq; void __iomem *acp3x_base; struct snd_pcm_substream *play_stream; struct snd_pcm_substream *capture_stream; @@ -132,6 +133,38 @@ static int acp3x_deinit(void __iomem *acp3x_base) return 0; } +static irqreturn_t i2s_irq_handler(int irq, void *dev_id) +{ + u16 play_flag, cap_flag; + u32 val; + struct i2s_dev_data *rv_i2s_data = dev_id; + + if (!rv_i2s_data) + return IRQ_NONE; + + play_flag = 0; + cap_flag = 0; + val = rv_readl(rv_i2s_data->acp3x_base + mmACP_EXTERNAL_INTR_STAT); + if ((val & BIT(BT_TX_THRESHOLD)) && rv_i2s_data->play_stream) { + rv_writel(BIT(BT_TX_THRESHOLD), rv_i2s_data->acp3x_base + + mmACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(rv_i2s_data->play_stream); + play_flag = 1; + } + + if ((val & BIT(BT_RX_THRESHOLD)) && rv_i2s_data->capture_stream) { + rv_writel(BIT(BT_RX_THRESHOLD), rv_i2s_data->acp3x_base + + mmACP_EXTERNAL_INTR_STAT); + snd_pcm_period_elapsed(rv_i2s_data->capture_stream); + cap_flag = 1; + } + + if (play_flag | cap_flag) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + static struct snd_pcm_ops acp3x_dma_ops = { .open = NULL, .close = NULL, @@ -205,6 +238,13 @@ static int acp3x_audio_probe(struct platform_device *pdev) adata->acp3x_base = devm_ioremap(&pdev->dev, res->start, resource_size(res)); + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + dev_err(&pdev->dev, "IORESOURCE_IRQ FAILED\n"); + return -ENODEV; + } + + adata->i2s_irq = res->start; adata->play_stream = NULL; adata->capture_stream = NULL; @@ -220,6 +260,12 @@ static int acp3x_audio_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Fail to register acp i2s dai\n"); goto dev_err; } + status = devm_request_irq(&pdev->dev, adata->i2s_irq, i2s_irq_handler, + irqflags, "ACP3x_I2S_IRQ", adata); + if (status) { + dev_err(&pdev->dev, "ACP3x I2S IRQ request failed\n"); + goto dev_err; + } return 0; dev_err: -- cgit v1.2.3-58-ga151 From 0b87d6bcd6482b4502d8fd21561380981dad501f Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:04:57 +0530 Subject: ASoC: amd: add acp3x pcm driver dma ops ACP3x has a DMA controller to access system memory. This controller transfers data from/to system memory to/from the ACP internal FIFO. The patch adds PCM driver DMA operations. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 262 +++++++++++++++++++++++++++++++++++- sound/soc/amd/raven/acp3x.h | 20 +++ 2 files changed, 275 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 94f915afd1b3..06c1985c4d56 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -33,6 +33,54 @@ struct i2s_dev_data { struct snd_pcm_substream *capture_stream; }; +struct i2s_stream_instance { + u16 num_pages; + u16 channels; + u32 xfer_resolution; + struct page *pg; + void __iomem *acp3x_base; +}; + +static const struct snd_pcm_hardware acp3x_pcm_hardware_playback = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000_96000, + .rate_min = 8000, + .rate_max = 96000, + .buffer_bytes_max = PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE, + .period_bytes_min = PLAYBACK_MIN_PERIOD_SIZE, + .period_bytes_max = PLAYBACK_MAX_PERIOD_SIZE, + .periods_min = PLAYBACK_MIN_NUM_PERIODS, + .periods_max = PLAYBACK_MAX_NUM_PERIODS, +}; + +static const struct snd_pcm_hardware acp3x_pcm_hardware_capture = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_BATCH | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | + SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .buffer_bytes_max = CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE, + .period_bytes_min = CAPTURE_MIN_PERIOD_SIZE, + .period_bytes_max = CAPTURE_MAX_PERIOD_SIZE, + .periods_min = CAPTURE_MIN_NUM_PERIODS, + .periods_max = CAPTURE_MAX_NUM_PERIODS, +}; + static int acp3x_power_on(void __iomem *acp3x_base, bool on) { u16 val, mask; @@ -165,14 +213,214 @@ static irqreturn_t i2s_irq_handler(int irq, void *dev_id) return IRQ_NONE; } +static void config_acp3x_dma(struct i2s_stream_instance *rtd, int direction) +{ + u16 page_idx; + u64 addr; + u32 low, high, val, acp_fifo_addr; + struct page *pg = rtd->pg; + + /* 8 scratch registers used to map one 64 bit address */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + val = 0; + else + val = rtd->num_pages * 8; + + /* Group Enable */ + rv_writel(ACP_SRAM_PTE_OFFSET | BIT(31), rtd->acp3x_base + + mmACPAXI2AXI_ATU_BASE_ADDR_GRP_1); + rv_writel(PAGE_SIZE_4K_ENABLE, rtd->acp3x_base + + mmACPAXI2AXI_ATU_PAGE_SIZE_GRP_1); + + for (page_idx = 0; page_idx < rtd->num_pages; page_idx++) { + /* Load the low address of page int ACP SRAM through SRBM */ + addr = page_to_phys(pg); + low = lower_32_bits(addr); + high = upper_32_bits(addr); + + rv_writel(low, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val); + high |= BIT(31); + rv_writel(high, rtd->acp3x_base + mmACP_SCRATCH_REG_0 + val + + 4); + /* Move to next physically contiguos page */ + val += 8; + pg++; + } + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + /* Config ringbuffer */ + rv_writel(MEM_WINDOW_START, rtd->acp3x_base + + mmACP_BT_TX_RINGBUFADDR); + rv_writel(MAX_BUFFER, rtd->acp3x_base + + mmACP_BT_TX_RINGBUFSIZE); + rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_TX_DMA_SIZE); + + /* Config audio fifo */ + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + (rtd->num_pages * 8) + + PLAYBACK_FIFO_ADDR_OFFSET; + rv_writel(acp_fifo_addr, rtd->acp3x_base + + mmACP_BT_TX_FIFOADDR); + rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_TX_FIFOSIZE); + } else { + /* Config ringbuffer */ + rv_writel(MEM_WINDOW_START + MAX_BUFFER, rtd->acp3x_base + + mmACP_BT_RX_RINGBUFADDR); + rv_writel(MAX_BUFFER, rtd->acp3x_base + + mmACP_BT_RX_RINGBUFSIZE); + rv_writel(DMA_SIZE, rtd->acp3x_base + mmACP_BT_RX_DMA_SIZE); + + /* Config audio fifo */ + acp_fifo_addr = ACP_SRAM_PTE_OFFSET + + (rtd->num_pages * 8) + CAPTURE_FIFO_ADDR_OFFSET; + rv_writel(acp_fifo_addr, rtd->acp3x_base + + mmACP_BT_RX_FIFOADDR); + rv_writel(FIFO_SIZE, rtd->acp3x_base + mmACP_BT_RX_FIFOSIZE); + } + + /* Enable watermark/period interrupt to host */ + rv_writel(BIT(BT_TX_THRESHOLD) | BIT(BT_RX_THRESHOLD), + rtd->acp3x_base + mmACP_EXTERNAL_INTR_CNTL); +} + +static int acp3x_dma_open(struct snd_pcm_substream *substream) +{ + int ret = 0; + + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + struct i2s_dev_data *adata = dev_get_drvdata(component->dev); + + struct i2s_stream_instance *i2s_data = kzalloc(sizeof(struct i2s_stream_instance), + GFP_KERNEL); + if (!i2s_data) + return -EINVAL; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = acp3x_pcm_hardware_playback; + else + runtime->hw = acp3x_pcm_hardware_capture; + + ret = snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + dev_err(component->dev, "set integer constraint failed\n"); + return ret; + } + + if (!adata->play_stream && !adata->capture_stream) + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + adata->play_stream = substream; + else + adata->capture_stream = substream; + + i2s_data->acp3x_base = adata->acp3x_base; + runtime->private_data = i2s_data; + return 0; +} + +static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int status; + u64 size; + struct snd_dma_buffer *dma_buffer; + struct page *pg; + struct snd_pcm_runtime *runtime = substream->runtime; + struct i2s_stream_instance *rtd = runtime->private_data; + + if (!rtd) + return -EINVAL; + + dma_buffer = &substream->dma_buffer; + size = params_buffer_bytes(params); + status = snd_pcm_lib_malloc_pages(substream, size); + if (status < 0) + return status; + + memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); + pg = virt_to_page(substream->dma_buffer.area); + if (pg) { + rtd->pg = pg; + rtd->num_pages = (PAGE_ALIGN(size) >> PAGE_SHIFT); + config_acp3x_dma(rtd, substream->stream); + status = 0; + } else { + status = -ENOMEM; + } + return status; +} + +static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream) +{ + u32 pos = 0; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + pos = rv_readl(rtd->acp3x_base + + mmACP_BT_TX_LINKPOSITIONCNTR); + else + pos = rv_readl(rtd->acp3x_base + + mmACP_BT_RX_LINKPOSITIONCNTR); + + if (pos >= MAX_BUFFER) + pos = 0; + + return bytes_to_frames(substream->runtime, pos); +} + +static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd) +{ + return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + NULL, MIN_BUFFER, + MAX_BUFFER); +} + +static int acp3x_dma_hw_free(struct snd_pcm_substream *substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +static int acp3x_dma_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + return snd_pcm_lib_default_mmap(substream, vma); +} + +static int acp3x_dma_close(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *prtd = substream->private_data; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(prtd, + DRV_NAME); + struct i2s_dev_data *adata = dev_get_drvdata(component->dev); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + adata->play_stream = NULL; + else + adata->capture_stream = NULL; + + /* Disable ACP irq, when the current stream is being closed and + * another stream is also not active. + */ + if (!adata->play_stream && !adata->capture_stream) + rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + kfree(rtd); + return 0; +} + static struct snd_pcm_ops acp3x_dma_ops = { - .open = NULL, - .close = NULL, - .ioctl = NULL, - .hw_params = NULL, - .hw_free = NULL, - .pointer = NULL, - .mmap = NULL, + .open = acp3x_dma_open, + .close = acp3x_dma_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = acp3x_dma_hw_params, + .hw_free = acp3x_dma_hw_free, + .pointer = acp3x_dma_pointer, + .mmap = acp3x_dma_mmap, }; struct snd_soc_dai_ops acp3x_dai_i2s_ops = { diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index f3d3e5128afc..9f33e34b868e 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -13,6 +13,26 @@ #define ACP3x_POWER_OFF_IN_PROGRESS 0x03 #define ACP3x_SOFT_RESET__SoftResetAudDone_MASK 0x00010001 +#define ACP_SRAM_PTE_OFFSET 0x02050000 +#define PAGE_SIZE_4K_ENABLE 0x2 +#define MEM_WINDOW_START 0x4000000 +#define PLAYBACK_FIFO_ADDR_OFFSET 0x400 +#define CAPTURE_FIFO_ADDR_OFFSET 0x500 + +#define PLAYBACK_MIN_NUM_PERIODS 2 +#define PLAYBACK_MAX_NUM_PERIODS 8 +#define PLAYBACK_MAX_PERIOD_SIZE 16384 +#define PLAYBACK_MIN_PERIOD_SIZE 4096 +#define CAPTURE_MIN_NUM_PERIODS 2 +#define CAPTURE_MAX_NUM_PERIODS 8 +#define CAPTURE_MAX_PERIOD_SIZE 16384 +#define CAPTURE_MIN_PERIOD_SIZE 4096 + +#define MAX_BUFFER (PLAYBACK_MAX_PERIOD_SIZE * PLAYBACK_MAX_NUM_PERIODS) +#define MIN_BUFFER MAX_BUFFER +#define FIFO_SIZE 0x100 +#define DMA_SIZE 0x40 + static inline u32 rv_readl(void __iomem *base_addr) { return readl(base_addr - ACP3x_PHY_BASE_ADDRESS); -- cgit v1.2.3-58-ga151 From 2b5f290e10266037338631c03ab6bea51f0a7ebf Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:04:58 +0530 Subject: ASoC: amd: add acp3x i2s ops ACP3x has a i2s controller block for playback and capture. This patch adds ACP3x i2s DAI operations. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 89 +++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 06c1985c4d56..ed2c16379d2c 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -423,10 +423,93 @@ static struct snd_pcm_ops acp3x_dma_ops = { .mmap = acp3x_dma_mmap, }; +static int acp3x_dai_i2s_hwparams(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + u32 val = 0; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + rtd->xfer_resolution = 0x0; + break; + case SNDRV_PCM_FORMAT_S16_LE: + rtd->xfer_resolution = 0x02; + break; + case SNDRV_PCM_FORMAT_S24_LE: + rtd->xfer_resolution = 0x04; + break; + case SNDRV_PCM_FORMAT_S32_LE: + rtd->xfer_resolution = 0x05; + break; + default: + return -EINVAL; + } + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); + val = val | (rtd->xfer_resolution << 3); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); + else + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); + + return 0; +} + +static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + int ret = 0; + struct i2s_stream_instance *rtd = substream->runtime->private_data; + u32 val, period_bytes; + + period_bytes = frames_to_bytes(substream->runtime, + substream->runtime->period_size); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rv_writel(period_bytes, rtd->acp3x_base + + mmACP_BT_TX_INTR_WATERMARK_SIZE); + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); + val = val | BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); + } else { + rv_writel(period_bytes, rtd->acp3x_base + + mmACP_BT_RX_INTR_WATERMARK_SIZE); + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER); + val = val | BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); + } + rv_writel(1, rtd->acp3x_base + mmACP_BTTDM_IER); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_ITER); + val = val & ~BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_ITER); + } else { + val = rv_readl(rtd->acp3x_base + mmACP_BTTDM_IRER); + val = val & ~BIT(0); + rv_writel(val, rtd->acp3x_base + mmACP_BTTDM_IRER); + } + rv_writel(0, rtd->acp3x_base + mmACP_BTTDM_IER); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + struct snd_soc_dai_ops acp3x_dai_i2s_ops = { - .hw_params = NULL, - .trigger = NULL, - .set_fmt = NULL, + .hw_params = acp3x_dai_i2s_hwparams, + .trigger = acp3x_dai_i2s_trigger, }; static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { -- cgit v1.2.3-58-ga151 From 67aa06aef5836d2ceb1b9280ca2cee9cc4b1e004 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:04:59 +0530 Subject: ASoC: amd: add acp3x tdm mode support ACP3x I2S (CPU DAI) can act in normal I2S and TDM modes. Added support for TDM mode. Desired mode can be selected from ASoC machine driver. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 62 +++++++++++++++++++++++++++++++++++++ sound/soc/amd/raven/acp3x.h | 7 +++++ 2 files changed, 69 insertions(+) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index ed2c16379d2c..15a5f3b4db71 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -27,7 +27,9 @@ #define DRV_NAME "acp3x-i2s-audio" struct i2s_dev_data { + bool tdm_mode; unsigned int i2s_irq; + u32 tdm_fmt; void __iomem *acp3x_base; struct snd_pcm_substream *play_stream; struct snd_pcm_substream *capture_stream; @@ -423,6 +425,64 @@ static struct snd_pcm_ops acp3x_dma_ops = { .mmap = acp3x_dma_mmap, }; + +static int acp3x_dai_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + + struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + adata->tdm_mode = false; + break; + case SND_SOC_DAIFMT_DSP_A: + adata->tdm_mode = true; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int acp3x_dai_set_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask, + u32 rx_mask, int slots, int slot_width) +{ + u32 val = 0; + u16 slot_len; + + struct i2s_dev_data *adata = snd_soc_dai_get_drvdata(cpu_dai); + + switch (slot_width) { + case SLOT_WIDTH_8: + slot_len = 8; + break; + case SLOT_WIDTH_16: + slot_len = 16; + break; + case SLOT_WIDTH_24: + slot_len = 24; + break; + case SLOT_WIDTH_32: + slot_len = 0; + break; + default: + return -EINVAL; + } + + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER); + rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_ITER); + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER); + rv_writel((val | 0x2), adata->acp3x_base + mmACP_BTTDM_IRER); + + val = (FRM_LEN | (slots << 15) | (slot_len << 18)); + rv_writel(val, adata->acp3x_base + mmACP_BTTDM_TXFRMT); + rv_writel(val, adata->acp3x_base + mmACP_BTTDM_RXFRMT); + + adata->tdm_fmt = val; + return 0; +} + static int acp3x_dai_i2s_hwparams(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) @@ -510,6 +570,8 @@ static int acp3x_dai_i2s_trigger(struct snd_pcm_substream *substream, struct snd_soc_dai_ops acp3x_dai_i2s_ops = { .hw_params = acp3x_dai_i2s_hwparams, .trigger = acp3x_dai_i2s_trigger, + .set_fmt = acp3x_dai_i2s_set_fmt, + .set_tdm_slot = acp3x_dai_set_tdm_slot, }; static struct snd_soc_dai_driver acp3x_i2s_dai_driver = { diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index 9f33e34b868e..95fef8570577 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -32,6 +32,13 @@ #define MIN_BUFFER MAX_BUFFER #define FIFO_SIZE 0x100 #define DMA_SIZE 0x40 +#define FRM_LEN 0x100 + +#define SLOT_WIDTH_8 0x08 +#define SLOT_WIDTH_16 0x10 +#define SLOT_WIDTH_24 0x18 +#define SLOT_WIDTH_32 0x20 + static inline u32 rv_readl(void __iomem *base_addr) { -- cgit v1.2.3-58-ga151 From 56e4dd8f68019f50a885be9122981f1899cd89ed Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:05:00 +0530 Subject: ASoC: amd: add acp3x runtime pm ops Added runtime PM operations for ACP3x PCM platform device. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 15a5f3b4db71..710c5c5ae625 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -660,6 +661,9 @@ static int acp3x_audio_probe(struct platform_device *pdev) goto dev_err; } + pm_runtime_set_autosuspend_delay(&pdev->dev, 10000); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); return 0; dev_err: status = acp3x_deinit(adata->acp3x_base); @@ -682,14 +686,49 @@ static int acp3x_audio_remove(struct platform_device *pdev) else dev_info(&pdev->dev, "ACP de-initialized\n"); + pm_runtime_disable(&pdev->dev); return 0; } +static int acp3x_pcm_runtime_suspend(struct device *dev) +{ + int status; + struct i2s_dev_data *adata = dev_get_drvdata(dev); + + status = acp3x_deinit(adata->acp3x_base); + if (status) + dev_err(dev, "ACP de-init failed\n"); + else + dev_info(dev, "ACP de-initialized\n"); + + rv_writel(0, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + + return 0; +} + +static int acp3x_pcm_runtime_resume(struct device *dev) +{ + int status; + struct i2s_dev_data *adata = dev_get_drvdata(dev); + + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + return 0; +} + +static const struct dev_pm_ops acp3x_pm_ops = { + .runtime_suspend = acp3x_pcm_runtime_suspend, + .runtime_resume = acp3x_pcm_runtime_resume, +}; + static struct platform_driver acp3x_dma_driver = { .probe = acp3x_audio_probe, .remove = acp3x_audio_remove, .driver = { .name = "acp3x_rv_i2s", + .pm = &acp3x_pm_ops, }, }; -- cgit v1.2.3-58-ga151 From 8de1b5ed03370e46bf9f5b425e3f260dac3bc336 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:05:01 +0530 Subject: ASoC: amd: add acp3x system resume pm op When system wide suspend happens, ACP will be powered off. When system resumes, all the runtime configuration data for ACP needs to be programmed again. Added 'resume'pm call back to ACP pm ops. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 46 +++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 710c5c5ae625..2e61cef00f41 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -690,6 +690,51 @@ static int acp3x_audio_remove(struct platform_device *pdev) return 0; } +static int acp3x_resume(struct device *dev) +{ + int status; + u32 val; + struct i2s_dev_data *adata = dev_get_drvdata(dev); + + status = acp3x_init(adata->acp3x_base); + if (status) + return -ENODEV; + + if (adata->play_stream && adata->play_stream->runtime) { + struct i2s_stream_instance *rtd = + adata->play_stream->runtime->private_data; + config_acp3x_dma(rtd, SNDRV_PCM_STREAM_PLAYBACK); + rv_writel((rtd->xfer_resolution << 3), + rtd->acp3x_base + mmACP_BTTDM_ITER); + if (adata->tdm_mode == true) { + rv_writel(adata->tdm_fmt, adata->acp3x_base + + mmACP_BTTDM_TXFRMT); + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_ITER); + rv_writel((val | 0x2), adata->acp3x_base + + mmACP_BTTDM_ITER); + } + } + + if (adata->capture_stream && adata->capture_stream->runtime) { + struct i2s_stream_instance *rtd = + adata->capture_stream->runtime->private_data; + config_acp3x_dma(rtd, SNDRV_PCM_STREAM_CAPTURE); + rv_writel((rtd->xfer_resolution << 3), + rtd->acp3x_base + mmACP_BTTDM_IRER); + if (adata->tdm_mode == true) { + rv_writel(adata->tdm_fmt, adata->acp3x_base + + mmACP_BTTDM_RXFRMT); + val = rv_readl(adata->acp3x_base + mmACP_BTTDM_IRER); + rv_writel((val | 0x2), adata->acp3x_base + + mmACP_BTTDM_IRER); + } + } + + rv_writel(1, adata->acp3x_base + mmACP_EXTERNAL_INTR_ENB); + return 0; +} + + static int acp3x_pcm_runtime_suspend(struct device *dev) { int status; @@ -721,6 +766,7 @@ static int acp3x_pcm_runtime_resume(struct device *dev) static const struct dev_pm_ops acp3x_pm_ops = { .runtime_suspend = acp3x_pcm_runtime_suspend, .runtime_resume = acp3x_pcm_runtime_resume, + .resume = acp3x_resume, }; static struct platform_driver acp3x_dma_driver = { -- cgit v1.2.3-58-ga151 From 818ba93b0e9ed4a08cba575dd49c1391ef4b7776 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Mon, 12 Nov 2018 11:05:02 +0530 Subject: ASoC: amd: enable acp3x drivers build ACP3x drivers can be built by selecting necessary kernel config option. The patch enables build support of the same. Signed-off-by: Maruthi Bayyavarapu Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 5 +++++ sound/soc/amd/Makefile | 1 + sound/soc/amd/raven/Makefile | 4 ++++ 3 files changed, 10 insertions(+) create mode 100644 sound/soc/amd/raven/Makefile (limited to 'sound') diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 58c1dcb4d255..5f3b56087ba1 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -19,3 +19,8 @@ config SND_SOC_AMD_CZ_RT5645_MACH depends on SND_SOC_AMD_ACP && I2C help This option enables machine driver for rt5645. + +config SND_SOC_AMD_ACP3x + tristate "AMD Audio Coprocessor-v3.x support" + help + This option enables ACP v3.x I2S support on AMD platform diff --git a/sound/soc/amd/Makefile b/sound/soc/amd/Makefile index 79b0622fa5d3..8e1c571c3161 100644 --- a/sound/soc/amd/Makefile +++ b/sound/soc/amd/Makefile @@ -5,3 +5,4 @@ snd-soc-acp-rt5645-mach-objs := acp-rt5645.o obj-$(CONFIG_SND_SOC_AMD_ACP) += acp_audio_dma.o obj-$(CONFIG_SND_SOC_AMD_CZ_DA7219MX98357_MACH) += snd-soc-acp-da7219mx98357-mach.o obj-$(CONFIG_SND_SOC_AMD_CZ_RT5645_MACH) += snd-soc-acp-rt5645-mach.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += raven/ diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile new file mode 100644 index 000000000000..8c03e6fccf6a --- /dev/null +++ b/sound/soc/amd/raven/Makefile @@ -0,0 +1,4 @@ +snd-pci-acp3x-objs := pci-acp3x.o +snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o +obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-acp3x-pcm-dma.o -- cgit v1.2.3-58-ga151 From 496fa3ba0a66c0192be86a45f44f94827ab8bcb6 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 27 Oct 2018 02:19:59 +0000 Subject: ASoC: stm32: sai: fix invalid use of sizeof in stm32_sai_add_mclk_provider() sizeof() when applied to a pointer typed expression gives the size of the pointer, not that of the pointed data. Fixes: 8307b2afd386 ("ASoC: stm32: sai: set sai as mclk clock provider") Signed-off-by: Wei Yongjun Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 506423d5861a..d4825700b63f 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -389,7 +389,7 @@ static int stm32_sai_add_mclk_provider(struct stm32_sai_sub_data *sai) char *mclk_name, *p, *s = (char *)pname; int ret, i = 0; - mclk = devm_kzalloc(dev, sizeof(mclk), GFP_KERNEL); + mclk = devm_kzalloc(dev, sizeof(*mclk), GFP_KERNEL); if (!mclk) return -ENOMEM; -- cgit v1.2.3-58-ga151 From 9d4b17efa51f3bb0918021090c09b0013039df00 Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Mon, 12 Nov 2018 08:08:33 +0100 Subject: ASoC: pcm3060: Add DT property for single-ended output DAC output may be differential (default) or single-ended. Signed-off-by: Kirill Marinushkin Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3060.c | 14 ++++++++++++++ sound/soc/codecs/pcm3060.h | 1 + 2 files changed, 15 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 494d9d662be8..5f479aa61097 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -274,9 +274,23 @@ EXPORT_SYMBOL(pcm3060_regmap); /* device */ +static void pcm3060_parse_dt(const struct device_node *np, + struct pcm3060_priv *priv) +{ + priv->out_se = of_property_read_bool(np, "ti,out-single-ended"); +} + int pcm3060_probe(struct device *dev) { int rc; + struct pcm3060_priv *priv = dev_get_drvdata(dev); + + if (dev->of_node) + pcm3060_parse_dt(dev->of_node, priv); + + if (priv->out_se) + regmap_update_bits(priv->regmap, PCM3060_REG64, + PCM3060_REG_SE, PCM3060_REG_SE); rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver, pcm3060_dai, diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h index fd89a68aa8a7..c895cf40ee10 100644 --- a/sound/soc/codecs/pcm3060.h +++ b/sound/soc/codecs/pcm3060.h @@ -25,6 +25,7 @@ struct pcm3060_priv_dai { struct pcm3060_priv { struct regmap *regmap; struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM]; + u8 out_se: 1; }; int pcm3060_probe(struct device *dev); -- cgit v1.2.3-58-ga151 From 46dce4042659755a2b9753369d0c2d8ca33e35cf Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 14 Nov 2018 21:31:48 +0000 Subject: ASoC: amd: fix memory leak of i2s_data on error return Currently when snd_pcm_hw_constraint_integer fails there is a memory leak of i2s_data on the error return path. Fix this by kfree'ing i2s_data before returning. Detected by CoverityScan, CID#1475479 ("Resource leak") Fixes: 0b87d6bcd648 ("ASoC: amd: add acp3x pcm driver dma ops") Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 2e61cef00f41..b16cdd187139 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -309,6 +309,7 @@ static int acp3x_dma_open(struct snd_pcm_substream *substream) SNDRV_PCM_HW_PARAM_PERIODS); if (ret < 0) { dev_err(component->dev, "set integer constraint failed\n"); + kfree(i2s_data); return ret; } -- cgit v1.2.3-58-ga151 From ba34f253711a8427d2ef29475afe406b2d6e5118 Mon Sep 17 00:00:00 2001 From: David Lin Date: Thu, 15 Nov 2018 17:14:47 +0800 Subject: ASoC: nau8822: convert to SPDX identifiers This patch updates license to SPDX-License-Identifier instead of verbose license text. Signed-off-by: David Lin Signed-off-by: Mark Brown --- sound/soc/codecs/nau8822.c | 26 +++++++++++--------------- sound/soc/codecs/nau8822.h | 11 +++++------ 2 files changed, 16 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index 622ce947f134..c6152a044416 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1,18 +1,14 @@ -/* - * nau8822.c -- NAU8822 ALSA Soc Audio Codec driver - * - * Copyright 2017 Nuvoton Technology Corp. - * - * Author: David Lin - * Co-author: John Hsu - * Co-author: Seven Li - * - * Based on WM8974.c - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// nau8822.c -- NAU8822 ALSA Soc Audio driver +// +// Copyright 2017 Nuvoton Technology Crop. +// +// Author: David Lin +// Co-author: John Hsu +// Co-author: Seven Li +// +// Based on WM8974.c #include #include diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index aa79c969cd44..d15afd782815 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -1,13 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* - * nau8822.h -- NAU8822 Soc Audio Codec driver + * nau8822.h -- NAU8822 ALSA SoC Audio driver + * + * Copyright 2017 Nuvoton Technology Crop. * * Author: David Lin - * Co-author: John Hsu + * Co-author: John Hsu * Co-author: Seven Li - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __NAU8822_H__ -- cgit v1.2.3-58-ga151 From 0c8224c34aa82901037e6c5921b692ea1b78fd24 Mon Sep 17 00:00:00 2001 From: David Lin Date: Thu, 15 Nov 2018 17:49:13 +0800 Subject: ASoC: nau8822: convert to SPDX identifiers This patch fixes typo in the comment. Signed-off-by: David Lin Signed-off-by: Mark Brown --- sound/soc/codecs/nau8822.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index d15afd782815..9c552983a293 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -5,7 +5,7 @@ * Copyright 2017 Nuvoton Technology Crop. * * Author: David Lin - * Co-author: John Hsu + * Co-author: John Hsu * Co-author: Seven Li */ -- cgit v1.2.3-58-ga151 From 1c4fb2457e541c614792e6d9986bc95461e67e41 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 15 Nov 2018 21:43:49 +0530 Subject: ASoC: amd: Fixed build errors Fixed build errors. - Implicit declaration of pci_enable_msi() & pci_disable_msi() api's for openrisc architecture. - type defaults to 'int' in declaration of 'module_pci_driver' Enabled build for x86 architecture. Reviewed-by: Alex Deucher Signed-off-by: Vijendar Mukunda Tested-by: Ravulapati Vishnu vardhan Rao Signed-off-by: Mark Brown --- sound/soc/amd/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig index 5f3b56087ba1..33ebec990c2f 100644 --- a/sound/soc/amd/Kconfig +++ b/sound/soc/amd/Kconfig @@ -22,5 +22,6 @@ config SND_SOC_AMD_CZ_RT5645_MACH config SND_SOC_AMD_ACP3x tristate "AMD Audio Coprocessor-v3.x support" + depends on X86 && PCI help This option enables ACP v3.x I2S support on AMD platform -- cgit v1.2.3-58-ga151 From d5a932e541f61a2be1cca7bab1e00d2bf7803e71 Mon Sep 17 00:00:00 2001 From: Vijendar Mukunda Date: Thu, 15 Nov 2018 21:43:50 +0530 Subject: ASoC: amd: Switch to SPDX identifier Adopt the SPDX license identifier headers to ease license compliance management. Reviewed-by: Alex Deucher Signed-off-by: Vijendar Mukunda Signed-off-by: Mark Brown --- sound/soc/amd/raven/Makefile | 2 ++ sound/soc/amd/raven/acp3x-pcm-dma.c | 19 +++++-------------- sound/soc/amd/raven/acp3x.h | 7 +++++++ sound/soc/amd/raven/chip_offset_byte.h | 22 +++------------------- sound/soc/amd/raven/pci-acp3x.c | 19 +++++-------------- 5 files changed, 22 insertions(+), 47 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/Makefile b/sound/soc/amd/raven/Makefile index 8c03e6fccf6a..108d1acf189b 100644 --- a/sound/soc/amd/raven/Makefile +++ b/sound/soc/amd/raven/Makefile @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0+ +# Raven Ridge platform Support snd-pci-acp3x-objs := pci-acp3x.o snd-acp3x-pcm-dma-objs := acp3x-pcm-dma.o obj-$(CONFIG_SND_SOC_AMD_ACP3x) += snd-pci-acp3x.o diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index b16cdd187139..2aa34208ae92 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -1,17 +1,8 @@ -/* - * AMD ALSA SoC PCM Driver - * - * Copyright 2016 Advanced Micro Devices, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ALSA SoC PCM Driver +// +//Copyright 2016 Advanced Micro Devices, Inc. #include #include diff --git a/sound/soc/amd/raven/acp3x.h b/sound/soc/amd/raven/acp3x.h index 95fef8570577..4f2cadd90a87 100644 --- a/sound/soc/amd/raven/acp3x.h +++ b/sound/soc/amd/raven/acp3x.h @@ -1,3 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * AMD ALSA SoC PCM Driver + * + * Copyright 2016 Advanced Micro Devices, Inc. + */ + #include "chip_offset_byte.h" #define ACP3x_PHY_BASE_ADDRESS 0x1240000 diff --git a/sound/soc/amd/raven/chip_offset_byte.h b/sound/soc/amd/raven/chip_offset_byte.h index fceb7bb40297..9c1fac58fb2a 100644 --- a/sound/soc/amd/raven/chip_offset_byte.h +++ b/sound/soc/amd/raven/chip_offset_byte.h @@ -1,24 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ /* - * ACP 3.0 Register documentation + * AMD ACP 3.0 Register Documentation * - * Copyright (C) 2016 Advanced Micro Devices, Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR - * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * Copyright 2016 Advanced Micro Devices, Inc. */ #ifndef _acp_ip_OFFSET_HEADER diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index 0700162b0ed1..ef805e71a98f 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -1,17 +1,8 @@ -/* - * AMD ALSA SoC PCM Driver - * - * Copyright 2016 Advanced Micro Devices, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - */ +// SPDX-License-Identifier: GPL-2.0+ +// +// AMD ACP PCI Driver +// +//Copyright 2016 Advanced Micro Devices, Inc. #include #include -- cgit v1.2.3-58-ga151 From 424d5b56d020b750803134cc15bbd4d0addaaee7 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Thu, 15 Nov 2018 01:56:35 +0000 Subject: ASoC: amd: remove set but not used variable 'dma_buffer' Fixes gcc '-Wunused-but-set-variable' warning: sound/soc/amd/raven/acp3x-pcm-dma.c: In function 'acp3x_dma_hw_params': sound/soc/amd/raven/acp3x-pcm-dma.c:333:25: warning: variable 'dma_buffer' set but not used [-Wunused-but-set-variable] It never used since introduction in commit 8de1b5ed0337 ("ASoC: amd: add acp3x system resume pm op") Signed-off-by: YueHaibing Signed-off-by: Mark Brown --- sound/soc/amd/raven/acp3x-pcm-dma.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c index 2aa34208ae92..022a8912c8a2 100644 --- a/sound/soc/amd/raven/acp3x-pcm-dma.c +++ b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -322,7 +322,6 @@ static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, { int status; u64 size; - struct snd_dma_buffer *dma_buffer; struct page *pg; struct snd_pcm_runtime *runtime = substream->runtime; struct i2s_stream_instance *rtd = runtime->private_data; @@ -330,7 +329,6 @@ static int acp3x_dma_hw_params(struct snd_pcm_substream *substream, if (!rtd) return -EINVAL; - dma_buffer = &substream->dma_buffer; size = params_buffer_bytes(params); status = snd_pcm_lib_malloc_pages(substream, size); if (status < 0) -- cgit v1.2.3-58-ga151 From bf897b441bd3afdd43e19605df4fb5a105678664 Mon Sep 17 00:00:00 2001 From: Adrien Charruel Date: Wed, 14 Nov 2018 13:16:41 +0100 Subject: ASoC: ak4118: Add support for AK4118 S/PDIF transceiver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AK4118A is a digital audio transceiver supporting 8 input channels at 192kHz and with 24bits resolution. It converts the S/PDIF signal to I2S format and is configurable over I2C. This driver introduce a minimal support of the AK4118, like selecting the input channel, reading input frequency and detecting some errors. Datasheet is available here: https://www.akm.com/akm/en/file/datasheet/AK4118AEQ.pdf Signed-off-by: Adrien Charruel Signed-off-by: ClÊment PÊron Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/ak4118.c | 438 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 446 insertions(+) create mode 100644 sound/soc/codecs/ak4118.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9cc4f1848c9b..62bdb7e333b8 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -35,6 +35,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ADAU7002 select SND_SOC_ADS117X select SND_SOC_AK4104 if SPI_MASTER + select SND_SOC_AK4118 if I2C select SND_SOC_AK4458 if I2C select SND_SOC_AK4535 if I2C select SND_SOC_AK4554 @@ -392,6 +393,11 @@ config SND_SOC_AK4104 tristate "AKM AK4104 CODEC" depends on SPI_MASTER +config SND_SOC_AK4118 + tristate "AKM AK4118 CODEC" + depends on I2C + select REGMAP_I2C + config SND_SOC_AK4458 tristate "AKM AK4458 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 8ffab8c8dbfa..66f55d185620 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -27,6 +27,7 @@ snd-soc-adav801-objs := adav801.o snd-soc-adav803-objs := adav803.o snd-soc-ads117x-objs := ads117x.o snd-soc-ak4104-objs := ak4104.o +snd-soc-ak4118-objs := ak4118.o snd-soc-ak4458-objs := ak4458.o snd-soc-ak4535-objs := ak4535.o snd-soc-ak4554-objs := ak4554.o @@ -290,6 +291,7 @@ obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o obj-$(CONFIG_SND_SOC_ADS117X) += snd-soc-ads117x.o obj-$(CONFIG_SND_SOC_AK4104) += snd-soc-ak4104.o +obj-$(CONFIG_SND_SOC_AK4118) += snd-soc-ak4118.o obj-$(CONFIG_SND_SOC_AK4458) += snd-soc-ak4458.o obj-$(CONFIG_SND_SOC_AK4535) += snd-soc-ak4535.o obj-$(CONFIG_SND_SOC_AK4554) += snd-soc-ak4554.o diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c new file mode 100644 index 000000000000..238ab29f2bf4 --- /dev/null +++ b/sound/soc/codecs/ak4118.c @@ -0,0 +1,438 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ak4118.c -- Asahi Kasei ALSA Soc Audio driver + * + * Copyright 2018 DEVIALET + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define AK4118_REG_CLK_PWR_CTL 0x00 +#define AK4118_REG_FORMAT_CTL 0x01 +#define AK4118_REG_IO_CTL0 0x02 +#define AK4118_REG_IO_CTL1 0x03 +#define AK4118_REG_INT0_MASK 0x04 +#define AK4118_REG_INT1_MASK 0x05 +#define AK4118_REG_RCV_STATUS0 0x06 +#define AK4118_REG_RCV_STATUS1 0x07 +#define AK4118_REG_RXCHAN_STATUS0 0x08 +#define AK4118_REG_RXCHAN_STATUS1 0x09 +#define AK4118_REG_RXCHAN_STATUS2 0x0a +#define AK4118_REG_RXCHAN_STATUS3 0x0b +#define AK4118_REG_RXCHAN_STATUS4 0x0c +#define AK4118_REG_TXCHAN_STATUS0 0x0d +#define AK4118_REG_TXCHAN_STATUS1 0x0e +#define AK4118_REG_TXCHAN_STATUS2 0x0f +#define AK4118_REG_TXCHAN_STATUS3 0x10 +#define AK4118_REG_TXCHAN_STATUS4 0x11 +#define AK4118_REG_BURST_PREAMB_PC0 0x12 +#define AK4118_REG_BURST_PREAMB_PC1 0x13 +#define AK4118_REG_BURST_PREAMB_PD0 0x14 +#define AK4118_REG_BURST_PREAMB_PD1 0x15 +#define AK4118_REG_QSUB_CTL 0x16 +#define AK4118_REG_QSUB_TRACK 0x17 +#define AK4118_REG_QSUB_INDEX 0x18 +#define AK4118_REG_QSUB_MIN 0x19 +#define AK4118_REG_QSUB_SEC 0x1a +#define AK4118_REG_QSUB_FRAME 0x1b +#define AK4118_REG_QSUB_ZERO 0x1c +#define AK4118_REG_QSUB_ABS_MIN 0x1d +#define AK4118_REG_QSUB_ABS_SEC 0x1e +#define AK4118_REG_QSUB_ABS_FRAME 0x1f +#define AK4118_REG_GPE 0x20 +#define AK4118_REG_GPDR 0x21 +#define AK4118_REG_GPSCR 0x22 +#define AK4118_REG_GPLR 0x23 +#define AK4118_REG_DAT_MASK_DTS 0x24 +#define AK4118_REG_RX_DETECT 0x25 +#define AK4118_REG_STC_DAT_DETECT 0x26 +#define AK4118_REG_RXCHAN_STATUS5 0x27 +#define AK4118_REG_TXCHAN_STATUS5 0x28 +#define AK4118_REG_MAX 0x29 + +#define AK4118_REG_FORMAT_CTL_DIF0 (1 << 4) +#define AK4118_REG_FORMAT_CTL_DIF1 (1 << 5) +#define AK4118_REG_FORMAT_CTL_DIF2 (1 << 6) + +struct ak4118_priv { + struct regmap *regmap; + struct gpio_desc *reset; + struct gpio_desc *irq; + struct snd_soc_component *component; +}; + +static const struct reg_default ak4118_reg_defaults[] = { + {AK4118_REG_CLK_PWR_CTL, 0x43}, + {AK4118_REG_FORMAT_CTL, 0x6a}, + {AK4118_REG_IO_CTL0, 0x88}, + {AK4118_REG_IO_CTL1, 0x48}, + {AK4118_REG_INT0_MASK, 0xee}, + {AK4118_REG_INT1_MASK, 0xb5}, + {AK4118_REG_RCV_STATUS0, 0x00}, + {AK4118_REG_RCV_STATUS1, 0x10}, + {AK4118_REG_TXCHAN_STATUS0, 0x00}, + {AK4118_REG_TXCHAN_STATUS1, 0x00}, + {AK4118_REG_TXCHAN_STATUS2, 0x00}, + {AK4118_REG_TXCHAN_STATUS3, 0x00}, + {AK4118_REG_TXCHAN_STATUS4, 0x00}, + {AK4118_REG_GPE, 0x77}, + {AK4118_REG_GPDR, 0x00}, + {AK4118_REG_GPSCR, 0x00}, + {AK4118_REG_GPLR, 0x00}, + {AK4118_REG_DAT_MASK_DTS, 0x3f}, + {AK4118_REG_RX_DETECT, 0x00}, + {AK4118_REG_STC_DAT_DETECT, 0x00}, + {AK4118_REG_TXCHAN_STATUS5, 0x00}, +}; + +static const char * const ak4118_input_select_txt[] = { + "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7", +}; +static SOC_ENUM_SINGLE_DECL(ak4118_insel_enum, AK4118_REG_IO_CTL1, 0x0, + ak4118_input_select_txt); + +static const struct snd_kcontrol_new ak4118_input_mux_controls = + SOC_DAPM_ENUM("Input Select", ak4118_insel_enum); + +static const char * const ak4118_iec958_fs_txt[] = { + "44100", "48000", "32000", "22050", "11025", "24000", "16000", "88200", + "8000", "96000", "64000", "176400", "192000", +}; + +static const int ak4118_iec958_fs_val[] = { + 0x0, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xE, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(ak4118_iec958_fs_enum, AK4118_REG_RCV_STATUS1, + 0x4, 0x4, ak4118_iec958_fs_txt, + ak4118_iec958_fs_val); + +static struct snd_kcontrol_new ak4118_iec958_controls[] = { + SOC_SINGLE("IEC958 Parity Errors", AK4118_REG_RCV_STATUS0, 0, 1, 0), + SOC_SINGLE("IEC958 No Audio", AK4118_REG_RCV_STATUS0, 1, 1, 0), + SOC_SINGLE("IEC958 PLL Lock", AK4118_REG_RCV_STATUS0, 4, 1, 1), + SOC_SINGLE("IEC958 Non PCM", AK4118_REG_RCV_STATUS0, 6, 1, 0), + SOC_ENUM("IEC958 Sampling Freq", ak4118_iec958_fs_enum), +}; + +static const struct snd_soc_dapm_widget ak4118_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INRX0"), + SND_SOC_DAPM_INPUT("INRX1"), + SND_SOC_DAPM_INPUT("INRX2"), + SND_SOC_DAPM_INPUT("INRX3"), + SND_SOC_DAPM_INPUT("INRX4"), + SND_SOC_DAPM_INPUT("INRX5"), + SND_SOC_DAPM_INPUT("INRX6"), + SND_SOC_DAPM_INPUT("INRX7"), + SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, + &ak4118_input_mux_controls), +}; + +static const struct snd_soc_dapm_route ak4118_dapm_routes[] = { + {"Input Mux", "RX0", "INRX0"}, + {"Input Mux", "RX1", "INRX1"}, + {"Input Mux", "RX2", "INRX2"}, + {"Input Mux", "RX3", "INRX3"}, + {"Input Mux", "RX4", "INRX4"}, + {"Input Mux", "RX5", "INRX5"}, + {"Input Mux", "RX6", "INRX6"}, + {"Input Mux", "RX7", "INRX7"}, +}; + + +static int ak4118_set_dai_fmt_master(struct ak4118_priv *ak4118, + unsigned int format) +{ + int dif; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF2; + break; + case SND_SOC_DAIFMT_RIGHT_J: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1; + break; + case SND_SOC_DAIFMT_LEFT_J: + dif = AK4118_REG_FORMAT_CTL_DIF2; + break; + default: + return -ENOTSUPP; + } + + return dif; +} + +static int ak4118_set_dai_fmt_slave(struct ak4118_priv *ak4118, + unsigned int format) +{ + int dif; + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + dif = AK4118_REG_FORMAT_CTL_DIF0 | AK4118_REG_FORMAT_CTL_DIF1 | + AK4118_REG_FORMAT_CTL_DIF2; + break; + case SND_SOC_DAIFMT_LEFT_J: + dif = AK4118_REG_FORMAT_CTL_DIF1 | AK4118_REG_FORMAT_CTL_DIF2; + break; + default: + return -ENOTSUPP; + } + + return dif; +} + +static int ak4118_set_dai_fmt(struct snd_soc_dai *dai, + unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + int dif; + int ret = 0; + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + /* component is master */ + dif = ak4118_set_dai_fmt_master(ak4118, format); + break; + case SND_SOC_DAIFMT_CBS_CFS: + /*component is slave */ + dif = ak4118_set_dai_fmt_slave(ak4118, format); + break; + default: + ret = -ENOTSUPP; + goto exit; + } + + /* format not supported */ + if (dif < 0) { + ret = dif; + goto exit; + } + + ret = regmap_update_bits(ak4118->regmap, AK4118_REG_FORMAT_CTL, + AK4118_REG_FORMAT_CTL_DIF0 | + AK4118_REG_FORMAT_CTL_DIF1 | + AK4118_REG_FORMAT_CTL_DIF2, dif); + if (ret < 0) + goto exit; + +exit: + return ret; +} + +static int ak4118_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + return 0; +} + +static const struct snd_soc_dai_ops ak4118_dai_ops = { + .hw_params = ak4118_hw_params, + .set_fmt = ak4118_set_dai_fmt, +}; + +static struct snd_soc_dai_driver ak4118_dai = { + .name = "ak4118-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_3LE | + SNDRV_PCM_FMTBIT_S24_LE + }, + .ops = &ak4118_dai_ops, +}; + +static irqreturn_t ak4118_irq_handler(int irq, void *data) +{ + struct ak4118_priv *ak4118 = data; + struct snd_soc_component *component = ak4118->component; + struct snd_kcontrol_new *kctl_new; + struct snd_kcontrol *kctl; + struct snd_ctl_elem_id *id; + unsigned int i; + + if (!component) + return IRQ_NONE; + + for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) { + kctl_new = &ak4118_iec958_controls[i]; + kctl = snd_soc_card_get_kcontrol(component->card, + kctl_new->name); + if (!kctl) + continue; + id = &kctl->id; + snd_ctl_notify(component->card->snd_card, + SNDRV_CTL_EVENT_MASK_VALUE, id); + } + + return IRQ_HANDLED; +} + +static int ak4118_probe(struct snd_soc_component *component) +{ + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + int ret = 0; + + ak4118->component = component; + + /* release reset */ + gpiod_set_value(ak4118->reset, 0); + + /* unmask all int1 sources */ + ret = regmap_write(ak4118->regmap, AK4118_REG_INT1_MASK, 0x00); + if (ret < 0) { + dev_err(component->dev, + "failed to write regmap 0x%x 0x%x: %d\n", + AK4118_REG_INT1_MASK, 0x00, ret); + return ret; + } + + /* rx detect enable on all channels */ + ret = regmap_write(ak4118->regmap, AK4118_REG_RX_DETECT, 0xff); + if (ret < 0) { + dev_err(component->dev, + "failed to write regmap 0x%x 0x%x: %d\n", + AK4118_REG_RX_DETECT, 0xff, ret); + return ret; + } + + ret = snd_soc_add_component_controls(component, ak4118_iec958_controls, + ARRAY_SIZE(ak4118_iec958_controls)); + if (ret) { + dev_err(component->dev, + "failed to add component kcontrols: %d\n", ret); + return ret; + } + + return 0; +} + +static void ak4118_remove(struct snd_soc_component *component) +{ + struct ak4118_priv *ak4118 = snd_soc_component_get_drvdata(component); + + /* hold reset */ + gpiod_set_value(ak4118->reset, 1); +} + +static const struct snd_soc_component_driver soc_component_drv_ak4118 = { + .probe = ak4118_probe, + .remove = ak4118_remove, + .dapm_widgets = ak4118_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4118_dapm_widgets), + .dapm_routes = ak4118_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ak4118_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config ak4118_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .reg_defaults = ak4118_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ak4118_reg_defaults), + + .cache_type = REGCACHE_NONE, + .max_register = AK4118_REG_MAX - 1, +}; + +static int ak4118_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct ak4118_priv *ak4118; + int ret; + + ak4118 = devm_kzalloc(&i2c->dev, sizeof(struct ak4118_priv), + GFP_KERNEL); + if (ak4118 == NULL) + return -ENOMEM; + + ak4118->regmap = devm_regmap_init_i2c(i2c, &ak4118_regmap); + if (IS_ERR(ak4118->regmap)) + return PTR_ERR(ak4118->regmap); + + i2c_set_clientdata(i2c, ak4118); + + ak4118->reset = devm_gpiod_get(&i2c->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ak4118->reset)) { + ret = PTR_ERR(ak4118->reset); + if (ret != -EPROBE_DEFER) + dev_err(&i2c->dev, "Failed to get reset: %d\n", ret); + return ret; + } + + ak4118->irq = devm_gpiod_get(&i2c->dev, "irq", GPIOD_IN); + if (IS_ERR(ak4118->irq)) { + ret = PTR_ERR(ak4118->irq); + if (ret != -EPROBE_DEFER) + dev_err(&i2c->dev, "Failed to get IRQ: %d\n", ret); + return ret; + } + + ret = devm_request_threaded_irq(&i2c->dev, gpiod_to_irq(ak4118->irq), + NULL, ak4118_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + "ak4118-irq", ak4118); + if (ret < 0) { + dev_err(&i2c->dev, "Fail to request_irq: %d\n", ret); + return ret; + } + + return snd_soc_register_component(&i2c->dev, &soc_component_drv_ak4118, + &ak4118_dai, 1); +} + +static int ak4118_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + return 0; +} + +static const struct of_device_id ak4118_of_match[] = { + { .compatible = "asahi-kasei,ak4118", }, + {} +}; +MODULE_DEVICE_TABLE(of, ak4118_of_match); + +static const struct i2c_device_id ak4118_id_table[] = { + { "ak4118", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ak4118_id_table); + +static struct i2c_driver ak4118_i2c_driver = { + .driver = { + .name = "ak4118", + .of_match_table = of_match_ptr(ak4118_of_match), + }, + .id_table = ak4118_id_table, + .probe = ak4118_i2c_probe, + .remove = ak4118_i2c_remove, +}; + +module_i2c_driver(ak4118_i2c_driver); + +MODULE_DESCRIPTION("Asahi Kasei AK4118 ALSA SoC driver"); +MODULE_AUTHOR("Adrien Charruel "); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From e81a2a6d12e85fbd7b19e96ad72b82a3cc8a6b2d Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Thu, 15 Nov 2018 12:13:34 +0800 Subject: ASoC: rt5663: Add regulator support Add regulator support to turn on cpvdd and avdd in probe. If a regulator is not given from device tree, a dummy regulator will be used. Signed-off-by: Cheng-Yi Chiang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5663.c | 68 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 61 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 7eb2cbd39d6e..29c059ed0682 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,9 @@ #define RT5663_DEVICE_ID_2 0x6451 #define RT5663_DEVICE_ID_1 0x6406 +#define RT5663_POWER_ON_DELAY_MS 300 +#define RT5663_SUPPLY_CURRENT_UA 500000 + enum { CODEC_VER_1, CODEC_VER_0, @@ -48,6 +52,11 @@ struct impedance_mapping_table { unsigned int dc_offset_r_manual_mic; }; +static const char *const rt5663_supply_names[] = { + "avdd", + "cpvdd", +}; + struct rt5663_priv { struct snd_soc_component *component; struct rt5663_platform_data pdata; @@ -56,6 +65,7 @@ struct rt5663_priv { struct snd_soc_jack *hs_jack; struct timer_list btn_check_timer; struct impedance_mapping_table *imp_table; + struct regulator_bulk_data supplies[ARRAY_SIZE(rt5663_supply_names)]; int codec_ver; int sysclk; @@ -3483,7 +3493,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, { struct rt5663_platform_data *pdata = dev_get_platdata(&i2c->dev); struct rt5663_priv *rt5663; - int ret; + int ret, i; unsigned int val; struct regmap *regmap; @@ -3500,6 +3510,37 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, else rt5663_parse_dp(rt5663, &i2c->dev); + for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) + rt5663->supplies[i].supply = rt5663_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, + ARRAY_SIZE(rt5663->supplies), + rt5663->supplies); + if (ret) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + + /* Set load for regulator. */ + for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) { + ret = regulator_set_load(rt5663->supplies[i].consumer, + RT5663_SUPPLY_CURRENT_UA); + if (ret) { + dev_err(&i2c->dev, + "Failed to set regulator %s, ret: %d\n", + rt5663->supplies[i].supply, ret); + } + } + + ret = regulator_bulk_enable(ARRAY_SIZE(rt5663->supplies), + rt5663->supplies); + + if (ret) { + dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } + msleep(RT5663_POWER_ON_DELAY_MS); + regmap = devm_regmap_init_i2c(i2c, &temp_regmap); if (IS_ERR(regmap)) { ret = PTR_ERR(regmap); @@ -3530,7 +3571,8 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Device with ID register %#x is not rt5663\n", val); - return -ENODEV; + ret = -ENODEV; + goto err_enable; } if (IS_ERR(rt5663->regmap)) { @@ -3635,20 +3677,30 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, ret = request_irq(i2c->irq, rt5663_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "rt5663", rt5663); - if (ret) + if (ret) { dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n", __func__, ret); + goto err_enable; + } } ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5663, rt5663_dai, ARRAY_SIZE(rt5663_dai)); - if (ret) { - if (i2c->irq) - free_irq(i2c->irq, rt5663); - } + if (ret) + goto err_irq; + return 0; + +err_irq: + if (i2c->irq) + free_irq(i2c->irq, rt5663); + +err_enable: + dev_err(&i2c->dev, + "%s: Disable regulator after probe error\n", __func__); + regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); return ret; } @@ -3659,6 +3711,8 @@ static int rt5663_i2c_remove(struct i2c_client *i2c) if (i2c->irq) free_irq(i2c->irq, rt5663); + regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); + return 0; } -- cgit v1.2.3-58-ga151 From dc43d3aa045c6588f163f4c0f5084e09cb5c0295 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 16 Nov 2018 15:43:49 -0600 Subject: ASoC: Convert a few more users to using %pOFn instead of device_node.name In preparation to remove the node name pointer from struct device_node, convert printf users to use the %pOFn format specifier. Cc: Liam Girdwood Cc: Mark Brown Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: Olivier Moysan Cc: Arnaud Pouliquen Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: alsa-devel@alsa-project.org Cc: linux-stm32@st-md-mailman.stormreply.com Cc: linux-arm-kernel@lists.infradead.org Signed-off-by: Rob Herring Signed-off-by: Mark Brown --- sound/soc/mediatek/mt6797/mt6797-afe-pcm.c | 2 +- sound/soc/mediatek/mt8173/mt8173-afe-pcm.c | 2 +- sound/soc/stm/stm32_sai.c | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c index 192f4d7b37b6..bff7d71d0742 100644 --- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c +++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c @@ -828,7 +828,7 @@ static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev) /* request irq */ irq_id = platform_get_irq(pdev, 0); if (!irq_id) { - dev_err(dev, "%s no irq found\n", dev->of_node->name); + dev_err(dev, "%pOFn no irq found\n", dev->of_node); return -ENXIO; } ret = devm_request_irq(dev, irq_id, mt6797_afe_irq_handler, diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c index c0b6697503fd..166aed28330d 100644 --- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c +++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c @@ -1092,7 +1092,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev) irq_id = platform_get_irq(pdev, 0); if (irq_id <= 0) { - dev_err(afe->dev, "np %s no irq\n", afe->dev->of_node->name); + dev_err(afe->dev, "np %pOFn no irq\n", afe->dev->of_node); return irq_id < 0 ? irq_id : -ENXIO; } ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler, diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c index d597eba61992..bcb35cae2a2c 100644 --- a/sound/soc/stm/stm32_sai.c +++ b/sound/soc/stm/stm32_sai.c @@ -74,14 +74,14 @@ static int stm32_sai_sync_conf_provider(struct stm32_sai_data *sai, int synco) return ret; } - dev_dbg(&sai->pdev->dev, "Set %s%s as synchro provider\n", - sai->pdev->dev.of_node->name, + dev_dbg(&sai->pdev->dev, "Set %pOFn%s as synchro provider\n", + sai->pdev->dev.of_node, synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); prev_synco = FIELD_GET(SAI_GCR_SYNCOUT_MASK, readl_relaxed(sai->base)); if (prev_synco != STM_SAI_SYNC_OUT_NONE && synco != prev_synco) { - dev_err(&sai->pdev->dev, "%s%s already set as sync provider\n", - sai->pdev->dev.of_node->name, + dev_err(&sai->pdev->dev, "%pOFn%s already set as sync provider\n", + sai->pdev->dev.of_node, prev_synco == STM_SAI_SYNC_OUT_A ? "A" : "B"); clk_disable_unprepare(sai->pclk); return -EINVAL; -- cgit v1.2.3-58-ga151 From 7806869c6e5ea3c48aee80a72c790c55e6c3c303 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Nov 2018 15:06:33 +0000 Subject: ASoC: tlv320aic31xx: asihpi: clean up indentation, remove extraneous tab The return statement is indented too much by one level, fix this by removing an extraneous tab. Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic31xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 608ad49ad978..c6048d95c6d3 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1095,7 +1095,7 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai, if (freq/i > 20000000) { dev_err(aic31xx->dev, "%s: Too high mclk frequency %u\n", __func__, freq); - return -EINVAL; + return -EINVAL; } aic31xx->p_div = i; -- cgit v1.2.3-58-ga151 From 6857b9d0881e4cbf30237a1091e83a8e3e7ee7c3 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Nov 2018 15:06:34 +0000 Subject: ASoC: tlv320dac33: clean up indentation, remove extraneous tab The goto statement is indented too much by one level, fix this by removing the extraneous tab. Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320dac33.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index a957eaeb7bc1..32907b1e20cf 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -394,7 +394,7 @@ static int dac33_hard_power(struct snd_soc_component *component, int power) if (ret != 0) { dev_err(component->dev, "Failed to enable supplies: %d\n", ret); - goto exit; + goto exit; } if (dac33->power_gpio >= 0) -- cgit v1.2.3-58-ga151 From 812fb75d977efb0257fe41700e6f8e04900ab27c Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Nov 2018 15:06:35 +0000 Subject: ASoC: arizona: fix indentation issue with return statement The return statement is indented incorrectly. Fix this by adding in the missing tab. Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/codecs/wm8998.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c index 61294c787f27..409bed30a4e4 100644 --- a/sound/soc/codecs/wm8998.c +++ b/sound/soc/codecs/wm8998.c @@ -60,7 +60,7 @@ static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w, dev_warn(component->dev, "Unsupported ASRC rate1 (%s)\n", arizona_sample_rate_val_to_name(val)); - return -EINVAL; + return -EINVAL; } break; default: -- cgit v1.2.3-58-ga151 From e8d4bf8ae8dbcf635a87883437539fb3e5deb6d4 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Nov 2018 15:06:36 +0000 Subject: ASoC: qcom: clean up indentation, remove extraneous tab The return statement is indented too much by one level, fix this by removing the extraneous tab. Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index d07271ea4c45..028bce671cbc 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -91,7 +91,7 @@ static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); - return ret; + return ret; } data->dma_ch = dma_ch; -- cgit v1.2.3-58-ga151 From 00347e4ea8ca4c6ed5e254d7cebad0917175a07e Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 16 Nov 2018 13:39:43 +0000 Subject: ASoC: amd: fix spelling mistake "Inavlid" -> "Invalid" There is a spelling mistake in a dev_err message. Fix this. Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- sound/soc/amd/raven/pci-acp3x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index ef805e71a98f..c28457fd9b81 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -105,7 +105,7 @@ static int snd_acp3x_probe(struct pci_dev *pci, } break; default: - dev_err(&pci->dev, "Inavlid ACP audio mode : %d\n", val); + dev_err(&pci->dev, "Invalid ACP audio mode : %d\n", val); ret = -ENODEV; goto unmap_mmio; } -- cgit v1.2.3-58-ga151 From 746dca0aebd4d77adccb76c500a60028a900dabb Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Fri, 16 Nov 2018 17:28:56 +0800 Subject: ASoC: rt5663: Fix error handling of regulator_set_load The default implementation of regulator_set_load returns REGULATOR_MODE_NORMAL, which is positive. [This was a bug which is being fixed but the change is valid anyway -- bronie] rt5663_i2c_probe should only do error handling when return value of regulator_set_load is negative. In this case, rt5663_i2c_probe should return error. Also, consolidate err_irq into err_enable. Fix the missing goto for temporary regmap and rt5663->regmap. Signed-off-by: Cheng-Yi Chiang Signed-off-by: Mark Brown --- sound/soc/codecs/rt5663.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index 29c059ed0682..da6647015708 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3525,10 +3525,11 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, for (i = 0; i < ARRAY_SIZE(rt5663->supplies); i++) { ret = regulator_set_load(rt5663->supplies[i].consumer, RT5663_SUPPLY_CURRENT_UA); - if (ret) { + if (ret < 0) { dev_err(&i2c->dev, - "Failed to set regulator %s, ret: %d\n", + "Failed to set regulator load on %s, ret: %d\n", rt5663->supplies[i].supply, ret); + return ret; } } @@ -3546,7 +3547,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(regmap); dev_err(&i2c->dev, "Failed to allocate temp register map: %d\n", ret); - return ret; + goto err_enable; } ret = regmap_read(regmap, RT5663_VENDOR_ID_2, &val); @@ -3579,7 +3580,7 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, ret = PTR_ERR(rt5663->regmap); dev_err(&i2c->dev, "Failed to allocate register map: %d\n", ret); - return ret; + goto err_enable; } /* reset and calibrate */ @@ -3689,17 +3690,19 @@ static int rt5663_i2c_probe(struct i2c_client *i2c, rt5663_dai, ARRAY_SIZE(rt5663_dai)); if (ret) - goto err_irq; + goto err_enable; return 0; -err_irq: + + /* + * Error after enabling regulators should goto err_enable + * to disable regulators. + */ +err_enable: if (i2c->irq) free_irq(i2c->irq, rt5663); -err_enable: - dev_err(&i2c->dev, - "%s: Disable regulator after probe error\n", __func__); regulator_bulk_disable(ARRAY_SIZE(rt5663->supplies), rt5663->supplies); return ret; } -- cgit v1.2.3-58-ga151 From 4647598cde0e55d5c6d6a3c4223953611a472289 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 20 Nov 2018 14:42:53 +0200 Subject: ASoC: tlv320aic3x: Add support for CBM_CFS and CBS_CFM clocking modes The codec can support any variation of bclk/fs master/slave configuration. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic3x.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index 6a271e6e6b8f..6aa0edf8c5ef 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1260,6 +1260,16 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai, aic3x->master = 0; iface_areg &= ~(BIT_CLK_MASTER | WORD_CLK_MASTER); break; + case SND_SOC_DAIFMT_CBM_CFS: + aic3x->master = 1; + iface_areg |= BIT_CLK_MASTER; + iface_areg &= ~WORD_CLK_MASTER; + break; + case SND_SOC_DAIFMT_CBS_CFM: + aic3x->master = 1; + iface_areg |= WORD_CLK_MASTER; + iface_areg &= ~BIT_CLK_MASTER; + break; default: return -EINVAL; } -- cgit v1.2.3-58-ga151 From b92826fa8c5a423edf6c9e385b5d433c61375cc8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 16 Nov 2018 18:47:05 -0600 Subject: ASoC: Intel: fix interface for Chromebook machine drivers The changes for HDaudio overlooked the fact that the machine drivers used for Chromebooks rely on the dmic number information passed as pdata. Add dmic_num field to standard interface and use standard interface instead of SKL-specific one. Also clean-up pdata definition to remove fields that are no longer used. Fixes: 842bb5135f10 ('ASoC: Intel: use standard interface for Hdaudio machine driver') Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- include/sound/soc-acpi.h | 1 + sound/soc/intel/boards/kbl_rt5663_max98927.c | 10 +++++----- sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 10 +++++----- sound/soc/intel/boards/skl_nau88l25_max98357a.c | 10 +++++----- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 10 +++++----- sound/soc/intel/skylake/skl.c | 2 +- sound/soc/intel/skylake/skl.h | 3 --- 7 files changed, 22 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h index 5154c6359609..266e64e3c24c 100644 --- a/include/sound/soc-acpi.h +++ b/include/sound/soc-acpi.h @@ -48,6 +48,7 @@ struct snd_soc_acpi_mach_params { u32 acpi_ipc_irq_index; const char *platform; u32 codec_mask; + u32 dmic_num; }; /** diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index 6ea969c0a5fb..c2a96940f9f2 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -25,9 +25,9 @@ #include #include #include +#include #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #include #include #include @@ -969,7 +969,7 @@ static struct snd_soc_card kabylake_audio_card_rt5663 = { static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_rt5663_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; int ret; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -984,9 +984,9 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card->dev = &pdev->dev; snd_soc_card_set_drvdata(kabylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = dev_get_drvdata(&pdev->dev); + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; ctx->mclk = devm_clk_get(&pdev->dev, "ssp1_mclk"); diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index a737c915d46a..d7df307b406f 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -26,10 +26,10 @@ #include #include #include +#include #include "../../codecs/rt5514.h" #include "../../codecs/rt5663.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #define KBL_REALTEK_CODEC_DAI "rt5663-aif" #define KBL_REALTEK_DMIC_CODEC_DAI "rt5514-aif1" @@ -648,7 +648,7 @@ static struct snd_soc_card kabylake_audio_card = { static int kabylake_audio_probe(struct platform_device *pdev) { struct kbl_codec_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -659,9 +659,9 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&kabylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = dev_get_drvdata(&pdev->dev); + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; return devm_snd_soc_register_card(&pdev->dev, &kabylake_audio_card); diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 552958ce736d..931890f9aa74 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -21,9 +21,9 @@ #include #include #include +#include #include "../../codecs/nau8825.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_MAXIM_CODEC_DAI "HiFi" @@ -641,7 +641,7 @@ static struct snd_soc_card skylake_audio_card = { static int skylake_audio_probe(struct platform_device *pdev) { struct skl_nau8825_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -652,9 +652,9 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = dev_get_drvdata(&pdev->dev); + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index f985b30a1d0e..657c650766fc 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -23,11 +23,11 @@ #include #include #include +#include #include #include #include "../../codecs/nau8825.h" #include "../../codecs/hdac_hdmi.h" -#include "../skylake/skl.h" #define SKL_NUVOTON_CODEC_DAI "nau8825-hifi" #define SKL_SSM_CODEC_DAI "ssm4567-hifi" @@ -694,7 +694,7 @@ static struct snd_soc_card skylake_audio_card = { static int skylake_audio_probe(struct platform_device *pdev) { struct skl_nau88125_private *ctx; - struct skl_machine_pdata *pdata; + struct snd_soc_acpi_mach *mach; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -705,9 +705,9 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - pdata = dev_get_drvdata(&pdev->dev); - if (pdata) - dmic_constraints = pdata->dmic_num == 2 ? + mach = dev_get_drvdata(&pdev->dev); + if (mach) + dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ebab04b51bc1..b66d66acbc35 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -515,7 +515,7 @@ static int skl_find_machine(struct skl *skl, void *driver_data) if (pdata) { skl->use_tplg_pcm = pdata->use_tplg_pcm; - pdata->dmic_num = skl_get_dmic_geo(skl); + mach->mach_params.dmic_num = skl_get_dmic_geo(skl); } return 0; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 8d48cd7c56c8..85f8bb6687dc 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -119,10 +119,7 @@ struct skl_dma_params { }; struct skl_machine_pdata { - u32 dmic_num; bool use_tplg_pcm; /* use dais and dai links from topology */ - const char *platform; - u32 codec_mask; }; struct skl_dsp_ops { -- cgit v1.2.3-58-ga151 From 5a619b9e8883a0f9691964d001b8480df44df5b9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 16 Nov 2018 18:47:06 -0600 Subject: ASoC: Intel: use platform_data for machine drivers For some reason we have different mechanisms for passing data to machine drivers. Use the solution used by Atom/SST and SOF instead of using drv_data as done by Skylake. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/kbl_rt5663_max98927.c | 2 +- sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 2 +- sound/soc/intel/boards/skl_hda_dsp_generic.c | 2 +- sound/soc/intel/boards/skl_nau88l25_max98357a.c | 2 +- sound/soc/intel/boards/skl_nau88l25_ssm4567.c | 2 +- sound/soc/intel/skylake/skl.c | 13 ++++++++++--- 6 files changed, 15 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c index c2a96940f9f2..d71475200b08 100644 --- a/sound/soc/intel/boards/kbl_rt5663_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -984,7 +984,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card->dev = &pdev->dev; snd_soc_card_set_drvdata(kabylake_audio_card, ctx); - mach = dev_get_drvdata(&pdev->dev); + mach = (&pdev->dev)->platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c index d7df307b406f..7044d8c2b187 100644 --- a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c +++ b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -659,7 +659,7 @@ static int kabylake_audio_probe(struct platform_device *pdev) kabylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&kabylake_audio_card, ctx); - mach = dev_get_drvdata(&pdev->dev); + mach = (&pdev->dev)->platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c index 15c502d6774d..b9a21e64ead2 100644 --- a/sound/soc/intel/boards/skl_hda_dsp_generic.c +++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -146,7 +146,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev) INIT_LIST_HEAD(&ctx->hdmi_pcm_list); - mach = dev_get_drvdata(&pdev->dev); + mach = (&pdev->dev)->platform_data; if (!mach) return -EINVAL; diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c index 931890f9aa74..0922106bd323 100644 --- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c +++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c @@ -652,7 +652,7 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - mach = dev_get_drvdata(&pdev->dev); + mach = (&pdev->dev)->platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c index 657c650766fc..8433c521d39f 100644 --- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c +++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c @@ -705,7 +705,7 @@ static int skylake_audio_probe(struct platform_device *pdev) skylake_audio_card.dev = &pdev->dev; snd_soc_card_set_drvdata(&skylake_audio_card, ctx); - mach = dev_get_drvdata(&pdev->dev); + mach = (&pdev->dev)->platform_data; if (mach) dmic_constraints = mach->mach_params.dmic_num == 2 ? &constraints_dmic_2ch : &constraints_dmic_channels; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index b66d66acbc35..f81e7996aa0c 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -534,6 +534,16 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } + mach->mach_params.platform = dev_name(bus->dev); + mach->mach_params.codec_mask = bus->codec_mask; + + ret = platform_device_add_data(pdev, (const void *)mach, sizeof(*mach)); + if (ret) { + dev_err(bus->dev, "failed to add machine device platform data\n"); + platform_device_put(pdev); + return ret; + } + ret = platform_device_add(pdev); if (ret) { dev_err(bus->dev, "failed to add machine device\n"); @@ -541,9 +551,6 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } - mach->mach_params.platform = dev_name(bus->dev); - mach->mach_params.codec_mask = bus->codec_mask; - dev_set_drvdata(&pdev->dev, mach); skl->i2s_dev = pdev; -- cgit v1.2.3-58-ga151 From a1849b7dd30a07654b53f3599cc8f028afeb5d9f Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 16 Nov 2018 13:11:57 +0530 Subject: ASoC: sdm845: Update slot_width for Quaternary TDM port Change slot_width for quaternary TDM port to 16 and update bclk rate for TDM and MI2S interfaces accordingly. Signed-off-by: Rohit kumar Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 8d0cdff64377..84e6ee7d78d3 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -13,7 +13,8 @@ #define DEFAULT_SAMPLE_RATE_48K 48000 #define DEFAULT_MCLK_RATE 24576000 -#define DEFAULT_BCLK_RATE 12288000 +#define TDM_BCLK_RATE 6144000 +#define MI2S_BCLK_RATE 1536000 struct sdm845_snd_data { struct snd_soc_card *card; @@ -33,7 +34,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - slot_width = 32; + slot_width = 16; break; default: dev_err(rtd->dev, "%s: invalid param format 0x%x\n", @@ -115,7 +116,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, - DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); } snd_soc_dai_set_fmt(cpu_dai, fmt); break; @@ -125,7 +126,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) if (++(data->quat_tdm_clk_count) == 1) { snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, - DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); + TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); } break; -- cgit v1.2.3-58-ga151 From 992e75b3c80952fc60946458c75e1bf609e86aa2 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 16 Nov 2018 13:11:58 +0530 Subject: ASoC: sdm845: Add support for Secondary MI2S interface Add support to configure bit clock for secondary MI2S TX interface. Signed-off-by: Rohit kumar Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 84e6ee7d78d3..58593db2ab15 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -19,6 +19,7 @@ struct sdm845_snd_data { struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; + uint32_t sec_mi2s_clk_count; uint32_t quat_tdm_clk_count; }; @@ -121,6 +122,15 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) snd_soc_dai_set_fmt(cpu_dai, fmt); break; + case SECONDARY_MI2S_TX: + if (++(data->sec_mi2s_clk_count) == 1) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + MI2S_BCLK_RATE, SNDRV_PCM_STREAM_CAPTURE); + } + snd_soc_dai_set_fmt(cpu_dai, fmt); + break; + case QUATERNARY_TDM_RX_0: case QUATERNARY_TDM_TX_0: if (++(data->quat_tdm_clk_count) == 1) { @@ -157,6 +167,14 @@ static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) }; break; + case SECONDARY_MI2S_TX: + if (--(data->sec_mi2s_clk_count) == 0) { + snd_soc_dai_set_sysclk(cpu_dai, + Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT, + 0, SNDRV_PCM_STREAM_CAPTURE); + } + break; + case QUATERNARY_TDM_RX_0: case QUATERNARY_TDM_TX_0: if (--(data->quat_tdm_clk_count) == 0) { -- cgit v1.2.3-58-ga151 From 331e875432ecfb651d9335a18149160c307429ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Nov 2018 01:58:30 +0000 Subject: ASoC: rsnd: tidyup rsnd_parse_connect_ssiu_compatible() rsnd_parse_connect_ssiu_compatible() is doing - using rsnd_ssiu_id(), but we use it via rsnd_mod_id() - we can break loop if rsnd_dai_connect() was called This patch fixup these. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssiu.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 650b14e99b05..04b1a4836bd3 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -382,9 +382,11 @@ static void rsnd_parse_connect_ssiu_compatible(struct rsnd_priv *priv, for_each_rsnd_ssiu(ssiu, priv, i) { mod = rsnd_mod_get(ssiu); - if ((rsnd_mod_id(ssi_mod) == rsnd_ssiu_id(mod)) && - (rsnd_mod_id_sub(mod) == 0)) + if ((rsnd_mod_id(ssi_mod) == rsnd_mod_id(mod)) && + (rsnd_mod_id_sub(mod) == 0)) { rsnd_dai_connect(mod, io, mod->type); + return; + } } } -- cgit v1.2.3-58-ga151 From 0ade2ccfb81244d0a271a5f9917f9fccc349b6cf Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Nov 2018 08:15:30 +0000 Subject: ASoC: rsnd: makes rsnd_ssi_is_dma_mode() static ssi.c only is using rsnd_ssi_is_dma_mode(). Let's move it as static function. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/rsnd.h | 1 - sound/soc/sh/rcar/ssi.c | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 64c3a3bbcbb9..3c57129af6d1 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -726,7 +726,6 @@ extern const char * const volume_ramp_rate[]; int rsnd_ssi_probe(struct rsnd_priv *priv); void rsnd_ssi_remove(struct rsnd_priv *priv); struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); int rsnd_ssi_use_busif(struct rsnd_dai_stream *io); u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io); diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 26fc4a5da0e8..1996aea21172 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -117,6 +117,8 @@ struct rsnd_ssi { (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod))) #define rsnd_ssi_can_output_clk(mod) (!__rsnd_ssi_is_pin_sharing(mod)) +static int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); + int rsnd_ssi_use_busif(struct rsnd_dai_stream *io) { struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); @@ -1003,12 +1005,11 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = { .get_status = rsnd_ssi_get_status, }; -int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) +static int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod) { return mod->ops == &rsnd_ssi_dma_ops; } - /* * ssi mod function */ -- cgit v1.2.3-58-ga151 From c0f4697c5dbca0c9bdd1264bcf2d7c5d15a14f67 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Nov 2018 02:08:59 +0000 Subject: ASoC: simple-card-utils: remove asoc_simple_card_clk_register() asoc_simple_card_clk_register() is used but only 1 user, and very pointless code. Let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index f34cc6cddfa2..996bfe42bc8d 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -151,12 +151,6 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); -static void asoc_simple_card_clk_register(struct asoc_simple_dai *dai, - struct clk *clk) -{ - dai->clk = clk; -} - int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai) { return clk_prepare_enable(dai->clk); @@ -200,7 +194,7 @@ int asoc_simple_card_parse_clk(struct device *dev, if (!IS_ERR(clk)) { simple_dai->sysclk = clk_get_rate(clk); - asoc_simple_card_clk_register(simple_dai, clk); + simple_dai->clk = clk; } else if (!of_property_read_u32(node, "system-clock-frequency", &val)) { simple_dai->sysclk = val; } else { -- cgit v1.2.3-58-ga151 From f31a17105560f5f074f4d26fd5469a221f50af18 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Nov 2018 02:09:16 +0000 Subject: ASoC: simple-card-utils: accept NULL parameter on asoc_simple_card_xxx() If simple-card-utils accept NULL pointer on asoc_simple_card_xxx(), each driver code will be more simple. Let's accept NULL pointer. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 996bfe42bc8d..f3183a4718b3 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -153,13 +153,17 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name); int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai) { - return clk_prepare_enable(dai->clk); + if (dai) + return clk_prepare_enable(dai->clk); + + return 0; } EXPORT_SYMBOL_GPL(asoc_simple_card_clk_enable); void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai) { - clk_disable_unprepare(dai->clk); + if (dai) + clk_disable_unprepare(dai->clk); } EXPORT_SYMBOL_GPL(asoc_simple_card_clk_disable); @@ -342,6 +346,9 @@ int asoc_simple_card_init_dai(struct snd_soc_dai *dai, { int ret; + if (!simple_dai) + return 0; + if (simple_dai->sysclk) { ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, simple_dai->clk_direction); -- cgit v1.2.3-58-ga151 From a48bf02b35d9325eedb56ab531038511695a2734 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Nov 2018 02:10:51 +0000 Subject: ASoC: simple-card-utils: tidyup asoc_simple_card_parse_convert() Current simple-card-utils has asoc_simple_card_parse_convert() to parse convert channel/rate for be_hw_params_fixup. But, it is parsing from top of node. If sound card had multi subnode, we need to parse it from each sub node. This patch tidyup asoc_simple_card_parse_convert() to allow parsing settings from each node. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 ++- sound/soc/generic/audio-graph-scu-card.c | 2 +- sound/soc/generic/simple-card-utils.c | 5 +++-- sound/soc/generic/simple-scu-card.c | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index fb0318f9b10f..d1f17172dce2 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -116,7 +116,8 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card); void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, struct snd_pcm_hw_params *params); -void asoc_simple_card_parse_convert(struct device *dev, char *prefix, +void asoc_simple_card_parse_convert(struct device *dev, + struct device_node *np, char *prefix, struct asoc_simple_card_data *data); int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index 2ba35bdd3b38..1f754933ea9f 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -204,7 +204,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, NULL, &priv->adata); + asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata); /* * it supports multi CPU, single CODEC only here diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index f3183a4718b3..a16373193b56 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -32,10 +32,11 @@ void asoc_simple_card_convert_fixup(struct asoc_simple_card_data *data, } EXPORT_SYMBOL_GPL(asoc_simple_card_convert_fixup); -void asoc_simple_card_parse_convert(struct device *dev, char *prefix, +void asoc_simple_card_parse_convert(struct device *dev, + struct device_node *np, + char *prefix, struct asoc_simple_card_data *data) { - struct device_node *np = dev->of_node; char prop[128]; if (!prefix) diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 85b46f0eae0f..2d3ef6ebe863 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -202,7 +202,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, PREFIX, &priv->adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, &priv->adata); /* find 1st codec */ np = of_get_child_by_name(node, PREFIX "codec"); -- cgit v1.2.3-58-ga151 From 33404f3f10beaf0d69d8765f5804b6fb3b305f0f Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 21 Nov 2018 02:11:13 +0000 Subject: ASoC: simple_card_utils: remove "option" from asoc_simple_card_of_parse_routing() asoc_simple_card_of_parse_routing() had "option" parameter to consider error handling, but it is very pointless parameter. Let's remove it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/simple_card_utils.h | 3 +-- sound/soc/generic/audio-graph-card.c | 2 +- sound/soc/generic/audio-graph-scu-card.c | 2 +- sound/soc/generic/simple-card-utils.c | 10 +++------- sound/soc/generic/simple-card.c | 2 +- sound/soc/generic/simple-scu-card.c | 2 +- 6 files changed, 8 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index d1f17172dce2..6d69ed2bd7b1 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -121,8 +121,7 @@ void asoc_simple_card_parse_convert(struct device *dev, struct asoc_simple_card_data *data); int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, - char *prefix, - int optional); + char *prefix); int asoc_simple_card_of_parse_widgets(struct snd_soc_card *card, char *prefix); diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 26eb6ff9b9f3..101c7da2df7c 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -238,7 +238,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; - ret = asoc_simple_card_of_parse_routing(card, NULL, 1); + ret = asoc_simple_card_of_parse_routing(card, NULL); if (ret < 0) return ret; diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index 1f754933ea9f..c46947393f80 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -200,7 +200,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) * see simple-card */ - ret = asoc_simple_card_of_parse_routing(card, NULL, 0); + ret = asoc_simple_card_of_parse_routing(card, NULL); if (ret < 0) return ret; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index a16373193b56..c69ce1e563cd 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -417,8 +417,7 @@ int asoc_simple_card_clean_reference(struct snd_soc_card *card) EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference); int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, - char *prefix, - int optional) + char *prefix) { struct device_node *node = card->dev->of_node; char prop[128]; @@ -428,11 +427,8 @@ int asoc_simple_card_of_parse_routing(struct snd_soc_card *card, snprintf(prop, sizeof(prop), "%s%s", prefix, "routing"); - if (!of_property_read_bool(node, prop)) { - if (optional) - return 0; - return -EINVAL; - } + if (!of_property_read_bool(node, prop)) + return 0; return snd_soc_of_parse_audio_routing(card, prop); } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 5a3f59aa4ba5..ff3f9fa9ab5a 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -300,7 +300,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) if (ret < 0) goto card_parse_end; - ret = asoc_simple_card_of_parse_routing(card, PREFIX, 1); + ret = asoc_simple_card_of_parse_routing(card, PREFIX); if (ret < 0) goto card_parse_end; diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 2d3ef6ebe863..e650143eb532 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -198,7 +198,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) if (ret < 0) return ret; - ret = asoc_simple_card_of_parse_routing(card, PREFIX, 0); + ret = asoc_simple_card_of_parse_routing(card, PREFIX); if (ret < 0) return ret; -- cgit v1.2.3-58-ga151 From ca0412a05756cd0b94751ca4d54fe6522f00b1f4 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 22 Nov 2018 18:23:19 +0800 Subject: ASoC: sunxi: sun50i-codec-analog: Add support for cpvdd regulator supply On the Allwinner A64 SoCs, the audio codec has a built-in headphone amplifier. This amplifier has a power supply separate from the rest of the analog audio circuitry, labeled cpvdd. This patch adds a DAPM widget for this supply, and ties it to the headphone amp widget. Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 8f5f999df631..df1fed0aa001 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -274,6 +274,7 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { * stream widgets at the card level. */ + SND_SOC_DAPM_REGULATOR_SUPPLY("hpvcc", 0, 0), SND_SOC_DAPM_MUX("Headphone Source Playback Route", SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL, @@ -361,6 +362,7 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "Headphone Amp", NULL, "hpvcc" }, { "HP", NULL, "Headphone Amp" }, /* Microphone Routes */ -- cgit v1.2.3-58-ga151 From 6debd01a74b8c15ab3f2a6f7ff4a7dcbada101cb Mon Sep 17 00:00:00 2001 From: Vasily Khoruzhick Date: Thu, 22 Nov 2018 18:23:21 +0800 Subject: ASoC: simple-amplifier: add VCC regulator widget Amplifier may have assosicated regulator, so add a widget for it and appropriate route. Signed-off-by: Vasily Khoruzhick Signed-off-by: Chen-Yu Tsai Acked-by: Maxime Ripard Signed-off-by: Mark Brown --- sound/soc/codecs/simple-amplifier.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index 85524acf3e9c..c07e8a80b4b7 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -19,6 +19,7 @@ #include #include +#include #include #define DRV_NAME "simple-amplifier" @@ -58,11 +59,14 @@ static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = { (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), + SND_SOC_DAPM_REGULATOR_SUPPLY("VCC", 20, 0), }; static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = { { "DRV", NULL, "INL" }, { "DRV", NULL, "INR" }, + { "OUTL", NULL, "VCC" }, + { "OUTR", NULL, "VCC" }, { "OUTL", NULL, "DRV" }, { "OUTR", NULL, "DRV" }, }; -- cgit v1.2.3-58-ga151 From 3b7103562c03cffb1a351f8c235b3bba2acd9e9b Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:55:09 +0000 Subject: ASoC: soc-core: add snd_soc_of_parse_node_prefix() Current ASoC has snd_soc_of_parse_audio_prefix() to get codec_conf settings from DT which is used to avoid DAI naming conflict when CPU/Codec matching. Currently, it is parsing from "top node", but, we want to parse from "each sub node" if sound card had multi cpus/codecs. This patch adds new snd_soc_of_parse_node_prefix() to allow parsing settings from selected node. It is keeping existing snd_soc_of_parse_audio_prefix() by using macro. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- include/sound/soc.h | 6 +++++- sound/soc/soc-core.c | 11 +++++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/include/sound/soc.h b/include/sound/soc.h index 5a8b84140f01..93b7510e7f7d 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -1477,10 +1477,14 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, unsigned int *rx_mask, unsigned int *slots, unsigned int *slot_width); -void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, +void snd_soc_of_parse_node_prefix(struct device_node *np, struct snd_soc_codec_conf *codec_conf, struct device_node *of_node, const char *propname); +#define snd_soc_of_parse_audio_prefix(card, conf, node, name) \ + snd_soc_of_parse_node_prefix((card)->dev->of_node, \ + (conf), (node), (name)) + int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname); unsigned int snd_soc_of_parse_daifmt(struct device_node *np, diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 6ddcf12bc030..012000de4bf0 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -3484,12 +3484,11 @@ int snd_soc_of_parse_tdm_slot(struct device_node *np, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_tdm_slot); -void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, - struct snd_soc_codec_conf *codec_conf, - struct device_node *of_node, - const char *propname) +void snd_soc_of_parse_node_prefix(struct device_node *np, + struct snd_soc_codec_conf *codec_conf, + struct device_node *of_node, + const char *propname) { - struct device_node *np = card->dev->of_node; const char *str; int ret; @@ -3502,7 +3501,7 @@ void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card, codec_conf->of_node = of_node; codec_conf->name_prefix = str; } -EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_prefix); +EXPORT_SYMBOL_GPL(snd_soc_of_parse_node_prefix); int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, const char *propname) -- cgit v1.2.3-58-ga151 From ca8d95c709378d0792c9dbacdab176a75e521ffb Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:55:40 +0000 Subject: ASoC: simple-scu-card: tidyup "prefix" parsing simple-scu-card.c is supporting "prefix" which is used to avoid DAI naming conflict when CPU/Codec matching. But, sound card might have multi sub-devices, and each codec might need each prefix. Now, ASoC is supporting snd_soc_of_parse_node_prefix(), let's support it on audio-graph-scu-card, too. It is keeping existing DT style. It can't support each codec's prefix if sound card had multi sub-devices without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index e650143eb532..7ae1901b2f85 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -157,10 +157,17 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, if (ret < 0) return ret; + /* check "prefix" from top node */ snd_soc_of_parse_audio_prefix(card, &priv->codec_conf, dai_link->codecs->of_node, PREFIX "prefix"); + /* check "prefix" from each node if top doesn't have */ + if (!priv->codec_conf.of_node) + snd_soc_of_parse_node_prefix(np, + &priv->codec_conf, + dai_link->codecs->of_node, + "prefix"); } ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai); -- cgit v1.2.3-58-ga151 From 2624fc7882244abeb4dabbd5b67328676a109b6a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:56:21 +0000 Subject: ASoC: audio-graph-scu-card: tidyup "prefix" parsing audio-graph-scu-card.c is supporting "prefix" which is used to avoid DAI naming conflict when CPU/Codec matching. But, sound card might have multi sub-devices, and each codec might need each prefix. Now, ASoC is supporting snd_soc_of_parse_node_prefix(), let's support it on audio-graph-scu-card, too. It is keeping existing DT style. It can't support each codec's prefix if sound card had multi sub-devices without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index c46947393f80..fdc61f1c1d79 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -39,6 +39,8 @@ struct graph_card_data { #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) #define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) +#define PREFIX "audio-graph-card," + static int asoc_graph_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -98,6 +100,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *node = of_graph_get_port_parent(ep); int ret; if (is_fe) { @@ -154,10 +157,17 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, if (ret < 0) return ret; + /* check "prefix" from top node */ snd_soc_of_parse_audio_prefix(card, &priv->codec_conf, dai_link->codecs->of_node, "prefix"); + /* check "prefix" from each node if top doesn't have */ + if (!priv->codec_conf.of_node) + snd_soc_of_parse_node_prefix(node, + &priv->codec_conf, + dai_link->codecs->of_node, + PREFIX "prefix"); } ret = asoc_simple_card_of_parse_tdm(ep, &dai_props->dai); -- cgit v1.2.3-58-ga151 From 7f07a39acf0591fd3823abc1af3c5456e708b77e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:56:53 +0000 Subject: ASoC: audio-graph-scu-card: tidyup "convert-rate/channels" parsing audio-graph-scu-card.c is supporting "convert-rate/channels" which is used for DPCM. But, sound card might have multi codecs, and each codec might need each convert-rate/channels. This patch supports each codec's convert-rate/channles support. top node convert-rate/channels will overwrite settings if exist. It can't support each codec's convert-rate/channels if sound card had multi codecs without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index fdc61f1c1d79..0f13f11e0d88 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -29,6 +29,7 @@ struct graph_card_data { struct asoc_simple_dai dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; } *dai_props; struct snd_soc_dai_link *dai_link; struct asoc_simple_card_data adata; @@ -85,7 +86,11 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + /* overwrite by top level adata if exist */ asoc_simple_card_convert_fixup(&priv->adata, params); return 0; @@ -170,6 +175,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, PREFIX "prefix"); } + asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); + ret = asoc_simple_card_of_parse_tdm(ep, &dai_props->dai); if (ret) return ret; -- cgit v1.2.3-58-ga151 From 338d7159c488268dd4ccc6a69ab4af8f829f75d0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:57:09 +0000 Subject: ASoC: audio-graph-scu-card: tidyup asoc_simple_card_parse_daifmt() timing Current audio-graph-scu-card driver is parsing codec position for DPCM and consider DAI format. But, current operation is doing totally pointless, because 1) asoc_simple_card_parse_daifmt() will be called not only for 1st codec on current implementation, and it will be used as fixed format 2) it should be called for each CPU/Codec pair. Let's tidyup asoc_simple_card_parse_daifmt() timing. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 54 ++++++++++---------------------- 1 file changed, 16 insertions(+), 38 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index 0f13f11e0d88..ce1f1085ad25 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -96,15 +96,16 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *ep, +static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, + struct device_node *codec_ep, struct graph_card_data *priv, - unsigned int daifmt, int idx, int is_fe) { struct device *dev = graph_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *ep = is_fe ? cpu_ep : codec_ep; struct device_node *node = of_graph_get_port_parent(ep); int ret; @@ -185,7 +186,11 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, if (ret < 0) return ret; - dai_link->dai_fmt = daifmt; + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + return ret; + dai_link->dpcm_playback = 1; dai_link->dpcm_capture = 1; dai_link->ops = &asoc_graph_card_ops; @@ -205,7 +210,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device_node *codec_ep; struct device_node *codec_port; struct device_node *codec_port_old; - unsigned int daifmt = 0; int dai_idx, ret; int rc, codec; @@ -228,26 +232,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) * see asoc_graph_get_dais_count */ - /* find 1st codec */ - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - cpu_port = it.node; - cpu_ep = of_get_next_child(cpu_port, NULL); - codec_ep = of_graph_get_remote_endpoint(cpu_ep); - - of_node_put(cpu_ep); - of_node_put(codec_ep); - - if (!codec_ep) - continue; - - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, - NULL, &daifmt); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } - dai_idx = 0; codec_port_old = NULL; for (codec = 0; codec < 2; codec++) { @@ -264,28 +248,22 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) of_node_put(cpu_ep); of_node_put(codec_ep); + of_node_put(cpu_port); of_node_put(codec_port); + it.node = NULL; if (codec) { if (codec_port_old == codec_port) continue; codec_port_old = codec_port; - - /* Back-End (= Codec) */ - ret = asoc_graph_card_dai_link_of(codec_ep, priv, daifmt, dai_idx++, 0); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } - } else { - /* Front-End (= CPU) */ - ret = asoc_graph_card_dai_link_of(cpu_ep, priv, daifmt, dai_idx++, 1); - if (ret < 0) { - of_node_put(cpu_port); - goto parse_of_err; - } } + + ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep, + priv, dai_idx++, + !codec); + if (ret < 0) + goto parse_of_err; } } -- cgit v1.2.3-58-ga151 From c3830f1a00f0c7b224aa64e99adbdc14e86d78d5 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:57:23 +0000 Subject: ASoC: audio-graph-card: tidyup typo SND_AUDIO_GRAPH_CARD 1 "simple" is enough on Kconfig help Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index c954be0a0f96..fa5aff9c1041 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -20,7 +20,7 @@ config SND_AUDIO_GRAPH_CARD depends on OF select SND_SIMPLE_CARD_UTILS help - This option enables generic simple simple sound card support + This option enables generic simple sound card support with OF-graph DT bindings. config SND_AUDIO_GRAPH_SCU_CARD -- cgit v1.2.3-58-ga151 From 64ef0817b9679b525f86773e929777be96f5c728 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 22 Nov 2018 00:57:40 +0000 Subject: ASoC: audio-graph-card: tidyup define position commit f986907c9225 ("ASoC: audio-graph-card: add widgets and routing for external amplifier support") added new function asoc_graph_card_outdrv_event(), but the inserted position breaks define area. This patch tidyup it Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 101c7da2df7c..1b158cc2df88 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -36,6 +36,11 @@ struct graph_card_data { struct gpio_desc *pa_gpio; }; +#define graph_priv_to_card(priv) (&(priv)->snd_card) +#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) +#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) +#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) + static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -63,11 +68,6 @@ static const struct snd_soc_dapm_widget asoc_graph_card_dapm_widgets[] = { SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), }; -#define graph_priv_to_card(priv) (&(priv)->snd_card) -#define graph_priv_to_props(priv, i) ((priv)->dai_props + (i)) -#define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) -#define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) - static int asoc_graph_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; -- cgit v1.2.3-58-ga151 From 83b12c2e1d942459f420490d4219fc1ad38c3620 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 26 Nov 2018 11:13:07 +0300 Subject: ASoC: amd: Fix a NULL vs IS_ERR() check in probe The platform_device_register_full() function doesn't return NULL, it returns error pointers. Fixes: 7894a7e7ea3d ("ASoC: amd: create ACP3x PCM platform device") Signed-off-by: Dan Carpenter Signed-off-by: Mark Brown --- sound/soc/amd/raven/pci-acp3x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/raven/pci-acp3x.c b/sound/soc/amd/raven/pci-acp3x.c index c28457fd9b81..facec2472b34 100644 --- a/sound/soc/amd/raven/pci-acp3x.c +++ b/sound/soc/amd/raven/pci-acp3x.c @@ -97,10 +97,10 @@ static int snd_acp3x_probe(struct pci_dev *pci, pdevinfo.size_data = sizeof(irqflags); adata->pdev = platform_device_register_full(&pdevinfo); - if (!adata->pdev) { + if (IS_ERR(adata->pdev)) { dev_err(&pci->dev, "cannot register %s device\n", pdevinfo.name); - ret = -ENODEV; + ret = PTR_ERR(adata->pdev); goto unmap_mmio; } break; -- cgit v1.2.3-58-ga151 From 933662f28981cf8520a814e47acaea53061ec894 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Fri, 23 Nov 2018 15:27:00 +0100 Subject: ASoC: max9867: Fix whitespace Minor changes to match coding style. Signed-off-by: Ladislav Michl Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 4ea3287162ad..1cda54b59854 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -283,13 +283,13 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, /* Set the prescaler based on the master clock frequency*/ if (freq >= 10000000 && freq <= 20000000) { value |= MAX9867_PSCLK_10_20; - max9867->pclk = freq; + max9867->pclk = freq; } else if (freq >= 20000000 && freq <= 40000000) { value |= MAX9867_PSCLK_20_40; - max9867->pclk = freq/2; + max9867->pclk = freq / 2; } else if (freq >= 40000000 && freq <= 60000000) { value |= MAX9867_PSCLK_40_60; - max9867->pclk = freq/4; + max9867->pclk = freq / 4; } else { dev_err(component->dev, "Invalid clock frequency %uHz (required 10-60MHz)\n", @@ -478,8 +478,7 @@ static int max9867_i2c_probe(struct i2c_client *i2c, struct max9867_priv *max9867; int ret = 0, reg; - max9867 = devm_kzalloc(&i2c->dev, - sizeof(*max9867), GFP_KERNEL); + max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); if (!max9867) return -ENOMEM; @@ -490,8 +489,7 @@ static int max9867_i2c_probe(struct i2c_client *i2c, dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); return ret; } - ret = regmap_read(max9867->regmap, - MAX9867_REVISION, ®); + ret = regmap_read(max9867->regmap, MAX9867_REVISION, ®); if (ret < 0) { dev_err(&i2c->dev, "Failed to read: %d\n", ret); return ret; -- cgit v1.2.3-58-ga151 From aa15679b2dc898049e9117fbe3ddda0b50fa52d2 Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 29 Jan 2018 21:45:59 +0530 Subject: ASoC: Intel: Skylake: Make DSP replies more human readable Add more meaning to the IPC replies for easy debugging. Replace the switch case with a lookup table to lookup for the IPC replies and print in human readable form. Signed-off-by: Subhransu S. Prusty Signed-off-by: Sriram Periyasamy Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-ipc.c | 44 ++++++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 5234fafb758a..8708755a8f9a 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -392,18 +392,43 @@ int skl_ipc_process_notification(struct sst_generic_ipc *ipc, return 0; } -static int skl_ipc_set_reply_error_code(u32 reply) +struct skl_ipc_err_map { + const char *msg; + enum skl_ipc_glb_reply reply; + int err; +}; + +static struct skl_ipc_err_map skl_err_map[] = { + {"DSP out of memory", IPC_GLB_REPLY_OUT_OF_MEMORY, -ENOMEM}, + {"DSP busy", IPC_GLB_REPLY_BUSY, -EBUSY}, +}; + +static int skl_ipc_set_reply_error_code(struct sst_generic_ipc *ipc, u32 reply) { - switch (reply) { - case IPC_GLB_REPLY_OUT_OF_MEMORY: - return -ENOMEM; + int i; - case IPC_GLB_REPLY_BUSY: - return -EBUSY; + for (i = 0; i < ARRAY_SIZE(skl_err_map); i++) { + if (skl_err_map[i].reply == reply) + break; + } - default: + if (i == ARRAY_SIZE(skl_err_map)) { + dev_err(ipc->dev, "ipc FW reply: %d FW Error Code: %u\n", + reply, + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); return -EINVAL; } + + if (skl_err_map[i].err < 0) + dev_err(ipc->dev, "ipc FW reply: %s FW Error Code: %u\n", + skl_err_map[i].msg, + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); + else + dev_info(ipc->dev, "ipc FW reply: %s FW Error Code: %u\n", + skl_err_map[i].msg, + ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); + + return skl_err_map[i].err; } void skl_ipc_process_reply(struct sst_generic_ipc *ipc, @@ -441,10 +466,7 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, } } else { - msg->errno = skl_ipc_set_reply_error_code(reply); - dev_err(ipc->dev, "ipc FW reply: reply=%d\n", reply); - dev_err(ipc->dev, "FW Error Code: %u\n", - ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp)); + msg->errno = skl_ipc_set_reply_error_code(ipc, reply); switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) { case IPC_GLB_LOAD_MULTIPLE_MODS: case IPC_GLB_LOAD_LIBRARY: -- cgit v1.2.3-58-ga151 From 61f94ee4a7435c35d78b22e4ae6e0551908000ae Mon Sep 17 00:00:00 2001 From: "Subhransu S. Prusty" Date: Mon, 29 Jan 2018 21:46:00 +0530 Subject: ASoC: Intel: Skylake: Add FW reply for MCLK/SCLK IPC If mclk/sclk is already running, FW responds with IPC reply MCLK/SCLK already running. Add these to the IPC reply lookup table. Signed-off-by: Subhransu S. Prusty Signed-off-by: Sriram Periyasamy Signed-off-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-ipc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 8708755a8f9a..9f3ce73593ae 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -249,6 +249,8 @@ enum skl_ipc_glb_reply { IPC_GLB_REPLY_INVALID_CONFIG_DATA_LEN = 121, IPC_GLB_REPLY_GATEWAY_NOT_INITIALIZED = 140, IPC_GLB_REPLY_GATEWAY_NOT_EXIST = 141, + IPC_GLB_REPLY_SCLK_ALREADY_RUNNING = 150, + IPC_GLB_REPLY_MCLK_ALREADY_RUNNING = 151, IPC_GLB_REPLY_PPL_NOT_INITIALIZED = 160, IPC_GLB_REPLY_PPL_NOT_EXIST = 161, @@ -401,6 +403,10 @@ struct skl_ipc_err_map { static struct skl_ipc_err_map skl_err_map[] = { {"DSP out of memory", IPC_GLB_REPLY_OUT_OF_MEMORY, -ENOMEM}, {"DSP busy", IPC_GLB_REPLY_BUSY, -EBUSY}, + {"SCLK already running", IPC_GLB_REPLY_SCLK_ALREADY_RUNNING, + IPC_GLB_REPLY_SCLK_ALREADY_RUNNING}, + {"MCLK already running", IPC_GLB_REPLY_MCLK_ALREADY_RUNNING, + IPC_GLB_REPLY_MCLK_ALREADY_RUNNING}, }; static int skl_ipc_set_reply_error_code(struct sst_generic_ipc *ipc, u32 reply) -- cgit v1.2.3-58-ga151 From 1003c27acfc2174558b2a0803bd6974e19be2738 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 Nov 2018 15:41:38 +0200 Subject: ASoC: davinci-mcasp: Clear TXSTAT register before activating serializers Follow the guideline from the TRM: Before starting, clear the respective transmitter and receiver status registers To avoid stale state stored in the status registers. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 267aee776b2d..740030b4d37a 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -192,6 +192,7 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp) } /* Activate serializer(s) */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); /* Release RX state machine */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); @@ -220,6 +221,7 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); /* Activate serializer(s) */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); /* wait for XDATA to be cleared */ -- cgit v1.2.3-58-ga151 From ca3d9433349ed6a8eadfc9d0ec9e88fff439d0e9 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 16 Nov 2018 15:41:39 +0200 Subject: ASoC: davinci-mcasp: Update PDIR (pin direction) register handling When McASP is master and the PDIR for the clock pins are configured as outputs before the clocking is configured it will output whatever clock is generated at the moment internally. The clock will switch to the correct rate only when the we start the clock generators. To avoid this we must only set the pin as output after the clock is configured and enabled. AXR pins configured as outputs behaves somehow interesting as well: when McASP is not enabled and the pin is selected as output it will not honor the DISMOD settings for the inactive state, but will pull the pin down. Add a new bitfield and mark the pins there which needs to be output and set the pins only at the time when they will behave correctly. On stream stop configure the pins back to input which makes them to obey the global pin configuration regarding to pull up/down. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- sound/soc/davinci/davinci-mcasp.c | 88 +++++++++++++++++++++++++++++++++------ sound/soc/davinci/davinci-mcasp.h | 29 +++++-------- 2 files changed, 85 insertions(+), 32 deletions(-) (limited to 'sound') diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 740030b4d37a..0f3911be1c8e 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -95,6 +96,8 @@ struct davinci_mcasp { int sysclk_freq; bool bclk_master; + unsigned long pdir; /* Pin direction bitfield */ + /* McASP FIFO related */ u8 txnumevt; u8 rxnumevt; @@ -169,6 +172,30 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; } +static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) +{ + u32 bit = PIN_BIT_AMUTE; + + for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + +static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) +{ + u32 bit; + + for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AFSR) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + static void mcasp_start_rx(struct davinci_mcasp *mcasp) { if (mcasp->rxnumevt) { /* enable FIFO */ @@ -220,6 +247,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) /* Start clocks */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + mcasp_set_clk_pdir(mcasp, true); + /* Activate serializer(s) */ mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); @@ -230,6 +259,8 @@ static void mcasp_start_tx(struct davinci_mcasp *mcasp) (cnt < 100000)) cnt++; + mcasp_set_axr_pdir(mcasp, true); + /* Release TX state machine */ mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); /* Release Frame Sync generator */ @@ -260,8 +291,10 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp) * In synchronous mode stop the TX clocks if no other stream is * running */ - if (mcasp_is_synchronous(mcasp) && !mcasp->streams) + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { + mcasp_set_clk_pdir(mcasp, false); mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); + } mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); @@ -287,6 +320,9 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) */ if (mcasp_is_synchronous(mcasp) && mcasp->streams) val = TXHCLKRST | TXCLKRST | TXFSRST; + else + mcasp_set_clk_pdir(mcasp, false); + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); @@ -296,6 +332,8 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp) mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); } + + mcasp_set_axr_pdir(mcasp, false); } static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) @@ -446,8 +484,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + /* BCLK */ + set_bit(PIN_BIT_ACLKX, &mcasp->pdir); + set_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + set_bit(PIN_BIT_AFSX, &mcasp->pdir); + set_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 1; break; case SND_SOC_DAIFMT_CBS_CFM: @@ -458,8 +501,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + /* BCLK */ + set_bit(PIN_BIT_ACLKX, &mcasp->pdir); + set_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + clear_bit(PIN_BIT_AFSX, &mcasp->pdir); + clear_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 1; break; case SND_SOC_DAIFMT_CBM_CFS: @@ -470,8 +518,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, ACLKX | ACLKR); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AFSX | AFSR); + /* BCLK */ + clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); + clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + set_bit(PIN_BIT_AFSX, &mcasp->pdir); + set_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 0; break; case SND_SOC_DAIFMT_CBM_CFM: @@ -482,8 +535,13 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, - ACLKX | AFSX | ACLKR | AHCLKR | AFSR); + /* BCLK */ + clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); + clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + clear_bit(PIN_BIT_AFSX, &mcasp->pdir); + clear_bit(PIN_BIT_AFSR, &mcasp->pdir); + mcasp->bclk_master = 0; break; default: @@ -598,11 +656,11 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, if (dir == SND_SOC_CLOCK_OUT) { mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); } else { mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AHCLKX); + clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); } mcasp->sysclk_freq = freq; @@ -775,17 +833,21 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, mcasp->serial_dir[i]); if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_active_serializers) { - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), DISMOD_LOW, DISMOD_MASK); + set_bit(PIN_BIT_AXR(i), &mcasp->pdir); tx_ser++; } else if (mcasp->serial_dir[i] == RX_MODE && rx_ser < max_active_serializers) { - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, AXR(i)); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); rx_ser++; } else if (mcasp->serial_dir[i] == INACTIVE_MODE) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), SRMOD_INACTIVE, SRMOD_MASK); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); + } else if (mcasp->serial_dir[i] == TX_MODE) { + /* Unused TX pins, clear PDIR */ + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); } } diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index afddc8010c54..acb024ab6a9d 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -108,27 +108,18 @@ /* * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits - */ -#define AXR(n) (1< Date: Fri, 16 Nov 2018 15:41:41 +0200 Subject: ASoC: davinci-mcasp: Implement configurable dismod handling If the dismod is specified in the DT node, use the specified custom value to configure the drive on state of the inactive TX slots. If the dismod is not present or booted in legacy mode, the dismod is set to low as it was the original behavior. Signed-off-by: Peter Ujfalusi Signed-off-by: Mark Brown --- include/linux/platform_data/davinci_asp.h | 1 + sound/soc/davinci/davinci-mcasp.c | 19 ++++++++++++++++++- sound/soc/davinci/davinci-mcasp.h | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index 85ad68f9206a..7fe80f1c7e08 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -79,6 +79,7 @@ struct davinci_mcasp_pdata { /* McASP specific fields */ int tdm_slots; u8 op_mode; + u8 dismod; u8 num_serializer; u8 *serial_dir; u8 version; diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 0f3911be1c8e..40d3a916fb74 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -85,6 +85,7 @@ struct davinci_mcasp { u32 tdm_mask[2]; int slot_width; u8 op_mode; + u8 dismod; u8 num_serializer; u8 *serial_dir; u8 version; @@ -834,7 +835,7 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, if (mcasp->serial_dir[i] == TX_MODE && tx_ser < max_active_serializers) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - DISMOD_LOW, DISMOD_MASK); + mcasp->dismod, DISMOD_MASK); set_bit(PIN_BIT_AXR(i), &mcasp->pdir); tx_ser++; } else if (mcasp->serial_dir[i] == RX_MODE && @@ -847,6 +848,8 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); } else if (mcasp->serial_dir[i] == TX_MODE) { /* Unused TX pins, clear PDIR */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + mcasp->dismod, DISMOD_MASK); clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); } } @@ -1709,6 +1712,7 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( if (pdev->dev.platform_data) { pdata = pdev->dev.platform_data; + pdata->dismod = DISMOD_LOW; return pdata; } else if (match) { pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata), @@ -1798,6 +1802,18 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( if (ret >= 0) pdata->sram_size_capture = val; + ret = of_property_read_u32(np, "dismod", &val); + if (ret >= 0) { + if (val == 0 || val == 2 || val == 3) { + pdata->dismod = DISMOD_VAL(val); + } else { + dev_warn(&pdev->dev, "Invalid dismod value: %u\n", val); + pdata->dismod = DISMOD_LOW; + } + } else { + pdata->dismod = DISMOD_LOW; + } + return pdata; nodata: @@ -1973,6 +1989,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->version = pdata->version; mcasp->txnumevt = pdata->txnumevt; mcasp->rxnumevt = pdata->rxnumevt; + mcasp->dismod = pdata->dismod; mcasp->dev = &pdev->dev; diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h index acb024ab6a9d..5e4060d8fe56 100644 --- a/sound/soc/davinci/davinci-mcasp.h +++ b/sound/soc/davinci/davinci-mcasp.h @@ -209,6 +209,7 @@ #define DISMOD_3STATE (0x0) #define DISMOD_LOW (0x2 << 2) #define DISMOD_HIGH (0x3 << 2) +#define DISMOD_VAL(x) ((x) << 2) #define DISMOD_MASK DISMOD_HIGH #define TXSTATE BIT(4) #define RXSTATE BIT(5) -- cgit v1.2.3-58-ga151 From 7f1806ebb08cc28e17dc3320790a01ccdff3d17d Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Sat, 24 Nov 2018 19:09:45 +0800 Subject: ASoC: qcom: sdm845: Add board specific dapm widgets Add board specific dapm widgets so these widgets can be used in the route. Signed-off-by: Rohit kumar Signed-off-by: Cheng-Yi Chiang Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 58593db2ab15..95d8d4422dae 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -212,6 +212,14 @@ static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_MIC("Int Mic", NULL), +}; + static void sdm845_add_be_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; @@ -243,6 +251,8 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) goto data_alloc_fail; } + card->dapm_widgets = sdm845_snd_widgets; + card->num_dapm_widgets = ARRAY_SIZE(sdm845_snd_widgets); card->dev = dev; dev_set_drvdata(dev, card); ret = qcom_snd_parse_of(card); -- cgit v1.2.3-58-ga151 From 80e733fda42f511b346106351ba7b73d5a8196d4 Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Sat, 24 Nov 2018 19:09:46 +0800 Subject: ASoC: qcom: sdm845: Create and setup jack in init callback Add a callback for init ops on dai_link to create and setup jack. Signed-off-by: Rohit kumar Signed-off-by: Cheng-Yi Chiang Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 95d8d4422dae..43c03f8e8cdc 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "common.h" #include "qdsp6/q6afe.h" @@ -17,6 +19,8 @@ #define MI2S_BCLK_RATE 1536000 struct sdm845_snd_data { + struct snd_soc_jack jack; + bool jack_setup; struct snd_soc_card *card; uint32_t pri_mi2s_clk_count; uint32_t sec_mi2s_clk_count; @@ -100,6 +104,54 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, return ret; } +static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component; + struct snd_soc_dai_link *dai_link = rtd->dai_link; + struct snd_soc_card *card = rtd->card; + struct sdm845_snd_data *pdata = snd_soc_card_get_drvdata(card); + int i, rval; + + if (!pdata->jack_setup) { + struct snd_jack *jack; + + rval = snd_soc_card_jack_new(card, "Headset Jack", + SND_JACK_HEADSET | + SND_JACK_HEADPHONE | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3, + &pdata->jack, NULL, 0); + + if (rval < 0) { + dev_err(card->dev, "Unable to add Headphone Jack\n"); + return rval; + } + + jack = pdata->jack.jack; + + snd_jack_set_key(jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + pdata->jack_setup = true; + } + + for (i = 0 ; i < dai_link->num_codecs; i++) { + struct snd_soc_dai *dai = rtd->codec_dais[i]; + + component = dai->component; + rval = snd_soc_component_set_jack( + component, &pdata->jack, NULL); + if (rval != 0 && rval != -ENOTSUPP) { + dev_warn(card->dev, "Failed to set jack: %d\n", rval); + return rval; + } + } + + return 0; +} + + static int sdm845_snd_startup(struct snd_pcm_substream *substream) { unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; @@ -220,7 +272,7 @@ static const struct snd_soc_dapm_widget sdm845_snd_widgets[] = { SND_SOC_DAPM_MIC("Int Mic", NULL), }; -static void sdm845_add_be_ops(struct snd_soc_card *card) +static void sdm845_add_ops(struct snd_soc_card *card) { struct snd_soc_dai_link *link; int i; @@ -230,6 +282,7 @@ static void sdm845_add_be_ops(struct snd_soc_card *card) link->ops = &sdm845_be_ops; link->be_hw_params_fixup = sdm845_be_hw_params_fixup; } + link->init = sdm845_dai_init; } } @@ -264,7 +317,7 @@ static int sdm845_snd_platform_probe(struct platform_device *pdev) data->card = card; snd_soc_card_set_drvdata(card, data); - sdm845_add_be_ops(card); + sdm845_add_ops(card); ret = snd_soc_register_card(card); if (ret) { dev_err(dev, "Sound card registration failed\n"); -- cgit v1.2.3-58-ga151 From bc0a7dbc5a54a06b925064adba8b07d65acf8718 Mon Sep 17 00:00:00 2001 From: Jenny TC Date: Wed, 28 Nov 2018 12:22:45 +0530 Subject: ASoC: dmic: introduce mode switch delay On startup, applications such as PulseAudio or CRAS enable playback or capture on all PCM devices to verify that configurations are correct, and close them immediately. For DMICs, this can result in the clock being turned off very quickly, which may not compatible with internal state machine transition requirements. This patch add a mode-switch delay which will prevent the clock from being turned off without complying with manufacturer timing specifications. While the DMIC clock may be controlled at a lower level, be it with hardware or firmware, applying the delay during the STOP_TRIGGER phase ensures that there is no race condition, e.g. with the hardware/firmware turning off the clock earlier Signed-off-by: Sathyanarayana Nujella Signed-off-by: Jairaj Arava Signed-off-by: Harsha Priya Signed-off-by: Jenny TC Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/sound/dmic.txt | 2 ++ sound/soc/codecs/dmic.c | 35 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+) (limited to 'sound') diff --git a/Documentation/devicetree/bindings/sound/dmic.txt b/Documentation/devicetree/bindings/sound/dmic.txt index e957b4136716..32e871037269 100644 --- a/Documentation/devicetree/bindings/sound/dmic.txt +++ b/Documentation/devicetree/bindings/sound/dmic.txt @@ -9,6 +9,7 @@ Optional properties: - dmicen-gpios: GPIO specifier for dmic to control start and stop - num-channels: Number of microphones on this DAI - wakeup-delay-ms: Delay (in ms) after enabling the DMIC + - modeswitch-delay-ms: Delay (in ms) to complete DMIC mode switch Example node: @@ -17,4 +18,5 @@ Example node: dmicen-gpios = <&gpio4 3 GPIO_ACTIVE_HIGH>; num-channels = <1>; wakeup-delay-ms <50>; + modeswitch-delay-ms <35>; }; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index 71322e0410ee..f4eb0a438a3a 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -30,9 +30,36 @@ #include #include +#define MAX_MODESWITCH_DELAY 70 +static int modeswitch_delay; +module_param(modeswitch_delay, uint, 0644); + struct dmic { struct gpio_desc *gpio_en; int wakeup_delay; + /* Delay after DMIC mode switch */ + int modeswitch_delay; +}; + +int dmic_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct dmic *dmic = snd_soc_component_get_drvdata(component); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_STOP: + if (dmic->modeswitch_delay) + mdelay(dmic->modeswitch_delay); + + break; + } + + return 0; +} + +static const struct snd_soc_dai_ops dmic_dai_ops = { + .trigger = dmic_daiops_trigger, }; static int dmic_aif_event(struct snd_soc_dapm_widget *w, @@ -68,6 +95,7 @@ static struct snd_soc_dai_driver dmic_dai = { | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, }, + .ops = &dmic_dai_ops, }; static int dmic_component_probe(struct snd_soc_component *component) @@ -85,6 +113,13 @@ static int dmic_component_probe(struct snd_soc_component *component) device_property_read_u32(component->dev, "wakeup-delay-ms", &dmic->wakeup_delay); + device_property_read_u32(component->dev, "modeswitch-delay-ms", + &dmic->modeswitch_delay); + if (modeswitch_delay) + dmic->modeswitch_delay = modeswitch_delay; + + if (dmic->modeswitch_delay > MAX_MODESWITCH_DELAY) + dmic->modeswitch_delay = MAX_MODESWITCH_DELAY; snd_soc_component_set_drvdata(component, dmic); -- cgit v1.2.3-58-ga151 From f6f30a609c526dbf6d59490a8c85adaf6ac9b0fa Mon Sep 17 00:00:00 2001 From: Jenny TC Date: Wed, 28 Nov 2018 12:22:46 +0530 Subject: ASoC: dmic: introduce module_param wakeup_delay Introducing a module param for wakeup_delay in order to align with modeswitch_delay parameter. With this change, both wakeup_delay and modeswitch_delay parameters can be passed as module parameters. Signed-off-by: Jenny TC Signed-off-by: Mark Brown --- sound/soc/codecs/dmic.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c index f4eb0a438a3a..da921da50ef0 100644 --- a/sound/soc/codecs/dmic.c +++ b/sound/soc/codecs/dmic.c @@ -34,6 +34,9 @@ static int modeswitch_delay; module_param(modeswitch_delay, uint, 0644); +static int wakeup_delay; +module_param(wakeup_delay, uint, 0644); + struct dmic { struct gpio_desc *gpio_en; int wakeup_delay; @@ -115,6 +118,8 @@ static int dmic_component_probe(struct snd_soc_component *component) &dmic->wakeup_delay); device_property_read_u32(component->dev, "modeswitch-delay-ms", &dmic->modeswitch_delay); + if (wakeup_delay) + dmic->wakeup_delay = wakeup_delay; if (modeswitch_delay) dmic->modeswitch_delay = modeswitch_delay; -- cgit v1.2.3-58-ga151 From cd7fdc45bc69a62b4e22c6e875f1f1aea566256d Mon Sep 17 00:00:00 2001 From: Young_X Date: Tue, 27 Nov 2018 06:33:16 +0000 Subject: ASoC: au8540: use 64-bit arithmetic instead of 32-bit Add suffix ULL to constant 256 in order to give the compiler complete information about the proper arithmetic to use. Notice that such constant is used in a context that expects an expression of type u64 (64 bits, unsigned) and the following expression is currently being evaluated using 32-bit arithmetic: 256 * fs * 2 * mclk_src_scaling[i].param Signed-off-by: Young_X Signed-off-by: Mark Brown --- sound/soc/codecs/nau8540.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index e3c8cd17daf2..4dd1a609756b 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -585,7 +585,7 @@ static int nau8540_calc_fll_param(unsigned int fll_in, fvco_max = 0; fvco_sel = ARRAY_SIZE(mclk_src_scaling); for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) { - fvco = 256 * fs * 2 * mclk_src_scaling[i].param; + fvco = 256ULL * fs * 2 * mclk_src_scaling[i].param; if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX && fvco_max < fvco) { fvco_max = fvco; -- cgit v1.2.3-58-ga151 From 9923e9072d813e2efa591d01e6971e0833c38815 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 30 Nov 2018 18:54:37 -0600 Subject: ASoC: Intel: common: add ACPI matching tables for ICL Entry needed for ICL RVP w/ RT274 Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- include/sound/soc-acpi-intel-match.h | 1 + sound/soc/intel/common/Makefile | 2 +- sound/soc/intel/common/soc-acpi-intel-icl-match.c | 32 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/common/soc-acpi-intel-icl-match.c (limited to 'sound') diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h index f48f59e5b7b0..bb5e1e4ce8bf 100644 --- a/include/sound/soc-acpi-intel-match.h +++ b/include/sound/soc-acpi-intel-match.h @@ -24,6 +24,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[]; /* * generic table used for HDA codec-based platforms, possibly with diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index c1f50a079d34..56c81e20b5bf 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -7,7 +7,7 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o \ + soc-acpi-intel-cnl-match.o soc-acpi-intel-icl-match.o \ soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c new file mode 100644 index 000000000000..33b441dca4d3 --- /dev/null +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * soc-apci-intel-icl-match.c - tables and support for ICL ACPI enumeration. + * + * Copyright (c) 2018, Intel Corporation. + * + */ + +#include +#include +#include "../skylake/skl.h" + +static struct skl_machine_pdata icl_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_icl_machines[] = { + { + .id = "INT34C2", + .drv_name = "icl_rt274", + .fw_filename = "intel/dsp_fw_icl.bin", + .pdata = &icl_pdata, + .sof_fw_filename = "intel/sof-icl.ri", + .sof_tplg_filename = "intel/sof-icl-rt274.tplg", + .asoc_plat_name = "0000:00:1f.3", + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_icl_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); -- cgit v1.2.3-58-ga151 From f84a6273dd9107cbf61d46ee8e11eb8bdb4de506 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Sat, 1 Dec 2018 15:08:46 +0100 Subject: ASoC: pxa: remove raumfeld machine driver These boards are now fully ported to devicetree and make use of the simple-card driver, so the platform specific machine driver can be removed. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- sound/soc/pxa/Kconfig | 10 -- sound/soc/pxa/Makefile | 1 - sound/soc/pxa/raumfeld.c | 318 ----------------------------------------------- 3 files changed, 329 deletions(-) delete mode 100644 sound/soc/pxa/raumfeld.c (limited to 'sound') diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig index 6075fc32e6b9..67159a6b90a8 100644 --- a/sound/soc/pxa/Kconfig +++ b/sound/soc/pxa/Kconfig @@ -169,16 +169,6 @@ config SND_SOC_ZYLONITE Say Y if you want to add support for SoC audio on the Marvell Zylonite reference platform. -config SND_SOC_RAUMFELD - tristate "SoC Audio support Raumfeld audio adapter" - depends on SND_PXA2XX_SOC && (MACH_RAUMFELD_SPEAKER || MACH_RAUMFELD_CONNECTOR) - depends on I2C && SPI_MASTER - select SND_PXA_SOC_SSP - select SND_SOC_CS4270 - select SND_SOC_AK4104 - help - Say Y if you want to add support for SoC audio on Raumfeld devices - config SND_PXA2XX_SOC_HX4700 tristate "SoC Audio support for HP iPAQ hx4700" depends on SND_PXA2XX_SOC && MACH_H4700 && I2C diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile index 5b265662f04f..0ab2a9dcb720 100644 --- a/sound/soc/pxa/Makefile +++ b/sound/soc/pxa/Makefile @@ -49,6 +49,5 @@ obj-$(CONFIG_SND_PXA2XX_SOC_MIOA701) += snd-soc-mioa701.o obj-$(CONFIG_SND_PXA2XX_SOC_Z2) += snd-soc-z2.o obj-$(CONFIG_SND_SOC_ZYLONITE) += snd-soc-zylonite.o obj-$(CONFIG_SND_PXA2XX_SOC_IMOTE2) += snd-soc-imote2.o -obj-$(CONFIG_SND_SOC_RAUMFELD) += snd-soc-raumfeld.o obj-$(CONFIG_SND_MMP_SOC_BROWNSTONE) += snd-soc-brownstone.o obj-$(CONFIG_SND_SOC_TTC_DKB) += snd-soc-ttc-dkb.o diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c deleted file mode 100644 index 111a907c4eb9..000000000000 --- a/sound/soc/pxa/raumfeld.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * raumfeld_audio.c -- SoC audio for Raumfeld audio devices - * - * Copyright (c) 2009 Daniel Mack - * - * based on code from: - * - * Wolfson Microelectronics PLC. - * Openedhand Ltd. - * Liam Girdwood - * Richard Purdie - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "pxa-ssp.h" - -#define GPIO_SPDIF_RESET (38) -#define GPIO_MCLK_RESET (111) -#define GPIO_CODEC_RESET (120) - -static struct i2c_client *max9486_client; -static struct i2c_board_info max9486_hwmon_info = { - I2C_BOARD_INFO("max9485", 0x63), -}; - -#define MAX9485_MCLK_FREQ_112896 0x22 -#define MAX9485_MCLK_FREQ_122880 0x23 -#define MAX9485_MCLK_FREQ_225792 0x32 -#define MAX9485_MCLK_FREQ_245760 0x33 - -static void set_max9485_clk(char clk) -{ - i2c_master_send(max9486_client, &clk, 1); -} - -static void raumfeld_enable_audio(bool en) -{ - if (en) { - gpio_set_value(GPIO_MCLK_RESET, 1); - - /* wait some time to let the clocks become stable */ - msleep(100); - - gpio_set_value(GPIO_SPDIF_RESET, 1); - gpio_set_value(GPIO_CODEC_RESET, 1); - } else { - gpio_set_value(GPIO_MCLK_RESET, 0); - gpio_set_value(GPIO_SPDIF_RESET, 0); - gpio_set_value(GPIO_CODEC_RESET, 0); - } -} - -/* CS4270 */ -static int raumfeld_cs4270_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* set freq to 0 to enable all possible codec sample rates */ - return snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); -} - -static void raumfeld_cs4270_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* set freq to 0 to enable all possible codec sample rates */ - snd_soc_dai_set_sysclk(codec_dai, 0, 0, 0); -} - -static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - unsigned int clk = 0; - int ret = 0; - - switch (params_rate(params)) { - case 44100: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; - break; - case 48000: - set_max9485_clk(MAX9485_MCLK_FREQ_122880); - clk = 12288000; - break; - case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_225792); - clk = 22579200; - break; - case 96000: - set_max9485_clk(MAX9485_MCLK_FREQ_245760); - clk = 24576000; - break; - default: - return -EINVAL; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, clk, 0); - if (ret < 0) - return ret; - - /* setup the CPU DAI */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); - if (ret < 0) - return ret; - - return 0; -} - -static const struct snd_soc_ops raumfeld_cs4270_ops = { - .startup = raumfeld_cs4270_startup, - .shutdown = raumfeld_cs4270_shutdown, - .hw_params = raumfeld_cs4270_hw_params, -}; - -static int raumfeld_analog_suspend(struct snd_soc_card *card) -{ - raumfeld_enable_audio(false); - return 0; -} - -static int raumfeld_analog_resume(struct snd_soc_card *card) -{ - raumfeld_enable_audio(true); - return 0; -} - -/* AK4104 */ - -static int raumfeld_ak4104_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0, clk = 0; - - switch (params_rate(params)) { - case 44100: - set_max9485_clk(MAX9485_MCLK_FREQ_112896); - clk = 11289600; - break; - case 48000: - set_max9485_clk(MAX9485_MCLK_FREQ_122880); - clk = 12288000; - break; - case 88200: - set_max9485_clk(MAX9485_MCLK_FREQ_225792); - clk = 22579200; - break; - case 96000: - set_max9485_clk(MAX9485_MCLK_FREQ_245760); - clk = 24576000; - break; - default: - return -EINVAL; - } - - /* setup the CPU DAI */ - ret = snd_soc_dai_set_pll(cpu_dai, 0, 0, 0, clk); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_clkdiv(cpu_dai, PXA_SSP_DIV_SCR, 4); - if (ret < 0) - return ret; - - ret = snd_soc_dai_set_sysclk(cpu_dai, PXA_SSP_CLK_EXT, clk, 1); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops raumfeld_ak4104_ops = { - .hw_params = raumfeld_ak4104_hw_params, -}; - -#define DAI_LINK_CS4270 \ -{ \ - .name = "CS4270", \ - .stream_name = "CS4270", \ - .cpu_dai_name = "pxa-ssp-dai.0", \ - .platform_name = "pxa-pcm-audio", \ - .codec_dai_name = "cs4270-hifi", \ - .codec_name = "cs4270.0-0048", \ - .dai_fmt = SND_SOC_DAIFMT_I2S | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS, \ - .ops = &raumfeld_cs4270_ops, \ -} - -#define DAI_LINK_AK4104 \ -{ \ - .name = "ak4104", \ - .stream_name = "Playback", \ - .cpu_dai_name = "pxa-ssp-dai.1", \ - .codec_dai_name = "ak4104-hifi", \ - .platform_name = "pxa-pcm-audio", \ - .dai_fmt = SND_SOC_DAIFMT_I2S | \ - SND_SOC_DAIFMT_NB_NF | \ - SND_SOC_DAIFMT_CBS_CFS, \ - .ops = &raumfeld_ak4104_ops, \ - .codec_name = "spi0.0", \ -} - -static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = { - DAI_LINK_CS4270, - DAI_LINK_AK4104, -}; - -static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = { - DAI_LINK_CS4270, -}; - -static struct snd_soc_card snd_soc_raumfeld_connector = { - .name = "Raumfeld Connector", - .owner = THIS_MODULE, - .dai_link = snd_soc_raumfeld_connector_dai, - .num_links = ARRAY_SIZE(snd_soc_raumfeld_connector_dai), - .suspend_post = raumfeld_analog_suspend, - .resume_pre = raumfeld_analog_resume, -}; - -static struct snd_soc_card snd_soc_raumfeld_speaker = { - .name = "Raumfeld Speaker", - .owner = THIS_MODULE, - .dai_link = snd_soc_raumfeld_speaker_dai, - .num_links = ARRAY_SIZE(snd_soc_raumfeld_speaker_dai), - .suspend_post = raumfeld_analog_suspend, - .resume_pre = raumfeld_analog_resume, -}; - -static struct platform_device *raumfeld_audio_device; - -static int __init raumfeld_audio_init(void) -{ - int ret; - - if (!machine_is_raumfeld_speaker() && - !machine_is_raumfeld_connector()) - return 0; - - max9486_client = i2c_new_device(i2c_get_adapter(0), - &max9486_hwmon_info); - - if (!max9486_client) - return -ENOMEM; - - set_max9485_clk(MAX9485_MCLK_FREQ_122880); - - /* Register analog device */ - raumfeld_audio_device = platform_device_alloc("soc-audio", 0); - if (!raumfeld_audio_device) - return -ENOMEM; - - if (machine_is_raumfeld_speaker()) - platform_set_drvdata(raumfeld_audio_device, - &snd_soc_raumfeld_speaker); - - if (machine_is_raumfeld_connector()) - platform_set_drvdata(raumfeld_audio_device, - &snd_soc_raumfeld_connector); - - ret = platform_device_add(raumfeld_audio_device); - if (ret < 0) { - platform_device_put(raumfeld_audio_device); - return ret; - } - - raumfeld_enable_audio(true); - return 0; -} - -static void __exit raumfeld_audio_exit(void) -{ - raumfeld_enable_audio(false); - - platform_device_unregister(raumfeld_audio_device); - - i2c_unregister_device(max9486_client); - - gpio_free(GPIO_MCLK_RESET); - gpio_free(GPIO_CODEC_RESET); - gpio_free(GPIO_SPDIF_RESET); -} - -module_init(raumfeld_audio_init); -module_exit(raumfeld_audio_exit); - -/* Module information */ -MODULE_AUTHOR("Daniel Mack "); -MODULE_DESCRIPTION("Raumfeld audio SoC"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 5d9bb5554c1dadb141eda9afc06dea350e277b18 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Dec 2018 07:59:05 +0000 Subject: ASoC: rsnd: indicates Channel and Mode for debug For TDM debug purpose, indicating Channel and Mode is very useful. This patch indicate it if it has #define DEBUG Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ssi.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index 1996aea21172..1fdcc7c3fc7e 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c @@ -329,7 +329,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod, ssi->rate = rate; ssi->chan = chan; - dev_dbg(dev, "%s outputs %u Hz\n", rsnd_mod_name(mod), rate); + dev_dbg(dev, "%s outputs %d chan %u Hz\n", + rsnd_mod_name(mod), chan, rate); return 0; } @@ -360,6 +361,8 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, struct rsnd_dai_stream *io) { struct rsnd_dai *rdai = rsnd_io_to_rdai(io); + struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai); + struct device *dev = rsnd_priv_to_dev(priv); struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); u32 cr_own = ssi->cr_own; @@ -371,6 +374,11 @@ static void rsnd_ssi_config_init(struct rsnd_mod *mod, is_tdm = rsnd_runtime_is_tdm(io); is_tdm_split = rsnd_runtime_is_tdm_split(io); + if (is_tdm) + dev_dbg(dev, "TDM mode\n"); + if (is_tdm_split) + dev_dbg(dev, "TDM Split mode\n"); + cr_own |= FORCE | rsnd_rdai_width_to_swl(rdai); if (rdai->bit_clk_inv) -- cgit v1.2.3-58-ga151 From 665c1ade11e8aee0db691c7a928de1586b712344 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Dec 2018 08:00:47 +0000 Subject: ASoC: rsnd: fixup mod ID for CTU regmap read/write commit c16015f36cc12824 ("ASoC: rsnd: add .get_id/.get_id_sub") add new .get_id/.get_id_sub to indicate module ID/subID. It is used for SSIU and CTU. In SSIU case, subID indicates BUSIF, but register settings is based on SSIU ID. OTOH, in CTU case, subID indicates CTU channel, and register settings is based on it. This means regmap read/write function needs to care it. This patch fixup this issue. It can't play MIXed sound without this patch. Fixes: c16015f36cc12824 ("ASoC: rsnd: add .get_id/.get_id_sub") Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/ctu.c | 1 + sound/soc/sh/rcar/gen.c | 14 +++++++++++--- sound/soc/sh/rcar/rsnd.h | 1 + 3 files changed, 13 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index 4dda7d968894..ff41a5ee6092 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -317,6 +317,7 @@ static struct rsnd_mod_ops rsnd_ctu_ops = { .get_status = rsnd_mod_get_status, .id = rsnd_ctu_id, .id_sub = rsnd_ctu_id_sub, + .id_cmd = rsnd_mod_id_raw, }; struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id) diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index ca639404f2cd..3283d677c937 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -71,6 +71,14 @@ static int rsnd_is_accessible_reg(struct rsnd_priv *priv, return 1; } +static int rsnd_mod_id_cmd(struct rsnd_mod *mod) +{ + if (mod->ops->id_cmd) + return mod->ops->id_cmd(mod); + + return rsnd_mod_id(mod); +} + u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg) { @@ -81,7 +89,7 @@ u32 rsnd_read(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return 0; - regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val); + regmap_fields_read(gen->regs[reg], rsnd_mod_id_cmd(mod), &val); dev_dbg(dev, "r %s - %-18s (%4d) : %08x\n", rsnd_mod_name(mod), @@ -100,7 +108,7 @@ void rsnd_write(struct rsnd_priv *priv, if (!rsnd_is_accessible_reg(priv, gen, reg)) return; - regmap_fields_force_write(gen->regs[reg], rsnd_mod_id(mod), data); + regmap_fields_force_write(gen->regs[reg], rsnd_mod_id_cmd(mod), data); dev_dbg(dev, "w %s - %-18s (%4d) : %08x\n", rsnd_mod_name(mod), @@ -117,7 +125,7 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, return; regmap_fields_force_update_bits(gen->regs[reg], - rsnd_mod_id(mod), mask, data); + rsnd_mod_id_cmd(mod), mask, data); dev_dbg(dev, "b %s - %-18s (%4d) : %08x/%08x\n", rsnd_mod_name(mod), diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 3c57129af6d1..94d51fee8999 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -306,6 +306,7 @@ struct rsnd_mod_ops { enum rsnd_mod_type type); int (*id)(struct rsnd_mod *mod); int (*id_sub)(struct rsnd_mod *mod); + int (*id_cmd)(struct rsnd_mod *mod); }; struct rsnd_dai_stream; -- cgit v1.2.3-58-ga151 From 538a4ffea3f719ed8f853f78826e360aee5fe9d7 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Dec 2018 08:02:43 +0000 Subject: ASoC: rsnd: add missing TDM Split mode support for simple-card commit f69f452243e4e1 ("ASoC: rsnd: add TDM Split mode support") added TDM Split mode support for rsnd driver. But, it cares audio-graph-card style only. We can't use TDM Split mode on simple-card style now. This patch fixup this issue. Fixes: f69f452243e4e1 ("ASoC: rsnd: add TDM Split mode support") Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/sh/rcar/core.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 56469ac4bc8c..ad6298330fdb 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c @@ -1031,6 +1031,49 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { .prepare = rsnd_soc_dai_prepare, }; +static void rsnd_parse_connect_simple(struct rsnd_priv *priv, + struct device_node *dai_np, + int dai_i, int is_play) +{ + struct device *dev = rsnd_priv_to_dev(priv); + struct rsnd_dai *rdai = rsnd_rdai_get(priv, dai_i); + struct rsnd_dai_stream *io = is_play ? + &rdai->playback : + &rdai->capture; + struct device_node *ssiu_np = rsnd_ssiu_of_node(priv); + struct device_node *np; + int i, j; + + if (!ssiu_np) + return; + + if (!rsnd_io_to_mod_ssi(io)) + return; + + /* + * This driver assumes that it is TDM Split mode + * if it includes ssiu node + */ + for (i = 0;; i++) { + struct device_node *node = is_play ? + of_parse_phandle(dai_np, "playback", i) : + of_parse_phandle(dai_np, "capture", i); + + if (!node) + break; + + j = 0; + for_each_child_of_node(ssiu_np, np) { + if (np == node) { + rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT); + dev_dbg(dev, "%s is part of TDM Split\n", io->name); + } + j++; + } + + } +} + static void rsnd_parse_connect_graph(struct rsnd_priv *priv, struct rsnd_dai_stream *io, struct device_node *endpoint) @@ -1246,8 +1289,14 @@ static int rsnd_dai_probe(struct rsnd_priv *priv) dai_i++; } } else { - for_each_child_of_node(dai_node, dai_np) - __rsnd_dai_probe(priv, dai_np, dai_i++); + for_each_child_of_node(dai_node, dai_np) { + __rsnd_dai_probe(priv, dai_np, dai_i); + if (rsnd_is_gen3(priv)) { + rsnd_parse_connect_simple(priv, dai_np, dai_i, 1); + rsnd_parse_connect_simple(priv, dai_np, dai_i, 0); + } + dai_i++; + } } return 0; -- cgit v1.2.3-58-ga151 From b6f3fc005a2c8b425d7a0973b43bef05890bf479 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Nov 2018 02:04:13 +0000 Subject: ASoC: simple-card-utils: fixup asoc_simple_card_get_dai_id() counting asoc_simple_card_get_dai_id() returns DAI ID, but it is based on DT node's "endpoint" position. Almost all cases 1 port has 1 endpoint, thus, it was no problem. But in reality, port : endpoint = 1 : N, thus, counting endpoint is BUG, it should based on "port" ID. This patch fixup it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index c69ce1e563cd..6a31d07976b9 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -269,35 +269,19 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); static int asoc_simple_card_get_dai_id(struct device_node *ep) { - struct device_node *node; - struct device_node *endpoint; - int i, id; + struct of_endpoint info; int ret; ret = snd_soc_get_dai_id(ep); if (ret != -ENOTSUPP) return ret; - node = of_graph_get_port_parent(ep); - /* * Non HDMI sound case, counting port/endpoint on its DT * is enough. Let's count it. */ - i = 0; - id = -1; - for_each_endpoint_of_node(node, endpoint) { - if (endpoint == ep) - id = i; - i++; - } - - of_node_put(node); - - if (id < 0) - return -ENODEV; - - return id; + of_graph_parse_endpoint(ep, &info); + return info.port; } int asoc_simple_card_parse_graph_dai(struct device_node *ep, -- cgit v1.2.3-58-ga151 From c89ff03ac8c67773d43e78f6dd452a4832492722 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Nov 2018 02:06:51 +0000 Subject: ASoC: audio-graph-scu-card: care link / dai count In DPCM case, it uses CPU-dummy / dummy-Codec dai links. If sound card is caring only DPCM, link count = dai count, but, if non DPCM case, link count != dai count. Now, we want to merge audio-graph-card and audio-graph-scu-card, then, we need to care both link / dai count more carefly This patch cares it, and prepare for merging audio card Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 76 ++++++++++++++++++++++++++------ 1 file changed, 62 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index ce1f1085ad25..a0a28671948a 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -277,7 +277,10 @@ parse_of_err: return ret; } -static int asoc_graph_get_dais_count(struct device *dev) +static void asoc_graph_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) { struct of_phandle_iterator it; struct device_node *node = dev->of_node; @@ -286,10 +289,48 @@ static int asoc_graph_get_dais_count(struct device *dev) struct device_node *codec_ep; struct device_node *codec_port; struct device_node *codec_port_old; - int count = 0; + struct device_node *codec_port_old2; int rc; + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for dummy-Codec + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ codec_port_old = NULL; + codec_port_old2 = NULL; of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { cpu_port = it.node; cpu_ep = of_get_next_child(cpu_port, NULL); @@ -300,16 +341,22 @@ static int asoc_graph_get_dais_count(struct device *dev) of_node_put(codec_ep); of_node_put(codec_port); - count++; + (*link_num)++; + (*dais_num)++; + + if (codec_port_old == codec_port) { + if (codec_port_old2 != codec_port_old) { + (*link_num)++; + (*ccnf_num)++; + } - if (codec_port_old == codec_port) + codec_port_old2 = codec_port_old; continue; + } - count++; + (*dais_num)++; codec_port_old = codec_port; } - - return count; } static int asoc_graph_card_probe(struct platform_device *pdev) @@ -319,19 +366,20 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct graph_dai_props *dai_props; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret, i; + int lnum = 0, dnum = 0, cnum = 0; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = asoc_graph_get_dais_count(dev); - if (num == 0) + asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); if (!dai_props || !dai_link) return -ENOMEM; @@ -341,7 +389,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -355,7 +403,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; + card->num_links = lnum; card->codec_conf = &priv->codec_conf; card->num_configs = 1; -- cgit v1.2.3-58-ga151 From a1af9858edae12eca8f77b152d17ee049835e57d Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Nov 2018 02:07:15 +0000 Subject: ASoC: audio-graph-scu-card: use cpu/codec pointer on graph_dai_props In DPCM case, it uses CPU-dummy / dummy-Codec dai links, and non DPCM case, it uses CPU-Codec dai links. Now, we want to merge audio-graph-card and audio-graph-scu-card. These sound cards are using silimar but not same logic on each functions. Then, of course we want to share same logic. To compromise, this patch uses cpu/codec pointer on audio-graph-scu-card. It is same logic with audio-graph-card, thus easy merging. This is prepare for merging audio card Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 72 ++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index a0a28671948a..ed128d430060 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -26,12 +26,14 @@ struct graph_card_data { struct snd_soc_card snd_card; struct snd_soc_codec_conf codec_conf; struct graph_dai_props { - struct asoc_simple_dai dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; struct asoc_simple_card_data adata; } *dai_props; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; struct asoc_simple_card_data adata; }; @@ -47,8 +49,17 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream) struct snd_soc_pcm_runtime *rtd = substream->private_data; struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; - return asoc_simple_card_clk_enable(&dai_props->dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); + if (ret) + return ret; + + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); + if (ret) + asoc_simple_card_clk_disable(dai_props->cpu_dai); + + return ret; } static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) @@ -57,7 +68,9 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); + + asoc_simple_card_clk_disable(dai_props->codec_dai); } static const struct snd_soc_ops asoc_graph_card_ops = { @@ -68,18 +81,20 @@ static const struct snd_soc_ops asoc_graph_card_ops = { static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct graph_dai_props *dai_props; - int num = rtd->num; + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; - dai_link = graph_priv_to_link(priv, num); - dai_props = graph_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); + if (ret < 0) + return ret; - return asoc_simple_card_init_dai(dai, &dai_props->dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; } static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -99,14 +114,15 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, struct device_node *codec_ep, struct graph_card_data *priv, - int idx, int is_fe) + int *dai_idx, int link_idx, int is_fe) { struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); struct snd_soc_card *card = graph_priv_to_card(priv); struct device_node *ep = is_fe ? cpu_ep : codec_ep; struct device_node *node = of_graph_get_port_parent(ep); + struct asoc_simple_dai *dai; int ret; if (is_fe) { @@ -122,11 +138,14 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); if (ret) return ret; - ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai); if (ret < 0) return ret; @@ -149,11 +168,14 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); if (ret < 0) return ret; - ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai); if (ret < 0) return ret; @@ -178,7 +200,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); - ret = asoc_simple_card_of_parse_tdm(ep, &dai_props->dai); + ret = asoc_simple_card_of_parse_tdm(ep, dai); if (ret) return ret; @@ -210,7 +232,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device_node *codec_ep; struct device_node *codec_port; struct device_node *codec_port_old; - int dai_idx, ret; + int dai_idx, link_idx, ret; int rc, codec; if (!node) @@ -232,6 +254,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) * see asoc_graph_get_dais_count */ + link_idx = 0; dai_idx = 0; codec_port_old = NULL; for (codec = 0; codec < 2; codec++) { @@ -260,8 +283,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) } ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep, - priv, dai_idx++, - !codec); + priv, &dai_idx, + link_idx++, !codec); if (ret < 0) goto parse_of_err; } @@ -364,6 +387,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct graph_card_data *priv; struct snd_soc_dai_link *dai_link; struct graph_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; int lnum = 0, dnum = 0, cnum = 0; @@ -380,7 +404,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev) dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -397,6 +422,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; /* Init snd_soc_card */ card = graph_priv_to_card(priv); -- cgit v1.2.3-58-ga151 From 7c2ae7981d73f9e4f10ed9d1dcb5bc6056189b30 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Nov 2018 02:07:32 +0000 Subject: ASoC: audio-graph-scu-card: care multi DPCM codec_conf Current audio-graph-scu-card didn't care about codec_conf for multi DPCM case. This patch cares it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-scu-card.c | 40 +++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c index ed128d430060..e1b192ea147b 100644 --- a/sound/soc/generic/audio-graph-scu-card.c +++ b/sound/soc/generic/audio-graph-scu-card.c @@ -24,17 +24,18 @@ struct graph_card_data { struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; struct graph_dai_props { struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; } *dai_props; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; }; #define graph_priv_to_card(priv) (&(priv)->snd_card) @@ -114,7 +115,8 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, struct device_node *codec_ep, struct graph_card_data *priv, - int *dai_idx, int link_idx, int is_fe) + int *dai_idx, int link_idx, + int *conf_idx, int is_fe) { struct device *dev = graph_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); @@ -159,6 +161,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, asoc_simple_card_canonicalize_cpu(dai_link, of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); } else { + struct snd_soc_codec_conf *cconf; + /* FE is dummy */ dai_link->cpu_of_node = NULL; dai_link->cpu_dai_name = "snd-soc-dummy-dai"; @@ -171,6 +175,9 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, dai = dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); if (ret < 0) return ret; @@ -186,14 +193,12 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, return ret; /* check "prefix" from top node */ - snd_soc_of_parse_audio_prefix(card, - &priv->codec_conf, + snd_soc_of_parse_audio_prefix(card, cconf, dai_link->codecs->of_node, "prefix"); /* check "prefix" from each node if top doesn't have */ - if (!priv->codec_conf.of_node) - snd_soc_of_parse_node_prefix(node, - &priv->codec_conf, + if (!cconf->of_node) + snd_soc_of_parse_node_prefix(node, cconf, dai_link->codecs->of_node, PREFIX "prefix"); } @@ -232,7 +237,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device_node *codec_ep; struct device_node *codec_port; struct device_node *codec_port_old; - int dai_idx, link_idx, ret; + int dai_idx, link_idx, conf_idx, ret; int rc, codec; if (!node) @@ -256,6 +261,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) link_idx = 0; dai_idx = 0; + conf_idx = 0; codec_port_old = NULL; for (codec = 0; codec < 2; codec++) { /* @@ -284,7 +290,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) ret = asoc_graph_card_dai_link_of(cpu_ep, codec_ep, priv, &dai_idx, - link_idx++, !codec); + link_idx++, &conf_idx, + !codec); if (ret < 0) goto parse_of_err; } @@ -294,6 +301,14 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret) goto parse_of_err; + if ((card->num_links != link_idx) || + (card->num_configs != conf_idx)) { + dev_err(dev, "dai_link or codec_config wrong (%d/%d, %d/%d)\n", + card->num_links, link_idx, card->num_configs, conf_idx); + ret = -EINVAL; + goto parse_of_err; + } + ret = 0; parse_of_err: @@ -390,6 +405,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; + struct snd_soc_codec_conf *cconf; int lnum = 0, dnum = 0, cnum = 0; int ret, i; @@ -405,6 +421,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); if (!dai_props || !dai_link || !dais) return -ENOMEM; @@ -423,6 +440,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = graph_priv_to_card(priv); @@ -430,8 +448,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev) card->dev = dev; card->dai_link = priv->dai_link; card->num_links = lnum; - card->codec_conf = &priv->codec_conf; - card->num_configs = 1; + card->codec_conf = cconf; + card->num_configs = cnum; ret = asoc_graph_card_parse_of(priv); if (ret < 0) { -- cgit v1.2.3-58-ga151 From 0e3460bceedc98176684602a34659d51cd8bd4ce Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 30 Nov 2018 02:07:48 +0000 Subject: ASoC: audio-graph-card: use cpu/codec pointer on graph_dai_props In DPCM case, it uses CPU-dummy / dummy-Codec dai links, and non DPCM case, it uses CPU-Codec dai links. Now, we want to merge audio-graph-card and audio-graph-scu-card. These sound cards are using silimar but not same logic on each functions. Then, of course we want to share same logic. To compromise, this patch uses cpu/codec pointer on audio-graph-card. It is same logic with audio-graph-scu-card, thus easy merging. This is prepare for merging audio card Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 57 +++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 1b158cc2df88..1da9532ef897 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -23,8 +23,8 @@ struct graph_card_data { struct snd_soc_card snd_card; struct graph_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platform; unsigned int mclk_fs; @@ -33,6 +33,7 @@ struct graph_card_data { struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; struct gpio_desc *pa_gpio; }; @@ -75,13 +76,13 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream) struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); if (ret) return ret; - ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); if (ret) - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); return ret; } @@ -92,9 +93,9 @@ static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); - asoc_simple_card_clk_disable(&dai_props->codec_dai); + asoc_simple_card_clk_disable(dai_props->codec_dai); } static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, @@ -139,17 +140,16 @@ static const struct snd_soc_ops asoc_graph_card_ops = { static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct graph_dai_props *dai_props = - graph_priv_to_props(priv, rtd->num); - int ret; + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + int ret = 0; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); if (ret < 0) return ret; @@ -158,17 +158,22 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, struct graph_card_data *priv, - int idx) + int *dai_idx, int link_idx) { struct device *dev = graph_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); int ret; + cpu_dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + codec_dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) @@ -231,8 +236,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device *dev = graph_priv_to_dev(priv); struct snd_soc_card *card = graph_priv_to_card(priv); struct device_node *node = dev->of_node; - int rc, idx = 0; - int ret; + int rc, ret; + int link_idx, dai_idx; ret = asoc_simple_card_of_parse_widgets(card, NULL); if (ret < 0) @@ -245,8 +250,11 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) /* Factor to mclk, used in hw_params() */ of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); + link_idx = 0; + dai_idx = 0; of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - ret = asoc_graph_card_dai_link_of(it.node, priv, idx++); + ret = asoc_graph_card_dai_link_of(it.node, priv, + &dai_idx, link_idx++); if (ret < 0) { of_node_put(it.node); @@ -291,6 +299,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct graph_card_data *priv; struct snd_soc_dai_link *dai_link; struct graph_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; int num, ret, i; @@ -306,7 +315,8 @@ static int asoc_graph_card_probe(struct platform_device *pdev) dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dais = devm_kcalloc(dev, num * 2, sizeof(*dais), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -330,6 +340,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; /* Init snd_soc_card */ card = graph_priv_to_card(priv); -- cgit v1.2.3-58-ga151 From 20f2ab247d3b787af91c1aa5eb27c5061744c154 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Fri, 30 Nov 2018 03:21:09 +0000 Subject: ASoC: max98373: Added max98373_reset for stable amp reset This patch added max98373_reset function to avoid amp software reset failure and code duplication. Reset verification step has been added for stable amp reset and it repeats verification maximum 3 times when it is failed. Chip revision ID is available when the amp is in the idle state which means software reset is completed well. Additional 10ms delay was added for every retrial and maximum 30ms delay can be applied. Signed-off-by: Ryan Lee Signed-off-by: Mark Brown --- sound/soc/codecs/max98373.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c index a09d01318f79..9c8616a7b61c 100644 --- a/sound/soc/codecs/max98373.c +++ b/sound/soc/codecs/max98373.c @@ -724,14 +724,39 @@ static struct snd_soc_dai_driver max98373_dai[] = { } }; +static void max98373_reset(struct max98373_priv *max98373, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98373->regmap, + MAX98373_R2000_SW_RESET, + MAX98373_SOFT_RESET, + MAX98373_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98373->regmap, + MAX98373_R21FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + static int max98373_probe(struct snd_soc_component *component) { struct max98373_priv *max98373 = snd_soc_component_get_drvdata(component); /* Software Reset */ - regmap_write(max98373->regmap, - MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET); - usleep_range(10000, 11000); + max98373_reset(max98373, component->dev); /* IV default slot configuration */ regmap_write(max98373->regmap, @@ -818,9 +843,7 @@ static int max98373_resume(struct device *dev) { struct max98373_priv *max98373 = dev_get_drvdata(dev); - regmap_write(max98373->regmap, - MAX98373_R2000_SW_RESET, MAX98373_SOFT_RESET); - usleep_range(10000, 11000); + max98373_reset(max98373, dev); regcache_cache_only(max98373->regmap, false); regcache_sync(max98373->regmap); return 0; -- cgit v1.2.3-58-ga151 From d6d08273996b3363178b920ccfa74acabaf67963 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Tue, 4 Dec 2018 15:42:52 -0700 Subject: ASoC: use DMA addr rather than CPU pa for acp_audio_dma We shouldn't assume CPU physical address we get from page_to_phys() is same as DMA address we get from dma_alloc_coherent(). On x86_64, we won't run into any problem with the assumption when dma_ops is nommu_dma_ops. However, DMA address is IOVA when IOMMU is enabled. And it's most likely different from CPU physical address when AMD IOMMU is not in passthrough mode. Signed-off-by: Yu Zhao Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 15 +++++---------- sound/soc/amd/acp.h | 2 +- 2 files changed, 6 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index cdebab2f8ce5..fd3db4c37882 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -303,11 +303,10 @@ static void set_acp_to_i2s_dma_descriptors(void __iomem *acp_mmio, u32 size, } /* Create page table entries in ACP SRAM for the allocated memory */ -static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, +static void acp_pte_config(void __iomem *acp_mmio, dma_addr_t addr, u16 num_of_pages, u32 pte_offset) { u16 page_idx; - u64 addr; u32 low; u32 high; u32 offset; @@ -317,7 +316,6 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, /* Load the low address of page int ACP SRAM through SRBM */ acp_reg_write((offset + (page_idx * 8)), acp_mmio, mmACP_SRBM_Targ_Idx_Addr); - addr = page_to_phys(pg); low = lower_32_bits(addr); high = upper_32_bits(addr); @@ -333,7 +331,7 @@ static void acp_pte_config(void __iomem *acp_mmio, struct page *pg, acp_reg_write(high, acp_mmio, mmACP_SRBM_Targ_Idx_Data); /* Move to next physically contiguos page */ - pg++; + addr += PAGE_SIZE; } } @@ -343,7 +341,7 @@ static void config_acp_dma(void __iomem *acp_mmio, { u16 ch_acp_sysmem, ch_acp_i2s; - acp_pte_config(acp_mmio, rtd->pg, rtd->num_of_pages, + acp_pte_config(acp_mmio, rtd->dma_addr, rtd->num_of_pages, rtd->pte_offset); if (rtd->direction == SNDRV_PCM_STREAM_PLAYBACK) { @@ -850,7 +848,6 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, int status; uint64_t size; u32 val = 0; - struct page *pg; struct snd_pcm_runtime *runtime; struct audio_substream_data *rtd; struct snd_soc_pcm_runtime *prtd = substream->private_data; @@ -986,16 +983,14 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, return status; memset(substream->runtime->dma_area, 0, params_buffer_bytes(params)); - pg = virt_to_page(substream->dma_buffer.area); - if (pg) { + if (substream->dma_buffer.area) { acp_set_sram_bank_state(rtd->acp_mmio, 0, true); /* Save for runtime private data */ - rtd->pg = pg; + rtd->dma_addr = substream->dma_buffer.addr; rtd->order = get_order(size); /* Fill the page table entries in ACP SRAM */ - rtd->pg = pg; rtd->size = size; rtd->num_of_pages = PAGE_ALIGN(size) >> PAGE_SHIFT; rtd->direction = substream->stream; diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h index dbbb1a85638d..e5ab6c6040a6 100644 --- a/sound/soc/amd/acp.h +++ b/sound/soc/amd/acp.h @@ -123,7 +123,7 @@ enum acp_dma_priority_level { }; struct audio_substream_data { - struct page *pg; + dma_addr_t dma_addr; unsigned int order; u16 num_of_pages; u16 i2s_instance; -- cgit v1.2.3-58-ga151 From 23aa128bb28d9da69bb1bdb2b70e50128857884a Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Tue, 4 Dec 2018 15:42:53 -0700 Subject: ASoC: use dma_ops of parent device for acp_audio_dma AMD platform device acp_audio_dma can only be created by parent PCI device driver (drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c). Pass struct device of the parent to snd_pcm_lib_preallocate_pages() so dma_alloc_coherent() can use correct dma_ops. Otherwise, it will use default dma_ops which is nommu_dma_ops on x86_64 even when IOMMU is enabled and set to non passthrough mode. Though platform device inherits some dma related fields during its creation in mfd_add_device(), we can't simply pass its struct device to snd_pcm_lib_preallocate_pages() because dma_ops is not among the inherited fields. Even it were, drivers/iommu/amd_iommu.c would ignore it because get_device_id() doesn't handle platform device. This change shouldn't give us any trouble even struct device of the parent becomes null or represents some non PCI device in the future, because get_dma_ops() correctly handles null struct device or uses the default dma_ops if struct device doesn't have it set. Signed-off-by: Yu Zhao Signed-off-by: Mark Brown --- sound/soc/amd/acp-pcm-dma.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c index fd3db4c37882..f4011bebc7ec 100644 --- a/sound/soc/amd/acp-pcm-dma.c +++ b/sound/soc/amd/acp-pcm-dma.c @@ -1146,18 +1146,21 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); + struct device *parent = component->dev->parent; switch (adata->asic_type) { case CHIP_STONEY: ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - NULL, ST_MIN_BUFFER, + parent, + ST_MIN_BUFFER, ST_MAX_BUFFER); break; default: ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, - NULL, MIN_BUFFER, + parent, + MIN_BUFFER, MAX_BUFFER); break; } -- cgit v1.2.3-58-ga151 From 1d52a74ea2300158f87196fa381cde52d98cf4e4 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Wed, 5 Dec 2018 13:50:49 -0600 Subject: ASoC: Use of_node_name_eq for node name comparisons Convert string compares of DT node names to use of_node_name_eq helper instead. This removes direct access to the node name pointer. For the FSL ASoC card, the full node names appear to be "ssi", "esai", and "sai", so there's not any reason to use strstr and of_node_name_eq can be used instead. Cc: Timur Tabi Cc: Nicolin Chen Cc: Xiubo Li Cc: Fabio Estevam Cc: Liam Girdwood Cc: Mark Brown Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: alsa-devel@alsa-project.org Cc: linuxppc-dev@lists.ozlabs.org Signed-off-by: Rob Herring Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 6 +++--- sound/soc/generic/simple-scu-card.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 44433b20435c..81f2fe2c6d23 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -571,17 +571,17 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) } /* Common settings for corresponding Freescale CPU DAI driver */ - if (strstr(cpu_np->name, "ssi")) { + if (of_node_name_eq(cpu_np, "ssi")) { /* Only SSI needs to configure AUDMUX */ ret = fsl_asoc_card_audmux_init(np, priv); if (ret) { dev_err(&pdev->dev, "failed to init audmux\n"); goto asrc_fail; } - } else if (strstr(cpu_np->name, "esai")) { + } else if (of_node_name_eq(cpu_np, "esai")) { priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; - } else if (strstr(cpu_np->name, "sai")) { + } else if (of_node_name_eq(cpu_np, "sai")) { priv->cpu_priv.sysclk_id[1] = FSL_SAI_CLK_MAST1; priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1; } diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 7ae1901b2f85..656abe2015e1 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -223,7 +223,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) i = 0; for_each_child_of_node(node, np) { is_fe = false; - if (strcmp(np->name, PREFIX "cpu") == 0) + if (of_node_name_eq(np, PREFIX "cpu")) is_fe = true; ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe); -- cgit v1.2.3-58-ga151 From f98acd8a3115e1de780083ced42039522075d155 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 6 Dec 2018 13:24:26 +0100 Subject: ASoC: codecs: cs4270: move to GPIO consumer API Get the reset GPIO through the GPIO consumer API. This allows specifying the DT property as "reset-gpios" without breaking existing DT users. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- sound/soc/codecs/cs4270.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 3c266eeb89bf..33d74f163bd7 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -29,8 +29,8 @@ #include #include #include +#include #include -#include /* * The codec isn't really big-endian or little-endian, since the I2S @@ -658,8 +658,8 @@ static const struct regmap_config cs4270_regmap = { static int cs4270_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) { - struct device_node *np = i2c_client->dev.of_node; struct cs4270_private *cs4270; + struct gpio_desc *reset_gpiod; unsigned int val; int ret, i; @@ -678,20 +678,11 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client, if (ret < 0) return ret; - /* See if we have a way to bring the codec out of reset */ - if (np) { - enum of_gpio_flags flags; - int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); - - if (gpio_is_valid(gpio)) { - ret = devm_gpio_request_one(&i2c_client->dev, gpio, - flags & OF_GPIO_ACTIVE_LOW ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, - "cs4270 reset"); - if (ret < 0) - return ret; - } - } + reset_gpiod = devm_gpiod_get_optional(&i2c_client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpiod) && + PTR_ERR(reset_gpiod) == -EPROBE_DEFER) + return -EPROBE_DEFER; cs4270->regmap = devm_regmap_init_i2c(i2c_client, &cs4270_regmap); if (IS_ERR(cs4270->regmap)) -- cgit v1.2.3-58-ga151 From d8f2c8592b95fd1fb06f0fea19738bf447bbc5f2 Mon Sep 17 00:00:00 2001 From: Daniel Mack Date: Thu, 6 Dec 2018 13:24:57 +0100 Subject: ASoC: codecs: ak4104: move to GPIO consumer API Get the reset GPIO through the GPIO consumer API. This allows specifying the DT property as "reset-gpios" without breaking existing DT users. Signed-off-by: Daniel Mack Signed-off-by: Mark Brown --- sound/soc/codecs/ak4104.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index 32bc545c19cf..6dec8a65eafc 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include @@ -268,8 +268,8 @@ static const struct regmap_config ak4104_regmap = { static int ak4104_spi_probe(struct spi_device *spi) { - struct device_node *np = spi->dev.of_node; struct ak4104_private *ak4104; + struct gpio_desc *reset_gpiod; unsigned int val; int ret; @@ -297,19 +297,11 @@ static int ak4104_spi_probe(struct spi_device *spi) return ret; } - if (np) { - enum of_gpio_flags flags; - int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags); - - if (gpio_is_valid(gpio)) { - ret = devm_gpio_request_one(&spi->dev, gpio, - flags & OF_GPIO_ACTIVE_LOW ? - GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH, - "ak4104 reset"); - if (ret < 0) - return ret; - } - } + reset_gpiod = devm_gpiod_get_optional(&spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(reset_gpiod) && + PTR_ERR(reset_gpiod) == -EPROBE_DEFER) + return -EPROBE_DEFER; /* read the 'reserved' register - according to the datasheet, it * should contain 0x5b. Not a good way to verify the presence of -- cgit v1.2.3-58-ga151 From 8efc1afd7e0570d9bb37f5127f0742304a8ad2fc Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 4 Dec 2018 19:18:17 +0100 Subject: ASoC: max9867: Remove useless assignment ret is assigned later, no need to initialize it. Signed-off-by: Ladislav Michl Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 1cda54b59854..1b155f40b106 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -476,7 +476,7 @@ static int max9867_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct max9867_priv *max9867; - int ret = 0, reg; + int ret, reg; max9867 = devm_kzalloc(&i2c->dev, sizeof(*max9867), GFP_KERNEL); if (!max9867) @@ -497,10 +497,8 @@ static int max9867_i2c_probe(struct i2c_client *i2c, dev_info(&i2c->dev, "device revision: %x\n", reg); ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component, max9867_dai, ARRAY_SIZE(max9867_dai)); - if (ret < 0) { + if (ret < 0) dev_err(&i2c->dev, "Failed to register component: %d\n", ret); - return ret; - } return ret; } -- cgit v1.2.3-58-ga151 From 29f58ff06795a923407d011d4721eaf3e8d39acc Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 4 Dec 2018 19:19:51 +0100 Subject: ASoC: max9867: Fix power management Implement set_bias_level to drive shutdown bit, so device is put to sleep when unused. Signed-off-by: Ladislav Michl Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 72 +++++++++++++++++++++++++++++----------------- sound/soc/codecs/max9867.h | 2 +- 2 files changed, 46 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 1b155f40b106..c454de434d76 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -248,17 +248,6 @@ static int max9867_dai_hw_params(struct snd_pcm_substream *substream, return 0; } -static int max9867_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_component *component = dai->component; - struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - - regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, - MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); - return 0; -} - static int max9867_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_component *component = dai->component; @@ -361,7 +350,6 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, static const struct snd_soc_dai_ops max9867_dai_ops = { .set_fmt = max9867_dai_set_fmt, .set_sysclk = max9867_set_dai_sysclk, - .prepare = max9867_prepare, .digital_mute = max9867_mute, .hw_params = max9867_dai_hw_params, }; @@ -392,27 +380,59 @@ static struct snd_soc_dai_driver max9867_dai[] = { } }; -#ifdef CONFIG_PM_SLEEP -static int max9867_suspend(struct device *dev) +#ifdef CONFIG_PM +static int max9867_suspend(struct snd_soc_component *component) { - struct max9867_priv *max9867 = dev_get_drvdata(dev); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); - /* Drop down to power saving mode when system is suspended */ - regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, - MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK); return 0; } -static int max9867_resume(struct device *dev) +static int max9867_resume(struct snd_soc_component *component) { - struct max9867_priv *max9867 = dev_get_drvdata(dev); + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); - regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, - MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK); return 0; } +#else +#define max9867_suspend NULL +#define max9867_resume NULL #endif +static int max9867_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + int err; + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_STANDBY: + if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { + err = regcache_sync(max9867->regmap); + if (err) + return err; + + err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN, MAX9867_SHTDOWN); + if (err) + return err; + } + break; + case SND_SOC_BIAS_OFF: + err = regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_SHTDOWN, 0); + if (err) + return err; + + regcache_mark_dirty(max9867->regmap); + break; + default: + break; + } + + return 0; +} + static const struct snd_soc_component_driver max9867_component = { .controls = max9867_snd_controls, .num_controls = ARRAY_SIZE(max9867_snd_controls), @@ -420,6 +440,9 @@ static const struct snd_soc_component_driver max9867_component = { .num_dapm_routes = ARRAY_SIZE(max9867_audio_map), .dapm_widgets = max9867_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets), + .suspend = max9867_suspend, + .resume = max9867_resume, + .set_bias_level = max9867_set_bias_level, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, @@ -514,15 +537,10 @@ static const struct of_device_id max9867_of_match[] = { }; MODULE_DEVICE_TABLE(of, max9867_of_match); -static const struct dev_pm_ops max9867_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume) -}; - static struct i2c_driver max9867_i2c_driver = { .driver = { .name = "max9867", .of_match_table = of_match_ptr(max9867_of_match), - .pm = &max9867_pm_ops, }, .probe = max9867_i2c_probe, .id_table = max9867_i2c_id, diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h index 55cd9976ff47..d9170850c96e 100644 --- a/sound/soc/codecs/max9867.h +++ b/sound/soc/codecs/max9867.h @@ -67,7 +67,7 @@ #define MAX9867_MICCONFIG 0x15 #define MAX9867_MODECONFIG 0x16 #define MAX9867_PWRMAN 0x17 -#define MAX9867_SHTDOWN_MASK (1<<7) +#define MAX9867_SHTDOWN 0x80 #define MAX9867_REVISION 0xff #define MAX9867_CACHEREGNUM 10 -- cgit v1.2.3-58-ga151 From 715ee1916b95bfd0773487397a0354ad9a4660e6 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 4 Dec 2018 19:21:04 +0100 Subject: ASoC: max9867: Calculate LRCLK divider Drop "Common NI Values Table" and calculate LRCLK divider, then add allowed rate constraints based on master clock frequency. Signed-off-by: Ladislav Michl Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 207 ++++++++++++++++++++++----------------------- sound/soc/codecs/max9867.h | 31 +++---- 2 files changed, 110 insertions(+), 128 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index c454de434d76..faec094161c1 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -126,124 +126,106 @@ static const struct snd_soc_dapm_route max9867_audio_map[] = { {"LINE_IN", NULL, "Right Line"}, }; -enum rates { - pcm_rate_8, pcm_rate_16, pcm_rate_24, - pcm_rate_32, pcm_rate_44, - pcm_rate_48, max_pcm_rate, +static const unsigned int max9867_rates_44k1[] = { + 11025, 22050, 44100, }; -static const struct ni_div_rates { - u32 mclk; - u16 ni[max_pcm_rate]; -} ni_div[] = { - {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} }, - {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} }, - {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} }, - {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} }, - {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} }, - {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} }, - {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} }, - {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} }, +static const struct snd_pcm_hw_constraint_list max9867_constraints_44k1 = { + .list = max9867_rates_44k1, + .count = ARRAY_SIZE(max9867_rates_44k1), }; -static inline int get_ni_value(int mclk, int rate) +static const unsigned int max9867_rates_48k[] = { + 8000, 16000, 32000, 48000, +}; + +static const struct snd_pcm_hw_constraint_list max9867_constraints_48k = { + .list = max9867_rates_48k, + .count = ARRAY_SIZE(max9867_rates_48k), +}; + +struct max9867_priv { + struct regmap *regmap; + const struct snd_pcm_hw_constraint_list *constraints; + unsigned int sysclk, pclk; + bool master, dsp_a; +}; + +static int max9867_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { - int i, ret = 0; + struct max9867_priv *max9867 = + snd_soc_component_get_drvdata(dai->component); - /* find the closest rate index*/ - for (i = 0; i < ARRAY_SIZE(ni_div); i++) { - if (ni_div[i].mclk >= mclk) - break; - } - if (i == ARRAY_SIZE(ni_div)) - return -EINVAL; + if (max9867->constraints) + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, max9867->constraints); - switch (rate) { - case 8000: - return ni_div[i].ni[pcm_rate_8]; - case 16000: - return ni_div[i].ni[pcm_rate_16]; - case 32000: - return ni_div[i].ni[pcm_rate_32]; - case 44100: - return ni_div[i].ni[pcm_rate_44]; - case 48000: - return ni_div[i].ni[pcm_rate_48]; - default: - pr_err("%s wrong rate %d\n", __func__, rate); - ret = -EINVAL; - } - return ret; + return 0; } static int max9867_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + int value; + unsigned long int rate, ratio; struct snd_soc_component *component = dai->component; struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - unsigned int ni_h, ni_l; - int value; + unsigned int ni = DIV_ROUND_CLOSEST_ULL(96ULL * 0x10000 * params_rate(params), + max9867->pclk); - value = get_ni_value(max9867->sysclk, params_rate(params)); - if (value < 0) - return value; - - ni_h = (0xFF00 & value) >> 8; - ni_l = 0x00FF & value; /* set up the ni value */ regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, - MAX9867_NI_HIGH_MASK, ni_h); + MAX9867_NI_HIGH_MASK, (0xFF00 & ni) >> 8); regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, - MAX9867_NI_LOW_MASK, ni_l); - if (!max9867->master) { - /* - * digital pll locks on to any externally supplied LRCLK signal - * and also enable rapid lock mode. - */ - regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, - MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); - regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, - MAX9867_PLL, MAX9867_PLL); - } else { - unsigned long int bclk_rate, pclk_bclk_ratio; - int bclk_value; - - bclk_rate = params_rate(params) * 2 * params_width(params); - pclk_bclk_ratio = max9867->pclk/bclk_rate; - switch (params_width(params)) { - case 8: - case 16: - switch (pclk_bclk_ratio) { - case 2: - bclk_value = MAX9867_IFC1B_PCLK_2; - break; - case 4: - bclk_value = MAX9867_IFC1B_PCLK_4; - break; + MAX9867_NI_LOW_MASK, 0x00FF & ni); + if (max9867->master) { + if (max9867->dsp_a) { + value = MAX9867_IFC1B_48X; + } else { + rate = params_rate(params) * 2 * params_width(params); + ratio = max9867->pclk / rate; + switch (params_width(params)) { case 8: - bclk_value = MAX9867_IFC1B_PCLK_8; - break; case 16: - bclk_value = MAX9867_IFC1B_PCLK_16; + switch (ratio) { + case 2: + value = MAX9867_IFC1B_PCLK_2; + break; + case 4: + value = MAX9867_IFC1B_PCLK_4; + break; + case 8: + value = MAX9867_IFC1B_PCLK_8; + break; + case 16: + value = MAX9867_IFC1B_PCLK_16; + break; + default: + return -EINVAL; + } + break; + case 24: + value = MAX9867_IFC1B_48X; + break; + case 32: + value = MAX9867_IFC1B_64X; break; default: - dev_err(component->dev, - "unsupported sampling rate\n"); return -EINVAL; } - break; - case 24: - bclk_value = MAX9867_IFC1B_24BIT; - break; - case 32: - bclk_value = MAX9867_IFC1B_32BIT; - break; - default: - dev_err(component->dev, "unsupported sampling rate\n"); - return -EINVAL; } regmap_update_bits(max9867->regmap, MAX9867_IFC1B, - MAX9867_IFC1B_BCLK_MASK, bclk_value); + MAX9867_IFC1B_BCLK_MASK, value); + } else { + /* + * digital pll locks on to any externally supplied LRCLK signal + * and also enable rapid lock mode. + */ + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW, + MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK); + regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH, + MAX9867_PLL, MAX9867_PLL); } return 0; } @@ -285,8 +267,16 @@ static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, freq); return -EINVAL; } - value = value << MAX9867_PSCLK_SHIFT; + if (freq % 48000 == 0) + max9867->constraints = &max9867_constraints_48k; + else if (freq % 44100 == 0) + max9867->constraints = &max9867_constraints_44k1; + else + dev_warn(component->dev, + "Unable to set exact rate with %uHz clock frequency\n", + freq); max9867->sysclk = freq; + value = value << MAX9867_PSCLK_SHIFT; /* exact integer mode is not supported */ value &= ~MAX9867_FREQ_MASK; regmap_update_bits(max9867->regmap, MAX9867_SYSCLK, @@ -299,16 +289,17 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - u8 iface1A = 0, iface1B = 0; + u8 iface1A, iface1B; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: - max9867->master = 1; - iface1A |= MAX9867_MASTER; + max9867->master = true; + iface1A = MAX9867_MASTER; + iface1B = MAX9867_IFC1B_48X; break; case SND_SOC_DAIFMT_CBS_CFS: - max9867->master = 0; - iface1A &= ~MAX9867_MASTER; + max9867->master = false; + iface1A = iface1B = 0; break; default: return -EINVAL; @@ -316,9 +307,11 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: + max9867->dsp_a = false; iface1A |= MAX9867_I2S_DLY; break; case SND_SOC_DAIFMT_DSP_A: + max9867->dsp_a = true; iface1A |= MAX9867_TDM_MODE | MAX9867_SDOUT_HIZ; break; default: @@ -344,20 +337,18 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai, regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A); regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B); + return 0; } static const struct snd_soc_dai_ops max9867_dai_ops = { - .set_fmt = max9867_dai_set_fmt, .set_sysclk = max9867_set_dai_sysclk, + .set_fmt = max9867_dai_set_fmt, .digital_mute = max9867_mute, - .hw_params = max9867_dai_hw_params, + .startup = max9867_startup, + .hw_params = max9867_dai_hw_params, }; -#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ - SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) -#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE) - static struct snd_soc_dai_driver max9867_dai[] = { { .name = "max9867-aif1", @@ -365,15 +356,15 @@ static struct snd_soc_dai_driver max9867_dai[] = { .stream_name = "HiFi Playback", .channels_min = 2, .channels_max = 2, - .rates = MAX9867_RATES, - .formats = MAX9867_FORMATS, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .capture = { .stream_name = "HiFi Capture", .channels_min = 2, .channels_max = 2, - .rates = MAX9867_RATES, - .formats = MAX9867_FORMATS, + .rates = SNDRV_PCM_RATE_8000_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, }, .ops = &max9867_dai_ops, .symmetric_rates = 1, diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h index d9170850c96e..7f037ab701a5 100644 --- a/sound/soc/codecs/max9867.h +++ b/sound/soc/codecs/max9867.h @@ -26,13 +26,11 @@ #define MAX9867_PSCLK_10_20 0x1 #define MAX9867_PSCLK_20_40 0x2 #define MAX9867_PSCLK_40_60 0x3 -#define MAX9867_AUDIOCLKHIGH 0x06 -#define MAX9867_NI_HIGH_WIDTH 0x7 -#define MAX9867_NI_HIGH_MASK 0x7F -#define MAX9867_NI_LOW_MASK 0x7F -#define MAX9867_NI_LOW_SHIFT 0x1 -#define MAX9867_PLL (1<<7) -#define MAX9867_AUDIOCLKLOW 0x07 +#define MAX9867_AUDIOCLKHIGH 0x06 +#define MAX9867_NI_HIGH_MASK 0x7F +#define MAX9867_NI_LOW_MASK 0xFE +#define MAX9867_PLL (1<<7) +#define MAX9867_AUDIOCLKLOW 0x07 #define MAX9867_RAPID_LOCK 0x01 #define MAX9867_IFC1A 0x08 #define MAX9867_MASTER (1<<7) @@ -43,12 +41,12 @@ #define MAX9867_BCI_MODE (1<<5) #define MAX9867_IFC1B 0x09 #define MAX9867_IFC1B_BCLK_MASK 7 -#define MAX9867_IFC1B_32BIT 0x01 -#define MAX9867_IFC1B_24BIT 0x02 -#define MAX9867_IFC1B_PCLK_2 4 -#define MAX9867_IFC1B_PCLK_4 5 -#define MAX9867_IFC1B_PCLK_8 6 -#define MAX9867_IFC1B_PCLK_16 7 +#define MAX9867_IFC1B_64X 0x01 +#define MAX9867_IFC1B_48X 0x02 +#define MAX9867_IFC1B_PCLK_2 0x04 +#define MAX9867_IFC1B_PCLK_4 0x05 +#define MAX9867_IFC1B_PCLK_8 0x06 +#define MAX9867_IFC1B_PCLK_16 0x07 #define MAX9867_CODECFLTR 0x0a #define MAX9867_DACGAIN 0x0b #define MAX9867_DACLEVEL 0x0c @@ -72,11 +70,4 @@ #define MAX9867_CACHEREGNUM 10 -/* codec private data */ -struct max9867_priv { - struct regmap *regmap; - unsigned int sysclk; - unsigned int pclk; - unsigned int master; -}; #endif -- cgit v1.2.3-58-ga151 From bc2610a6c4c92f0bf6bbf4c6641407536f30daaf Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Tue, 4 Dec 2018 19:21:33 +0100 Subject: ASoC: max9867: Fix signal paths Sound capture and line bypass currently do not work as well as some mixer controls. Fix that by building proper audio paths and adjusting volume controls to match datasheet. Signed-off-by: Ladislav Michl Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 188 ++++++++++++++++++++++++--------------------- sound/soc/codecs/max9867.h | 8 +- 2 files changed, 102 insertions(+), 94 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index faec094161c1..7a08e2c70e17 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -23,107 +23,124 @@ static const char *const max9867_spmode[] = { "Stereo Single", "Mono Single", "Stereo Single Fast", "Mono Single Fast" }; -static const char *const max9867_sidetone_text[] = { - "None", "Left", "Right", "LeftRight", "LeftRightDiv2", -}; static const char *const max9867_filter_text[] = {"IIR", "FIR"}; static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, max9867_filter_text); static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0, max9867_spmode); -static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6, - max9867_sidetone_text); -static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0); -static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1); -static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1); -static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1); -static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max98088_micboost_tlv, - 0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0), - 2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0), +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_master_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-8600, 200, 1), + 3, 17, TLV_DB_SCALE_ITEM(-7800, 400, 0), + 18, 25, TLV_DB_SCALE_ITEM(-2000, 200, 0), + 26, 34, TLV_DB_SCALE_ITEM( -500, 100, 0), + 35, 40, TLV_DB_SCALE_ITEM( 350, 50, 0), +); +static DECLARE_TLV_DB_SCALE(max9867_mic_tlv, 0, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_line_tlv, -600, 200, 0); +static DECLARE_TLV_DB_SCALE(max9867_adc_tlv, -1200, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_dac_tlv, -1500, 100, 0); +static DECLARE_TLV_DB_SCALE(max9867_dacboost_tlv, 0, 600, 0); +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max9867_micboost_tlv, + 0, 2, TLV_DB_SCALE_ITEM(-2000, 2000, 1), + 3, 3, TLV_DB_SCALE_ITEM(3000, 0, 0), ); static const struct snd_kcontrol_new max9867_snd_controls[] = { - SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL, - MAX9867_RIGHTVOL, 0, 63, 1), - SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, - 0, 15, 1, max9860_capture_tlv), - SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv), - SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN, - MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv), - SOC_ENUM("Digital Sidetone Src", max9867_sidetone), - SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1), - SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0), - SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1), - SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL, - 4, 15, 1, max9860_adc_left_tlv), - SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL, - 0, 15, 1, max9860_adc_right_tlv), + SOC_DOUBLE_R_TLV("Master Playback Volume", MAX9867_LEFTVOL, + MAX9867_RIGHTVOL, 0, 41, 1, max9867_master_tlv), + SOC_DOUBLE_R_TLV("Line Capture Volume", MAX9867_LEFTLINELVL, + MAX9867_RIGHTLINELVL, 0, 15, 1, max9867_line_tlv), + SOC_DOUBLE_R_TLV("Mic Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 0, 20, 1, max9867_mic_tlv), + SOC_DOUBLE_R_TLV("Mic Boost Capture Volume", MAX9867_LEFTMICGAIN, + MAX9867_RIGHTMICGAIN, 5, 4, 0, max9867_micboost_tlv), + SOC_SINGLE("Digital Sidetone Volume", MAX9867_SIDETONE, 0, 31, 1), + SOC_SINGLE_TLV("Digital Playback Volume", MAX9867_DACLEVEL, 0, 15, 1, + max9867_dac_tlv), + SOC_SINGLE_TLV("Digital Boost Playback Volume", MAX9867_DACLEVEL, 4, 3, 0, + max9867_dacboost_tlv), + SOC_DOUBLE_TLV("Digital Capture Volume", MAX9867_ADCLEVEL, 0, 4, 15, 1, + max9867_adc_tlv), SOC_ENUM("Speaker Mode", max9867_spkmode), SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), - SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0), + SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0), SOC_ENUM("DSP Filter", max9867_filter), }; -static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"}; +/* Input mixer */ +static const struct snd_kcontrol_new max9867_input_mixer_controls[] = { + SOC_DAPM_DOUBLE("Line Capture Switch", MAX9867_INPUTCONFIG, 7, 5, 1, 0), + SOC_DAPM_DOUBLE("Mic Capture Switch", MAX9867_INPUTCONFIG, 6, 4, 1, 0), +}; + +/* Output mixer */ +static const struct snd_kcontrol_new max9867_output_mixer_controls[] = { + SOC_DAPM_DOUBLE_R("Line Bypass Switch", + MAX9867_LEFTLINELVL, MAX9867_RIGHTLINELVL, 6, 1, 1), +}; -static SOC_ENUM_SINGLE_DECL(max9867_mux_enum, - MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT, - max9867_mux); +/* Sidetone mixer */ +static const struct snd_kcontrol_new max9867_sidetone_mixer_controls[] = { + SOC_DAPM_DOUBLE("Sidetone Switch", MAX9867_SIDETONE, 6, 7, 1, 0), +}; -static const struct snd_kcontrol_new max9867_dapm_mux_controls = - SOC_DAPM_ENUM("Route", max9867_mux_enum); +/* Line out switch */ +static const struct snd_kcontrol_new max9867_line_out_control = + SOC_DAPM_DOUBLE_R("Switch", + MAX9867_LEFTVOL, MAX9867_RIGHTVOL, 6, 1, 1); -static const struct snd_kcontrol_new max9867_left_dapm_control = - SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0); -static const struct snd_kcontrol_new max9867_right_dapm_control = - SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0); -static const struct snd_kcontrol_new max9867_line_dapm_control = - SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1); static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { - SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0), - SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0), - SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_OUTPUT("HPOUT"), - - SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0), - SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0), - SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0, - &max9867_dapm_mux_controls), - - SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0), - SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1, - &max9867_left_dapm_control), - SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1, - &max9867_right_dapm_control), - SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0, - &max9867_line_dapm_control), - SND_SOC_DAPM_INPUT("LINE_IN"), + SND_SOC_DAPM_INPUT("MICL"), + SND_SOC_DAPM_INPUT("MICR"), + SND_SOC_DAPM_INPUT("LINL"), + SND_SOC_DAPM_INPUT("LINR"), + + SND_SOC_DAPM_PGA("Left Line Input", MAX9867_PWRMAN, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Line Input", MAX9867_PWRMAN, 5, 0, NULL, 0), + SND_SOC_DAPM_MIXER_NAMED_CTL("Input Mixer", SND_SOC_NOPM, 0, 0, + max9867_input_mixer_controls, + ARRAY_SIZE(max9867_input_mixer_controls)), + SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", MAX9867_PWRMAN, 1, 0), + SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", MAX9867_PWRMAN, 0, 0), + + SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0, + max9867_sidetone_mixer_controls, + ARRAY_SIZE(max9867_sidetone_mixer_controls)), + SND_SOC_DAPM_MIXER_NAMED_CTL("Output Mixer", SND_SOC_NOPM, 0, 0, + max9867_output_mixer_controls, + ARRAY_SIZE(max9867_output_mixer_controls)), + SND_SOC_DAPM_DAC("DACL", "HiFi Playback", MAX9867_PWRMAN, 3, 0), + SND_SOC_DAPM_DAC("DACR", "HiFi Playback", MAX9867_PWRMAN, 2, 0), + SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0, + &max9867_line_out_control), + SND_SOC_DAPM_OUTPUT("LOUT"), + SND_SOC_DAPM_OUTPUT("ROUT"), }; static const struct snd_soc_dapm_route max9867_audio_map[] = { - {"Left DAC", NULL, "DAI_OUT"}, - {"Right DAC", NULL, "DAI_OUT"}, - {"Output Mixer", NULL, "Left DAC"}, - {"Output Mixer", NULL, "Right DAC"}, - {"HPOUT", NULL, "Output Mixer"}, - - {"Left ADC", NULL, "DAI_IN"}, - {"Right ADC", NULL, "DAI_IN"}, - {"Input Mixer", NULL, "Left ADC"}, - {"Input Mixer", NULL, "Right ADC"}, - {"Input Mux", "Line", "Input Mixer"}, - {"Input Mux", "Mic", "Input Mixer"}, - {"Input Mux", "Mic_Line", "Input Mixer"}, - {"Right Line", "Switch", "Input Mux"}, - {"Left Line", "Switch", "Input Mux"}, - {"LINE_IN", NULL, "Left Line"}, - {"LINE_IN", NULL, "Right Line"}, + {"Left Line Input", NULL, "LINL"}, + {"Right Line Input", NULL, "LINR"}, + {"Input Mixer", "Mic Capture Switch", "MICL"}, + {"Input Mixer", "Mic Capture Switch", "MICR"}, + {"Input Mixer", "Line Capture Switch", "Left Line Input"}, + {"Input Mixer", "Line Capture Switch", "Right Line Input"}, + {"ADCL", NULL, "Input Mixer"}, + {"ADCR", NULL, "Input Mixer"}, + + {"Digital", "Sidetone Switch", "ADCL"}, + {"Digital", "Sidetone Switch", "ADCR"}, + {"DACL", NULL, "Digital"}, + {"DACR", NULL, "Digital"}, + + {"Output Mixer", "Line Bypass Switch", "Left Line Input"}, + {"Output Mixer", "Line Bypass Switch", "Right Line Input"}, + {"Output Mixer", NULL, "DACL"}, + {"Output Mixer", NULL, "DACR"}, + {"Master Playback", "Switch", "Output Mixer"}, + {"LOUT", NULL, "Master Playback"}, + {"ROUT", NULL, "Master Playback"}, }; static const unsigned int max9867_rates_44k1[] = { @@ -235,13 +252,8 @@ static int max9867_mute(struct snd_soc_dai *dai, int mute) struct snd_soc_component *component = dai->component; struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); - if (mute) - regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, - MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK); - else - regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, - MAX9867_DAC_MUTE_MASK, 0); - return 0; + return regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL, + 1 << 6, !!mute << 6); } static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai, @@ -464,8 +476,8 @@ static const struct reg_default max9867_reg[] = { { 0x0B, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x00 }, - { 0x0E, 0x00 }, - { 0x0F, 0x00 }, + { 0x0E, 0x40 }, + { 0x0F, 0x40 }, { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h index 7f037ab701a5..2277798291a1 100644 --- a/sound/soc/codecs/max9867.h +++ b/sound/soc/codecs/max9867.h @@ -48,20 +48,16 @@ #define MAX9867_IFC1B_PCLK_8 0x06 #define MAX9867_IFC1B_PCLK_16 0x07 #define MAX9867_CODECFLTR 0x0a -#define MAX9867_DACGAIN 0x0b +#define MAX9867_SIDETONE 0x0b #define MAX9867_DACLEVEL 0x0c -#define MAX9867_DAC_MUTE_SHIFT 0x6 -#define MAX9867_DAC_MUTE_WIDTH 0x1 -#define MAX9867_DAC_MUTE_MASK (0x1< Date: Wed, 28 Nov 2018 17:00:35 +0800 Subject: ASoC: sdm845: Add TDM configuration for speaker Set TDM time slots and DAI format for speaker codec. Signed-off-by: Cheng-Yi Chiang Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index 43c03f8e8cdc..d0df7ec6042e 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -6,9 +6,11 @@ #include #include #include +#include #include #include #include +#include #include #include "common.h" #include "qdsp6/q6afe.h" @@ -17,6 +19,10 @@ #define DEFAULT_MCLK_RATE 24576000 #define TDM_BCLK_RATE 6144000 #define MI2S_BCLK_RATE 1536000 +#define LEFT_SPK_TDM_TX_MASK 0x30 +#define RIGHT_SPK_TDM_TX_MASK 0xC0 +#define SPK_TDM_RX_MASK 0x03 +#define NUM_TDM_SLOTS 8 struct sdm845_snd_data { struct snd_soc_jack jack; @@ -34,7 +40,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; + int ret = 0, j; int channels, slot_width; switch (params_format(params)) { @@ -81,6 +87,35 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, goto end; } } + + for (j = 0; j < rtd->num_codecs; j++) { + struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name_prefix, "Left")) { + ret = snd_soc_dai_set_tdm_slot( + codec_dai, LEFT_SPK_TDM_TX_MASK, + SPK_TDM_RX_MASK, NUM_TDM_SLOTS, + slot_width); + if (ret < 0) { + dev_err(rtd->dev, + "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } + + if (!strcmp(codec_dai->component->name_prefix, "Right")) { + ret = snd_soc_dai_set_tdm_slot( + codec_dai, RIGHT_SPK_TDM_TX_MASK, + SPK_TDM_RX_MASK, NUM_TDM_SLOTS, + slot_width); + if (ret < 0) { + dev_err(rtd->dev, + "DEV1 TDM slot err:%d\n", ret); + return ret; + } + } + } + end: return ret; } @@ -155,10 +190,14 @@ static int sdm845_dai_init(struct snd_soc_pcm_runtime *rtd) static int sdm845_snd_startup(struct snd_pcm_substream *substream) { unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; + unsigned int codec_dai_fmt = SND_SOC_DAIFMT_CBS_CFS; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int j; + int ret; switch (cpu_dai->id) { case PRIMARY_MI2S_RX: @@ -190,6 +229,34 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); } + + codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B; + + for (j = 0; j < rtd->num_codecs; j++) { + codec_dai = rtd->codec_dais[j]; + + if (!strcmp(codec_dai->component->name_prefix, + "Left")) { + ret = snd_soc_dai_set_fmt( + codec_dai, codec_dai_fmt); + if (ret < 0) { + dev_err(rtd->dev, + "Left TDM fmt err:%d\n", ret); + return ret; + } + } + + if (!strcmp(codec_dai->component->name_prefix, + "Right")) { + ret = snd_soc_dai_set_fmt( + codec_dai, codec_dai_fmt); + if (ret < 0) { + dev_err(rtd->dev, + "Right TDM slot err:%d\n", ret); + return ret; + } + } + } break; default: -- cgit v1.2.3-58-ga151 From ce55698d32329b83ea695e0011b58d1bc11189b8 Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Wed, 28 Nov 2018 17:00:36 +0800 Subject: ASoC: sdm845: Add configuration for headset codec Set DAI format and sysclk for headset codec. Signed-off-by: Cheng-Yi Chiang Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/sdm845.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c index d0df7ec6042e..1db8ef668223 100644 --- a/sound/soc/qcom/sdm845.c +++ b/sound/soc/qcom/sdm845.c @@ -14,6 +14,7 @@ #include #include "common.h" #include "qdsp6/q6afe.h" +#include "../codecs/rt5663.h" #define DEFAULT_SAMPLE_RATE_48K 48000 #define DEFAULT_MCLK_RATE 24576000 @@ -125,9 +126,27 @@ static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_dai *codec_dai = rtd->codec_dai; int ret = 0; switch (cpu_dai->id) { + case PRIMARY_MI2S_RX: + case PRIMARY_MI2S_TX: + /* + * Use ASRC for internal clocks, as PLL rate isn't multiple + * of BCLK. + */ + rt5663_sel_asrc_clk_src( + codec_dai->component, + RT5663_DA_STEREO_FILTER | RT5663_AD_STEREO_FILTER, + RT5663_CLK_SEL_I2S1_ASRC); + ret = snd_soc_dai_set_sysclk( + codec_dai, RT5663_SCLK_S_MCLK, DEFAULT_MCLK_RATE, + SND_SOC_CLOCK_IN); + if (ret < 0) + dev_err(rtd->dev, + "snd_soc_dai_set_sysclk err = %d\n", ret); + break; case QUATERNARY_TDM_RX_0: case QUATERNARY_TDM_TX_0: ret = sdm845_tdm_snd_hw_params(substream, params); @@ -202,6 +221,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) switch (cpu_dai->id) { case PRIMARY_MI2S_RX: case PRIMARY_MI2S_TX: + codec_dai_fmt |= SND_SOC_DAIFMT_NB_NF; if (++(data->pri_mi2s_clk_count) == 1) { snd_soc_dai_set_sysclk(cpu_dai, Q6AFE_LPASS_CLK_ID_MCLK_1, @@ -211,6 +231,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream) MI2S_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); } snd_soc_dai_set_fmt(cpu_dai, fmt); + snd_soc_dai_set_fmt(codec_dai, codec_dai_fmt); break; case SECONDARY_MI2S_TX: -- cgit v1.2.3-58-ga151 From 8084945d3c934cf730cd5fdfb5cc11ebfc4cc386 Mon Sep 17 00:00:00 2001 From: Ladislav Michl Date: Thu, 6 Dec 2018 22:06:38 +0100 Subject: ASoC: max9867: Add copyright and module author Driver rewritten, assign copyright notice and change module author as original one remains silent and I want to be notified about bugs. Signed-off-by: Ladislav Michl Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 7a08e2c70e17..8600c5439e1e 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -1,12 +1,10 @@ -/* - * max9867.c -- max9867 ALSA SoC Audio driver - * - * Copyright 2013-15 Maxim Integrated Products - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// MAX9867 ALSA SoC codec driver +// +// Copyright 2013-2015 Maxim Integrated Products +// Copyright 2018 Ladislav Michl +// #include #include @@ -551,6 +549,6 @@ static struct i2c_driver max9867_i2c_driver = { module_i2c_driver(max9867_i2c_driver); -MODULE_AUTHOR("anish kumar "); -MODULE_DESCRIPTION("ALSA SoC MAX9867 driver"); +MODULE_AUTHOR("Ladislav Michl "); +MODULE_DESCRIPTION("ASoC MAX9867 driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 271248f4c2bf56dc6b3582e37c7ac19dd483d989 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 8 Dec 2018 14:01:13 +0100 Subject: ASoC: Intel: bytcr_rt5640: Add quirk for the Prowise PT301 tablet Add a quirk for the Prowise PT301 tablet, this BYTCR tablet has no CHAN package in its ACPI tables and uses SSP0-AIF1 rather then SSP0-AIF2 which is the default for BYTCR devices. Also it uses IN1 for its MIC and JD2 for jack-detect, rather then the default IN3 and JD1. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 09591144ea7d..a48ea8ef21ce 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -673,6 +673,20 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { + /* Prowise PT301 */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Prowise"), + DMI_MATCH(DMI_PRODUCT_NAME, "PT301"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { .matches = { DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"), -- cgit v1.2.3-58-ga151 From 02e5af6575627ca0692d5b93e7c3fc3b86f62f40 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 8 Dec 2018 14:01:14 +0100 Subject: ASoC: Intel: bytcr_rt5640: Add quirk for the Point of View Mobii TAB-P1005W-232 Add a quirk for the Point of View Mobii TAB-P1005W-232 v2.0 tablet, this BYTCR device uses IN1 for its MIC and JD2 for jack-detect, rather then the default IN3 and JD1. Signed-off-by: Hans de Goede Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcr_rt5640.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index a48ea8ef21ce..a22366ce33c4 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -673,6 +673,19 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF2 | BYT_RT5640_MCLK_EN), }, + { /* Point of View Mobii TAB-P1005W-232 (V2.0) */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "POV"), + DMI_EXACT_MATCH(DMI_BOARD_NAME, "I102A"), + }, + .driver_data = (void *)(BYT_RT5640_IN1_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_2000UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { /* Prowise PT301 */ .matches = { -- cgit v1.2.3-58-ga151 From 7f63196eaa83e033de523602324ff91288390a67 Mon Sep 17 00:00:00 2001 From: Zhuohao Lee Date: Mon, 10 Dec 2018 12:32:13 +0800 Subject: ASoC: Intel: kbl_da7219_max98927: fix the audio jack button remapping From the da7219 spec, the button A, B, C and D are remapped to 0, 1, 2 and 3 respectively where button A is KEY_PLAYPAUSE, B is KEY_VOLUMEUP, C is KEY_VOLUMEDOWN and D is KEY_VOICECOMMAND. Signed-off-by: Zhuohao Lee Signed-off-by: Max Chang Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/kbl_da7219_max98927.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c index 58eb0fe69978..723a4935ed76 100644 --- a/sound/soc/intel/boards/kbl_da7219_max98927.c +++ b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -262,9 +262,9 @@ static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) jack = &ctx->kabylake_headset; snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); - snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); - snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); - snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND); da7219_aad_jack_det(component, &ctx->kabylake_headset); -- cgit v1.2.3-58-ga151 From e9dc919043c56dccedce93fe402e6c94423a70b0 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 21:49:28 +0100 Subject: ASoC: sdm845: add rt5663 codec select Calling into the codec driver adds a dependency on that being reachable from the module: ERROR: "rt5663_sel_asrc_clk_src" [sound/soc/qcom/snd-soc-sdm845.ko] undefined! Add the corresponding select statement, as it is done in the other user (Intel). Fixes: f7485875a687 ("ASoC: sdm845: Add configuration for headset codec") Signed-off-by: Arnd Bergmann Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 2a4c912d1e48..1b9fc0665792 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -100,6 +100,7 @@ config SND_SOC_SDM845 depends on QCOM_APR select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON + select SND_SOC_RT5663 help To add support for audio on Qualcomm Technologies Inc. SDM845 SoC-based systems. -- cgit v1.2.3-58-ga151 From 2b320e046495e6cf02d1f0b1abf07042ac5635ea Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Mon, 10 Dec 2018 21:46:10 +0100 Subject: ASoC: simple-card-utils: fix build warning without CONFIG_OF When CONFIG_OF is disabled, of_graph_parse_endpoint() does not initialize 'info', and gcc can see that: sound/soc/generic/simple-card-utils.c: In function 'asoc_simple_card_parse_graph_dai': sound/soc/generic/simple-card-utils.c:284:13: error: 'info.port' may be used uninitialized in this function [-Werror=maybe-uninitialized] It's probably best to check the return code anyway, and that also takes care of the warning. Fixes: b6f3fc005a2c ("ASoC: simple-card-utils: fixup asoc_simple_card_get_dai_id() counting") Signed-off-by: Arnd Bergmann Acked-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 6a31d07976b9..17d8aee43835 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -280,7 +280,10 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep) * Non HDMI sound case, counting port/endpoint on its DT * is enough. Let's count it. */ - of_graph_parse_endpoint(ep, &info); + ret = of_graph_parse_endpoint(ep, &info); + if (ret) + return -ENXIO; + return info.port; } -- cgit v1.2.3-58-ga151 From 4fb7f4df49d37a9a769b6e8ba517e453903142cc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Dec 2018 08:19:43 +0000 Subject: ASoC: simple-card: use cpu/codec pointer on simple_dai_props In DPCM case, it uses CPU-dummy / dummy-Codec dai links, and non DPCM case, it uses CPU-Codec dai links. Now, we want to merge simple-card and simple-scu-card. These sound cards are using silimar but not same logic on each functions. Then, of course we want to share same logic. To compromise, this patch uses cpu/codec pointer on simple-card. It is same logic with simple-scu-card, thus easy merging. This is prepare for merging audio card Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 62 ++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 26 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index ff3f9fa9ab5a..766123485d7c 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -18,8 +18,8 @@ struct simple_card_data { struct snd_soc_card snd_card; struct simple_dai_props { - struct asoc_simple_dai cpu_dai; - struct asoc_simple_dai codec_dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platform; unsigned int mclk_fs; @@ -28,6 +28,7 @@ struct simple_card_data { struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -47,13 +48,13 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_clk_enable(&dai_props->cpu_dai); + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); if (ret) return ret; - ret = asoc_simple_card_clk_enable(&dai_props->codec_dai); + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); if (ret) - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); return ret; } @@ -65,9 +66,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->cpu_dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); - asoc_simple_card_clk_disable(&dai_props->codec_dai); + asoc_simple_card_clk_disable(dai_props->codec_dai); } static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, @@ -102,11 +103,11 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, if (mclk_fs) { mclk = params_rate(params) * mclk_fs; - ret = asoc_simple_set_clk_rate(&dai_props->codec_dai, mclk); + ret = asoc_simple_set_clk_rate(dai_props->codec_dai, mclk); if (ret < 0) return ret; - ret = asoc_simple_set_clk_rate(&dai_props->cpu_dai, mclk); + ret = asoc_simple_set_clk_rate(dai_props->cpu_dai, mclk); if (ret < 0) return ret; @@ -134,17 +135,16 @@ static const struct snd_soc_ops asoc_simple_card_ops = { static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *codec = rtd->codec_dai; - struct snd_soc_dai *cpu = rtd->cpu_dai; - struct simple_dai_props *dai_props = - simple_priv_to_props(priv, rtd->num); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); int ret; - ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai); + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); if (ret < 0) return ret; - ret = asoc_simple_card_init_dai(cpu, &dai_props->cpu_dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); if (ret < 0) return ret; @@ -153,14 +153,14 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) static int asoc_simple_card_dai_link_of(struct device_node *node, struct simple_card_data *priv, - int idx, + int *dai_idx, int link_idx, bool is_top_level_node) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); - struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai; - struct asoc_simple_dai *codec_dai = &dai_props->codec_dai; + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct device_node *cpu = NULL; struct device_node *plat = NULL; struct device_node *codec = NULL; @@ -193,6 +193,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, goto dai_link_of_err; } + cpu_dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + codec_dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); if (ret < 0) @@ -290,6 +295,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) struct device_node *dai_link; struct device_node *node = dev->of_node; int ret; + int link_idx, dai_idx; if (!node) return -EINVAL; @@ -308,23 +314,24 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); /* Single/Muti DAI link(s) & New style of DT node */ + link_idx = 0; + dai_idx = 0; if (dai_link) { struct device_node *np = NULL; - int i = 0; for_each_child_of_node(node, np) { - dev_dbg(dev, "\tlink %d:\n", i); + dev_dbg(dev, "\tlink %d:\n", link_idx); ret = asoc_simple_card_dai_link_of(np, priv, - i, false); + &dai_idx, link_idx++, false); if (ret < 0) { of_node_put(np); goto card_parse_end; } - i++; } } else { /* For single DAI link & old style of DT node */ - ret = asoc_simple_card_dai_link_of(node, priv, 0, true); + ret = asoc_simple_card_dai_link_of(node, priv, + &dai_idx, link_idx++, true); if (ret < 0) goto card_parse_end; } @@ -362,6 +369,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct simple_card_data *priv; struct snd_soc_dai_link *dai_link; struct simple_dai_props *dai_props; + struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; @@ -380,7 +388,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dais = devm_kcalloc(dev, num * 2, sizeof(*dais), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -397,6 +406,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; /* Init snd_soc_card */ card = simple_priv_to_card(priv); -- cgit v1.2.3-58-ga151 From dbd08fe59cf8c2f4bad0a72e912913c20db4774e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 4 Dec 2018 08:20:13 +0000 Subject: ASoC: simple-scu-card: tidyup asoc_simple_card_parse_daifmt() timing Current simple-scu-card driver is parsing codec position for DPCM and consider DAI format. But, current operation is doing totally pointless, because it should be called for each CPU/Codec pair. Let's tidyup asoc_simple_card_parse_daifmt() timing. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 656abe2015e1..24099e61d1b1 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -92,17 +92,24 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int asoc_simple_card_dai_link_of(struct device_node *np, +static int asoc_simple_card_dai_link_of(struct device_node *link, + struct device_node *np, + struct device_node *codec, struct simple_card_data *priv, - unsigned int daifmt, - int idx, bool is_fe) + int idx, bool is_fe, + bool is_top_level_node) { struct device *dev = simple_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); struct snd_soc_card *card = simple_priv_to_card(priv); + char *prefix = ""; int ret; + /* For single DAI link & old style of DT node */ + if (is_top_level_node) + prefix = PREFIX; + if (is_fe) { int is_single_links = 0; struct snd_soc_dai_link_component *codecs; @@ -178,7 +185,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, if (ret < 0) return ret; - dai_link->dai_fmt = daifmt; + ret = asoc_simple_card_parse_daifmt(dev, link, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + return ret; + dai_link->dpcm_playback = 1; dai_link->dpcm_capture = 1; dai_link->ops = &asoc_simple_card_ops; @@ -191,10 +202,10 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *node = dev->of_node; struct device_node *np; + struct device_node *codec; struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *node = dev->of_node; - unsigned int daifmt = 0; bool is_fe; int ret, i; @@ -211,22 +222,18 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) asoc_simple_card_parse_convert(dev, node, PREFIX, &priv->adata); - /* find 1st codec */ - np = of_get_child_by_name(node, PREFIX "codec"); - if (!np) + i = 0; + codec = of_get_child_by_name(node, PREFIX "codec"); + if (!codec) return -ENODEV; - ret = asoc_simple_card_parse_daifmt(dev, node, np, PREFIX, &daifmt); - if (ret < 0) - return ret; - - i = 0; for_each_child_of_node(node, np) { is_fe = false; if (of_node_name_eq(np, PREFIX "cpu")) is_fe = true; - ret = asoc_simple_card_dai_link_of(np, priv, daifmt, i, is_fe); + ret = asoc_simple_card_dai_link_of(node, np, codec, priv, + i, is_fe, true); if (ret < 0) return ret; i++; -- cgit v1.2.3-58-ga151 From da32d6537802a257879e9ba91a5e351a24ada889 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Dec 2018 03:24:57 +0000 Subject: ASoC: simple-scu-card: add dai-link support simple-card is supporting dai-link support, but simple-scu-card doesn't have it. This patch support it. This is prepare for merging simple-card and simple-scu-card. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 46 ++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 24099e61d1b1..666ace6175be 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -202,14 +202,15 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); - struct device_node *node = dev->of_node; + struct device_node *top = dev->of_node; + struct device_node *node; struct device_node *np; struct device_node *codec; struct snd_soc_card *card = simple_priv_to_card(priv); bool is_fe; - int ret, i; + int ret, i, loop; - if (!node) + if (!top) return -EINVAL; ret = asoc_simple_card_of_parse_widgets(card, PREFIX); @@ -220,25 +221,34 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, node, PREFIX, &priv->adata); + asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); + loop = 1; i = 0; - codec = of_get_child_by_name(node, PREFIX "codec"); - if (!codec) - return -ENODEV; - - for_each_child_of_node(node, np) { - is_fe = false; - if (of_node_name_eq(np, PREFIX "cpu")) - is_fe = true; - - ret = asoc_simple_card_dai_link_of(node, np, codec, priv, - i, is_fe, true); - if (ret < 0) - return ret; - i++; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = dev->of_node; + loop = 0; } + do { + codec = of_get_child_by_name(node, + loop ? "codec" : PREFIX "codec"); + if (!codec) + return -ENODEV; + + for_each_child_of_node(node, np) { + is_fe = (np != codec); + + ret = asoc_simple_card_dai_link_of(node, np, codec, priv, + i, is_fe, !loop); + if (ret < 0) + return ret; + i++; + } + node = of_get_next_child(top, node); + } while (loop && node); + ret = asoc_simple_card_parse_card_name(card, PREFIX); if (ret < 0) return ret; -- cgit v1.2.3-58-ga151 From b8d8f9a58fd971ec215fb8bfec5a4fb6e539a3ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Dec 2018 03:25:08 +0000 Subject: ASoC: simple-scu-card: care link / dai count In DPCM case, it uses CPU-dummy / dummy-Codec dai links. If sound card is caring only DPCM, link count = dai count, but, if non DPCM case, link count != dai count. Now, we want to merge simple-card and simple-scu-card, then, we need to care both link / dai count more carefly This patch cares it, and prepare for merging simple card Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 90 ++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 666ace6175be..1c6382fa54a0 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -256,6 +256,80 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) return 0; } +static void asoc_simple_card_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) +{ + struct device_node *top = dev->of_node; + struct device_node *node; + int loop; + int num; + + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ + if (!top) { + (*link_num) = 1; + (*dais_num) = 2; + (*ccnf_num) = 0; + return; + } + + loop = 1; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = top; + loop = 0; + } + + do { + num = of_get_child_count(node); + (*dais_num) += num; + if (num > 2) { + (*link_num) += num; + (*ccnf_num)++; + } else { + (*link_num)++; + } + node = of_get_next_child(top, node); + } while (loop && node); +} + static int asoc_simple_card_probe(struct platform_device *pdev) { struct simple_card_data *priv; @@ -263,18 +337,20 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct simple_dai_props *dai_props; struct snd_soc_card *card; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int num, ret, i; + int ret, i; + int lnum = 0, dnum = 0, cnum = 0; /* Allocate the private data */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = of_get_child_count(np); + asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) + return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); if (!dai_props || !dai_link) return -ENOMEM; @@ -284,7 +360,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -298,7 +374,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) card->owner = THIS_MODULE; card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; + card->num_links = lnum; card->codec_conf = &priv->codec_conf; card->num_configs = 1; -- cgit v1.2.3-58-ga151 From 09ee833679761f4995daaa6fd6200c77b8fcdaef Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Dec 2018 03:25:18 +0000 Subject: ASoC: simple-scu-card: tidyup "convert-rate/channels" parsing simple-scu-card.c is supporting "convert-rate/channels" which is used for DPCM. But, sound card might have multi codecs, and each codec might need each convert-rate/channels. This patch supports each codec's convert-rate/channles support. top node convert-rate/channels will overwrite settings if exist. It can't support each codec's convert-rate/channels if sound card had multi codecs without this patch. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 1c6382fa54a0..3c4e28995872 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -26,6 +26,7 @@ struct simple_card_data { struct asoc_simple_dai dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; } *dai_props; struct snd_soc_dai_link *dai_link; struct asoc_simple_card_data adata; @@ -86,7 +87,11 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + /* overwrite by top level adata if exist */ asoc_simple_card_convert_fixup(&priv->adata, params); return 0; @@ -177,6 +182,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, "prefix"); } + asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata); + ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai); if (ret) return ret; -- cgit v1.2.3-58-ga151 From e6a3ff2ed4c9c4df217c64e0fc0a280a24055790 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Dec 2018 03:25:27 +0000 Subject: ASoC: simple-scu-card: use cpu/codec pointer on graph_dai_props In DPCM case, it uses CPU-dummy / dummy-Codec dai links, and non DPCM case, it uses CPU-Codec dai links. Now, we want to merge simple-card and simple-scu-card. These sound cards are using silimar but not same logic on each functions. Then, of course we want to share same logic. To compromise, this patch uses cpu/codec pointer on simple-scu-card. It is same logic with simple-card, thus easy merging. This is prepare for merging simple card Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 77 +++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 3c4e28995872..944a6f435f88 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -23,12 +23,14 @@ struct simple_card_data { struct snd_soc_card snd_card; struct snd_soc_codec_conf codec_conf; struct simple_dai_props { - struct asoc_simple_dai dai; + struct asoc_simple_dai *cpu_dai; + struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; struct asoc_simple_card_data adata; } *dai_props; struct snd_soc_dai_link *dai_link; + struct asoc_simple_dai *dais; struct asoc_simple_card_data adata; }; @@ -47,8 +49,17 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + int ret; + + ret = asoc_simple_card_clk_enable(dai_props->cpu_dai); + if (ret) + return ret; + + ret = asoc_simple_card_clk_enable(dai_props->codec_dai); + if (ret) + asoc_simple_card_clk_disable(dai_props->cpu_dai); - return asoc_simple_card_clk_enable(&dai_props->dai); + return ret; } static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) @@ -58,7 +69,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(&dai_props->dai); + asoc_simple_card_clk_disable(dai_props->cpu_dai); + + asoc_simple_card_clk_disable(dai_props->codec_dai); } static const struct snd_soc_ops asoc_simple_card_ops = { @@ -68,19 +81,21 @@ static const struct snd_soc_ops asoc_simple_card_ops = { static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) { - struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct snd_soc_dai *dai; - struct snd_soc_dai_link *dai_link; - struct simple_dai_props *dai_props; - int num = rtd->num; + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + int ret; - dai_link = simple_priv_to_link(priv, num); - dai_props = simple_priv_to_props(priv, num); - dai = dai_link->dynamic ? - rtd->cpu_dai : - rtd->codec_dai; + ret = asoc_simple_card_init_dai(rtd->codec_dai, + dai_props->codec_dai); + if (ret < 0) + return ret; - return asoc_simple_card_init_dai(dai, &dai_props->dai); + ret = asoc_simple_card_init_dai(rtd->cpu_dai, + dai_props->cpu_dai); + if (ret < 0) + return ret; + + return 0; } static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -101,13 +116,14 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, struct device_node *np, struct device_node *codec, struct simple_card_data *priv, - int idx, bool is_fe, + int *dai_idx, int link_idx, int is_fe, bool is_top_level_node) { struct device *dev = simple_priv_to_dev(priv); - struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); struct snd_soc_card *card = simple_priv_to_card(priv); + struct asoc_simple_dai *dai; char *prefix = ""; int ret; @@ -129,12 +145,15 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, dai_link->dynamic = 1; dai_link->dpcm_merged_format = 1; + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, &is_single_links); if (ret) return ret; - ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai); if (ret < 0) return ret; @@ -155,11 +174,14 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, dai_link->no_pcm = 1; dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); if (ret < 0) return ret; - ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai); + ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai); if (ret < 0) return ret; @@ -184,7 +206,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, asoc_simple_card_parse_convert(dev, link, prefix, &dai_props->adata); - ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai); + ret = asoc_simple_card_of_parse_tdm(np, dai); if (ret) return ret; @@ -215,7 +237,8 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) struct device_node *codec; struct snd_soc_card *card = simple_priv_to_card(priv); bool is_fe; - int ret, i, loop; + int ret, loop; + int dai_idx, link_idx; if (!top) return -EINVAL; @@ -231,7 +254,8 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); loop = 1; - i = 0; + link_idx = 0; + dai_idx = 0; node = of_get_child_by_name(top, PREFIX "dai-link"); if (!node) { node = dev->of_node; @@ -248,10 +272,10 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) is_fe = (np != codec); ret = asoc_simple_card_dai_link_of(node, np, codec, priv, - i, is_fe, !loop); + &dai_idx, link_idx++, + is_fe, !loop); if (ret < 0) return ret; - i++; } node = of_get_next_child(top, node); } while (loop && node); @@ -342,6 +366,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct simple_card_data *priv; struct snd_soc_dai_link *dai_link; struct simple_dai_props *dai_props; + struct asoc_simple_dai *dais; struct snd_soc_card *card; struct device *dev = &pdev->dev; int ret, i; @@ -358,7 +383,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); - if (!dai_props || !dai_link) + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + if (!dai_props || !dai_link || !dais) return -ENOMEM; /* @@ -375,6 +401,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; + priv->dais = dais; /* Init snd_soc_card */ card = simple_priv_to_card(priv); -- cgit v1.2.3-58-ga151 From 0a26d6df4fd8aa8f4fb444446d162e01c3e349c0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Tue, 11 Dec 2018 03:25:37 +0000 Subject: ASoC: simple-scu-card: care multi DPCM codec_conf Current simple-scu-card didn't care about codec_conf for multi DPCM case. This patch cares it. Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-scu-card.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c index 944a6f435f88..9d7299d536a8 100644 --- a/sound/soc/generic/simple-scu-card.c +++ b/sound/soc/generic/simple-scu-card.c @@ -21,17 +21,18 @@ struct simple_card_data { struct snd_soc_card snd_card; - struct snd_soc_codec_conf codec_conf; struct simple_dai_props { struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; struct snd_soc_dai_link_component platform; struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; } *dai_props; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -116,7 +117,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, struct device_node *np, struct device_node *codec, struct simple_card_data *priv, - int *dai_idx, int link_idx, int is_fe, + int *dai_idx, int link_idx, + int *conf_idx, int is_fe, bool is_top_level_node) { struct device *dev = simple_priv_to_dev(priv); @@ -165,6 +167,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); } else { + struct snd_soc_codec_conf *cconf; + /* FE is dummy */ dai_link->cpu_of_node = NULL; dai_link->cpu_dai_name = "snd-soc-dummy-dai"; @@ -177,6 +181,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, dai = dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); if (ret < 0) return ret; @@ -192,14 +199,12 @@ static int asoc_simple_card_dai_link_of(struct device_node *link, return ret; /* check "prefix" from top node */ - snd_soc_of_parse_audio_prefix(card, - &priv->codec_conf, + snd_soc_of_parse_audio_prefix(card, cconf, dai_link->codecs->of_node, PREFIX "prefix"); /* check "prefix" from each node if top doesn't have */ - if (!priv->codec_conf.of_node) - snd_soc_of_parse_node_prefix(np, - &priv->codec_conf, + if (!cconf->of_node) + snd_soc_of_parse_node_prefix(np, cconf, dai_link->codecs->of_node, "prefix"); } @@ -238,7 +243,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) struct snd_soc_card *card = simple_priv_to_card(priv); bool is_fe; int ret, loop; - int dai_idx, link_idx; + int dai_idx, link_idx, conf_idx; if (!top) return -EINVAL; @@ -256,6 +261,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) loop = 1; link_idx = 0; dai_idx = 0; + conf_idx = 0; node = of_get_child_by_name(top, PREFIX "dai-link"); if (!node) { node = dev->of_node; @@ -273,6 +279,7 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) ret = asoc_simple_card_dai_link_of(node, np, codec, priv, &dai_idx, link_idx++, + &conf_idx, is_fe, !loop); if (ret < 0) return ret; @@ -368,6 +375,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct simple_dai_props *dai_props; struct asoc_simple_dai *dais; struct snd_soc_card *card; + struct snd_soc_codec_conf *cconf; struct device *dev = &pdev->dev; int ret, i; int lnum = 0, dnum = 0, cnum = 0; @@ -384,6 +392,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); if (!dai_props || !dai_link || !dais) return -ENOMEM; @@ -402,6 +411,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = simple_priv_to_card(priv); @@ -409,8 +419,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev) card->dev = dev; card->dai_link = priv->dai_link; card->num_links = lnum; - card->codec_conf = &priv->codec_conf; - card->num_configs = 1; + card->codec_conf = cconf; + card->num_configs = cnum; ret = asoc_simple_card_parse_of(priv); if (ret < 0) { -- cgit v1.2.3-58-ga151 From b7169ddea2f2a90538f606688adf4948f2da82ce Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 12 Dec 2018 16:03:58 +0900 Subject: ASoC: rsnd: remove RSND_REG_ from rsnd_reg Current rsnd is using RSND_REG_xxx for register naming, and using RSND_REG_##f style macro for read/write. The biggest reason why it uses this style is that we can avoid non-existing register access. But, its demerit is sequential register access code will be very ugly. Current rsnd driver is well tested, so, let's remove RSND_REG_ from rsnd_reg, and cleanup sequential register access code. Signed-off-by: Kuninori Morimoto Tested-by: Hiroyuki Yokoyama Signed-off-by: Mark Brown --- sound/soc/sh/rcar/adg.c | 36 +----- sound/soc/sh/rcar/ctu.c | 69 +++------- sound/soc/sh/rcar/dvc.c | 10 +- sound/soc/sh/rcar/gen.c | 23 ++-- sound/soc/sh/rcar/rsnd.h | 329 +++++++++++++++++++++++------------------------ sound/soc/sh/rcar/ssiu.c | 59 ++------- 6 files changed, 212 insertions(+), 314 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 6768a66588eb..e821ccc70f47 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c @@ -249,28 +249,8 @@ int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, out = out << shift; mask = 0x0f1f << shift; - switch (id / 2) { - case 0: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out); - break; - case 1: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out); - break; - case 2: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out); - break; - case 3: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out); - break; - case 4: - rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4, mask, in); - rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out); - break; - } + rsnd_mod_bset(adg_mod, SRCIN_TIMSEL(id / 2), mask, in); + rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL(id / 2), mask, out); if (en) rsnd_mod_bset(adg_mod, DIV_EN, en, en); @@ -299,17 +279,7 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) if (id == 8) return; - switch (id / 4) { - case 0: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val); - break; - case 1: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val); - break; - case 2: - rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val); - break; - } + rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL(id / 4), mask, val); dev_dbg(dev, "AUDIO_CLK_SEL is 0x%x\n", val); } diff --git a/sound/soc/sh/rcar/ctu.c b/sound/soc/sh/rcar/ctu.c index ff41a5ee6092..8cb06dab234e 100644 --- a/sound/soc/sh/rcar/ctu.c +++ b/sound/soc/sh/rcar/ctu.c @@ -72,10 +72,7 @@ struct rsnd_ctu { struct rsnd_mod mod; struct rsnd_kctrl_cfg_m pass; - struct rsnd_kctrl_cfg_m sv0; - struct rsnd_kctrl_cfg_m sv1; - struct rsnd_kctrl_cfg_m sv2; - struct rsnd_kctrl_cfg_m sv3; + struct rsnd_kctrl_cfg_m sv[4]; struct rsnd_kctrl_cfg_s reset; int channels; u32 flags; @@ -120,7 +117,7 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod); u32 cpmdr = 0; u32 scmdr = 0; - int i; + int i, j; for (i = 0; i < RSND_MAX_CHANNELS; i++) { u32 val = rsnd_kctrl_valm(ctu->pass, i); @@ -139,45 +136,13 @@ static void rsnd_ctu_value_init(struct rsnd_dai_stream *io, rsnd_mod_write(mod, CTU_SCMDR, scmdr); - if (scmdr > 0) { - rsnd_mod_write(mod, CTU_SV00R, rsnd_kctrl_valm(ctu->sv0, 0)); - rsnd_mod_write(mod, CTU_SV01R, rsnd_kctrl_valm(ctu->sv0, 1)); - rsnd_mod_write(mod, CTU_SV02R, rsnd_kctrl_valm(ctu->sv0, 2)); - rsnd_mod_write(mod, CTU_SV03R, rsnd_kctrl_valm(ctu->sv0, 3)); - rsnd_mod_write(mod, CTU_SV04R, rsnd_kctrl_valm(ctu->sv0, 4)); - rsnd_mod_write(mod, CTU_SV05R, rsnd_kctrl_valm(ctu->sv0, 5)); - rsnd_mod_write(mod, CTU_SV06R, rsnd_kctrl_valm(ctu->sv0, 6)); - rsnd_mod_write(mod, CTU_SV07R, rsnd_kctrl_valm(ctu->sv0, 7)); - } - if (scmdr > 1) { - rsnd_mod_write(mod, CTU_SV10R, rsnd_kctrl_valm(ctu->sv1, 0)); - rsnd_mod_write(mod, CTU_SV11R, rsnd_kctrl_valm(ctu->sv1, 1)); - rsnd_mod_write(mod, CTU_SV12R, rsnd_kctrl_valm(ctu->sv1, 2)); - rsnd_mod_write(mod, CTU_SV13R, rsnd_kctrl_valm(ctu->sv1, 3)); - rsnd_mod_write(mod, CTU_SV14R, rsnd_kctrl_valm(ctu->sv1, 4)); - rsnd_mod_write(mod, CTU_SV15R, rsnd_kctrl_valm(ctu->sv1, 5)); - rsnd_mod_write(mod, CTU_SV16R, rsnd_kctrl_valm(ctu->sv1, 6)); - rsnd_mod_write(mod, CTU_SV17R, rsnd_kctrl_valm(ctu->sv1, 7)); - } - if (scmdr > 2) { - rsnd_mod_write(mod, CTU_SV20R, rsnd_kctrl_valm(ctu->sv2, 0)); - rsnd_mod_write(mod, CTU_SV21R, rsnd_kctrl_valm(ctu->sv2, 1)); - rsnd_mod_write(mod, CTU_SV22R, rsnd_kctrl_valm(ctu->sv2, 2)); - rsnd_mod_write(mod, CTU_SV23R, rsnd_kctrl_valm(ctu->sv2, 3)); - rsnd_mod_write(mod, CTU_SV24R, rsnd_kctrl_valm(ctu->sv2, 4)); - rsnd_mod_write(mod, CTU_SV25R, rsnd_kctrl_valm(ctu->sv2, 5)); - rsnd_mod_write(mod, CTU_SV26R, rsnd_kctrl_valm(ctu->sv2, 6)); - rsnd_mod_write(mod, CTU_SV27R, rsnd_kctrl_valm(ctu->sv2, 7)); - } - if (scmdr > 3) { - rsnd_mod_write(mod, CTU_SV30R, rsnd_kctrl_valm(ctu->sv3, 0)); - rsnd_mod_write(mod, CTU_SV31R, rsnd_kctrl_valm(ctu->sv3, 1)); - rsnd_mod_write(mod, CTU_SV32R, rsnd_kctrl_valm(ctu->sv3, 2)); - rsnd_mod_write(mod, CTU_SV33R, rsnd_kctrl_valm(ctu->sv3, 3)); - rsnd_mod_write(mod, CTU_SV34R, rsnd_kctrl_valm(ctu->sv3, 4)); - rsnd_mod_write(mod, CTU_SV35R, rsnd_kctrl_valm(ctu->sv3, 5)); - rsnd_mod_write(mod, CTU_SV36R, rsnd_kctrl_valm(ctu->sv3, 6)); - rsnd_mod_write(mod, CTU_SV37R, rsnd_kctrl_valm(ctu->sv3, 7)); + for (i = 0; i < 4; i++) { + + if (i >= scmdr) + break; + + for (j = 0; j < RSND_MAX_CHANNELS; j++) + rsnd_mod_write(mod, CTU_SVxxR(i, j), rsnd_kctrl_valm(ctu->sv[i], j)); } rsnd_mod_write(mod, CTU_CTUIR, 0); @@ -194,10 +159,10 @@ static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io, for (i = 0; i < RSND_MAX_CHANNELS; i++) { rsnd_kctrl_valm(ctu->pass, i) = 0; - rsnd_kctrl_valm(ctu->sv0, i) = 0; - rsnd_kctrl_valm(ctu->sv1, i) = 0; - rsnd_kctrl_valm(ctu->sv2, i) = 0; - rsnd_kctrl_valm(ctu->sv3, i) = 0; + rsnd_kctrl_valm(ctu->sv[0], i) = 0; + rsnd_kctrl_valm(ctu->sv[1], i) = 0; + rsnd_kctrl_valm(ctu->sv[2], i) = 0; + rsnd_kctrl_valm(ctu->sv[3], i) = 0; } rsnd_kctrl_vals(ctu->reset) = 0; } @@ -247,7 +212,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv0, RSND_MAX_CHANNELS, + &ctu->sv[0], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -256,7 +221,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv1, RSND_MAX_CHANNELS, + &ctu->sv[1], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -265,7 +230,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv2, RSND_MAX_CHANNELS, + &ctu->sv[2], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; @@ -274,7 +239,7 @@ static int rsnd_ctu_pcm_new(struct rsnd_mod *mod, ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3", rsnd_kctrl_accept_anytime, NULL, - &ctu->sv3, RSND_MAX_CHANNELS, + &ctu->sv[3], RSND_MAX_CHANNELS, 0x00FFFFFF); if (ret < 0) return ret; diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index d65f24bec991..17eff3e60bbe 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c @@ -89,14 +89,8 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io, val[i] = rsnd_kctrl_valm(dvc->volume, i); /* Enable Digital Volume */ - rsnd_mod_write(mod, DVC_VOL0R, val[0]); - rsnd_mod_write(mod, DVC_VOL1R, val[1]); - rsnd_mod_write(mod, DVC_VOL2R, val[2]); - rsnd_mod_write(mod, DVC_VOL3R, val[3]); - rsnd_mod_write(mod, DVC_VOL4R, val[4]); - rsnd_mod_write(mod, DVC_VOL5R, val[5]); - rsnd_mod_write(mod, DVC_VOL6R, val[6]); - rsnd_mod_write(mod, DVC_VOL7R, val[7]); + for (i = 0; i < RSND_MAX_CHANNELS; i++) + rsnd_mod_write(mod, DVC_VOLxR(i), val[i]); } static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io, diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index 3283d677c937..7cda60188f41 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c @@ -26,8 +26,8 @@ struct rsnd_gen { struct regmap *regmap[RSND_BASE_MAX]; /* RSND_REG_MAX base */ - struct regmap_field *regs[RSND_REG_MAX]; - const char *reg_name[RSND_REG_MAX]; + struct regmap_field *regs[REG_MAX]; + const char *reg_name[REG_MAX]; }; #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) @@ -49,11 +49,11 @@ struct rsnd_regmap_field_conf { } /* single address mapping */ #define RSND_GEN_S_REG(id, offset) \ - RSND_REG_SET(RSND_REG_##id, offset, 0, #id) + RSND_REG_SET(id, offset, 0, #id) /* multi address mapping */ #define RSND_GEN_M_REG(id, offset, _id_offset) \ - RSND_REG_SET(RSND_REG_##id, offset, _id_offset, #id) + RSND_REG_SET(id, offset, _id_offset, #id) /* * basic function @@ -79,9 +79,9 @@ static int rsnd_mod_id_cmd(struct rsnd_mod *mod) return rsnd_mod_id(mod); } -u32 rsnd_read(struct rsnd_priv *priv, - struct rsnd_mod *mod, enum rsnd_reg reg) +u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); u32 val; @@ -98,10 +98,10 @@ u32 rsnd_read(struct rsnd_priv *priv, return val; } -void rsnd_write(struct rsnd_priv *priv, - struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data) +void rsnd_mod_write(struct rsnd_mod *mod, + enum rsnd_reg reg, u32 data) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); @@ -115,9 +115,10 @@ void rsnd_write(struct rsnd_priv *priv, rsnd_reg_name(gen, reg), reg, data); } -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 mask, u32 data) +void rsnd_mod_bset(struct rsnd_mod *mod, + enum rsnd_reg reg, u32 mask, u32 data) { + struct rsnd_priv *priv = rsnd_mod_to_priv(mod); struct device *dev = rsnd_priv_to_dev(priv); struct rsnd_gen *gen = rsnd_priv_to_gen(priv); diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 94d51fee8999..605e4b934982 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h @@ -42,165 +42,175 @@ */ enum rsnd_reg { /* SCU (MIX/CTU/DVC) */ - RSND_REG_SRC_I_BUSIF_MODE, - RSND_REG_SRC_O_BUSIF_MODE, - RSND_REG_SRC_ROUTE_MODE0, - RSND_REG_SRC_SWRSR, - RSND_REG_SRC_SRCIR, - RSND_REG_SRC_ADINR, - RSND_REG_SRC_IFSCR, - RSND_REG_SRC_IFSVR, - RSND_REG_SRC_SRCCR, - RSND_REG_SRC_CTRL, - RSND_REG_SRC_BSDSR, - RSND_REG_SRC_BSISR, - RSND_REG_SRC_INT_ENABLE0, - RSND_REG_SRC_BUSIF_DALIGN, - RSND_REG_SRCIN_TIMSEL0, - RSND_REG_SRCIN_TIMSEL1, - RSND_REG_SRCIN_TIMSEL2, - RSND_REG_SRCIN_TIMSEL3, - RSND_REG_SRCIN_TIMSEL4, - RSND_REG_SRCOUT_TIMSEL0, - RSND_REG_SRCOUT_TIMSEL1, - RSND_REG_SRCOUT_TIMSEL2, - RSND_REG_SRCOUT_TIMSEL3, - RSND_REG_SRCOUT_TIMSEL4, - RSND_REG_SCU_SYS_STATUS0, - RSND_REG_SCU_SYS_STATUS1, - RSND_REG_SCU_SYS_INT_EN0, - RSND_REG_SCU_SYS_INT_EN1, - RSND_REG_CMD_CTRL, - RSND_REG_CMD_BUSIF_MODE, - RSND_REG_CMD_BUSIF_DALIGN, - RSND_REG_CMD_ROUTE_SLCT, - RSND_REG_CMDOUT_TIMSEL, - RSND_REG_CTU_SWRSR, - RSND_REG_CTU_CTUIR, - RSND_REG_CTU_ADINR, - RSND_REG_CTU_CPMDR, - RSND_REG_CTU_SCMDR, - RSND_REG_CTU_SV00R, - RSND_REG_CTU_SV01R, - RSND_REG_CTU_SV02R, - RSND_REG_CTU_SV03R, - RSND_REG_CTU_SV04R, - RSND_REG_CTU_SV05R, - RSND_REG_CTU_SV06R, - RSND_REG_CTU_SV07R, - RSND_REG_CTU_SV10R, - RSND_REG_CTU_SV11R, - RSND_REG_CTU_SV12R, - RSND_REG_CTU_SV13R, - RSND_REG_CTU_SV14R, - RSND_REG_CTU_SV15R, - RSND_REG_CTU_SV16R, - RSND_REG_CTU_SV17R, - RSND_REG_CTU_SV20R, - RSND_REG_CTU_SV21R, - RSND_REG_CTU_SV22R, - RSND_REG_CTU_SV23R, - RSND_REG_CTU_SV24R, - RSND_REG_CTU_SV25R, - RSND_REG_CTU_SV26R, - RSND_REG_CTU_SV27R, - RSND_REG_CTU_SV30R, - RSND_REG_CTU_SV31R, - RSND_REG_CTU_SV32R, - RSND_REG_CTU_SV33R, - RSND_REG_CTU_SV34R, - RSND_REG_CTU_SV35R, - RSND_REG_CTU_SV36R, - RSND_REG_CTU_SV37R, - RSND_REG_MIX_SWRSR, - RSND_REG_MIX_MIXIR, - RSND_REG_MIX_ADINR, - RSND_REG_MIX_MIXMR, - RSND_REG_MIX_MVPDR, - RSND_REG_MIX_MDBAR, - RSND_REG_MIX_MDBBR, - RSND_REG_MIX_MDBCR, - RSND_REG_MIX_MDBDR, - RSND_REG_MIX_MDBER, - RSND_REG_DVC_SWRSR, - RSND_REG_DVC_DVUIR, - RSND_REG_DVC_ADINR, - RSND_REG_DVC_DVUCR, - RSND_REG_DVC_ZCMCR, - RSND_REG_DVC_VOL0R, - RSND_REG_DVC_VOL1R, - RSND_REG_DVC_VOL2R, - RSND_REG_DVC_VOL3R, - RSND_REG_DVC_VOL4R, - RSND_REG_DVC_VOL5R, - RSND_REG_DVC_VOL6R, - RSND_REG_DVC_VOL7R, - RSND_REG_DVC_DVUER, - RSND_REG_DVC_VRCTR, - RSND_REG_DVC_VRPDR, - RSND_REG_DVC_VRDBR, + SRC_I_BUSIF_MODE, + SRC_O_BUSIF_MODE, + SRC_ROUTE_MODE0, + SRC_SWRSR, + SRC_SRCIR, + SRC_ADINR, + SRC_IFSCR, + SRC_IFSVR, + SRC_SRCCR, + SRC_CTRL, + SRC_BSDSR, + SRC_BSISR, + SRC_INT_ENABLE0, + SRC_BUSIF_DALIGN, + SRCIN_TIMSEL0, + SRCIN_TIMSEL1, + SRCIN_TIMSEL2, + SRCIN_TIMSEL3, + SRCIN_TIMSEL4, + SRCOUT_TIMSEL0, + SRCOUT_TIMSEL1, + SRCOUT_TIMSEL2, + SRCOUT_TIMSEL3, + SRCOUT_TIMSEL4, + SCU_SYS_STATUS0, + SCU_SYS_STATUS1, + SCU_SYS_INT_EN0, + SCU_SYS_INT_EN1, + CMD_CTRL, + CMD_BUSIF_MODE, + CMD_BUSIF_DALIGN, + CMD_ROUTE_SLCT, + CMDOUT_TIMSEL, + CTU_SWRSR, + CTU_CTUIR, + CTU_ADINR, + CTU_CPMDR, + CTU_SCMDR, + CTU_SV00R, + CTU_SV01R, + CTU_SV02R, + CTU_SV03R, + CTU_SV04R, + CTU_SV05R, + CTU_SV06R, + CTU_SV07R, + CTU_SV10R, + CTU_SV11R, + CTU_SV12R, + CTU_SV13R, + CTU_SV14R, + CTU_SV15R, + CTU_SV16R, + CTU_SV17R, + CTU_SV20R, + CTU_SV21R, + CTU_SV22R, + CTU_SV23R, + CTU_SV24R, + CTU_SV25R, + CTU_SV26R, + CTU_SV27R, + CTU_SV30R, + CTU_SV31R, + CTU_SV32R, + CTU_SV33R, + CTU_SV34R, + CTU_SV35R, + CTU_SV36R, + CTU_SV37R, + MIX_SWRSR, + MIX_MIXIR, + MIX_ADINR, + MIX_MIXMR, + MIX_MVPDR, + MIX_MDBAR, + MIX_MDBBR, + MIX_MDBCR, + MIX_MDBDR, + MIX_MDBER, + DVC_SWRSR, + DVC_DVUIR, + DVC_ADINR, + DVC_DVUCR, + DVC_ZCMCR, + DVC_VOL0R, + DVC_VOL1R, + DVC_VOL2R, + DVC_VOL3R, + DVC_VOL4R, + DVC_VOL5R, + DVC_VOL6R, + DVC_VOL7R, + DVC_DVUER, + DVC_VRCTR, + DVC_VRPDR, + DVC_VRDBR, /* ADG */ - RSND_REG_BRRA, - RSND_REG_BRRB, - RSND_REG_BRGCKR, - RSND_REG_DIV_EN, - RSND_REG_AUDIO_CLK_SEL0, - RSND_REG_AUDIO_CLK_SEL1, - RSND_REG_AUDIO_CLK_SEL2, + BRRA, + BRRB, + BRGCKR, + DIV_EN, + AUDIO_CLK_SEL0, + AUDIO_CLK_SEL1, + AUDIO_CLK_SEL2, /* SSIU */ - RSND_REG_SSI_MODE, - RSND_REG_SSI_MODE0, - RSND_REG_SSI_MODE1, - RSND_REG_SSI_MODE2, - RSND_REG_SSI_CONTROL, - RSND_REG_SSI_CTRL, - RSND_REG_SSI_BUSIF0_MODE, - RSND_REG_SSI_BUSIF0_ADINR, - RSND_REG_SSI_BUSIF0_DALIGN, - RSND_REG_SSI_BUSIF1_MODE, - RSND_REG_SSI_BUSIF1_ADINR, - RSND_REG_SSI_BUSIF1_DALIGN, - RSND_REG_SSI_BUSIF2_MODE, - RSND_REG_SSI_BUSIF2_ADINR, - RSND_REG_SSI_BUSIF2_DALIGN, - RSND_REG_SSI_BUSIF3_MODE, - RSND_REG_SSI_BUSIF3_ADINR, - RSND_REG_SSI_BUSIF3_DALIGN, - RSND_REG_SSI_BUSIF4_MODE, - RSND_REG_SSI_BUSIF4_ADINR, - RSND_REG_SSI_BUSIF4_DALIGN, - RSND_REG_SSI_BUSIF5_MODE, - RSND_REG_SSI_BUSIF5_ADINR, - RSND_REG_SSI_BUSIF5_DALIGN, - RSND_REG_SSI_BUSIF6_MODE, - RSND_REG_SSI_BUSIF6_ADINR, - RSND_REG_SSI_BUSIF6_DALIGN, - RSND_REG_SSI_BUSIF7_MODE, - RSND_REG_SSI_BUSIF7_ADINR, - RSND_REG_SSI_BUSIF7_DALIGN, - RSND_REG_SSI_INT_ENABLE, - RSND_REG_SSI_SYS_STATUS0, - RSND_REG_SSI_SYS_STATUS1, - RSND_REG_SSI_SYS_STATUS2, - RSND_REG_SSI_SYS_STATUS3, - RSND_REG_SSI_SYS_STATUS4, - RSND_REG_SSI_SYS_STATUS5, - RSND_REG_SSI_SYS_STATUS6, - RSND_REG_SSI_SYS_STATUS7, - RSND_REG_HDMI0_SEL, - RSND_REG_HDMI1_SEL, + SSI_MODE, + SSI_MODE0, + SSI_MODE1, + SSI_MODE2, + SSI_CONTROL, + SSI_CTRL, + SSI_BUSIF0_MODE, + SSI_BUSIF1_MODE, + SSI_BUSIF2_MODE, + SSI_BUSIF3_MODE, + SSI_BUSIF4_MODE, + SSI_BUSIF5_MODE, + SSI_BUSIF6_MODE, + SSI_BUSIF7_MODE, + SSI_BUSIF0_ADINR, + SSI_BUSIF1_ADINR, + SSI_BUSIF2_ADINR, + SSI_BUSIF3_ADINR, + SSI_BUSIF4_ADINR, + SSI_BUSIF5_ADINR, + SSI_BUSIF6_ADINR, + SSI_BUSIF7_ADINR, + SSI_BUSIF0_DALIGN, + SSI_BUSIF1_DALIGN, + SSI_BUSIF2_DALIGN, + SSI_BUSIF3_DALIGN, + SSI_BUSIF4_DALIGN, + SSI_BUSIF5_DALIGN, + SSI_BUSIF6_DALIGN, + SSI_BUSIF7_DALIGN, + SSI_INT_ENABLE, + SSI_SYS_STATUS0, + SSI_SYS_STATUS1, + SSI_SYS_STATUS2, + SSI_SYS_STATUS3, + SSI_SYS_STATUS4, + SSI_SYS_STATUS5, + SSI_SYS_STATUS6, + SSI_SYS_STATUS7, + HDMI0_SEL, + HDMI1_SEL, /* SSI */ - RSND_REG_SSICR, - RSND_REG_SSISR, - RSND_REG_SSITDR, - RSND_REG_SSIRDR, - RSND_REG_SSIWSR, + SSICR, + SSISR, + SSITDR, + SSIRDR, + SSIWSR, - RSND_REG_MAX, + REG_MAX, }; +#define SRCIN_TIMSEL(i) (SRCIN_TIMSEL0 + (i)) +#define SRCOUT_TIMSEL(i) (SRCOUT_TIMSEL0 + (i)) +#define CTU_SVxxR(i, j) (CTU_SV00R + (i * 8) + (j)) +#define DVC_VOLxR(i) (DVC_VOL0R + (i)) +#define AUDIO_CLK_SEL(i) (AUDIO_CLK_SEL0 + (i)) +#define SSI_BUSIF_MODE(i) (SSI_BUSIF0_MODE + (i)) +#define SSI_BUSIF_ADINR(i) (SSI_BUSIF0_ADINR + (i)) +#define SSI_BUSIF_DALIGN(i) (SSI_BUSIF0_DALIGN + (i)) +#define SSI_SYS_STATUS(i) (SSI_SYS_STATUS0 + (i)) + struct rsnd_priv; struct rsnd_mod; @@ -210,20 +220,9 @@ struct rsnd_dai_stream; /* * R-Car basic functions */ -#define rsnd_mod_read(m, r) \ - rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) -#define rsnd_mod_write(m, r, d) \ - rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) -#define rsnd_mod_bset(m, r, s, d) \ - rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) - -u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); -void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data); -void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod, - enum rsnd_reg reg, u32 data); -void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, - u32 mask, u32 data); +u32 rsnd_mod_read(struct rsnd_mod *mod, enum rsnd_reg reg); +void rsnd_mod_write(struct rsnd_mod *mod, enum rsnd_reg reg, u32 data); +void rsnd_mod_bset(struct rsnd_mod *mod, enum rsnd_reg reg, u32 mask, u32 data); u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io); u32 rsnd_get_busif_shift(struct rsnd_dai_stream *io, struct rsnd_mod *mod); diff --git a/sound/soc/sh/rcar/ssiu.c b/sound/soc/sh/rcar/ssiu.c index 04b1a4836bd3..c5934adcfd01 100644 --- a/sound/soc/sh/rcar/ssiu.c +++ b/sound/soc/sh/rcar/ssiu.c @@ -65,6 +65,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, int id = rsnd_mod_id(mod); u32 mask1, val1; u32 mask2, val2; + int i; /* clear status */ switch (id) { @@ -73,16 +74,12 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod, case 2: case 3: case 4: - rsnd_mod_write(mod, SSI_SYS_STATUS0, 0xf << (id * 4)); - rsnd_mod_write(mod, SSI_SYS_STATUS2, 0xf << (id * 4)); - rsnd_mod_write(mod, SSI_SYS_STATUS4, 0xf << (id * 4)); - rsnd_mod_write(mod, SSI_SYS_STATUS6, 0xf << (id * 4)); + for (i = 0; i < 4; i++) + rsnd_mod_write(mod, SSI_SYS_STATUS(i * 2), 0xf << (id * 4)); break; case 9: - rsnd_mod_write(mod, SSI_SYS_STATUS1, 0xf << 4); - rsnd_mod_write(mod, SSI_SYS_STATUS3, 0xf << 4); - rsnd_mod_write(mod, SSI_SYS_STATUS5, 0xf << 4); - rsnd_mod_write(mod, SSI_SYS_STATUS7, 0xf << 4); + for (i = 0; i < 4; i++) + rsnd_mod_write(mod, SSI_SYS_STATUS((i * 2) + 1), 0xf << (id * 4)); break; } @@ -198,43 +195,15 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod, id, busif); } -#define RSND_WRITE_BUSIF(i) \ - rsnd_mod_write(mod, SSI_BUSIF##i##_ADINR, \ - rsnd_get_adinr_bit(mod, io) | \ - (rsnd_io_is_play(io) ? \ - rsnd_runtime_channel_after_ctu(io) : \ - rsnd_runtime_channel_original(io))); \ - rsnd_mod_write(mod, SSI_BUSIF##i##_MODE, \ - rsnd_get_busif_shift(io, mod) | 1); \ - rsnd_mod_write(mod, SSI_BUSIF##i##_DALIGN, \ - rsnd_get_dalign(mod, io)) - - switch (busif) { - case 0: - RSND_WRITE_BUSIF(0); - break; - case 1: - RSND_WRITE_BUSIF(1); - break; - case 2: - RSND_WRITE_BUSIF(2); - break; - case 3: - RSND_WRITE_BUSIF(3); - break; - case 4: - RSND_WRITE_BUSIF(4); - break; - case 5: - RSND_WRITE_BUSIF(5); - break; - case 6: - RSND_WRITE_BUSIF(6); - break; - case 7: - RSND_WRITE_BUSIF(7); - break; - } + rsnd_mod_write(mod, SSI_BUSIF_ADINR(busif), + rsnd_get_adinr_bit(mod, io) | + (rsnd_io_is_play(io) ? + rsnd_runtime_channel_after_ctu(io) : + rsnd_runtime_channel_original(io))); + rsnd_mod_write(mod, SSI_BUSIF_MODE(busif), + rsnd_get_busif_shift(io, mod) | 1); + rsnd_mod_write(mod, SSI_BUSIF_DALIGN(busif), + rsnd_get_dalign(mod, io)); } if (has_hdmi0 || has_hdmi1) { -- cgit v1.2.3-58-ga151 From add6883619a9e3bf9658eaff1a547354131bbcd9 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 13 Dec 2018 00:08:38 -0200 Subject: ASoC: fsl: Fix SND_SOC_EUKREA_TLV320 build error on i.MX8M eukrea-tlv320.c machine driver runs on non-DT platforms and include header file in order to be able to use some machine_is_eukrea_xxx() macros. Building it for ARM64 causes the following build error: sound/soc/fsl/eukrea-tlv320.c:28:10: fatal error: asm/mach-types.h: No such file or directory Avoid this error by not allowing to build the SND_SOC_EUKREA_TLV320 driver when ARM64 is selected. This is needed in preparation for the i.MX8M support. Reported-by: kbuild test robot Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 6ec19fb4a934..2e75b5bc5f1d 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -221,7 +221,7 @@ config SND_SOC_PHYCORE_AC97 config SND_SOC_EUKREA_TLV320 tristate "Eukrea TLV320" - depends on ARCH_MXC && I2C + depends on ARCH_MXC && !ARM64 && I2C select SND_SOC_TLV320AIC23_I2C select SND_SOC_IMX_AUDMUX select SND_SOC_IMX_SSI -- cgit v1.2.3-58-ga151 From 869858f84a65b646052eff1ec75c1f49e638b62b Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 13 Dec 2018 14:15:20 +0900 Subject: ASoC: simple-card: Dereference pointer for memcpy sizeof in asoc_simple_card_probe Commit 4fb7f4df49d3 ("ASoC: simple-card: use cpu/codec pointer on simple_dai_props") updated {cpu,codec}_dai to be pointers in struct simple_dai_props but didn't update these locations to dereference the pointers. This patch fixup it for non DT simple-card use case. Signed-off-by: Nathan Chancellor Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 766123485d7c..93d68161f953 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -429,6 +429,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct asoc_simple_card_info *cinfo; struct snd_soc_dai_link_component *codecs; struct snd_soc_dai_link_component *platform; + int dai_idx = 0; cinfo = dev->platform_data; if (!cinfo) { @@ -445,6 +446,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev) return -EINVAL; } + dai_props->cpu_dai = &priv->dais[dai_idx++]; + dai_props->codec_dai = &priv->dais[dai_idx++]; + codecs = dai_link->codecs; codecs->name = cinfo->codec; codecs->dai_name = cinfo->codec_dai.name; @@ -458,10 +462,10 @@ static int asoc_simple_card_probe(struct platform_device *pdev) dai_link->cpu_dai_name = cinfo->cpu_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_card_dai_init; - memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, - sizeof(priv->dai_props->cpu_dai)); - memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai, - sizeof(priv->dai_props->codec_dai)); + memcpy(priv->dai_props->cpu_dai, &cinfo->cpu_dai, + sizeof(*priv->dai_props->cpu_dai)); + memcpy(priv->dai_props->codec_dai, &cinfo->codec_dai, + sizeof(*priv->dai_props->codec_dai)); } snd_soc_card_set_drvdata(card, priv); -- cgit v1.2.3-58-ga151 From 984463a94d5cb23120bfe7d689077b940c25128b Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 11 Dec 2018 14:47:10 +0100 Subject: ASoC: meson: axg-toddr: add support for spdifin backend add IEC958_SUBFRAME_LE to the list of format accepted by the fifo frontend. As opposed to what was initially noted in the toddr dai driver, the spdifin does not place the msb at bit 28, it just output a whole spdif subframe. Placing the msb at bit 28 in the toddr driver just filters out the parity, user, channel status and validity bits. It is better to just provide the whole spdif subframe to the userspace and let the iec958 plugin deal with it. Signed-off-by: Jerome Brunet Signed-off-by: Mark Brown --- sound/soc/meson/axg-fifo.h | 3 ++- sound/soc/meson/axg-toddr.c | 15 +++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/meson/axg-fifo.h b/sound/soc/meson/axg-fifo.h index cb6c4013ca33..d9f516cfbeda 100644 --- a/sound/soc/meson/axg-fifo.h +++ b/sound/soc/meson/axg-fifo.h @@ -25,7 +25,8 @@ struct snd_soc_pcm_runtime; SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S20_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) #define AXG_FIFO_BURST 8 #define AXG_FIFO_MIN_CNT 64 diff --git a/sound/soc/meson/axg-toddr.c b/sound/soc/meson/axg-toddr.c index c2c9bb312586..0e9ca3882ae5 100644 --- a/sound/soc/meson/axg-toddr.c +++ b/sound/soc/meson/axg-toddr.c @@ -25,6 +25,8 @@ #define CTRL0_TODDR_LSB_POS_MASK GENMASK(7, 3) #define CTRL0_TODDR_LSB_POS(x) ((x) << 3) +#define TODDR_MSB_POS 31 + static int axg_toddr_pcm_new(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai) { @@ -36,14 +38,7 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai); - unsigned int type, width, msb = 31; - - /* - * NOTE: - * Almost all backend will place the MSB at bit 31, except SPDIF Input - * which will put it at index 28. When adding support for the SPDIF - * Input, we'll need to find which type of backend we are connected to. - */ + unsigned int type, width; switch (params_physical_width(params)) { case 8: @@ -66,8 +61,8 @@ static int axg_toddr_dai_hw_params(struct snd_pcm_substream *substream, CTRL0_TODDR_MSB_POS_MASK | CTRL0_TODDR_LSB_POS_MASK, CTRL0_TODDR_TYPE(type) | - CTRL0_TODDR_MSB_POS(msb) | - CTRL0_TODDR_LSB_POS(msb - (width - 1))); + CTRL0_TODDR_MSB_POS(TODDR_MSB_POS) | + CTRL0_TODDR_LSB_POS(TODDR_MSB_POS - (width - 1))); return 0; } -- cgit v1.2.3-58-ga151 From 5ce5658375e6de2468c4884f7ae474c4ed40a13f Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Tue, 11 Dec 2018 14:47:12 +0100 Subject: ASoC: meson: add axg spdif input Add support for the spdif input decoder of the axg SoC family Signed-off-by: Jerome Brunet Signed-off-by: Mark Brown --- sound/soc/meson/Kconfig | 9 +- sound/soc/meson/Makefile | 2 + sound/soc/meson/axg-spdifin.c | 521 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 sound/soc/meson/axg-spdifin.c (limited to 'sound') diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig index 8b8426ed2363..8779fe23671d 100644 --- a/sound/soc/meson/Kconfig +++ b/sound/soc/meson/Kconfig @@ -54,6 +54,7 @@ config SND_MESON_AXG_SOUND_CARD imply SND_MESON_AXG_TDMIN imply SND_MESON_AXG_TDMOUT imply SND_MESON_AXG_SPDIFOUT + imply SND_MESON_AXG_SPDIFIN imply SND_MESON_AXG_PDM help Select Y or M to add support for the AXG SoC sound card @@ -67,6 +68,13 @@ config SND_MESON_AXG_SPDIFOUT Select Y or M to add support for SPDIF output serializer embedded in the Amlogic AXG SoC family +config SND_MESON_AXG_SPDIFIN + tristate "Amlogic AXG SPDIF Input Support" + imply SND_SOC_SPDIF + help + Select Y or M to add support for SPDIF input embedded + in the Amlogic AXG SoC family + config SND_MESON_AXG_PDM tristate "Amlogic AXG PDM Input Support" imply SND_SOC_DMIC @@ -74,5 +82,4 @@ config SND_MESON_AXG_PDM help Select Y or M to add support for PDM input embedded in the Amlogic AXG SoC family - endmenu diff --git a/sound/soc/meson/Makefile b/sound/soc/meson/Makefile index 4cd25104029d..b45dfb9e2f88 100644 --- a/sound/soc/meson/Makefile +++ b/sound/soc/meson/Makefile @@ -8,6 +8,7 @@ snd-soc-meson-axg-tdm-interface-objs := axg-tdm-interface.o snd-soc-meson-axg-tdmin-objs := axg-tdmin.o snd-soc-meson-axg-tdmout-objs := axg-tdmout.o snd-soc-meson-axg-sound-card-objs := axg-card.o +snd-soc-meson-axg-spdifin-objs := axg-spdifin.o snd-soc-meson-axg-spdifout-objs := axg-spdifout.o snd-soc-meson-axg-pdm-objs := axg-pdm.o @@ -19,5 +20,6 @@ obj-$(CONFIG_SND_MESON_AXG_TDM_INTERFACE) += snd-soc-meson-axg-tdm-interface.o obj-$(CONFIG_SND_MESON_AXG_TDMIN) += snd-soc-meson-axg-tdmin.o obj-$(CONFIG_SND_MESON_AXG_TDMOUT) += snd-soc-meson-axg-tdmout.o obj-$(CONFIG_SND_MESON_AXG_SOUND_CARD) += snd-soc-meson-axg-sound-card.o +obj-$(CONFIG_SND_MESON_AXG_SPDIFIN) += snd-soc-meson-axg-spdifin.o obj-$(CONFIG_SND_MESON_AXG_SPDIFOUT) += snd-soc-meson-axg-spdifout.o obj-$(CONFIG_SND_MESON_AXG_PDM) += snd-soc-meson-axg-pdm.o diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c new file mode 100644 index 000000000000..09f385a2bbba --- /dev/null +++ b/sound/soc/meson/axg-spdifin.c @@ -0,0 +1,521 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +// +// Copyright (c) 2018 BayLibre, SAS. +// Author: Jerome Brunet + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPDIFIN_CTRL0 0x00 +#define SPDIFIN_CTRL0_EN BIT(31) +#define SPDIFIN_CTRL0_RST_OUT BIT(29) +#define SPDIFIN_CTRL0_RST_IN BIT(28) +#define SPDIFIN_CTRL0_WIDTH_SEL BIT(24) +#define SPDIFIN_CTRL0_STATUS_CH_SHIFT 11 +#define SPDIFIN_CTRL0_STATUS_SEL GENMASK(10, 8) +#define SPDIFIN_CTRL0_SRC_SEL GENMASK(5, 4) +#define SPDIFIN_CTRL0_CHK_VALID BIT(3) +#define SPDIFIN_CTRL1 0x04 +#define SPDIFIN_CTRL1_BASE_TIMER GENMASK(19, 0) +#define SPDIFIN_CTRL1_IRQ_MASK GENMASK(27, 20) +#define SPDIFIN_CTRL2 0x08 +#define SPDIFIN_THRES_PER_REG 3 +#define SPDIFIN_THRES_WIDTH 10 +#define SPDIFIN_CTRL3 0x0c +#define SPDIFIN_CTRL4 0x10 +#define SPDIFIN_TIMER_PER_REG 4 +#define SPDIFIN_TIMER_WIDTH 8 +#define SPDIFIN_CTRL5 0x14 +#define SPDIFIN_CTRL6 0x18 +#define SPDIFIN_STAT0 0x1c +#define SPDIFIN_STAT0_MODE GENMASK(30, 28) +#define SPDIFIN_STAT0_MAXW GENMASK(17, 8) +#define SPDIFIN_STAT0_IRQ GENMASK(7, 0) +#define SPDIFIN_IRQ_MODE_CHANGED BIT(2) +#define SPDIFIN_STAT1 0x20 +#define SPDIFIN_STAT2 0x24 +#define SPDIFIN_MUTE_VAL 0x28 + +#define SPDIFIN_MODE_NUM 7 + +struct axg_spdifin_cfg { + const unsigned int *mode_rates; + unsigned int ref_rate; +}; + +struct axg_spdifin { + const struct axg_spdifin_cfg *conf; + struct regmap *map; + struct clk *refclk; + struct clk *pclk; +}; + +/* + * TODO: + * It would have been nice to check the actual rate against the sample rate + * requested in hw_params(). Unfortunately, I was not able to make the mode + * detection and IRQ work reliably: + * + * 1. IRQs are generated on mode change only, so there is no notification + * on transition between no signal and mode 0 (32kHz). + * 2. Mode detection very often has glitches, and may detects the + * lowest or the highest mode before zeroing in on the actual mode. + * + * This makes calling snd_pcm_stop() difficult to get right. Even notifying + * the kcontrol would be very unreliable at this point. + * Let's keep things simple until the magic spell that makes this work is + * found. + */ + +static unsigned int axg_spdifin_get_rate(struct axg_spdifin *priv) +{ + unsigned int stat, mode, rate = 0; + + regmap_read(priv->map, SPDIFIN_STAT0, &stat); + mode = FIELD_GET(SPDIFIN_STAT0_MODE, stat); + + /* + * If max width is zero, we are not capturing anything. + * Also Sometimes, when the capture is on but there is no data, + * mode is SPDIFIN_MODE_NUM, but not always ... + */ + if (FIELD_GET(SPDIFIN_STAT0_MAXW, stat) && + mode < SPDIFIN_MODE_NUM) + rate = priv->conf->mode_rates[mode]; + + return rate; +} + +static int axg_spdifin_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + + /* Apply both reset */ + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_RST_OUT | + SPDIFIN_CTRL0_RST_IN, + 0); + + /* Clear out reset before in reset */ + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_RST_OUT, SPDIFIN_CTRL0_RST_OUT); + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_RST_IN, SPDIFIN_CTRL0_RST_IN); + + return 0; +} + +static int axg_spdifin_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_prepare_enable(priv->refclk); + if (ret) { + dev_err(dai->dev, + "failed to enable spdifin reference clock\n"); + return ret; + } + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, + SPDIFIN_CTRL0_EN); + + return 0; +} + +static void axg_spdifin_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, SPDIFIN_CTRL0_EN, 0); + clk_disable_unprepare(priv->refclk); +} + +static void axg_spdifin_write_mode_param(struct regmap *map, int mode, + unsigned int val, + unsigned int num_per_reg, + unsigned int base_reg, + unsigned int width) +{ + unsigned int offset = mode, rem; + unsigned int reg, shift; + + rem = do_div(offset, num_per_reg); + + reg = offset * regmap_get_reg_stride(map) + base_reg; + shift = width * (num_per_reg - 1 - rem); + + regmap_update_bits(map, reg, GENMASK(width - 1, 0) << shift, + val << shift); +} + +static void axg_spdifin_write_timer(struct regmap *map, int mode, + unsigned int val) +{ + axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_TIMER_PER_REG, + SPDIFIN_CTRL4, SPDIFIN_TIMER_WIDTH); +} + +static void axg_spdifin_write_threshold(struct regmap *map, int mode, + unsigned int val) +{ + axg_spdifin_write_mode_param(map, mode, val, SPDIFIN_THRES_PER_REG, + SPDIFIN_CTRL2, SPDIFIN_THRES_WIDTH); +} + +static unsigned int axg_spdifin_mode_timer(struct axg_spdifin *priv, + int mode, + unsigned int rate) +{ + /* + * Number of period of the reference clock during a period of the + * input signal reference clock + */ + return rate / (128 * priv->conf->mode_rates[mode]); +} + +static int axg_spdifin_sample_mode_config(struct snd_soc_dai *dai, + struct axg_spdifin *priv) +{ + unsigned int rate, t_next; + int ret, i = SPDIFIN_MODE_NUM - 1; + + /* Set spdif input reference clock */ + ret = clk_set_rate(priv->refclk, priv->conf->ref_rate); + if (ret) { + dev_err(dai->dev, "reference clock rate set failed\n"); + return ret; + } + + /* + * The rate actually set might be slightly different, get + * the actual rate for the following mode calculation + */ + rate = clk_get_rate(priv->refclk); + + /* HW will update mode every 1ms */ + regmap_update_bits(priv->map, SPDIFIN_CTRL1, + SPDIFIN_CTRL1_BASE_TIMER, + FIELD_PREP(SPDIFIN_CTRL1_BASE_TIMER, rate / 1000)); + + /* Threshold based on the minimum width between two edges */ + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_WIDTH_SEL, SPDIFIN_CTRL0_WIDTH_SEL); + + /* Calculate the last timer which has no threshold */ + t_next = axg_spdifin_mode_timer(priv, i, rate); + axg_spdifin_write_timer(priv->map, i, t_next); + + do { + unsigned int t; + + i -= 1; + + /* Calculate the timer */ + t = axg_spdifin_mode_timer(priv, i, rate); + + /* Set the timer value */ + axg_spdifin_write_timer(priv->map, i, t); + + /* Set the threshold value */ + axg_spdifin_write_threshold(priv->map, i, t + t_next); + + /* Save the current timer for the next threshold calculation */ + t_next = t; + + } while (i > 0); + + return 0; +} + +static int axg_spdifin_dai_probe(struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + int ret; + + ret = clk_prepare_enable(priv->pclk); + if (ret) { + dev_err(dai->dev, "failed to enable pclk\n"); + return ret; + } + + ret = axg_spdifin_sample_mode_config(dai, priv); + if (ret) { + dev_err(dai->dev, "mode configuration failed\n"); + clk_disable_unprepare(priv->pclk); + return ret; + } + + return 0; +} + +static int axg_spdifin_dai_remove(struct snd_soc_dai *dai) +{ + struct axg_spdifin *priv = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(priv->pclk); + return 0; +} + +static const struct snd_soc_dai_ops axg_spdifin_ops = { + .prepare = axg_spdifin_prepare, + .startup = axg_spdifin_startup, + .shutdown = axg_spdifin_shutdown, +}; + +static int axg_spdifin_iec958_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; + uinfo->count = 1; + + return 0; +} + +static int axg_spdifin_get_status_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + int i; + + for (i = 0; i < 24; i++) + ucontrol->value.iec958.status[i] = 0xff; + + return 0; +} + +static int axg_spdifin_get_status(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_kcontrol_chip(kcontrol); + struct axg_spdifin *priv = snd_soc_component_get_drvdata(c); + int i, j; + + for (i = 0; i < 6; i++) { + unsigned int val; + + regmap_update_bits(priv->map, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_STATUS_SEL, + FIELD_PREP(SPDIFIN_CTRL0_STATUS_SEL, i)); + + regmap_read(priv->map, SPDIFIN_STAT1, &val); + + for (j = 0; j < 4; j++) { + unsigned int offset = i * 4 + j; + + ucontrol->value.iec958.status[offset] = + (val >> (j * 8)) & 0xff; + } + } + + return 0; +} + +#define AXG_SPDIFIN_IEC958_MASK \ + { \ + .access = SNDRV_CTL_ELEM_ACCESS_READ, \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), \ + .info = axg_spdifin_iec958_info, \ + .get = axg_spdifin_get_status_mask, \ + } + +#define AXG_SPDIFIN_IEC958_STATUS \ + { \ + .access = (SNDRV_CTL_ELEM_ACCESS_READ | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE), \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE), \ + .info = axg_spdifin_iec958_info, \ + .get = axg_spdifin_get_status, \ + } + +static const char * const spdifin_chsts_src_texts[] = { + "A", "B", +}; + +static SOC_ENUM_SINGLE_DECL(axg_spdifin_chsts_src_enum, SPDIFIN_CTRL0, + SPDIFIN_CTRL0_STATUS_CH_SHIFT, + spdifin_chsts_src_texts); + +static int axg_spdifin_rate_lock_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 192000; + + return 0; +} + +static int axg_spdifin_rate_lock_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *c = snd_kcontrol_chip(kcontrol); + struct axg_spdifin *priv = snd_soc_component_get_drvdata(c); + + ucontrol->value.integer.value[0] = axg_spdifin_get_rate(priv); + + return 0; +} + +#define AXG_SPDIFIN_LOCK_RATE(xname) \ + { \ + .iface = SNDRV_CTL_ELEM_IFACE_PCM, \ + .access = (SNDRV_CTL_ELEM_ACCESS_READ | \ + SNDRV_CTL_ELEM_ACCESS_VOLATILE), \ + .get = axg_spdifin_rate_lock_get, \ + .info = axg_spdifin_rate_lock_info, \ + .name = xname, \ + } + +static const struct snd_kcontrol_new axg_spdifin_controls[] = { + AXG_SPDIFIN_LOCK_RATE("Capture Rate Lock"), + SOC_DOUBLE("Capture Switch", SPDIFIN_CTRL0, 7, 6, 1, 1), + SOC_ENUM(SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Src", + axg_spdifin_chsts_src_enum), + AXG_SPDIFIN_IEC958_MASK, + AXG_SPDIFIN_IEC958_STATUS, +}; + +static const struct snd_soc_component_driver axg_spdifin_component_drv = { + .controls = axg_spdifin_controls, + .num_controls = ARRAY_SIZE(axg_spdifin_controls), +}; + +static const struct regmap_config axg_spdifin_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = SPDIFIN_MUTE_VAL, +}; + +static const unsigned int axg_spdifin_mode_rates[SPDIFIN_MODE_NUM] = { + 32000, 44100, 48000, 88200, 96000, 176400, 192000, +}; + +static const struct axg_spdifin_cfg axg_cfg = { + .mode_rates = axg_spdifin_mode_rates, + .ref_rate = 333333333, +}; + +static const struct of_device_id axg_spdifin_of_match[] = { + { + .compatible = "amlogic,axg-spdifin", + .data = &axg_cfg, + }, {} +}; +MODULE_DEVICE_TABLE(of, axg_spdifin_of_match); + +static struct snd_soc_dai_driver * +axg_spdifin_get_dai_drv(struct device *dev, struct axg_spdifin *priv) +{ + struct snd_soc_dai_driver *drv; + int i; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return ERR_PTR(-ENOMEM); + + drv->name = "SPDIF Input"; + drv->ops = &axg_spdifin_ops; + drv->probe = axg_spdifin_dai_probe; + drv->remove = axg_spdifin_dai_remove; + drv->capture.stream_name = "Capture"; + drv->capture.channels_min = 1; + drv->capture.channels_max = 2; + drv->capture.formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + + for (i = 0; i < SPDIFIN_MODE_NUM; i++) { + unsigned int rb = + snd_pcm_rate_to_rate_bit(priv->conf->mode_rates[i]); + + if (rb == SNDRV_PCM_RATE_KNOT) + return ERR_PTR(-EINVAL); + + drv->capture.rates |= rb; + } + + return drv; +} + +static int axg_spdifin_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct axg_spdifin *priv; + struct snd_soc_dai_driver *dai_drv; + struct resource *res; + void __iomem *regs; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + platform_set_drvdata(pdev, priv); + + priv->conf = of_device_get_match_data(dev); + if (!priv->conf) { + dev_err(dev, "failed to match device\n"); + return -ENODEV; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->map = devm_regmap_init_mmio(dev, regs, &axg_spdifin_regmap_cfg); + if (IS_ERR(priv->map)) { + dev_err(dev, "failed to init regmap: %ld\n", + PTR_ERR(priv->map)); + return PTR_ERR(priv->map); + } + + priv->pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(priv->pclk)) { + ret = PTR_ERR(priv->pclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get pclk: %d\n", ret); + return ret; + } + + priv->refclk = devm_clk_get(dev, "refclk"); + if (IS_ERR(priv->refclk)) { + ret = PTR_ERR(priv->refclk); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to get mclk: %d\n", ret); + return ret; + } + + dai_drv = axg_spdifin_get_dai_drv(dev, priv); + if (IS_ERR(dai_drv)) { + dev_err(dev, "failed to get dai driver: %ld\n", + PTR_ERR(dai_drv)); + return PTR_ERR(dai_drv); + } + + return devm_snd_soc_register_component(dev, &axg_spdifin_component_drv, + dai_drv, 1); +} + +static struct platform_driver axg_spdifin_pdrv = { + .probe = axg_spdifin_probe, + .driver = { + .name = "axg-spdifin", + .of_match_table = axg_spdifin_of_match, + }, +}; +module_platform_driver(axg_spdifin_pdrv); + +MODULE_DESCRIPTION("Amlogic AXG SPDIF Input driver"); +MODULE_AUTHOR("Jerome Brunet "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-58-ga151 From 8625db9416923b2941ef68776f55062555f7ce65 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Thu, 13 Dec 2018 21:02:16 +0800 Subject: ASoC: Intel: kbl_rt5660: Add a new machine driver for kbl with rt5660 The new Dell IoT platform uses kabylake + alc3277 codec, and alc3277 shares the driver with the codec rt5660, here we generate a new machine driver based on kbl_da7219_max98357a. The audio design on this IoT platform is as below: - Intel kabylake platform - connect the codec ALC3277 via SSP0 - line-out and line-in with Micbias jacks - line-out mute control and jack detection of line-out and line-in - two HDMI ports with audio capability Signed-off-by: Hui Wang Acked-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 10 + sound/soc/intel/boards/Makefile | 2 + sound/soc/intel/boards/kbl_rt5660.c | 543 ++++++++++++++++++++++ sound/soc/intel/common/soc-acpi-intel-kbl-match.c | 10 + 4 files changed, 565 insertions(+) create mode 100644 sound/soc/intel/boards/kbl_rt5660.c (limited to 'sound') diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index b177db2a0dbb..3839d6205fcf 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -293,6 +293,16 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH Say Y if you have such a device. If unsure select "N". +config SND_SOC_INTEL_KBL_RT5660_MACH + tristate "KBL with RT5660 in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_RT5660 + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for RT5660 I2S audio codec. + Say Y if you have such a device. + config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 5381e27df9cc..bf072ea299b7 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -20,6 +20,7 @@ snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o +snd-soc-kbl_rt5660-objs := kbl_rt5660.o snd-soc-skl_rt286-objs := skl_rt286.o snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o @@ -46,6 +47,7 @@ obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH) += snd-soc-kbl_da7219_max9 obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH) += snd-soc-kbl_da7219_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt5663_rt5514_max98927.o +obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5660_MACH) += snd-soc-kbl_rt5660.o obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o diff --git a/sound/soc/intel/boards/kbl_rt5660.c b/sound/soc/intel/boards/kbl_rt5660.c new file mode 100644 index 000000000000..3255e0029276 --- /dev/null +++ b/sound/soc/intel/boards/kbl_rt5660.c @@ -0,0 +1,543 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018-19 Canonical Corporation. + +/* + * Intel Kabylake I2S Machine Driver with RT5660 Codec + * + * Modified from: + * Intel Kabylake I2S Machine driver supporting MAXIM98357a and + * DA7219 codecs + * Also referred to: + * Intel Broadwell I2S Machine driver supporting RT5677 codec + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../../codecs/hdac_hdmi.h" +#include "../../codecs/rt5660.h" + +#define KBL_RT5660_CODEC_DAI "rt5660-aif1" +#define DUAL_CHANNEL 2 + +static struct snd_soc_card *kabylake_audio_card; +static struct snd_soc_jack skylake_hdmi[3]; +static struct snd_soc_jack lineout_jack; +static struct snd_soc_jack mic_jack; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_codec_private { + struct gpio_desc *gpio_lo_mute; + struct list_head hdmi_pcm_list; +}; + +enum { + KBL_DPCM_AUDIO_PB = 0, + KBL_DPCM_AUDIO_CP, + KBL_DPCM_AUDIO_HDMI1_PB, + KBL_DPCM_AUDIO_HDMI2_PB, + KBL_DPCM_AUDIO_HDMI3_PB, +}; + +#define GPIO_LINEOUT_MUTE_INDEX 0 +#define GPIO_LINEOUT_DET_INDEX 3 +#define GPIO_LINEIN_DET_INDEX 4 + +static const struct acpi_gpio_params lineout_mute_gpio = { GPIO_LINEOUT_MUTE_INDEX, 0, true }; +static const struct acpi_gpio_params lineout_det_gpio = { GPIO_LINEOUT_DET_INDEX, 0, false }; +static const struct acpi_gpio_params mic_det_gpio = { GPIO_LINEIN_DET_INDEX, 0, false }; + + +static const struct acpi_gpio_mapping acpi_rt5660_gpios[] = { + { "lineout-mute-gpios", &lineout_mute_gpio, 1 }, + { "lineout-det-gpios", &lineout_det_gpio, 1 }, + { "mic-det-gpios", &mic_det_gpio, 1 }, + { NULL }, +}; + +static struct snd_soc_jack_pin lineout_jack_pin = { + .pin = "Line Out", + .mask = SND_JACK_LINEOUT, +}; + +static struct snd_soc_jack_pin mic_jack_pin = { + .pin = "Line In", + .mask = SND_JACK_MICROPHONE, +}; + +static struct snd_soc_jack_gpio lineout_jack_gpio = { + .name = "lineout-det", + .report = SND_JACK_LINEOUT, + .debounce_time = 200, +}; + +static struct snd_soc_jack_gpio mic_jack_gpio = { + .name = "mic-det", + .report = SND_JACK_MICROPHONE, + .debounce_time = 200, +}; + +static int kabylake_5660_event_lineout(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct kbl_codec_private *priv = snd_soc_card_get_drvdata(dapm->card); + + gpiod_set_value_cansleep(priv->gpio_lo_mute, + !(SND_SOC_DAPM_EVENT_ON(event))); + + return 0; +} + +static const struct snd_kcontrol_new kabylake_rt5660_controls[] = { + SOC_DAPM_PIN_SWITCH("Line In"), + SOC_DAPM_PIN_SWITCH("Line Out"), +}; + +static const struct snd_soc_dapm_widget kabylake_rt5660_widgets[] = { + SND_SOC_DAPM_MIC("Line In", NULL), + SND_SOC_DAPM_LINE("Line Out", kabylake_5660_event_lineout), +}; + +static const struct snd_soc_dapm_route kabylake_rt5660_map[] = { + /* other jacks */ + {"IN1P", NULL, "Line In"}, + {"IN2P", NULL, "Line In"}, + {"Line Out", NULL, "LOUTR"}, + {"Line Out", NULL, "LOUTL"}, + + /* CODEC BE connections */ + { "AIF1 Playback", NULL, "ssp0 Tx"}, + { "ssp0 Tx", NULL, "codec0_out"}, + + { "codec0_in", NULL, "ssp0 Rx" }, + { "ssp0 Rx", NULL, "AIF1 Capture" }, + + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, +}; + +static int kabylake_ssp0_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + + /* The ADSP will convert the FE rate to 48k, stereo */ + rate->min = rate->max = 48000; + channels->min = channels->max = DUAL_CHANNEL; + + /* set SSP0 to 24 bit */ + snd_mask_none(fmt); + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + + return 0; +} + +static int kabylake_rt5660_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + ret = devm_acpi_dev_add_driver_gpios(component->dev, acpi_rt5660_gpios); + if (ret) + dev_warn(component->dev, "Failed to add driver gpios\n"); + + /* Request rt5660 GPIO for lineout mute control, return if fails */ + ctx->gpio_lo_mute = devm_gpiod_get(component->dev, "lineout-mute", + GPIOD_OUT_HIGH); + if (IS_ERR(ctx->gpio_lo_mute)) { + dev_err(component->dev, "Can't find GPIO_MUTE# gpio\n"); + return PTR_ERR(ctx->gpio_lo_mute); + } + + /* Create and initialize headphone jack, this jack is not mandatory, don't return if fails */ + ret = snd_soc_card_jack_new(rtd->card, "Lineout Jack", + SND_JACK_LINEOUT, &lineout_jack, + &lineout_jack_pin, 1); + if (ret) + dev_warn(component->dev, "Can't create Lineout jack\n"); + else { + lineout_jack_gpio.gpiod_dev = component->dev; + ret = snd_soc_jack_add_gpios(&lineout_jack, 1, + &lineout_jack_gpio); + if (ret) + dev_warn(component->dev, "Can't add Lineout jack gpio\n"); + } + + /* Create and initialize mic jack, this jack is not mandatory, don't return if fails */ + ret = snd_soc_card_jack_new(rtd->card, "Mic Jack", + SND_JACK_MICROPHONE, &mic_jack, + &mic_jack_pin, 1); + if (ret) + dev_warn(component->dev, "Can't create mic jack\n"); + else { + mic_jack_gpio.gpiod_dev = component->dev; + ret = snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio); + if (ret) + dev_warn(component->dev, "Can't add mic jack gpio\n"); + } + + /* Here we enable some dapms in advance to reduce the pop noise for recording via line-in */ + snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1"); + snd_soc_dapm_force_enable_pin(dapm, "BST1"); + snd_soc_dapm_force_enable_pin(dapm, "BST2"); + + return 0; +} + +static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = device; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int kabylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI1_PB); +} + +static int kabylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI2_PB); +} + +static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI3_PB); +} + +static int kabylake_rt5660_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int ret; + + ret = snd_soc_dai_set_sysclk(codec_dai, + RT5660_SCLK_S_PLL1, params_rate(params) * 512, + SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(rtd->dev, "snd_soc_dai_set_sysclk err = %d\n", ret); + return ret; + } + + ret = snd_soc_dai_set_pll(codec_dai, 0, + RT5660_PLL1_S_BCLK, + params_rate(params) * 50, + params_rate(params) * 512); + if (ret < 0) + dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); + + return ret; +} + +static struct snd_soc_ops kabylake_rt5660_ops = { + .hw_params = kabylake_rt5660_hw_params, +}; + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_rt5660_fe_ops = { + .startup = kbl_fe_startup, +}; + +/* kabylake digital audio interface glue - connects rt5660 codec <--> CPU */ +static struct snd_soc_dai_link kabylake_rt5660_dais[] = { + /* Front End DAI links */ + [KBL_DPCM_AUDIO_PB] = { + .name = "Kbl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &kabylake_rt5660_fe_ops, + }, + [KBL_DPCM_AUDIO_CP] = { + .name = "Kbl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &kabylake_rt5660_fe_ops, + }, + [KBL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Kbl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Kbl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Kbl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-10EC3277:00", + .codec_dai_name = KBL_RT5660_CODEC_DAI, + .init = kabylake_rt5660_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp0_fixup, + .ops = &kabylake_rt5660_ops, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "iDisp1", + .id = 1, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = kabylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 2, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 3, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi3_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + + +#define NAME_SIZE 32 +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &skylake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &skylake_hdmi[i]); + if (err < 0) + return err; + + i++; + + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} + +/* kabylake audio machine driver for rt5660 */ +static struct snd_soc_card kabylake_audio_card_rt5660 = { + .name = "kblrt5660", + .owner = THIS_MODULE, + .dai_link = kabylake_rt5660_dais, + .num_links = ARRAY_SIZE(kabylake_rt5660_dais), + .controls = kabylake_rt5660_controls, + .num_controls = ARRAY_SIZE(kabylake_rt5660_controls), + .dapm_widgets = kabylake_rt5660_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_rt5660_widgets), + .dapm_routes = kabylake_rt5660_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_rt5660_map), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_codec_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_audio_card = + (struct snd_soc_card *)pdev->id_entry->driver_data; + + kabylake_audio_card->dev = &pdev->dev; + snd_soc_card_set_drvdata(kabylake_audio_card, ctx); + return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card); +} + +static const struct platform_device_id kbl_board_ids[] = { + { + .name = "kbl_rt5660", + .driver_data = + (kernel_ulong_t)&kabylake_audio_card_rt5660, + }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kbl_rt5660", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio Machine driver-RT5660 in I2S mode"); +MODULE_AUTHOR("Hui Wang "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kbl_rt5660"); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c index a317b7790fce..e6fa6f470526 100644 --- a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -96,6 +96,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { .quirk_data = &kbl_7219_98927_codecs, .pdata = &skl_dmic_data }, + { + .id = "10EC5660", + .drv_name = "kbl_rt5660", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, + { + .id = "10EC3277", + .drv_name = "kbl_rt5660", + .fw_filename = "intel/dsp_fw_kbl.bin", + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); -- cgit v1.2.3-58-ga151 From 112a8900d4b0399f45d6ea025d3e1a4a1d6fa3f3 Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Sat, 8 Dec 2018 00:02:37 +0530 Subject: ASoC: xlnx: Add i2s driver I2S IP instance can work in transmitter/playback or receiver/capture mode exclusively. The patch registers corresponding instance as ASoC component with audio framework. Signed-off-by: Maruthi Srinivas Bayyavarapu Signed-off-by: Mark Brown --- sound/soc/xilinx/xlnx_i2s.c | 185 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 sound/soc/xilinx/xlnx_i2s.c (limited to 'sound') diff --git a/sound/soc/xilinx/xlnx_i2s.c b/sound/soc/xilinx/xlnx_i2s.c new file mode 100644 index 000000000000..d4ae9eff41ce --- /dev/null +++ b/sound/soc/xilinx/xlnx_i2s.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx ASoC I2S audio support + * + * Copyright (C) 2018 Xilinx, Inc. + * + * Author: Praveen Vuppala + * Author: Maruthi Srinivas Bayyavarapu + */ + +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "xlnx_i2s" + +#define I2S_CORE_CTRL_OFFSET 0x08 +#define I2S_I2STIM_OFFSET 0x20 +#define I2S_CH0_OFFSET 0x30 +#define I2S_I2STIM_VALID_MASK GENMASK(7, 0) + +static int xlnx_i2s_set_sclkout_div(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + void __iomem *base = snd_soc_dai_get_drvdata(cpu_dai); + + if (!div || (div & ~I2S_I2STIM_VALID_MASK)) + return -EINVAL; + + writel(div, base + I2S_I2STIM_OFFSET); + + return 0; +} + +static int xlnx_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *i2s_dai) +{ + u32 reg_off, chan_id; + void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai); + + chan_id = params_channels(params) / 2; + + while (chan_id > 0) { + reg_off = I2S_CH0_OFFSET + ((chan_id - 1) * 4); + writel(chan_id, base + reg_off); + chan_id--; + } + + return 0; +} + +static int xlnx_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *i2s_dai) +{ + void __iomem *base = snd_soc_dai_get_drvdata(i2s_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + writel(1, base + I2S_CORE_CTRL_OFFSET); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + writel(0, base + I2S_CORE_CTRL_OFFSET); + break; + default: + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops xlnx_i2s_dai_ops = { + .trigger = xlnx_i2s_trigger, + .set_clkdiv = xlnx_i2s_set_sclkout_div, + .hw_params = xlnx_i2s_hw_params +}; + +static const struct snd_soc_component_driver xlnx_i2s_component = { + .name = DRV_NAME, +}; + +static const struct of_device_id xlnx_i2s_of_match[] = { + { .compatible = "xlnx,i2s-transmitter-1.0", }, + { .compatible = "xlnx,i2s-receiver-1.0", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xlnx_i2s_of_match); + +static int xlnx_i2s_probe(struct platform_device *pdev) +{ + struct resource *res; + void __iomem *base; + struct snd_soc_dai_driver *dai_drv; + int ret; + u32 ch, format, data_width; + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + + dai_drv = devm_kzalloc(&pdev->dev, sizeof(*dai_drv), GFP_KERNEL); + if (!dai_drv) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = of_property_read_u32(node, "xlnx,num-channels", &ch); + if (ret < 0) { + dev_err(dev, "cannot get supported channels\n"); + return ret; + } + ch = ch * 2; + + ret = of_property_read_u32(node, "xlnx,dwidth", &data_width); + if (ret < 0) { + dev_err(dev, "cannot get data width\n"); + return ret; + } + switch (data_width) { + case 16: + format = SNDRV_PCM_FMTBIT_S16_LE; + break; + case 24: + format = SNDRV_PCM_FMTBIT_S24_LE; + break; + default: + return -EINVAL; + } + + if (of_device_is_compatible(node, "xlnx,i2s-transmitter-1.0")) { + dai_drv->name = "xlnx_i2s_playback"; + dai_drv->playback.stream_name = "Playback"; + dai_drv->playback.formats = format; + dai_drv->playback.channels_min = ch; + dai_drv->playback.channels_max = ch; + dai_drv->playback.rates = SNDRV_PCM_RATE_8000_192000; + dai_drv->ops = &xlnx_i2s_dai_ops; + } else if (of_device_is_compatible(node, "xlnx,i2s-receiver-1.0")) { + dai_drv->name = "xlnx_i2s_capture"; + dai_drv->capture.stream_name = "Capture"; + dai_drv->capture.formats = format; + dai_drv->capture.channels_min = ch; + dai_drv->capture.channels_max = ch; + dai_drv->capture.rates = SNDRV_PCM_RATE_8000_192000; + dai_drv->ops = &xlnx_i2s_dai_ops; + } else { + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, base); + + ret = devm_snd_soc_register_component(&pdev->dev, &xlnx_i2s_component, + dai_drv, 1); + if (ret) { + dev_err(&pdev->dev, "i2s component registration failed\n"); + return ret; + } + + dev_info(&pdev->dev, "%s DAI registered\n", dai_drv->name); + + return ret; +} + +static struct platform_driver xlnx_i2s_aud_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = xlnx_i2s_of_match, + }, + .probe = xlnx_i2s_probe, +}; + +module_platform_driver(xlnx_i2s_aud_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Praveen Vuppala "); +MODULE_AUTHOR("Maruthi Srinivas Bayyavarapu "); -- cgit v1.2.3-58-ga151 From 33f8db9a89200c18ff65d494a3dbd325d129e15e Mon Sep 17 00:00:00 2001 From: Maruthi Srinivas Bayyavarapu Date: Sat, 8 Dec 2018 00:02:38 +0530 Subject: ASoC: xlnx: enable i2s driver build Enabled i2s driver build option. Signed-off-by: Maruthi Srinivas Bayyavarapu Signed-off-by: Mark Brown --- sound/soc/Kconfig | 1 + sound/soc/Makefile | 1 + sound/soc/xilinx/Kconfig | 8 ++++++++ sound/soc/xilinx/Makefile | 2 ++ 4 files changed, 12 insertions(+) create mode 100644 sound/soc/xilinx/Kconfig create mode 100644 sound/soc/xilinx/Makefile (limited to 'sound') diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 1cf11cf51e1d..d21659f5a7e2 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -73,6 +73,7 @@ source "sound/soc/tegra/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/uniphier/Kconfig" source "sound/soc/ux500/Kconfig" +source "sound/soc/xilinx/Kconfig" source "sound/soc/xtensa/Kconfig" source "sound/soc/zte/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 62a5f87c3cfc..aff7b1989436 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -57,5 +57,6 @@ obj-$(CONFIG_SND_SOC) += tegra/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += uniphier/ obj-$(CONFIG_SND_SOC) += ux500/ +obj-$(CONFIG_SND_SOC) += xilinx/ obj-$(CONFIG_SND_SOC) += xtensa/ obj-$(CONFIG_SND_SOC) += zte/ diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig new file mode 100644 index 000000000000..25e287feb58c --- /dev/null +++ b/sound/soc/xilinx/Kconfig @@ -0,0 +1,8 @@ +config SND_SOC_XILINX_I2S + tristate "Audio support for the the Xilinx I2S" + help + Select this option to enable Xilinx I2S Audio. This enables + I2S playback and capture using xilinx soft IP. In transmitter + mode, IP receives audio in AES format, extracts PCM and sends + PCM data. In receiver mode, IP receives PCM audio and + encapsulates PCM in AES format and sends AES data. diff --git a/sound/soc/xilinx/Makefile b/sound/soc/xilinx/Makefile new file mode 100644 index 000000000000..6c1209b9ee75 --- /dev/null +++ b/sound/soc/xilinx/Makefile @@ -0,0 +1,2 @@ +snd-soc-xlnx-i2s-objs := xlnx_i2s.o +obj-$(CONFIG_SND_SOC_XILINX_I2S) += snd-soc-xlnx-i2s.o -- cgit v1.2.3-58-ga151 From bb9963b850ea9e1ef39f4368eae83a9462f60166 Mon Sep 17 00:00:00 2001 From: Kirill Marinushkin Date: Fri, 7 Dec 2018 20:02:34 +0100 Subject: ASoC: pcm3060: Add powersaving widgets for DAC and ADC Enable DAC/ADC only when playing/capturing Signed-off-by: Kirill Marinushkin Signed-off-by: Mark Brown --- sound/soc/codecs/pcm3060.c | 14 ++++++++++---- sound/soc/codecs/pcm3060.h | 2 ++ 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c index 1dd851a7b43b..6714aa8d9026 100644 --- a/sound/soc/codecs/pcm3060.c +++ b/sound/soc/codecs/pcm3060.c @@ -198,19 +198,25 @@ static const struct snd_kcontrol_new pcm3060_dapm_controls[] = { }; static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", "Playback", PCM3060_REG64, + PCM3060_REG_SHIFT_DAPSV, 1), + SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), SND_SOC_DAPM_INPUT("INL"), SND_SOC_DAPM_INPUT("INR"), + + SND_SOC_DAPM_ADC("ADC", "Capture", PCM3060_REG64, + PCM3060_REG_SHIFT_ADPSV, 1), }; static const struct snd_soc_dapm_route pcm3060_dapm_map[] = { - { "OUTL", NULL, "Playback" }, - { "OUTR", NULL, "Playback" }, + { "OUTL", NULL, "DAC" }, + { "OUTR", NULL, "DAC" }, - { "Capture", NULL, "INL" }, - { "Capture", NULL, "INR" }, + { "ADC", NULL, "INL" }, + { "ADC", NULL, "INR" }, }; /* soc component */ diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h index c895cf40ee10..6a027b4a845d 100644 --- a/sound/soc/codecs/pcm3060.h +++ b/sound/soc/codecs/pcm3060.h @@ -37,7 +37,9 @@ int pcm3060_remove(struct device *dev); #define PCM3060_REG_MRST 0x80 #define PCM3060_REG_SRST 0x40 #define PCM3060_REG_ADPSV 0x20 +#define PCM3060_REG_SHIFT_ADPSV 0x05 #define PCM3060_REG_DAPSV 0x10 +#define PCM3060_REG_SHIFT_DAPSV 0x04 #define PCM3060_REG_SE 0x01 #define PCM3060_REG65 0x41 -- cgit v1.2.3-58-ga151 From e6b98db945124987b1ecec3f5f030877627e01a9 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Fri, 7 Dec 2018 17:50:03 -0600 Subject: ASoC: Intel: Skylake: Add CFL-S support It's with CNP, supposed to be equivalent with CNL entry. Keep the existing declaration style for now, at a later point we may transition and use PCI_DEVICE_DATA(). Signed-off-by: Takashi Iwai Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-messages.c | 8 ++++++++ sound/soc/intel/skylake/skl.c | 3 +++ 2 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c index 8bfb8b0fa3d5..b0e6fb93eaf8 100644 --- a/sound/soc/intel/skylake/skl-messages.c +++ b/sound/soc/intel/skylake/skl-messages.c @@ -247,6 +247,14 @@ static const struct skl_dsp_ops dsp_ops[] = { .init_fw = cnl_sst_init_fw, .cleanup = cnl_sst_dsp_cleanup }, + { + .id = 0xa348, + .num_cores = 4, + .loader_ops = bxt_get_loader_ops, + .init = cnl_sst_dsp_init, + .init_fw = cnl_sst_init_fw, + .cleanup = cnl_sst_dsp_cleanup + }, }; const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id) diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 3f0ac1312982..df36b8fe6d5e 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1121,6 +1121,9 @@ static const struct pci_device_id skl_ids[] = { /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, + /* CFL */ + { PCI_DEVICE(0x8086, 0xa348), + .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); -- cgit v1.2.3-58-ga151 From c746de8dbc7b0ae9df491f7a99a6dab34203b51b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 7 Dec 2018 17:50:04 -0600 Subject: ASoC: Intel: Skylake: Harden DSP detection with PCI class/subclass info The existing PPCAP and GCAP fields cannot be used reliably to determine if the DSP is enabled by the BIOS. Instead rely on the class/subclass information to find out if this driver can run or not. The values in the code don't seem to be documented in publicly available documents but are part of recommendations made to BIOS writers and have been verified to be accurate on a number of platforms. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index df36b8fe6d5e..41a084b3d48f 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -916,6 +916,21 @@ static int skl_first_init(struct hdac_bus *bus) unsigned short gcap; int cp_streams, pb_streams, start_idx; + /* + * detect DSP by checking class/subclass/prog-id information + * class=04 subclass 03 prog-if 00: no DSP, legacy driver needs to be used + * class=04 subclass 01 prog-if 00: DSP is present (and may be required e.g. for DMIC or SSP support) + * class=04 subclass 03 prog-if 80: either of DSP or legacy mode can be used + */ + if (pci->class == 0x040300) { + dev_err(bus->dev, "The DSP is not enabled on this platform, aborting probe\n"); + return -ENODEV; + } else if (pci->class != 0x040100 && pci->class != 0x040380) { + dev_err(bus->dev, "Unknown PCI class/subclass/prog-if information (0x%06x) found, aborting probe\n", pci->class); + return -ENODEV; + } + dev_info(bus->dev, "DSP detected with PCI class/subclass/prog-if info 0x%06x\n", pci->class); + err = pci_request_regions(pci, "Skylake HD audio"); if (err < 0) return err; -- cgit v1.2.3-58-ga151 From fa11ab5688f744bc868356f3f14c3bb9f283a780 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 7 Dec 2018 17:50:05 -0600 Subject: ASoC: Intel: Skylake: stop probe if HDaudio capabilities don't exist Check immediately if required HDaudio capabilities can't be found (no PPCAP or no streams exposed in GCAP), and move all DMA inits after the error tests. PPCAP and GCAP are not reliable indicators of DSP presence, but if they don't exist then the driver will not work. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 41a084b3d48f..72e366bbba33 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -946,6 +946,12 @@ static int skl_first_init(struct hdac_bus *bus) snd_hdac_bus_parse_capabilities(bus); + /* check if PPCAP exists */ + if (!bus->ppcap) { + dev_err(bus->dev, "bus ppcap not set, HDaudio or DSP not present?\n"); + return -ENODEV; + } + if (skl_acquire_irq(bus, 0) < 0) return -EBUSY; @@ -955,23 +961,25 @@ static int skl_first_init(struct hdac_bus *bus) gcap = snd_hdac_chip_readw(bus, GCAP); dev_dbg(bus->dev, "chipset global capabilities = 0x%x\n", gcap); - /* allow 64bit DMA address if supported by H/W */ - if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) { - dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64)); - } else { - dma_set_mask(bus->dev, DMA_BIT_MASK(32)); - dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32)); - } - /* read number of streams from GCAP register */ cp_streams = (gcap >> 8) & 0x0f; pb_streams = (gcap >> 12) & 0x0f; - if (!pb_streams && !cp_streams) + if (!pb_streams && !cp_streams) { + dev_err(bus->dev, "no streams found in GCAP definitions?\n"); return -EIO; + } bus->num_streams = cp_streams + pb_streams; + /* allow 64bit DMA address if supported by H/W */ + if (!dma_set_mask(bus->dev, DMA_BIT_MASK(64))) { + dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(64)); + } else { + dma_set_mask(bus->dev, DMA_BIT_MASK(32)); + dma_set_coherent_mask(bus->dev, DMA_BIT_MASK(32)); + } + /* initialize streams */ snd_hdac_ext_stream_init_all (bus, 0, cp_streams, SNDRV_PCM_STREAM_CAPTURE); -- cgit v1.2.3-58-ga151 From 7f981bdcf55fda28a9a70c9e8151dd200771a0a8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 7 Dec 2018 17:50:06 -0600 Subject: ASoC: Intel: Skylake: remove useless tests on HDaudio capabilities bus->ppcap is now tested upfront, there is no need to re-check if the hardware is exposed as needed. Remove tests and remove indentation. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 72e366bbba33..f04e9ec4432b 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -826,12 +826,10 @@ static void skl_probe_work(struct work_struct *work) return; } - if (bus->ppcap) { - err = skl_machine_device_register(skl); - if (err < 0) { - dev_err(bus->dev, "machine register failed: %d\n", err); - goto out_err; - } + err = skl_machine_device_register(skl); + if (err < 0) { + dev_err(bus->dev, "machine register failed: %d\n", err); + goto out_err; } /* @@ -1034,25 +1032,23 @@ static int skl_probe(struct pci_dev *pci, pci_set_drvdata(skl->pci, bus); - /* check if dsp is there */ - if (bus->ppcap) { - /* create device for dsp clk */ - err = skl_clock_device_register(skl); - if (err < 0) - goto out_clk_free; + /* create device for dsp clk */ + err = skl_clock_device_register(skl); + if (err < 0) + goto out_clk_free; - err = skl_find_machine(skl, (void *)pci_id->driver_data); - if (err < 0) - goto out_nhlt_free; + err = skl_find_machine(skl, (void *)pci_id->driver_data); + if (err < 0) + goto out_nhlt_free; - err = skl_init_dsp(skl); - if (err < 0) { - dev_dbg(bus->dev, "error failed to register dsp\n"); - goto out_nhlt_free; - } - skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; - skl->skl_sst->clock_power_gating = skl_clock_power_gating; + err = skl_init_dsp(skl); + if (err < 0) { + dev_dbg(bus->dev, "error failed to register dsp\n"); + goto out_nhlt_free; } + skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge; + skl->skl_sst->clock_power_gating = skl_clock_power_gating; + if (bus->mlcap) snd_hdac_ext_bus_get_ml_capabilities(bus); -- cgit v1.2.3-58-ga151 From f231c34ca9772f0b0e2d1b781e9c415847aff522 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 7 Dec 2018 17:50:07 -0600 Subject: ASoC: Intel: Skylake: add error logs on probe, remove dependency on NHLT Add error logs to make probe debug easier. Also remove hard-coded dependency on NHLT. NHLT literally stands for NonHdaudioLinkTable and is only required for SSP/DMIC interfaces. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 3 +++ sound/soc/intel/skylake/skl.c | 41 +++++++++++++++++++++++++++----------- 2 files changed, 32 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index 01a050cf8775..5d125a3df527 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -180,6 +180,9 @@ int skl_get_dmic_geo(struct skl *skl) unsigned int dmic_geo = 0; u8 j; + if (!nhlt) + return 0; + epnt = (struct nhlt_endpoint *)nhlt->desc; for (j = 0; j < nhlt->endpoint_count; j++) { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f04e9ec4432b..f9888b1d5521 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1010,8 +1010,10 @@ static int skl_probe(struct pci_dev *pci, bus = skl_to_bus(skl); err = skl_first_init(bus); - if (err < 0) + if (err < 0) { + dev_err(bus->dev, "skl_first_init failed with err: %d\n", err); goto out_free; + } skl->pci_id = pci->device; @@ -1020,26 +1022,39 @@ static int skl_probe(struct pci_dev *pci, skl->nhlt = skl_nhlt_init(bus->dev); if (skl->nhlt == NULL) { +#if !IS_ENABLED(CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC) + dev_err(bus->dev, "no nhlt info found\n"); err = -ENODEV; goto out_free; - } +#else + dev_warn(bus->dev, "no nhlt info found, continuing to try to enable HDaudio codec\n"); +#endif + } else { - err = skl_nhlt_create_sysfs(skl); - if (err < 0) - goto out_nhlt_free; + err = skl_nhlt_create_sysfs(skl); + if (err < 0) { + dev_err(bus->dev, "skl_nhlt_create_sysfs failed with err: %d\n", err); + goto out_nhlt_free; + } - skl_nhlt_update_topology_bin(skl); + skl_nhlt_update_topology_bin(skl); + + /* create device for dsp clk */ + err = skl_clock_device_register(skl); + if (err < 0) { + dev_err(bus->dev, "skl_clock_device_register failed with err: %d\n", err); + goto out_clk_free; + } + } pci_set_drvdata(skl->pci, bus); - /* create device for dsp clk */ - err = skl_clock_device_register(skl); - if (err < 0) - goto out_clk_free; err = skl_find_machine(skl, (void *)pci_id->driver_data); - if (err < 0) + if (err < 0) { + dev_err(bus->dev, "skl_find_machine failed with err: %d\n", err); goto out_nhlt_free; + } err = skl_init_dsp(skl); if (err < 0) { @@ -1056,8 +1071,10 @@ static int skl_probe(struct pci_dev *pci, /* create device for soc dmic */ err = skl_dmic_device_register(skl); - if (err < 0) + if (err < 0) { + dev_err(bus->dev, "skl_dmic_device_register failed with err: %d\n", err); goto out_dsp_free; + } schedule_work(&skl->probe_work); -- cgit v1.2.3-58-ga151 From 165be3c260f88a5f8c55d89458ad1f1d9efd701f Mon Sep 17 00:00:00 2001 From: Jerome Brunet Date: Thu, 13 Dec 2018 16:44:28 +0100 Subject: ASoC: meson: fix do_div warning in spdifin Even if this spdif input driver is only supposed to be used on 64bits platform, there is possible problem with 32bits and do_div, as reported by the kbuild robot. Just fix it. Fixes: 5ce5658375e6 ("ASoC: meson: add axg spdif input") Signed-off-by: Jerome Brunet Signed-off-by: Mark Brown --- sound/soc/meson/axg-spdifin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/meson/axg-spdifin.c b/sound/soc/meson/axg-spdifin.c index 09f385a2bbba..01b2035fa841 100644 --- a/sound/soc/meson/axg-spdifin.c +++ b/sound/soc/meson/axg-spdifin.c @@ -146,8 +146,8 @@ static void axg_spdifin_write_mode_param(struct regmap *map, int mode, unsigned int base_reg, unsigned int width) { - unsigned int offset = mode, rem; - unsigned int reg, shift; + uint64_t offset = mode; + unsigned int reg, shift, rem; rem = do_div(offset, num_per_reg); -- cgit v1.2.3-58-ga151 From 3500f1c589e92e0b6b1f8d31b4084fbde08d49cb Mon Sep 17 00:00:00 2001 From: Dimitris Papavasiliou Date: Sat, 24 Nov 2018 22:05:42 +0200 Subject: ASoC: pcm512x: Implement the digital_mute interface Clicks and pops of various volumes can be produced while the device is opened, closed, put into and taken out of standby, or reconfigured. Fix this, by implementing the digital_mute interface, so that the output is muted during such operations. Signed-off-by: Dimitris Papavasiliou Signed-off-by: Mark Brown --- sound/soc/codecs/pcm512x.c | 121 ++++++++++++++++++++++++++++++++++++++++++++- sound/soc/codecs/pcm512x.h | 2 + 2 files changed, 121 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index f0f2d4fd3769..6cb1653be804 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -53,6 +53,8 @@ struct pcm512x_priv { unsigned long overclock_pll; unsigned long overclock_dac; unsigned long overclock_dsp; + int mute; + struct mutex mutex; }; /* @@ -384,6 +386,61 @@ static const struct soc_enum pcm512x_veds = SOC_ENUM_SINGLE(PCM512x_DIGITAL_MUTE_2, PCM512x_VEDS_SHIFT, 4, pcm512x_ramp_step_text); +static int pcm512x_update_mute(struct pcm512x_priv *pcm512x) +{ + return regmap_update_bits( + pcm512x->regmap, PCM512x_MUTE, PCM512x_RQML | PCM512x_RQMR, + (!!(pcm512x->mute & 0x5) << PCM512x_RQML_SHIFT) + | (!!(pcm512x->mute & 0x3) << PCM512x_RQMR_SHIFT)); +} + +static int pcm512x_digital_playback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + + mutex_lock(&pcm512x->mutex); + ucontrol->value.integer.value[0] = !(pcm512x->mute & 0x4); + ucontrol->value.integer.value[1] = !(pcm512x->mute & 0x2); + mutex_unlock(&pcm512x->mutex); + + return 0; +} + +static int pcm512x_digital_playback_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret, changed = 0; + + mutex_lock(&pcm512x->mutex); + + if ((pcm512x->mute & 0x4) == (ucontrol->value.integer.value[0] << 2)) { + pcm512x->mute ^= 0x4; + changed = 1; + } + if ((pcm512x->mute & 0x2) == (ucontrol->value.integer.value[1] << 1)) { + pcm512x->mute ^= 0x2; + changed = 1; + } + + if (changed) { + ret = pcm512x_update_mute(pcm512x); + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + } + + mutex_unlock(&pcm512x->mutex); + + return changed; +} + static const struct snd_kcontrol_new pcm512x_controls[] = { SOC_DOUBLE_R_TLV("Digital Playback Volume", PCM512x_DIGITAL_VOLUME_2, PCM512x_DIGITAL_VOLUME_3, 0, 255, 1, digital_tlv), @@ -391,8 +448,15 @@ SOC_DOUBLE_TLV("Analogue Playback Volume", PCM512x_ANALOG_GAIN_CTRL, PCM512x_LAGN_SHIFT, PCM512x_RAGN_SHIFT, 1, 1, analog_tlv), SOC_DOUBLE_TLV("Analogue Playback Boost Volume", PCM512x_ANALOG_GAIN_BOOST, PCM512x_AGBL_SHIFT, PCM512x_AGBR_SHIFT, 1, 0, boost_tlv), -SOC_DOUBLE("Digital Playback Switch", PCM512x_MUTE, PCM512x_RQML_SHIFT, - PCM512x_RQMR_SHIFT, 1, 1), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Digital Playback Switch", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = snd_ctl_boolean_stereo_info, + .get = pcm512x_digital_playback_switch_get, + .put = pcm512x_digital_playback_switch_put +}, SOC_SINGLE("Deemphasis Switch", PCM512x_DSP, PCM512x_DEMP_SHIFT, 1, 1), SOC_ENUM("DSP Program", pcm512x_dsp_program), @@ -1319,10 +1383,61 @@ static int pcm512x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int pcm512x_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + struct pcm512x_priv *pcm512x = snd_soc_component_get_drvdata(component); + int ret; + unsigned int mute_det; + + mutex_lock(&pcm512x->mutex); + + if (mute) { + pcm512x->mute |= 0x1; + ret = regmap_update_bits(pcm512x->regmap, PCM512x_MUTE, + PCM512x_RQML | PCM512x_RQMR, + PCM512x_RQML | PCM512x_RQMR); + if (ret != 0) { + dev_err(component->dev, + "Failed to set digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, + PCM512x_ANALOG_MUTE_DET, + mute_det, (mute_det & 0x3) == 0, + 200, 10000); + + mutex_unlock(&pcm512x->mutex); + } else { + pcm512x->mute &= ~0x1; + ret = pcm512x_update_mute(pcm512x); + if (ret != 0) { + dev_err(component->dev, + "Failed to update digital mute: %d\n", ret); + mutex_unlock(&pcm512x->mutex); + return ret; + } + + regmap_read_poll_timeout(pcm512x->regmap, + PCM512x_ANALOG_MUTE_DET, + mute_det, + (mute_det & 0x3) + == ((~pcm512x->mute >> 1) & 0x3), + 200, 10000); + } + + mutex_unlock(&pcm512x->mutex); + + return 0; +} + static const struct snd_soc_dai_ops pcm512x_dai_ops = { .startup = pcm512x_dai_startup, .hw_params = pcm512x_hw_params, .set_fmt = pcm512x_set_fmt, + .digital_mute = pcm512x_digital_mute, }; static struct snd_soc_dai_driver pcm512x_dai = { @@ -1388,6 +1503,8 @@ int pcm512x_probe(struct device *dev, struct regmap *regmap) if (!pcm512x) return -ENOMEM; + mutex_init(&pcm512x->mutex); + dev_set_drvdata(dev, pcm512x); pcm512x->regmap = regmap; diff --git a/sound/soc/codecs/pcm512x.h b/sound/soc/codecs/pcm512x.h index d70d9c0c2088..9dda8693498e 100644 --- a/sound/soc/codecs/pcm512x.h +++ b/sound/soc/codecs/pcm512x.h @@ -112,7 +112,9 @@ #define PCM512x_RQST_SHIFT 4 /* Page 0, Register 3 - mute */ +#define PCM512x_RQMR (1 << 0) #define PCM512x_RQMR_SHIFT 0 +#define PCM512x_RQML (1 << 4) #define PCM512x_RQML_SHIFT 4 /* Page 0, Register 4 - PLL */ -- cgit v1.2.3-58-ga151 From de17f14ea576d8a0f2932404467fa916542da94d Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Thu, 1 Nov 2018 18:08:49 +0530 Subject: ASoC: core: Invoke pcm_new() for all DAI-link Remove no_pcm check to invoke pcm_new() for backend dai-links too. This fixes crash in hdmi codec driver during hdmi_codec_startup() while accessing chmap_info struct. chmap_info struct memory is allocated in pcm_new() of hdmi codec driver which is not invoked in case of DPCM when hdmi codec driver is part of backend dai-link. Below is the crash stack: [ 61.635493] Unable to handle kernel NULL pointer dereference at virtual address 00000018 .. [ 61.666696] CM = 0, WnR = 1 [ 61.669778] user pgtable: 4k pages, 39-bit VAs, pgd = ffffffc0d6633000 [ 61.676526] [0000000000000018] *pgd=0000000153fc8003, *pud=0000000153fc8003, *pmd=0000000000000000 [ 61.685793] Internal error: Oops: 96000046 [#1] PREEMPT SMP [ 61.722955] CPU: 7 PID: 2238 Comm: aplay Not tainted 4.14.72 #21 .. [ 61.740269] PC is at hdmi_codec_startup+0x124/0x164 [ 61.745308] LR is at hdmi_codec_startup+0xe4/0x164 Signed-off-by: Rohit kumar Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index b0db59e6339d..0462b3ec977a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1467,7 +1467,7 @@ static int soc_link_dai_pcm_new(struct snd_soc_dai **dais, int num_dais, for (i = 0; i < num_dais; ++i) { struct snd_soc_dai_driver *drv = dais[i]->driver; - if (!rtd->dai_link->no_pcm && drv->pcm_new) + if (drv->pcm_new) ret = drv->pcm_new(rtd, dais[i]); if (ret < 0) { dev_err(dais[i]->dev, -- cgit v1.2.3-58-ga151 From b8b89f5ee0b2555fa5a9d778685d2d6fb5122bb9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:29:42 +0900 Subject: ASoC: simple-card-utils: fixup asoc_simple_card_get_dai_id() ID method commit b6f3fc005a2c8 ("ASoC: simple-card-utils: fixup asoc_simple_card_get_dai_id() counting") fixuped getting DAI ID method. It will get DAI ID from OF graph "port", but, we want to consider about "endpoint", too. And, we also want to keep compatibility. This patch fixup it as if (driver has specified DAI ID) use it as DAI ID else if (OF graph endpoint has reg) use it as DAI ID else if (OF graph port has reg) use it as DAI ID else use endpoint count as DAI ID Fixes: commit b6f3fc005a2c8 ("ASoC: simple-card-utils: fixup asoc_simple_card_get_dai_id() counting") Reported-by: Tony Lindgren Signed-off-by: Kuninori Morimoto Tested-by: Tony Lindgren Signed-off-by: Mark Brown --- sound/soc/generic/simple-card-utils.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c index 17d8aee43835..b807a47515eb 100644 --- a/sound/soc/generic/simple-card-utils.c +++ b/sound/soc/generic/simple-card-utils.c @@ -269,22 +269,46 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai); static int asoc_simple_card_get_dai_id(struct device_node *ep) { + struct device_node *node; + struct device_node *endpoint; struct of_endpoint info; + int i, id; int ret; + /* use driver specified DAI ID if exist */ ret = snd_soc_get_dai_id(ep); if (ret != -ENOTSUPP) return ret; + /* use endpoint/port reg if exist */ + ret = of_graph_parse_endpoint(ep, &info); + if (ret == 0) { + if (info.id) + return info.id; + if (info.port) + return info.port; + } + + node = of_graph_get_port_parent(ep); + /* * Non HDMI sound case, counting port/endpoint on its DT * is enough. Let's count it. */ - ret = of_graph_parse_endpoint(ep, &info); - if (ret) - return -ENXIO; + i = 0; + id = -1; + for_each_endpoint_of_node(node, endpoint) { + if (endpoint == ep) + id = i; + i++; + } + + of_node_put(node); + + if (id < 0) + return -ENODEV; - return info.port; + return id; } int asoc_simple_card_parse_graph_dai(struct device_node *ep, -- cgit v1.2.3-58-ga151 From ae3cb5790906b5caf519f6f21792c30fb5ddf8db Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:32:25 +0900 Subject: ASoC: audio-graph-card: merge audio-graph-scu-card audio-graph-card and audio-graph-scu-card are very similar driver, but the former is supporting normal sound card, the latter is supporting DPCM sound card. We couldn't use normal sound and DPCM sound in same sound card by audio-graph-card. This patch merges both sound card into it. Now we can use both feature on same driver. audio-grap-card is now supporting .compatible = "audio-graph-scu-card". Signed-off-by: Kuninori Morimoto Tested-by: Tony Lindgren Signed-off-by: Mark Brown --- sound/soc/generic/Kconfig | 1 + sound/soc/generic/audio-graph-card.c | 366 ++++++++++++++++++++++++++++++----- 2 files changed, 320 insertions(+), 47 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index fa5aff9c1041..5395782424b4 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -22,6 +22,7 @@ config SND_AUDIO_GRAPH_CARD help This option enables generic simple sound card support with OF-graph DT bindings. + It also support DPCM of multi CPU single Codec ststem. config SND_AUDIO_GRAPH_SCU_CARD tristate "ASoC Audio Graph SCU sound card support" diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 1da9532ef897..8ec09bc711f8 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -27,6 +27,8 @@ struct graph_card_data { struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -34,6 +36,8 @@ struct graph_card_data { struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; }; @@ -42,6 +46,8 @@ struct graph_card_data { #define graph_priv_to_dev(priv) (graph_priv_to_card(priv)->dev) #define graph_priv_to_link(priv, i) (graph_priv_to_card(priv)->dai_link + (i)) +#define PREFIX "audio-graph-card," + static int asoc_graph_card_outdrv_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) @@ -156,7 +162,140 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, +static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + /* overwrite by top level adata if exist */ + asoc_simple_card_convert_fixup(&priv->adata, params); + + return 0; +} + +static int asoc_graph_card_dai_link_of_dpcm(struct device_node *cpu_ep, + struct device_node *codec_ep, + struct graph_card_data *priv, + int *dai_idx, int link_idx, + int *conf_idx, int is_cpu) +{ + struct device *dev = graph_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); + struct snd_soc_card *card = graph_priv_to_card(priv); + struct device_node *ep = is_cpu ? cpu_ep : codec_ep; + struct device_node *node = of_graph_get_port_parent(ep); + struct asoc_simple_dai *dai; + int ret; + + dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec"); + + if (is_cpu) { + struct snd_soc_dai_link_component *codecs; + + /* BE is dummy */ + codecs = dai_link->codecs; + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + + ret = asoc_simple_card_parse_graph_cpu(ep, dai_link); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + /* card->num_links includes Codec */ + asoc_simple_card_canonicalize_cpu(dai_link, + of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); + } else { + struct snd_soc_codec_conf *cconf; + + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_graph_card_be_hw_params_fixup; + + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + + ret = asoc_simple_card_parse_graph_codec(ep, dai_link); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + dai_link->codecs->dai_name); + if (ret < 0) + return ret; + + /* check "prefix" from top node */ + snd_soc_of_parse_audio_prefix(card, cconf, + dai_link->codecs->of_node, + "prefix"); + /* check "prefix" from each node if top doesn't have */ + if (!cconf->of_node) + snd_soc_of_parse_node_prefix(node, cconf, + dai_link->codecs->of_node, + PREFIX "prefix"); + } + + asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); + + ret = asoc_simple_card_of_parse_tdm(ep, dai); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); + + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, + NULL, &dai_link->dai_fmt); + if (ret < 0) + return ret; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &asoc_graph_card_ops; + dai_link->init = asoc_graph_card_dai_init; + + return 0; +} + +static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, + struct device_node *codec_ep, struct graph_card_data *priv, int *dai_idx, int link_idx) { @@ -165,10 +304,10 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; - struct device_node *cpu_ep = of_get_next_child(cpu_port, NULL); - struct device_node *codec_ep = of_graph_get_remote_endpoint(cpu_ep); int ret; + dev_dbg(dev, "link_of\n"); + cpu_dai = dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; codec_dai = @@ -177,45 +316,45 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) - goto dai_link_of_err; + return ret; of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_graph_codec(codec_ep, dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_of_parse_tdm(cpu_ep, cpu_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_of_parse_tdm(codec_ep, codec_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_clk_cpu(dev, cpu_ep, dai_link, cpu_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_parse_clk_codec(dev, codec_ep, dai_link, codec_dai); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_canonicalize_dailink(dai_link); if (ret < 0) - goto dai_link_of_err; + return ret; ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, dai_link->codecs->dai_name); if (ret < 0) - goto dai_link_of_err; + return ret; dai_link->ops = &asoc_graph_card_ops; dai_link->init = asoc_graph_card_dai_init; @@ -223,11 +362,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, asoc_simple_card_canonicalize_cpu(dai_link, of_graph_get_endpoint_count(dai_link->cpu_of_node) == 1); -dai_link_of_err: - of_node_put(cpu_ep); - of_node_put(codec_ep); - - return ret; + return 0; } static int asoc_graph_card_parse_of(struct graph_card_data *priv) @@ -236,8 +371,14 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct device *dev = graph_priv_to_dev(priv); struct snd_soc_card *card = graph_priv_to_card(priv); struct device_node *node = dev->of_node; + struct device_node *cpu_port; + struct device_node *cpu_ep = NULL; + struct device_node *codec_ep = NULL; + struct device_node *codec_port = NULL; + struct device_node *codec_port_old = NULL; int rc, ret; - int link_idx, dai_idx; + int link_idx, dai_idx, conf_idx; + int cpu; ret = asoc_simple_card_of_parse_widgets(card, NULL); if (ret < 0) @@ -247,35 +388,159 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; + asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata); + /* Factor to mclk, used in hw_params() */ of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); - link_idx = 0; - dai_idx = 0; - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { - ret = asoc_graph_card_dai_link_of(it.node, priv, - &dai_idx, link_idx++); - if (ret < 0) { - of_node_put(it.node); - - return ret; + link_idx = 0; + dai_idx = 0; + conf_idx = 0; + codec_port_old = NULL; + for (cpu = 1; cpu >= 0; cpu--) { + /* + * Detect all CPU first, and Detect all Codec 2nd. + * + * In Normal sound case, all DAIs are detected + * as "CPU-Codec". + * + * In DPCM sound case, + * all CPUs are detected as "CPU-dummy", and + * all Codecs are detected as "dummy-Codec". + * To avoid random sub-device numbering, + * detect "dummy-Codec" in last; + */ + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = NULL; + while (1) { + cpu_ep = of_get_next_child(cpu_port, cpu_ep); + if (!cpu_ep) + break; + + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_get_parent(codec_ep); + + of_node_put(codec_ep); + of_node_put(codec_port); + + dev_dbg(dev, "%pOFf <-> %pOFf\n", cpu_ep, codec_ep); + + if (of_get_child_count(codec_port) > 1) { + /* + * for DPCM sound + */ + if (!cpu) { + if (codec_port_old == codec_port) + continue; + codec_port_old = codec_port; + } + ret = asoc_graph_card_dai_link_of_dpcm( + cpu_ep, codec_ep, priv, + &dai_idx, link_idx++, + &conf_idx, cpu); + } else if (cpu) { + /* + * for Normal sound + */ + ret = asoc_graph_card_dai_link_of( + cpu_ep, codec_ep, priv, + &dai_idx, link_idx++); + } + if (ret < 0) + return ret; + } } } return asoc_simple_card_parse_card_name(card, NULL); } -static int asoc_graph_get_dais_count(struct device *dev) +static void asoc_graph_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) { struct of_phandle_iterator it; struct device_node *node = dev->of_node; - int count = 0; + struct device_node *cpu_port; + struct device_node *cpu_ep; + struct device_node *codec_ep; + struct device_node *codec_port; + struct device_node *codec_port_old; + struct device_node *codec_port_old2; int rc; - of_for_each_phandle(&it, rc, node, "dais", NULL, 0) - count++; - - return count; + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ + codec_port_old = NULL; + codec_port_old2 = NULL; + of_for_each_phandle(&it, rc, node, "dais", NULL, 0) { + cpu_port = it.node; + cpu_ep = NULL; + while (1) { + cpu_ep = of_get_next_child(cpu_port, cpu_ep); + if (!cpu_ep) + break; + + codec_ep = of_graph_get_remote_endpoint(cpu_ep); + codec_port = of_get_parent(codec_ep); + + of_node_put(codec_ep); + of_node_put(codec_port); + + (*link_num)++; + (*dais_num)++; + + if (codec_port_old == codec_port) { + if (codec_port_old2 != codec_port_old) { + (*link_num)++; + (*ccnf_num)++; + } + + codec_port_old2 = codec_port_old; + continue; + } + + (*dais_num)++; + codec_port_old = codec_port; + } + } } static int asoc_graph_soc_card_probe(struct snd_soc_card *card) @@ -302,20 +567,23 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct asoc_simple_dai *dais; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret, i; + struct snd_soc_codec_conf *cconf; + int lnum = 0, dnum = 0, cnum = 0; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - num = asoc_graph_get_dais_count(dev); - if (num == 0) + asoc_graph_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) return -EINVAL; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, num * 2, sizeof(*dais), GFP_KERNEL); + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); if (!dai_props || !dai_link || !dais) return -ENOMEM; @@ -325,7 +593,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -341,16 +609,19 @@ static int asoc_graph_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = graph_priv_to_card(priv); - card->owner = THIS_MODULE; - card->dev = dev; - card->dai_link = dai_link; - card->num_links = num; - card->dapm_widgets = asoc_graph_card_dapm_widgets; - card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); - card->probe = asoc_graph_soc_card_probe; + card->owner = THIS_MODULE; + card->dev = dev; + card->dai_link = dai_link; + card->num_links = lnum; + card->dapm_widgets = asoc_graph_card_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(asoc_graph_card_dapm_widgets); + card->probe = asoc_graph_soc_card_probe; + card->codec_conf = cconf; + card->num_configs = cnum; ret = asoc_graph_card_parse_of(priv); if (ret < 0) { @@ -381,6 +652,7 @@ static int asoc_graph_card_remove(struct platform_device *pdev) static const struct of_device_id asoc_graph_of_match[] = { { .compatible = "audio-graph-card", }, + { .compatible = "audio-graph-scu-card", }, {}, }; MODULE_DEVICE_TABLE(of, asoc_graph_of_match); -- cgit v1.2.3-58-ga151 From 56eb818191fbe4b93bbe6e884f96061013c9a6ab Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:32:30 +0900 Subject: ASoC: audio-graph-card: tidyup mclk-fs method Current audio-graph-card is handling "mclk-fs" by many way. But, it is not useful and readable. We want to do is that allow having mclk-fs everywere. This patch support it. It will be overwrote if lower node has it. sound { mclk-fs = ; // initial }; codec { ports { mclk-fs = ; // overwrite port { mclk-fs = ; // overwrite endpoint { mclk-fs = ; // overwrite }; }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 52 ++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 8ec09bc711f8..7e32380e7a6f 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -31,7 +31,6 @@ struct graph_card_data { struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; - unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; @@ -115,9 +114,7 @@ static int asoc_graph_card_hw_params(struct snd_pcm_substream *substream, unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) + if (dai_props->mclk_fs) mclk_fs = dai_props->mclk_fs; if (mclk_fs) { @@ -176,7 +173,8 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int asoc_graph_card_dai_link_of_dpcm(struct device_node *cpu_ep, +static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, + struct device_node *cpu_ep, struct device_node *codec_ep, struct graph_card_data *priv, int *dai_idx, int link_idx, @@ -187,12 +185,21 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *cpu_ep, struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); struct snd_soc_card *card = graph_priv_to_card(priv); struct device_node *ep = is_cpu ? cpu_ep : codec_ep; + struct device_node *port = of_get_parent(ep); + struct device_node *ports = of_get_parent(port); struct device_node *node = of_graph_get_port_parent(ep); struct asoc_simple_dai *dai; int ret; dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec"); + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); + of_node_put(ports); + of_node_put(port); + if (is_cpu) { struct snd_soc_dai_link_component *codecs; @@ -279,8 +286,6 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *cpu_ep, if (ret < 0) return ret; - of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); - ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) @@ -294,7 +299,8 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *cpu_ep, return 0; } -static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, +static int asoc_graph_card_dai_link_of(struct device_node *top, + struct device_node *cpu_ep, struct device_node *codec_ep, struct graph_card_data *priv, int *dai_idx, int link_idx) @@ -302,6 +308,10 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, struct device *dev = graph_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); + struct device_node *cpu_port = of_get_parent(cpu_ep); + struct device_node *codec_port = of_get_parent(codec_ep); + struct device_node *cpu_ports = of_get_parent(cpu_port); + struct device_node *codec_ports = of_get_parent(codec_port); struct asoc_simple_dai *cpu_dai; struct asoc_simple_dai *codec_dai; int ret; @@ -313,14 +323,24 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_ep, codec_dai = dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + /* Factor to mclk, used in hw_params() */ + of_property_read_u32(top, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ports, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_port, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); + of_node_put(cpu_port); + of_node_put(cpu_ports); + of_node_put(codec_port); + of_node_put(codec_ports); + ret = asoc_simple_card_parse_daifmt(dev, cpu_ep, codec_ep, NULL, &dai_link->dai_fmt); if (ret < 0) return ret; - of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); - of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); - ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) return ret; @@ -370,7 +390,8 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) struct of_phandle_iterator it; struct device *dev = graph_priv_to_dev(priv); struct snd_soc_card *card = graph_priv_to_card(priv); - struct device_node *node = dev->of_node; + struct device_node *top = dev->of_node; + struct device_node *node = top; struct device_node *cpu_port; struct device_node *cpu_ep = NULL; struct device_node *codec_ep = NULL; @@ -390,9 +411,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata); - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, "mclk-fs", &priv->mclk_fs); - link_idx = 0; dai_idx = 0; conf_idx = 0; @@ -436,7 +454,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) codec_port_old = codec_port; } ret = asoc_graph_card_dai_link_of_dpcm( - cpu_ep, codec_ep, priv, + top, cpu_ep, codec_ep, priv, &dai_idx, link_idx++, &conf_idx, cpu); } else if (cpu) { @@ -444,7 +462,7 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) * for Normal sound */ ret = asoc_graph_card_dai_link_of( - cpu_ep, codec_ep, priv, + top, cpu_ep, codec_ep, priv, &dai_idx, link_idx++); } if (ret < 0) -- cgit v1.2.3-58-ga151 From 7ced65ffb82fa58c6e314c16770288fe9ce3dfc8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:32:36 +0900 Subject: ASoC: audio-graph-card: tidyup convert_rate/channel method Current audio-graph-card is handling "convert_rate/channel" by many ways. But, it is not useful and readable. We want to do is that allow having it everywere. This patch support it. It will be overwrote if lower node has it. sound { convert-channels = ; // initial }; codec { audio-graph-card,convert-channels = ; // overwrite ports { convert_channels = ; // overwrite port { convert_channels = ; // overwrite endpoint { convert_channels = ; // overwrite }; }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 7e32380e7a6f..368b8ae463af 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -35,7 +35,6 @@ struct graph_card_data { struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; - struct asoc_simple_card_data adata; struct snd_soc_codec_conf *codec_conf; struct gpio_desc *pa_gpio; }; @@ -167,9 +166,6 @@ static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, asoc_simple_card_convert_fixup(&dai_props->adata, params); - /* overwrite by top level adata if exist */ - asoc_simple_card_convert_fixup(&priv->adata, params); - return 0; } @@ -197,6 +193,13 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, of_property_read_u32(ports, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(port, "mclk-fs", &dai_props->mclk_fs); of_property_read_u32(ep, "mclk-fs", &dai_props->mclk_fs); + + asoc_simple_card_parse_convert(dev, top, NULL, &dai_props->adata); + asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); + asoc_simple_card_parse_convert(dev, ports, NULL, &dai_props->adata); + asoc_simple_card_parse_convert(dev, port, NULL, &dai_props->adata); + asoc_simple_card_parse_convert(dev, ep, NULL, &dai_props->adata); + of_node_put(ports); of_node_put(port); @@ -276,8 +279,6 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, PREFIX "prefix"); } - asoc_simple_card_parse_convert(dev, node, PREFIX, &dai_props->adata); - ret = asoc_simple_card_of_parse_tdm(ep, dai); if (ret) return ret; @@ -409,8 +410,6 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv) if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, node, NULL, &priv->adata); - link_idx = 0; dai_idx = 0; conf_idx = 0; -- cgit v1.2.3-58-ga151 From 66164a4da30dd295a1b60568c02bee37cfd9895a Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:32:43 +0900 Subject: ASoC: audio-graph-card: tidyup prefix for snd_soc_codec_conf Current audio-graph-card is handling "prefix" by many ways. But, it is not useful and readable. We want to do is that allow having it everywere. This patch supports it. It will be overwrote if lower node has it. sound { prefix = "xxx"; // initial }; codec { audio-graph-card,prefix = "xxx"; // overwrite ports { prefix = "xxx"; // overwrite port { prefix = "xxx"; // overwrite }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/audio-graph-card.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 368b8ae463af..0d6144560a1e 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -179,12 +179,12 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, struct device *dev = graph_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, link_idx); struct graph_dai_props *dai_props = graph_priv_to_props(priv, link_idx); - struct snd_soc_card *card = graph_priv_to_card(priv); struct device_node *ep = is_cpu ? cpu_ep : codec_ep; struct device_node *port = of_get_parent(ep); struct device_node *ports = of_get_parent(port); struct device_node *node = of_graph_get_port_parent(ep); struct asoc_simple_dai *dai; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; int ret; dev_dbg(dev, "link_of DPCM (for %s)\n", is_cpu ? "CPU" : "Codec"); @@ -204,10 +204,8 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, of_node_put(port); if (is_cpu) { - struct snd_soc_dai_link_component *codecs; /* BE is dummy */ - codecs = dai_link->codecs; codecs->of_node = NULL; codecs->dai_name = "snd-soc-dummy-dai"; codecs->name = "snd-soc-dummy"; @@ -264,19 +262,19 @@ static int asoc_graph_card_dai_link_of_dpcm(struct device_node *top, ret = asoc_simple_card_set_dailink_name(dev, dai_link, "be.%s", - dai_link->codecs->dai_name); + codecs->dai_name); if (ret < 0) return ret; /* check "prefix" from top node */ - snd_soc_of_parse_audio_prefix(card, cconf, - dai_link->codecs->of_node, + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, "prefix"); - /* check "prefix" from each node if top doesn't have */ - if (!cconf->of_node) - snd_soc_of_parse_node_prefix(node, cconf, - dai_link->codecs->of_node, - PREFIX "prefix"); + snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, + PREFIX "prefix"); + snd_soc_of_parse_node_prefix(ports, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(port, cconf, codecs->of_node, + "prefix"); } ret = asoc_simple_card_of_parse_tdm(ep, dai); -- cgit v1.2.3-58-ga151 From da215354eb55c382d3d5c426ea0e9aa7ef7c10e1 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:35:10 +0900 Subject: ASoC: simple-card: merge simple-scu-card simple-card and simple-scu-card are very similar driver, but the former is supporting normal sound card, the latter is supporting DPCM sound card. We couldn't use normal sound and DPCM sound in same time by one sound card. This patch merges both sound card into simple-card. Now we can use both feature on same driver. simple-card is now supporting .compatible = "simple-scu-audio-card". Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/Kconfig | 1 + sound/soc/generic/simple-card.c | 329 ++++++++++++++++++++++++++++++++++------ 2 files changed, 284 insertions(+), 46 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig index 5395782424b4..92c2cf06f40a 100644 --- a/sound/soc/generic/Kconfig +++ b/sound/soc/generic/Kconfig @@ -6,6 +6,7 @@ config SND_SIMPLE_CARD select SND_SIMPLE_CARD_UTILS help This option enables generic simple sound card support + It also support DPCM of multi CPU single Codec ststem. config SND_SIMPLE_SCU_CARD tristate "ASoC Simple SCU sound card support" diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 93d68161f953..c93f2e7dec4b 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -22,6 +22,8 @@ struct simple_card_data { struct asoc_simple_dai *codec_dai; struct snd_soc_dai_link_component codecs; /* single codec */ struct snd_soc_dai_link_component platform; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -29,6 +31,8 @@ struct simple_card_data { struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; + struct asoc_simple_card_data adata; + struct snd_soc_codec_conf *codec_conf; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) @@ -74,6 +78,9 @@ static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) static int asoc_simple_set_clk_rate(struct asoc_simple_dai *simple_dai, unsigned long rate) { + if (!simple_dai) + return 0; + if (!simple_dai->clk) return 0; @@ -151,6 +158,141 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) return 0; } +static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); + + asoc_simple_card_convert_fixup(&dai_props->adata, params); + + /* overwrite by top level adata if exist */ + asoc_simple_card_convert_fixup(&priv->adata, params); + + return 0; +} + +static int asoc_simple_card_dai_link_of_dpcm(struct device_node *node, + struct device_node *np, + struct device_node *codec, + struct simple_card_data *priv, + int *dai_idx, int link_idx, + int *conf_idx, int is_fe, + bool is_top_level_node) +{ + struct device *dev = simple_priv_to_dev(priv); + struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); + struct snd_soc_card *card = simple_priv_to_card(priv); + struct asoc_simple_dai *dai; + char *prefix = ""; + int ret; + + /* For single DAI link & old style of DT node */ + if (is_top_level_node) + prefix = PREFIX; + + if (is_fe) { + int is_single_links = 0; + struct snd_soc_dai_link_component *codecs; + + /* BE is dummy */ + codecs = dai_link->codecs; + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; + + /* FE settings */ + dai_link->dynamic = 1; + dai_link->dpcm_merged_format = 1; + + dai = + dai_props->cpu_dai = &priv->dais[(*dai_idx)++]; + + ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL, + &is_single_links); + if (ret) + return ret; + + ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "fe.%s", + dai_link->cpu_dai_name); + if (ret < 0) + return ret; + + asoc_simple_card_canonicalize_cpu(dai_link, is_single_links); + } else { + struct snd_soc_codec_conf *cconf; + + /* FE is dummy */ + dai_link->cpu_of_node = NULL; + dai_link->cpu_dai_name = "snd-soc-dummy-dai"; + dai_link->cpu_name = "snd-soc-dummy"; + + /* BE settings */ + dai_link->no_pcm = 1; + dai_link->be_hw_params_fixup = asoc_simple_card_be_hw_params_fixup; + + dai = + dai_props->codec_dai = &priv->dais[(*dai_idx)++]; + + cconf = + dai_props->codec_conf = &priv->codec_conf[(*conf_idx)++]; + + ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL); + if (ret < 0) + return ret; + + ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai); + if (ret < 0) + return ret; + + ret = asoc_simple_card_set_dailink_name(dev, dai_link, + "be.%s", + dai_link->codecs->dai_name); + if (ret < 0) + return ret; + + /* check "prefix" from top node */ + snd_soc_of_parse_audio_prefix(card, cconf, + dai_link->codecs->of_node, + PREFIX "prefix"); + /* check "prefix" from each node if top doesn't have */ + if (!cconf->of_node) + snd_soc_of_parse_node_prefix(np, cconf, + dai_link->codecs->of_node, + "prefix"); + } + + asoc_simple_card_parse_convert(dev, node, prefix, &dai_props->adata); + + ret = asoc_simple_card_of_parse_tdm(np, dai); + if (ret) + return ret; + + ret = asoc_simple_card_canonicalize_dailink(dai_link); + if (ret < 0) + return ret; + + of_property_read_u32(np, "mclk-fs", &dai_props->mclk_fs); + + ret = asoc_simple_card_parse_daifmt(dev, node, codec, + prefix, &dai_link->dai_fmt); + if (ret < 0) + return ret; + + dai_link->dpcm_playback = 1; + dai_link->dpcm_capture = 1; + dai_link->ops = &asoc_simple_card_ops; + dai_link->init = asoc_simple_card_dai_init; + + return 0; +} + static int asoc_simple_card_dai_link_of(struct device_node *node, struct simple_card_data *priv, int *dai_idx, int link_idx, @@ -291,63 +433,153 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node, static int asoc_simple_card_parse_of(struct simple_card_data *priv) { struct device *dev = simple_priv_to_dev(priv); + struct device_node *top = dev->of_node; struct snd_soc_card *card = simple_priv_to_card(priv); - struct device_node *dai_link; - struct device_node *node = dev->of_node; - int ret; - int link_idx, dai_idx; - - if (!node) + struct device_node *node; + struct device_node *np; + struct device_node *codec; + bool is_fe; + int ret, loop; + int dai_idx, link_idx, conf_idx; + + if (!top) return -EINVAL; - dai_link = of_get_child_by_name(node, PREFIX "dai-link"); - ret = asoc_simple_card_of_parse_widgets(card, PREFIX); if (ret < 0) - goto card_parse_end; + return ret; ret = asoc_simple_card_of_parse_routing(card, PREFIX); if (ret < 0) - goto card_parse_end; + return ret; /* Factor to mclk, used in hw_params() */ - of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs); + of_property_read_u32(top, PREFIX "mclk-fs", &priv->mclk_fs); + + asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); /* Single/Muti DAI link(s) & New style of DT node */ - link_idx = 0; - dai_idx = 0; - if (dai_link) { - struct device_node *np = NULL; - - for_each_child_of_node(node, np) { - dev_dbg(dev, "\tlink %d:\n", link_idx); - ret = asoc_simple_card_dai_link_of(np, priv, - &dai_idx, link_idx++, false); - if (ret < 0) { - of_node_put(np); - goto card_parse_end; + loop = 1; + link_idx = 0; + dai_idx = 0; + conf_idx = 0; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = dev->of_node; + loop = 0; + } + + do { + /* DPCM */ + if (of_get_child_count(node) > 2) { + for_each_child_of_node(node, np) { + codec = of_get_child_by_name(node, + loop ? "codec" : + PREFIX "codec"); + if (!codec) + return -ENODEV; + + is_fe = (np != codec); + + ret = asoc_simple_card_dai_link_of_dpcm( + node, np, codec, priv, + &dai_idx, link_idx++, &conf_idx, + is_fe, !loop); } + } else { + ret = asoc_simple_card_dai_link_of( + node, priv, + &dai_idx, link_idx++, !loop); } - } else { - /* For single DAI link & old style of DT node */ - ret = asoc_simple_card_dai_link_of(node, priv, - &dai_idx, link_idx++, true); if (ret < 0) - goto card_parse_end; - } + return ret; + + node = of_get_next_child(top, node); + } while (loop && node); ret = asoc_simple_card_parse_card_name(card, PREFIX); if (ret < 0) - goto card_parse_end; - - ret = asoc_simple_card_parse_aux_devs(node, priv); + return ret; -card_parse_end: - of_node_put(dai_link); + ret = asoc_simple_card_parse_aux_devs(top, priv); return ret; } +static void asoc_simple_card_get_dais_count(struct device *dev, + int *link_num, + int *dais_num, + int *ccnf_num) +{ + struct device_node *top = dev->of_node; + struct device_node *node; + int loop; + int num; + + /* + * link_num : number of links. + * CPU-Codec / CPU-dummy / dummy-Codec + * dais_num : number of DAIs + * ccnf_num : number of codec_conf + * same number for "dummy-Codec" + * + * ex1) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 7 + * CPU2 -/ ccnf : 1 + * CPU3 --- Codec2 + * + * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec + * => 7 DAIs = 4xCPU + 3xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex2) + * CPU0 --- Codec0 link : 5 + * CPU1 --- Codec1 dais : 6 + * CPU2 -/ ccnf : 1 + * CPU3 -/ + * + * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 1 ccnf = 1xdummy-Codec + * + * ex3) + * CPU0 --- Codec0 link : 6 + * CPU1 -/ dais : 6 + * CPU2 --- Codec1 ccnf : 2 + * CPU3 -/ + * + * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec + * => 6 DAIs = 4xCPU + 2xCodec + * => 2 ccnf = 2xdummy-Codec + */ + if (!top) { + (*link_num) = 1; + (*dais_num) = 2; + (*ccnf_num) = 0; + return; + } + + loop = 1; + node = of_get_child_by_name(top, PREFIX "dai-link"); + if (!node) { + node = top; + loop = 0; + } + + do { + num = of_get_child_count(node); + (*dais_num) += num; + if (num > 2) { + (*link_num) += num; + (*ccnf_num)++; + } else { + (*link_num)++; + } + node = of_get_next_child(top, node); + } while (loop && node); +} + static int asoc_simple_soc_card_probe(struct snd_soc_card *card) { struct simple_card_data *priv = snd_soc_card_get_drvdata(card); @@ -373,22 +605,23 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; - int num, ret, i; - - /* Get the number of DAI links */ - if (np && of_get_child_by_name(np, PREFIX "dai-link")) - num = of_get_child_count(np); - else - num = 1; + struct snd_soc_codec_conf *cconf; + int lnum = 0, dnum = 0, cnum = 0; + int ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; - dai_props = devm_kcalloc(dev, num, sizeof(*dai_props), GFP_KERNEL); - dai_link = devm_kcalloc(dev, num, sizeof(*dai_link), GFP_KERNEL); - dais = devm_kcalloc(dev, num * 2, sizeof(*dais), GFP_KERNEL); + asoc_simple_card_get_dais_count(dev, &lnum, &dnum, &cnum); + if (!lnum || !dnum) + return -EINVAL; + + dai_props = devm_kcalloc(dev, lnum, sizeof(*dai_props), GFP_KERNEL); + dai_link = devm_kcalloc(dev, lnum, sizeof(*dai_link), GFP_KERNEL); + dais = devm_kcalloc(dev, dnum, sizeof(*dais), GFP_KERNEL); + cconf = devm_kcalloc(dev, cnum, sizeof(*cconf), GFP_KERNEL); if (!dai_props || !dai_link || !dais) return -ENOMEM; @@ -398,7 +631,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) * see * soc-core.c :: snd_soc_init_multicodec() */ - for (i = 0; i < num; i++) { + for (i = 0; i < lnum; i++) { dai_link[i].codecs = &dai_props[i].codecs; dai_link[i].num_codecs = 1; dai_link[i].platform = &dai_props[i].platform; @@ -407,13 +640,16 @@ static int asoc_simple_card_probe(struct platform_device *pdev) priv->dai_props = dai_props; priv->dai_link = dai_link; priv->dais = dais; + priv->codec_conf = cconf; /* Init snd_soc_card */ card = simple_priv_to_card(priv); card->owner = THIS_MODULE; card->dev = dev; card->dai_link = priv->dai_link; - card->num_links = num; + card->num_links = lnum; + card->codec_conf = cconf; + card->num_configs = cnum; card->probe = asoc_simple_soc_card_probe; if (np && of_device_is_available(np)) { @@ -490,6 +726,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev) static const struct of_device_id asoc_simple_of_match[] = { { .compatible = "simple-audio-card", }, + { .compatible = "simple-scu-audio-card", }, {}, }; MODULE_DEVICE_TABLE(of, asoc_simple_of_match); -- cgit v1.2.3-58-ga151 From 79e834914d1fa2d078c586094ad347cc05aa428c Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:35:15 +0900 Subject: ASoC: simple-card: tidyup mclk-fs method Current simple-card is handling "mclk-fs" by many way. But, it is not useful and readable. We want to do is that allow having mclk-fs everywere. This patch support it. It will be overwrote if lower node has it. sound { simple-audio-card,mclk-fs = ; // for initial simple-audio-card,dai-link { mclk-fs = ; // overwrite cpu { mclk-fs = ; // overwrite }; codec { mclk-fs = ; // overwrite }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index c93f2e7dec4b..8593d51e0c6c 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -26,7 +26,6 @@ struct simple_card_data { struct snd_soc_codec_conf *codec_conf; unsigned int mclk_fs; } *dai_props; - unsigned int mclk_fs; struct asoc_simple_jack hp_jack; struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; @@ -102,9 +101,7 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream, unsigned int mclk, mclk_fs = 0; int ret = 0; - if (priv->mclk_fs) - mclk_fs = priv->mclk_fs; - else if (dai_props->mclk_fs) + if (dai_props->mclk_fs) mclk_fs = dai_props->mclk_fs; if (mclk_fs) { @@ -172,7 +169,8 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int asoc_simple_card_dai_link_of_dpcm(struct device_node *node, +static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, + struct device_node *node, struct device_node *np, struct device_node *codec, struct simple_card_data *priv, @@ -185,6 +183,7 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *node, struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); struct snd_soc_card *card = simple_priv_to_card(priv); struct asoc_simple_dai *dai; + char prop[128]; char *prefix = ""; int ret; @@ -278,7 +277,10 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *node, if (ret < 0) return ret; - of_property_read_u32(np, "mclk-fs", &dai_props->mclk_fs); + snprintf(prop, sizeof(prop), "%smclk-fs", prefix); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, prop, &dai_props->mclk_fs); + of_property_read_u32(np, prop, &dai_props->mclk_fs); ret = asoc_simple_card_parse_daifmt(dev, node, codec, prefix, &dai_link->dai_fmt); @@ -293,7 +295,8 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *node, return 0; } -static int asoc_simple_card_dai_link_of(struct device_node *node, +static int asoc_simple_card_dai_link_of(struct device_node *top, + struct device_node *node, struct simple_card_data *priv, int *dai_idx, int link_idx, bool is_top_level_node) @@ -345,7 +348,11 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, if (ret < 0) goto dai_link_of_err; - of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs); + snprintf(prop, sizeof(prop), "%smclk-fs", prefix); + of_property_read_u32(top, PREFIX "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(node, prop, &dai_props->mclk_fs); + of_property_read_u32(cpu, prop, &dai_props->mclk_fs); + of_property_read_u32(codec, prop, &dai_props->mclk_fs); ret = asoc_simple_card_parse_cpu(cpu, dai_link, DAI, CELL, &single_cpu); @@ -453,9 +460,6 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) if (ret < 0) return ret; - /* Factor to mclk, used in hw_params() */ - of_property_read_u32(top, PREFIX "mclk-fs", &priv->mclk_fs); - asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); /* Single/Muti DAI link(s) & New style of DT node */ @@ -482,13 +486,13 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) is_fe = (np != codec); ret = asoc_simple_card_dai_link_of_dpcm( - node, np, codec, priv, + top, node, np, codec, priv, &dai_idx, link_idx++, &conf_idx, is_fe, !loop); } } else { ret = asoc_simple_card_dai_link_of( - node, priv, + top, node, priv, &dai_idx, link_idx++, !loop); } if (ret < 0) -- cgit v1.2.3-58-ga151 From dd82410f7e939f00591c415d62915a89ac2fb8f0 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:35:20 +0900 Subject: ASoC: simple-card: tidyup convert_rate/channel method Current simple-card is handling "convert_rate/channel" by many ways. But, it is not useful and readable. We want to do is that allow having it everywere. This patch support it. It will be overwrote if lower node has it. sound { simple-audio-card,convert_channels = ; // initial simple-audio-card,dai-link { convert_channels = ; // overwrite cpu { convert_channels = ; // overwrite }; codec { convert_channels = ; // overwrite }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 8593d51e0c6c..fb18e5148aad 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -30,7 +30,6 @@ struct simple_card_data { struct asoc_simple_jack mic_jack; struct snd_soc_dai_link *dai_link; struct asoc_simple_dai *dais; - struct asoc_simple_card_data adata; struct snd_soc_codec_conf *codec_conf; }; @@ -163,9 +162,6 @@ static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, asoc_simple_card_convert_fixup(&dai_props->adata, params); - /* overwrite by top level adata if exist */ - asoc_simple_card_convert_fixup(&priv->adata, params); - return 0; } @@ -267,7 +263,9 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, "prefix"); } + asoc_simple_card_parse_convert(dev, top, PREFIX, &dai_props->adata); asoc_simple_card_parse_convert(dev, node, prefix, &dai_props->adata); + asoc_simple_card_parse_convert(dev, np, NULL, &dai_props->adata); ret = asoc_simple_card_of_parse_tdm(np, dai); if (ret) @@ -460,8 +458,6 @@ static int asoc_simple_card_parse_of(struct simple_card_data *priv) if (ret < 0) return ret; - asoc_simple_card_parse_convert(dev, top, PREFIX, &priv->adata); - /* Single/Muti DAI link(s) & New style of DT node */ loop = 1; link_idx = 0; -- cgit v1.2.3-58-ga151 From cfc652a73331c3b465e3f8dd39a3d6b71e6b3a66 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 14 Dec 2018 11:35:24 +0900 Subject: ASoC: simple-card: tidyup prefix for snd_soc_codec_conf Current simple-card is handling "prefix" by many ways. But, it is not useful and readable. We want to do is that allow having it everywere. This patch supports it. It will be overwrote if lower node has it. sound { simple-audio-card,prefix = "xxx"; // initial simple-audio-card,dai-link { prefix = "xxx"; // overwrite cpu { ... }; codec { prefix = "xxx"; // overwrite }; }; }; Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index fb18e5148aad..37e001cf9cd1 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -177,8 +177,9 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, struct device *dev = simple_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, link_idx); struct simple_dai_props *dai_props = simple_priv_to_props(priv, link_idx); - struct snd_soc_card *card = simple_priv_to_card(priv); struct asoc_simple_dai *dai; + struct snd_soc_dai_link_component *codecs = dai_link->codecs; + char prop[128]; char *prefix = ""; int ret; @@ -189,10 +190,8 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, if (is_fe) { int is_single_links = 0; - struct snd_soc_dai_link_component *codecs; /* BE is dummy */ - codecs = dai_link->codecs; codecs->of_node = NULL; codecs->dai_name = "snd-soc-dummy-dai"; codecs->name = "snd-soc-dummy"; @@ -248,19 +247,17 @@ static int asoc_simple_card_dai_link_of_dpcm(struct device_node *top, ret = asoc_simple_card_set_dailink_name(dev, dai_link, "be.%s", - dai_link->codecs->dai_name); + codecs->dai_name); if (ret < 0) return ret; /* check "prefix" from top node */ - snd_soc_of_parse_audio_prefix(card, cconf, - dai_link->codecs->of_node, + snd_soc_of_parse_node_prefix(top, cconf, codecs->of_node, PREFIX "prefix"); - /* check "prefix" from each node if top doesn't have */ - if (!cconf->of_node) - snd_soc_of_parse_node_prefix(np, cconf, - dai_link->codecs->of_node, - "prefix"); + snd_soc_of_parse_node_prefix(node, cconf, codecs->of_node, + "prefix"); + snd_soc_of_parse_node_prefix(np, cconf, codecs->of_node, + "prefix"); } asoc_simple_card_parse_convert(dev, top, PREFIX, &dai_props->adata); -- cgit v1.2.3-58-ga151 From 35bc99aaa1a3af23cf78b6b56f14230b5da3993b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Dec 2018 13:03:00 -0600 Subject: ASoC: Intel: Skylake: Add more platform granularity The current SKYLAKE kconfig is a all-you-can-eat selection that will support all known plaforms. This is however not necessarily a good thing: most platforms for SKL and KBL don't support the DSP, but a number of CNL/WHL ones do. Selecting this driver in all cases isn't really smart and will require users to muck with blacklists. Partition the configs to allow distributions to select on which platform this driver is used. Keep the existing SND_SOC_INTEL_SKYLAKE config to select everything for backwards compatibility. This patch does not provide new functionality, only finer-grained choices in supported platforms. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 73 ++++++++++++++++++++++++++++++++++++++---- sound/soc/intel/boards/Kconfig | 16 +++++++-- sound/soc/intel/skylake/skl.c | 12 +++++++ 3 files changed, 92 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 18e717703685..99a62ba409df 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -102,15 +102,74 @@ config SND_SST_ATOM_HIFI2_PLATFORM_ACPI recommended option config SND_SOC_INTEL_SKYLAKE - tristate "SKL/BXT/KBL/GLK/CNL... Platforms" + tristate "All Skylake/SST Platforms" depends on PCI && ACPI - select SND_SOC_INTEL_SKYLAKE_COMMON + select SND_SOC_INTEL_SKL + select SND_SOC_INTEL_APL + select SND_SOC_INTEL_KBL + select SND_SOC_INTEL_GLK + select SND_SOC_INTEL_CNL + select SND_SOC_INTEL_CFL help - If you have a Intel Skylake/Broxton/ApolloLake/KabyLake/ - GeminiLake or CannonLake platform with the DSP enabled in the BIOS - then enable this option by saying Y or m. + This is a backwards-compatible option to select all devices + supported by the Intel SST/Skylake driver. This option is no + longer recommended and will be deprecated when the SOF + driver is introduced. Distributions should explicitly + select which platform uses this driver. + +config SND_SOC_INTEL_SKL + tristate "Skylake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel Skylake platform with the DSP enabled + in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_APL + tristate "Broxton/ApolloLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel Broxton/ApolloLake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_KBL + tristate "Kabylake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel Kabylake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_GLK + tristate "GeminiLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel GeminiLake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_CNL + tristate "CannonLake/WhiskyLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel CNL/WHL platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_CFL + tristate "CoffeeLake Platforms" + depends on PCI && ACPI + select SND_SOC_INTEL_SKYLAKE_FAMILY + help + If you have a Intel CoffeeLake platform with the DSP + enabled in the BIOS then enable this option by saying Y or m. + +config SND_SOC_INTEL_SKYLAKE_FAMILY + tristate + select SND_SOC_INTEL_SKYLAKE_COMMON -if SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKYLAKE_FAMILY config SND_SOC_INTEL_SKYLAKE_SSP_CLK tristate @@ -135,7 +194,7 @@ config SND_SOC_INTEL_SKYLAKE_COMMON GeminiLake or CannonLake platform with the DSP enabled in the BIOS then enable this option by saying Y or m. -endif ## SND_SOC_INTEL_SKYLAKE +endif ## SND_SOC_INTEL_SKYLAKE_FAMILY config SND_SOC_ACPI_INTEL_MATCH tristate diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 3839d6205fcf..0a7e40d06395 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -172,7 +172,7 @@ config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH endif ## SND_SST_ATOM_HIFI2_PLATFORM -if SND_SOC_INTEL_SKYLAKE +if SND_SOC_INTEL_SKL config SND_SOC_INTEL_SKL_RT286_MACH tristate "SKL with RT286 I2S mode" @@ -212,6 +212,10 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_SKL + +if SND_SOC_INTEL_APL + config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH tristate "Broxton with DA7219 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -239,6 +243,10 @@ config SND_SOC_INTEL_BXT_RT298_MACH Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +endif ## SND_SOC_INTEL_APL + +if SND_SOC_INTEL_KBL + config SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH tristate "KBL with RT5663 and MAX98927 in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -303,6 +311,10 @@ config SND_SOC_INTEL_KBL_RT5660_MACH create an alsa sound card for RT5660 I2S audio codec. Say Y if you have such a device. +endif ## SND_SOC_INTEL_KBL + +if SND_SOC_INTEL_GLK + config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH tristate "GLK with RT5682 and MAX98357A in I2S Mode" depends on MFD_INTEL_LPSS && I2C && ACPI @@ -317,7 +329,7 @@ config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH Say Y if you have such a device. If unsure select "N". -endif ## SND_SOC_INTEL_SKYLAKE +endif ## SND_SOC_INTEL_GLK if SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index f9888b1d5521..5abd35ca4e41 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1142,24 +1142,36 @@ static void skl_remove(struct pci_dev *pci) /* PCI IDs */ static const struct pci_device_id skl_ids[] = { +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_SKL) /* Sunrise Point-LP */ { PCI_DEVICE(0x8086, 0x9d70), .driver_data = (unsigned long)&snd_soc_acpi_intel_skl_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_APL) /* BXT-P */ { PCI_DEVICE(0x8086, 0x5a98), .driver_data = (unsigned long)&snd_soc_acpi_intel_bxt_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_KBL) /* KBL */ { PCI_DEVICE(0x8086, 0x9D71), .driver_data = (unsigned long)&snd_soc_acpi_intel_kbl_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_GLK) /* GLK */ { PCI_DEVICE(0x8086, 0x3198), .driver_data = (unsigned long)&snd_soc_acpi_intel_glk_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CNL) /* CNL */ { PCI_DEVICE(0x8086, 0x9dc8), .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, +#endif +#if IS_ENABLED(CONFIG_SND_SOC_INTEL_CFL) /* CFL */ { PCI_DEVICE(0x8086, 0xa348), .driver_data = (unsigned long)&snd_soc_acpi_intel_cnl_machines}, +#endif { 0, } }; MODULE_DEVICE_TABLE(pci, skl_ids); -- cgit v1.2.3-58-ga151 From d00f749b00f7802bf944688ad2971455f84fdacb Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 15 Nov 2018 18:13:21 +0000 Subject: ALSA: compress: make use of runtime buffer for copy Default copy function uses kmalloc to allocate buffers, lets check if the runtime buffers are setup before making this allocations. This can be useful if the buffers are dma buffers. Signed-off-by: Srinivas Kandagatla Acked-by: Vinod Koul Signed-off-by: Mark Brown --- sound/core/compress_offload.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 26b5e245b074..a5b09e75e787 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -171,7 +171,8 @@ static int snd_compr_free(struct inode *inode, struct file *f) } data->stream.ops->free(&data->stream); - kfree(data->stream.runtime->buffer); + if (!data->stream.runtime->dma_buffer_p) + kfree(data->stream.runtime->buffer); kfree(data->stream.runtime); kfree(data); return 0; @@ -505,7 +506,7 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, struct snd_compr_params *params) { unsigned int buffer_size; - void *buffer; + void *buffer = NULL; buffer_size = params->buffer.fragment_size * params->buffer.fragments; if (stream->ops->copy) { @@ -514,7 +515,18 @@ static int snd_compr_allocate_buffer(struct snd_compr_stream *stream, * the data from core */ } else { - buffer = kmalloc(buffer_size, GFP_KERNEL); + if (stream->runtime->dma_buffer_p) { + + if (buffer_size > stream->runtime->dma_buffer_p->bytes) + dev_err(&stream->device->dev, + "Not enough DMA buffer"); + else + buffer = stream->runtime->dma_buffer_p->area; + + } else { + buffer = kmalloc(buffer_size, GFP_KERNEL); + } + if (!buffer) return -ENOMEM; } -- cgit v1.2.3-58-ga151 From f2e6c6aa0cb5f39e48627850680b87ec688070f8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 15 Nov 2018 18:13:23 +0000 Subject: ASoC: qdsp6: q6asm: add support to MP3 format This patch adds support to mp3 format in ASM module. Signed-off-by: Srinivas Kandagatla Acked-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index e1cfa846a1dc..4f85cb19a309 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -36,6 +37,7 @@ #define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3 #define ASM_SESSION_CMD_RUN_V2 0x00010DAA #define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5 +#define ASM_MEDIA_FMT_MP3 0x00010BE9 #define ASM_DATA_CMD_WRITE_V2 0x00010DAB #define ASM_DATA_CMD_READ_V2 0x00010DAC #define ASM_SESSION_CMD_SUSPEND 0x00010DEC @@ -868,6 +870,9 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, open->postprocopo_id = ASM_NULL_POPP_TOPOLOGY; switch (format) { + case SND_AUDIOCODEC_MP3: + open->dec_fmt_id = ASM_MEDIA_FMT_MP3; + break; case FORMAT_LINEAR_PCM: open->dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2; break; -- cgit v1.2.3-58-ga151 From 22930c79ac5c243c95e46508f0989e153836adc7 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 15 Nov 2018 18:13:24 +0000 Subject: ASoC: qdsp6: q6asm-dai: Add support to compress offload This patch adds MP3 playback support in q6asm dais, adding other codec support should be pretty trivial. Signed-off-by: Srinivas Kandagatla Acked-by: Vinod Koul Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 1 + sound/soc/qcom/qdsp6/q6asm-dai.c | 372 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 372 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 1b9fc0665792..804ae0d93058 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -66,6 +66,7 @@ config SND_SOC_QDSP6_ASM tristate config SND_SOC_QDSP6_ASM_DAI + select SND_SOC_COMPRESS tristate config SND_SOC_QDSP6 diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 86115de5c1b2..5b986b74dd36 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,6 +32,15 @@ #define CAPTURE_MIN_PERIOD_SIZE 320 #define SID_MASK_DEFAULT 0xF +/* Default values used if user space does not set */ +#define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) +#define COMPR_PLAYBACK_MAX_FRAGMENT_SIZE (128 * 1024) +#define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) +#define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) +#define Q6ASM_DAI_TX_RX 0 +#define Q6ASM_DAI_TX 1 +#define Q6ASM_DAI_RX 2 + enum stream_state { Q6ASM_STREAM_IDLE = 0, Q6ASM_STREAM_STOPPED, @@ -38,11 +49,18 @@ enum stream_state { struct q6asm_dai_rtd { struct snd_pcm_substream *substream; + struct snd_compr_stream *cstream; + struct snd_compr_params codec_param; + struct snd_dma_buffer dma_buffer; + spinlock_t lock; phys_addr_t phys; unsigned int pcm_size; unsigned int pcm_count; unsigned int pcm_irq_pos; /* IRQ position */ unsigned int periods; + unsigned int bytes_sent; + unsigned int bytes_received; + unsigned int copied_total; uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; @@ -137,6 +155,21 @@ static struct snd_pcm_hw_constraint_list constraints_sample_rates = { .mask = 0, }; +static const struct snd_compr_codec_caps q6asm_compr_caps = { + .num_descriptors = 1, + .descriptor[0].max_ch = 2, + .descriptor[0].sample_rates = { 8000, 11025, 12000, 16000, 22050, + 24000, 32000, 44100, 48000, 88200, + 96000, 176400, 192000 }, + .descriptor[0].num_sample_rates = 13, + .descriptor[0].bit_rate[0] = 320, + .descriptor[0].bit_rate[1] = 128, + .descriptor[0].num_bitrates = 2, + .descriptor[0].profiles = 0, + .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO, + .descriptor[0].formats = 0, +}; + static void event_handler(uint32_t opcode, uint32_t token, uint32_t *payload, void *priv) { @@ -460,6 +493,306 @@ static struct snd_pcm_ops q6asm_dai_ops = { .mmap = q6asm_dai_mmap, }; +static void compress_event_handler(uint32_t opcode, uint32_t token, + uint32_t *payload, void *priv) +{ + struct q6asm_dai_rtd *prtd = priv; + struct snd_compr_stream *substream = prtd->cstream; + unsigned long flags; + uint64_t avail; + + switch (opcode) { + case ASM_CLIENT_EVENT_CMD_RUN_DONE: + spin_lock_irqsave(&prtd->lock, flags); + if (!prtd->bytes_sent) { + q6asm_write_async(prtd->audio_client, prtd->pcm_count, + 0, 0, NO_TIMESTAMP); + prtd->bytes_sent += prtd->pcm_count; + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + + case ASM_CLIENT_EVENT_CMD_EOS_DONE: + prtd->state = Q6ASM_STREAM_STOPPED; + break; + + case ASM_CLIENT_EVENT_DATA_WRITE_DONE: + spin_lock_irqsave(&prtd->lock, flags); + + prtd->copied_total += prtd->pcm_count; + snd_compr_fragment_elapsed(substream); + + if (prtd->state != Q6ASM_STREAM_RUNNING) { + spin_unlock_irqrestore(&prtd->lock, flags); + break; + } + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail >= prtd->pcm_count) { + q6asm_write_async(prtd->audio_client, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); + prtd->bytes_sent += prtd->pcm_count; + } + + spin_unlock_irqrestore(&prtd->lock, flags); + break; + + default: + break; + } +} + +static int q6asm_dai_compr_open(struct snd_compr_stream *stream) +{ + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct snd_compr_runtime *runtime = stream->runtime; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct q6asm_dai_data *pdata; + struct device *dev = c->dev; + struct q6asm_dai_rtd *prtd; + int stream_id, size, ret; + + stream_id = cpu_dai->driver->id; + pdata = snd_soc_component_get_drvdata(c); + if (!pdata) { + dev_err(dev, "Drv data not found ..\n"); + return -EINVAL; + } + + prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); + if (!prtd) + return -ENOMEM; + + prtd->cstream = stream; + prtd->audio_client = q6asm_audio_client_alloc(dev, + (q6asm_cb)compress_event_handler, + prtd, stream_id, LEGACY_PCM_MODE); + if (!prtd->audio_client) { + dev_err(dev, "Could not allocate memory\n"); + kfree(prtd); + return -ENOMEM; + } + + size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE * + COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dev, size, + &prtd->dma_buffer); + if (ret) { + dev_err(dev, "Cannot allocate buffer(s)\n"); + return ret; + } + + if (pdata->sid < 0) + prtd->phys = prtd->dma_buffer.addr; + else + prtd->phys = prtd->dma_buffer.addr | (pdata->sid << 32); + + snd_compr_set_runtime_buffer(stream, &prtd->dma_buffer); + spin_lock_init(&prtd->lock); + runtime->private_data = prtd; + + return 0; +} + +static int q6asm_dai_compr_free(struct snd_compr_stream *stream) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + + if (prtd->audio_client) { + if (prtd->state) + q6asm_cmd(prtd->audio_client, CMD_CLOSE); + + snd_dma_free_pages(&prtd->dma_buffer); + q6asm_unmap_memory_regions(stream->direction, + prtd->audio_client); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + } + q6routing_stream_close(rtd->dai_link->id, stream->direction); + kfree(prtd); + + return 0; +} + +static int q6asm_dai_compr_set_params(struct snd_compr_stream *stream, + struct snd_compr_params *params) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + int dir = stream->direction; + struct q6asm_dai_data *pdata; + struct device *dev = c->dev; + int ret; + + memcpy(&prtd->codec_param, params, sizeof(*params)); + + pdata = snd_soc_component_get_drvdata(c); + if (!pdata) + return -EINVAL; + + if (!prtd || !prtd->audio_client) { + dev_err(dev, "private data null or audio client freed\n"); + return -EINVAL; + } + + prtd->periods = runtime->fragments; + prtd->pcm_count = runtime->fragment_size; + prtd->pcm_size = runtime->fragments * runtime->fragment_size; + prtd->bits_per_sample = 16; + if (dir == SND_COMPRESS_PLAYBACK) { + ret = q6asm_open_write(prtd->audio_client, params->codec.id, + prtd->bits_per_sample); + + if (ret < 0) { + dev_err(dev, "q6asm_open_write failed\n"); + q6asm_audio_client_free(prtd->audio_client); + prtd->audio_client = NULL; + return ret; + } + } + + prtd->session_id = q6asm_get_session_id(prtd->audio_client); + ret = q6routing_stream_open(rtd->dai_link->id, LEGACY_PCM_MODE, + prtd->session_id, dir); + if (ret) { + dev_err(dev, "Stream reg failed ret:%d\n", ret); + return ret; + } + + ret = q6asm_map_memory_regions(dir, prtd->audio_client, prtd->phys, + (prtd->pcm_size / prtd->periods), + prtd->periods); + + if (ret < 0) { + dev_err(dev, "Buffer Mapping failed ret:%d\n", ret); + return -ENOMEM; + } + + prtd->state = Q6ASM_STREAM_RUNNING; + + return 0; +} + +static int q6asm_dai_compr_trigger(struct snd_compr_stream *stream, int cmd) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0); + break; + case SNDRV_PCM_TRIGGER_STOP: + prtd->state = Q6ASM_STREAM_STOPPED; + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int q6asm_dai_compr_pointer(struct snd_compr_stream *stream, + struct snd_compr_tstamp *tstamp) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + + tstamp->copied_total = prtd->copied_total; + tstamp->byte_offset = prtd->copied_total % prtd->pcm_size; + + spin_unlock_irqrestore(&prtd->lock, flags); + + return 0; +} + +static int q6asm_dai_compr_ack(struct snd_compr_stream *stream, + size_t count) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + unsigned long flags; + + spin_lock_irqsave(&prtd->lock, flags); + prtd->bytes_received += count; + spin_unlock_irqrestore(&prtd->lock, flags); + + return count; +} + +static int q6asm_dai_compr_mmap(struct snd_compr_stream *stream, + struct vm_area_struct *vma) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + struct snd_soc_pcm_runtime *rtd = stream->private_data; + struct snd_soc_component *c = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + struct device *dev = c->dev; + + return dma_mmap_coherent(dev, vma, + prtd->dma_buffer.area, prtd->dma_buffer.addr, + prtd->dma_buffer.bytes); +} + +static int q6asm_dai_compr_get_caps(struct snd_compr_stream *stream, + struct snd_compr_caps *caps) +{ + caps->direction = SND_COMPRESS_PLAYBACK; + caps->min_fragment_size = COMPR_PLAYBACK_MIN_FRAGMENT_SIZE; + caps->max_fragment_size = COMPR_PLAYBACK_MAX_FRAGMENT_SIZE; + caps->min_fragments = COMPR_PLAYBACK_MIN_NUM_FRAGMENTS; + caps->max_fragments = COMPR_PLAYBACK_MAX_NUM_FRAGMENTS; + caps->num_codecs = 1; + caps->codecs[0] = SND_AUDIOCODEC_MP3; + + return 0; +} + +static int q6asm_dai_compr_get_codec_caps(struct snd_compr_stream *stream, + struct snd_compr_codec_caps *codec) +{ + switch (codec->codec) { + case SND_AUDIOCODEC_MP3: + *codec = q6asm_compr_caps; + break; + default: + break; + } + + return 0; +} + +static struct snd_compr_ops q6asm_dai_compr_ops = { + .open = q6asm_dai_compr_open, + .free = q6asm_dai_compr_free, + .set_params = q6asm_dai_compr_set_params, + .pointer = q6asm_dai_compr_pointer, + .trigger = q6asm_dai_compr_trigger, + .get_caps = q6asm_dai_compr_get_caps, + .get_codec_caps = q6asm_dai_compr_get_codec_caps, + .mmap = q6asm_dai_compr_mmap, + .ack = q6asm_dai_compr_ack, +}; + static int q6asm_dai_pcm_new(struct snd_soc_pcm_runtime *rtd) { struct snd_pcm_substream *psubstream, *csubstream; @@ -515,7 +848,7 @@ static const struct snd_soc_component_driver q6asm_fe_dai_component = { .ops = &q6asm_dai_ops, .pcm_new = q6asm_dai_pcm_new, .pcm_free = q6asm_dai_pcm_free, - + .compr_ops = &q6asm_dai_compr_ops, }; static struct snd_soc_dai_driver q6asm_fe_dais[] = { @@ -529,6 +862,41 @@ static struct snd_soc_dai_driver q6asm_fe_dais[] = { Q6ASM_FEDAI_DRIVER(8), }; +static int of_q6asm_parse_dai_data(struct device *dev, + struct q6asm_dai_data *pdata) +{ + static struct snd_soc_dai_driver *dai_drv; + struct snd_soc_pcm_stream empty_stream; + struct device_node *node; + int ret, id, dir; + + memset(&empty_stream, 0, sizeof(empty_stream)); + + for_each_child_of_node(dev->of_node, node) { + ret = of_property_read_u32(node, "reg", &id); + if (ret || id > MAX_SESSIONS || id < 0) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + dai_drv = &q6asm_fe_dais[id]; + + ret = of_property_read_u32(node, "direction", &dir); + if (ret) + continue; + + if (dir == Q6ASM_DAI_RX) + dai_drv->capture = empty_stream; + else if (dir == Q6ASM_DAI_TX) + dai_drv->playback = empty_stream; + + if (of_property_read_bool(node, "is-compress-dai")) + dai_drv->compress_new = snd_soc_new_compress; + } + + return 0; +} + static int q6asm_dai_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -549,6 +917,8 @@ static int q6asm_dai_probe(struct platform_device *pdev) dev_set_drvdata(dev, pdata); + of_q6asm_parse_dai_data(dev, pdata); + return devm_snd_soc_register_component(dev, &q6asm_fe_dai_component, q6asm_fe_dais, ARRAY_SIZE(q6asm_fe_dais)); -- cgit v1.2.3-58-ga151 From 3f6856a28f5eeb4fcaeb3ae25118171747354e51 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Dec 2018 17:59:27 +0530 Subject: ASoC: qdsp6: qdafe: add support for display_port_rx This patch adds support for Display_Port_Rx port in AFE. Signed-off-by: Rohit kumar Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 829b5e987b2a..e0945f7a58c8 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -71,6 +71,7 @@ /* Port IDs */ #define AFE_API_VERSION_HDMI_CONFIG 0x1 #define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E +#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020 #define AFE_API_VERSION_SLIMBUS_CONFIG 0x1 /* Clock set API version */ @@ -704,6 +705,8 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_TDM_RX_7, 1, 1}, [QUINARY_TDM_TX_7] = { AFE_PORT_ID_QUINARY_TDM_TX_7, QUINARY_TDM_TX_7, 0, 1}, + [DISPLAY_PORT_RX] = { AFE_PORT_ID_HDMI_OVER_DP_RX, + DISPLAY_PORT_RX, 1, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -1384,6 +1387,7 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) switch (port_id) { case AFE_PORT_ID_MULTICHAN_HDMI_RX: + case AFE_PORT_ID_HDMI_OVER_DP_RX: cfg_type = AFE_PARAM_ID_HDMI_CONFIG; break; case AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX: -- cgit v1.2.3-58-ga151 From 32d2c61ba05944aa1e61d186611310dd911e1703 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Dec 2018 17:59:28 +0530 Subject: ASoC: qdsp6: q6afe-dai: add support to Display port RX dais This patch adds support of AFE DAI for Display_port_rx port. Signed-off-by: Rohit kumar Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe-dai.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index bfc3bbf1d4de..dc645ba4d8d0 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -341,6 +341,7 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, switch (dai->id) { case HDMI_RX: + case DISPLAY_PORT_RX: q6afe_hdmi_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].hdmi); break; @@ -445,6 +446,7 @@ static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai, static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"HDMI Playback", NULL, "HDMI_RX"}, + {"Display Port Playback", NULL, "DISPLAY_PORT_RX"}, {"Slimbus1 Playback", NULL, "SLIMBUS_1_RX"}, {"Slimbus2 Playback", NULL, "SLIMBUS_2_RX"}, {"Slimbus3 Playback", NULL, "SLIMBUS_3_RX"}, @@ -1090,6 +1092,25 @@ static struct snd_soc_dai_driver q6afe_dais[] = { Q6AFE_TDM_CAP_DAI("Quinary", 5, QUINARY_TDM_TX_5), Q6AFE_TDM_CAP_DAI("Quinary", 6, QUINARY_TDM_TX_6), Q6AFE_TDM_CAP_DAI("Quinary", 7, QUINARY_TDM_TX_7), + { + .playback = { + .stream_name = "Display Port Playback", + .rates = SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + .channels_min = 2, + .channels_max = 8, + .rate_max = 192000, + .rate_min = 48000, + }, + .ops = &q6hdmi_ops, + .id = DISPLAY_PORT_RX, + .name = "DISPLAY_PORT", + .probe = msm_dai_q6_dai_probe, + .remove = msm_dai_q6_dai_remove, + }, }; static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, @@ -1311,6 +1332,7 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { 0, 0, 0, 0), SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL, 0, 0, 0, 0), + SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT_RX", "NULL", 0, 0, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { -- cgit v1.2.3-58-ga151 From 0718f87b0df49570cdcb96179997bd372cac1742 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Dec 2018 17:59:29 +0530 Subject: ASoC: qdsp6: q6routing: add display_port_rx port routing This patch add support to Display_port_rx mixers required to select path between ASM stream and AFE ports. Signed-off-by: Rohit kumar Acked-by: Srinivas Kandagatla Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6routing.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index d61b8404f7da..ddcd9978cf57 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -453,6 +453,9 @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new hdmi_mixer_controls[] = { Q6ROUTING_RX_MIXERS(HDMI_RX) }; +static const struct snd_kcontrol_new display_port_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(DISPLAY_PORT_RX) }; + static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = { Q6ROUTING_RX_MIXERS(PRIMARY_MI2S_RX) }; @@ -655,6 +658,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)), + SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Audio Mixer", SND_SOC_NOPM, 0, 0, + display_port_mixer_controls, + ARRAY_SIZE(display_port_mixer_controls)), + SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0, slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)), @@ -833,6 +840,8 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("HDMI Mixer", "HDMI_RX"), + Q6ROUTING_RX_DAPM_ROUTE("DISPLAY_PORT_RX Audio Mixer", + "DISPLAY_PORT_RX"), Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_0_RX Audio Mixer", "SLIMBUS_0_RX"), Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_1_RX Audio Mixer", "SLIMBUS_1_RX"), Q6ROUTING_RX_DAPM_ROUTE("SLIMBUS_2_RX Audio Mixer", "SLIMBUS_2_RX"), -- cgit v1.2.3-58-ga151 From f2055e145f2975a75dace8e386fad9364828cdb4 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 17 Dec 2018 14:21:34 +0200 Subject: ASoC: ti: Merge davinci and omap directories Create new directory to contain all Texas Instruments specific DAI, platform and machine drivers instead of scattering them under davinci and omap directories. There is already inter dependency between the two directories becasue of McASP (on dra7x it is serviced by sDMA, not EDMA). With the upcoming AM654 we will need to introduce new platform driver for UDMA and it does not fit under davinci, nor under omap. With the move I have restructured the Kconfig to be more usable in the era of simple-sound-card: CPU DAIs can be selected individually and they will select the platform driver they can be served with. To avoid breakage, I have moved over deprecated Kconfig options so defconfig builds will work without regression. Signed-off-by: Peter Ujfalusi For sound/soc/{omap => ti}: Tested-by: Jarkko Nikula Acked-by: Jarkko Nikula Acked-by: Tony Lindgren Signed-off-by: Mark Brown --- sound/soc/Kconfig | 3 +- sound/soc/Makefile | 3 +- sound/soc/davinci/Kconfig | 106 -- sound/soc/davinci/Makefile | 16 - sound/soc/davinci/davinci-evm.c | 511 --------- sound/soc/davinci/davinci-i2s.c | 782 ------------- sound/soc/davinci/davinci-i2s.h | 20 - sound/soc/davinci/davinci-mcasp.c | 2212 ------------------------------------- sound/soc/davinci/davinci-mcasp.h | 306 ----- sound/soc/davinci/davinci-vcif.c | 259 ----- sound/soc/davinci/edma-pcm.c | 59 - sound/soc/davinci/edma-pcm.h | 32 - sound/soc/omap/Kconfig | 120 -- sound/soc/omap/Makefile | 30 - sound/soc/omap/ams-delta.c | 594 ---------- sound/soc/omap/n810.c | 378 ------- sound/soc/omap/omap-abe-twl6040.c | 353 ------ sound/soc/omap/omap-dmic.c | 541 --------- sound/soc/omap/omap-dmic.h | 69 -- sound/soc/omap/omap-hdmi-audio.c | 418 ------- sound/soc/omap/omap-mcbsp-priv.h | 324 ------ sound/soc/omap/omap-mcbsp-st.c | 516 --------- sound/soc/omap/omap-mcbsp.c | 1479 ------------------------- sound/soc/omap/omap-mcbsp.h | 46 - sound/soc/omap/omap-mcpdm.c | 619 ----------- sound/soc/omap/omap-mcpdm.h | 107 -- sound/soc/omap/omap-twl4030.c | 353 ------ sound/soc/omap/omap3pandora.c | 315 ------ sound/soc/omap/osk5912.c | 187 ---- sound/soc/omap/rx51.c | 493 --------- sound/soc/omap/sdma-pcm.c | 74 -- sound/soc/omap/sdma-pcm.h | 21 - sound/soc/ti/Kconfig | 368 ++++++ sound/soc/ti/Makefile | 44 + sound/soc/ti/ams-delta.c | 594 ++++++++++ sound/soc/ti/davinci-evm.c | 511 +++++++++ sound/soc/ti/davinci-i2s.c | 782 +++++++++++++ sound/soc/ti/davinci-i2s.h | 20 + sound/soc/ti/davinci-mcasp.c | 2212 +++++++++++++++++++++++++++++++++++++ sound/soc/ti/davinci-mcasp.h | 306 +++++ sound/soc/ti/davinci-vcif.c | 259 +++++ sound/soc/ti/edma-pcm.c | 59 + sound/soc/ti/edma-pcm.h | 32 + sound/soc/ti/n810.c | 378 +++++++ sound/soc/ti/omap-abe-twl6040.c | 353 ++++++ sound/soc/ti/omap-dmic.c | 541 +++++++++ sound/soc/ti/omap-dmic.h | 69 ++ sound/soc/ti/omap-hdmi.c | 418 +++++++ sound/soc/ti/omap-mcbsp-priv.h | 324 ++++++ sound/soc/ti/omap-mcbsp-st.c | 516 +++++++++ sound/soc/ti/omap-mcbsp.c | 1479 +++++++++++++++++++++++++ sound/soc/ti/omap-mcbsp.h | 46 + sound/soc/ti/omap-mcpdm.c | 619 +++++++++++ sound/soc/ti/omap-mcpdm.h | 107 ++ sound/soc/ti/omap-twl4030.c | 353 ++++++ sound/soc/ti/omap3pandora.c | 315 ++++++ sound/soc/ti/osk5912.c | 187 ++++ sound/soc/ti/rx51.c | 493 +++++++++ sound/soc/ti/sdma-pcm.c | 74 ++ sound/soc/ti/sdma-pcm.h | 21 + 60 files changed, 11482 insertions(+), 11344 deletions(-) delete mode 100644 sound/soc/davinci/Kconfig delete mode 100644 sound/soc/davinci/Makefile delete mode 100644 sound/soc/davinci/davinci-evm.c delete mode 100644 sound/soc/davinci/davinci-i2s.c delete mode 100644 sound/soc/davinci/davinci-i2s.h delete mode 100644 sound/soc/davinci/davinci-mcasp.c delete mode 100644 sound/soc/davinci/davinci-mcasp.h delete mode 100644 sound/soc/davinci/davinci-vcif.c delete mode 100644 sound/soc/davinci/edma-pcm.c delete mode 100644 sound/soc/davinci/edma-pcm.h delete mode 100644 sound/soc/omap/Kconfig delete mode 100644 sound/soc/omap/Makefile delete mode 100644 sound/soc/omap/ams-delta.c delete mode 100644 sound/soc/omap/n810.c delete mode 100644 sound/soc/omap/omap-abe-twl6040.c delete mode 100644 sound/soc/omap/omap-dmic.c delete mode 100644 sound/soc/omap/omap-dmic.h delete mode 100644 sound/soc/omap/omap-hdmi-audio.c delete mode 100644 sound/soc/omap/omap-mcbsp-priv.h delete mode 100644 sound/soc/omap/omap-mcbsp-st.c delete mode 100644 sound/soc/omap/omap-mcbsp.c delete mode 100644 sound/soc/omap/omap-mcbsp.h delete mode 100644 sound/soc/omap/omap-mcpdm.c delete mode 100644 sound/soc/omap/omap-mcpdm.h delete mode 100644 sound/soc/omap/omap-twl4030.c delete mode 100644 sound/soc/omap/omap3pandora.c delete mode 100644 sound/soc/omap/osk5912.c delete mode 100644 sound/soc/omap/rx51.c delete mode 100644 sound/soc/omap/sdma-pcm.c delete mode 100644 sound/soc/omap/sdma-pcm.h create mode 100644 sound/soc/ti/Kconfig create mode 100644 sound/soc/ti/Makefile create mode 100644 sound/soc/ti/ams-delta.c create mode 100644 sound/soc/ti/davinci-evm.c create mode 100644 sound/soc/ti/davinci-i2s.c create mode 100644 sound/soc/ti/davinci-i2s.h create mode 100644 sound/soc/ti/davinci-mcasp.c create mode 100644 sound/soc/ti/davinci-mcasp.h create mode 100644 sound/soc/ti/davinci-vcif.c create mode 100644 sound/soc/ti/edma-pcm.c create mode 100644 sound/soc/ti/edma-pcm.h create mode 100644 sound/soc/ti/n810.c create mode 100644 sound/soc/ti/omap-abe-twl6040.c create mode 100644 sound/soc/ti/omap-dmic.c create mode 100644 sound/soc/ti/omap-dmic.h create mode 100644 sound/soc/ti/omap-hdmi.c create mode 100644 sound/soc/ti/omap-mcbsp-priv.h create mode 100644 sound/soc/ti/omap-mcbsp-st.c create mode 100644 sound/soc/ti/omap-mcbsp.c create mode 100644 sound/soc/ti/omap-mcbsp.h create mode 100644 sound/soc/ti/omap-mcpdm.c create mode 100644 sound/soc/ti/omap-mcpdm.h create mode 100644 sound/soc/ti/omap-twl4030.c create mode 100644 sound/soc/ti/omap3pandora.c create mode 100644 sound/soc/ti/osk5912.c create mode 100644 sound/soc/ti/rx51.c create mode 100644 sound/soc/ti/sdma-pcm.c create mode 100644 sound/soc/ti/sdma-pcm.h (limited to 'sound') diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index d21659f5a7e2..6592a422a047 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -46,13 +46,11 @@ source "sound/soc/atmel/Kconfig" source "sound/soc/au1x/Kconfig" source "sound/soc/bcm/Kconfig" source "sound/soc/cirrus/Kconfig" -source "sound/soc/davinci/Kconfig" source "sound/soc/dwc/Kconfig" source "sound/soc/fsl/Kconfig" source "sound/soc/hisilicon/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" -source "sound/soc/omap/Kconfig" source "sound/soc/kirkwood/Kconfig" source "sound/soc/img/Kconfig" source "sound/soc/intel/Kconfig" @@ -70,6 +68,7 @@ source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" source "sound/soc/tegra/Kconfig" +source "sound/soc/ti/Kconfig" source "sound/soc/txx9/Kconfig" source "sound/soc/uniphier/Kconfig" source "sound/soc/ux500/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index aff7b1989436..48c48c1c893c 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_SND_SOC) += atmel/ obj-$(CONFIG_SND_SOC) += au1x/ obj-$(CONFIG_SND_SOC) += bcm/ obj-$(CONFIG_SND_SOC) += cirrus/ -obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += dwc/ obj-$(CONFIG_SND_SOC) += fsl/ obj-$(CONFIG_SND_SOC) += hisilicon/ @@ -41,7 +40,6 @@ obj-$(CONFIG_SND_SOC) += mediatek/ obj-$(CONFIG_SND_SOC) += meson/ obj-$(CONFIG_SND_SOC) += mxs/ obj-$(CONFIG_SND_SOC) += nuc900/ -obj-$(CONFIG_SND_SOC) += omap/ obj-$(CONFIG_SND_SOC) += kirkwood/ obj-$(CONFIG_SND_SOC) += pxa/ obj-$(CONFIG_SND_SOC) += qcom/ @@ -54,6 +52,7 @@ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ obj-$(CONFIG_SND_SOC) += tegra/ +obj-$(CONFIG_SND_SOC) += ti/ obj-$(CONFIG_SND_SOC) += txx9/ obj-$(CONFIG_SND_SOC) += uniphier/ obj-$(CONFIG_SND_SOC) += ux500/ diff --git a/sound/soc/davinci/Kconfig b/sound/soc/davinci/Kconfig deleted file mode 100644 index 778faff28e0e..000000000000 --- a/sound/soc/davinci/Kconfig +++ /dev/null @@ -1,106 +0,0 @@ -config SND_DAVINCI_SOC - tristate - depends on ARCH_DAVINCI - select SND_EDMA_SOC - -config SND_EDMA_SOC - tristate "SoC Audio for Texas Instruments chips using eDMA" - depends on TI_EDMA - select SND_SOC_GENERIC_DMAENGINE_PCM - help - Say Y or M here if you want audio support for TI SoC which uses eDMA. - The following line of SoCs are supported by this platform driver: - - daVinci devices - - AM335x - - AM437x/AM438x - - DRA7xx family - -config SND_DAVINCI_SOC_I2S - tristate "DaVinci Multichannel Buffered Serial Port (McBSP) support" - depends on SND_EDMA_SOC - help - Say Y or M here if you want to have support for McBSP IP found in - Texas Instruments DaVinci DA850 SoCs. - -config SND_DAVINCI_SOC_MCASP - tristate "Multichannel Audio Serial Port (McASP) support" - depends on SND_SDMA_SOC || SND_EDMA_SOC - help - Say Y or M here if you want to have support for McASP IP found in - various Texas Instruments SoCs like: - - daVinci devices - - Sitara line of SoCs (AM335x, AM438x, etc) - - DRA7x devices - -config SND_DAVINCI_SOC_VCIF - tristate - -config SND_DAVINCI_SOC_GENERIC_EVM - tristate - select SND_SOC_TLV320AIC3X - select SND_DAVINCI_SOC_MCASP - -config SND_AM33XX_SOC_EVM - tristate "SoC Audio for the AM33XX chip based boards" - depends on SND_EDMA_SOC && SOC_AM33XX && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - help - Say Y or M if you want to add support for SoC audio on AM33XX - boards using McASP and TLV320AIC3X codec. For example AM335X-EVM, - AM335X-EVMSK, and BeagelBone with AudioCape boards have this - setup. - -config SND_DAVINCI_SOC_EVM - tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" - depends on SND_EDMA_SOC && I2C - depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM - select SND_DAVINCI_SOC_GENERIC_EVM - help - Say Y if you want to add support for SoC audio on TI - DaVinci DM6446, DM355 or DM365 EVM platforms. - -choice - prompt "DM365 codec select" - depends on SND_DAVINCI_SOC_EVM - depends on MACH_DAVINCI_DM365_EVM - -config SND_DM365_AIC3X_CODEC - tristate "Audio Codec - AIC3101" - help - Say Y if you want to add support for AIC3101 audio codec - -config SND_DM365_VOICE_CODEC - tristate "Voice Codec - CQ93VC" - select MFD_DAVINCI_VOICECODEC - select SND_DAVINCI_SOC_VCIF - select SND_SOC_CQ0093VC - help - Say Y if you want to add support for SoC On-chip voice codec -endchoice - -config SND_DM6467_SOC_EVM - tristate "SoC Audio support for DaVinci DM6467 EVM" - depends on SND_EDMA_SOC && MACH_DAVINCI_DM6467_EVM && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - select SND_SOC_SPDIF - - help - Say Y if you want to add support for SoC audio on TI - -config SND_DA830_SOC_EVM - tristate "SoC Audio support for DA830/OMAP-L137 EVM" - depends on SND_EDMA_SOC && MACH_DAVINCI_DA830_EVM && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - - help - Say Y if you want to add support for SoC audio on TI - DA830/OMAP-L137 EVM - -config SND_DA850_SOC_EVM - tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on SND_EDMA_SOC && MACH_DAVINCI_DA850_EVM && I2C - select SND_DAVINCI_SOC_GENERIC_EVM - help - Say Y if you want to add support for SoC audio on TI - DA850/OMAP-L138 EVM - diff --git a/sound/soc/davinci/Makefile b/sound/soc/davinci/Makefile deleted file mode 100644 index 23c6592eb31a..000000000000 --- a/sound/soc/davinci/Makefile +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# DAVINCI Platform Support -snd-soc-edma-objs := edma-pcm.o -snd-soc-davinci-i2s-objs := davinci-i2s.o -snd-soc-davinci-mcasp-objs:= davinci-mcasp.o -snd-soc-davinci-vcif-objs:= davinci-vcif.o - -obj-$(CONFIG_SND_EDMA_SOC) += snd-soc-edma.o -obj-$(CONFIG_SND_DAVINCI_SOC_I2S) += snd-soc-davinci-i2s.o -obj-$(CONFIG_SND_DAVINCI_SOC_MCASP) += snd-soc-davinci-mcasp.o -obj-$(CONFIG_SND_DAVINCI_SOC_VCIF) += snd-soc-davinci-vcif.o - -# Generic DAVINCI/AM33xx Machine Support -snd-soc-evm-objs := davinci-evm.o - -obj-$(CONFIG_SND_DAVINCI_SOC_GENERIC_EVM) += snd-soc-evm.o diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c deleted file mode 100644 index 7a369e0f2093..000000000000 --- a/sound/soc/davinci/davinci-evm.c +++ /dev/null @@ -1,511 +0,0 @@ -/* - * ASoC driver for TI DAVINCI EVM platform - * - * Author: Vladimir Barinov, - * Copyright: (C) 2007 MontaVista Software, Inc., - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -struct snd_soc_card_drvdata_davinci { - struct clk *mclk; - unsigned sysclk; -}; - -static int evm_startup(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *soc_card = rtd->card; - struct snd_soc_card_drvdata_davinci *drvdata = - snd_soc_card_get_drvdata(soc_card); - - if (drvdata->mclk) - return clk_prepare_enable(drvdata->mclk); - - return 0; -} - -static void evm_shutdown(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *soc_card = rtd->card; - struct snd_soc_card_drvdata_davinci *drvdata = - snd_soc_card_get_drvdata(soc_card); - - if (drvdata->mclk) - clk_disable_unprepare(drvdata->mclk); -} - -static int evm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct snd_soc_card *soc_card = rtd->card; - int ret = 0; - unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) - snd_soc_card_get_drvdata(soc_card))->sysclk; - - /* set the codec system clock */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT); - if (ret < 0) - return ret; - - /* set the CPU system clock */ - ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT); - if (ret < 0) - return ret; - - return 0; -} - -static struct snd_soc_ops evm_ops = { - .startup = evm_startup, - .shutdown = evm_shutdown, - .hw_params = evm_hw_params, -}; - -/* davinci-evm machine dapm widgets */ -static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_LINE("Line Out", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), -}; - -/* davinci-evm machine audio_mapnections to the codec pins */ -static const struct snd_soc_dapm_route audio_map[] = { - /* Headphone connected to HPLOUT, HPROUT */ - {"Headphone Jack", NULL, "HPLOUT"}, - {"Headphone Jack", NULL, "HPROUT"}, - - /* Line Out connected to LLOUT, RLOUT */ - {"Line Out", NULL, "LLOUT"}, - {"Line Out", NULL, "RLOUT"}, - - /* Mic connected to (MIC3L | MIC3R) */ - {"MIC3L", NULL, "Mic Bias"}, - {"MIC3R", NULL, "Mic Bias"}, - {"Mic Bias", NULL, "Mic Jack"}, - - /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */ - {"LINE1L", NULL, "Line In"}, - {"LINE2L", NULL, "Line In"}, - {"LINE1R", NULL, "Line In"}, - {"LINE2R", NULL, "Line In"}, -}; - -/* Logic for a aic3x as connected on a davinci-evm */ -static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - struct device_node *np = card->dev->of_node; - int ret; - - /* Add davinci-evm specific widgets */ - snd_soc_dapm_new_controls(&card->dapm, aic3x_dapm_widgets, - ARRAY_SIZE(aic3x_dapm_widgets)); - - if (np) { - ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); - if (ret) - return ret; - } else { - /* Set up davinci-evm specific audio path audio_map */ - snd_soc_dapm_add_routes(&card->dapm, audio_map, - ARRAY_SIZE(audio_map)); - } - - /* not connected */ - snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT"); - snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM"); - snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM"); - - return 0; -} - -/* davinci-evm digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link dm6446_evm_dai = { - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .cpu_dai_name = "davinci-mcbsp", - .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.1-001b", - .platform_name = "davinci-mcbsp", - .init = evm_aic3x_init, - .ops = &evm_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - -static struct snd_soc_dai_link dm355_evm_dai = { - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .cpu_dai_name = "davinci-mcbsp.1", - .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.1-001b", - .platform_name = "davinci-mcbsp.1", - .init = evm_aic3x_init, - .ops = &evm_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - -static struct snd_soc_dai_link dm365_evm_dai = { -#ifdef CONFIG_SND_DM365_AIC3X_CODEC - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .cpu_dai_name = "davinci-mcbsp", - .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.1-0018", - .platform_name = "davinci-mcbsp", - .init = evm_aic3x_init, - .ops = &evm_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -#elif defined(CONFIG_SND_DM365_VOICE_CODEC) - .name = "Voice Codec - CQ93VC", - .stream_name = "CQ93", - .cpu_dai_name = "davinci-vcif", - .codec_dai_name = "cq93vc-hifi", - .codec_name = "cq93vc-codec", - .platform_name = "davinci-vcif", -#endif -}; - -static struct snd_soc_dai_link dm6467_evm_dai[] = { - { - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .cpu_dai_name= "davinci-mcasp.0", - .codec_dai_name = "tlv320aic3x-hifi", - .platform_name = "davinci-mcasp.0", - .codec_name = "tlv320aic3x-codec.0-001a", - .init = evm_aic3x_init, - .ops = &evm_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, - }, - { - .name = "McASP", - .stream_name = "spdif", - .cpu_dai_name= "davinci-mcasp.1", - .codec_dai_name = "dit-hifi", - .codec_name = "spdif_dit", - .platform_name = "davinci-mcasp.1", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, - }, -}; - -static struct snd_soc_dai_link da830_evm_dai = { - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .cpu_dai_name = "davinci-mcasp.1", - .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.1-0018", - .platform_name = "davinci-mcasp.1", - .init = evm_aic3x_init, - .ops = &evm_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - -static struct snd_soc_dai_link da850_evm_dai = { - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .cpu_dai_name= "davinci-mcasp.0", - .codec_dai_name = "tlv320aic3x-hifi", - .codec_name = "tlv320aic3x-codec.1-0018", - .platform_name = "davinci-mcasp.0", - .init = evm_aic3x_init, - .ops = &evm_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - -/* davinci dm6446 evm audio machine driver */ -/* - * ASP0 in DM6446 EVM is clocked by U55, as configured by - * board-dm644x-evm.c using GPIOs from U18. There are six - * options; here we "know" we use a 48 KHz sample rate. - */ -static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = { - .sysclk = 12288000, -}; - -static struct snd_soc_card dm6446_snd_soc_card_evm = { - .name = "DaVinci DM6446 EVM", - .owner = THIS_MODULE, - .dai_link = &dm6446_evm_dai, - .num_links = 1, - .drvdata = &dm6446_snd_soc_card_drvdata, -}; - -/* davinci dm355 evm audio machine driver */ -/* ASP1 on DM355 EVM is clocked by an external oscillator */ -static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = { - .sysclk = 27000000, -}; - -static struct snd_soc_card dm355_snd_soc_card_evm = { - .name = "DaVinci DM355 EVM", - .owner = THIS_MODULE, - .dai_link = &dm355_evm_dai, - .num_links = 1, - .drvdata = &dm355_snd_soc_card_drvdata, -}; - -/* davinci dm365 evm audio machine driver */ -static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = { - .sysclk = 27000000, -}; - -static struct snd_soc_card dm365_snd_soc_card_evm = { - .name = "DaVinci DM365 EVM", - .owner = THIS_MODULE, - .dai_link = &dm365_evm_dai, - .num_links = 1, - .drvdata = &dm365_snd_soc_card_drvdata, -}; - -/* davinci dm6467 evm audio machine driver */ -static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = { - .sysclk = 27000000, -}; - -static struct snd_soc_card dm6467_snd_soc_card_evm = { - .name = "DaVinci DM6467 EVM", - .owner = THIS_MODULE, - .dai_link = dm6467_evm_dai, - .num_links = ARRAY_SIZE(dm6467_evm_dai), - .drvdata = &dm6467_snd_soc_card_drvdata, -}; - -static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = { - .sysclk = 24576000, -}; - -static struct snd_soc_card da830_snd_soc_card = { - .name = "DA830/OMAP-L137 EVM", - .owner = THIS_MODULE, - .dai_link = &da830_evm_dai, - .num_links = 1, - .drvdata = &da830_snd_soc_card_drvdata, -}; - -static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = { - .sysclk = 24576000, -}; - -static struct snd_soc_card da850_snd_soc_card = { - .name = "DA850/OMAP-L138 EVM", - .owner = THIS_MODULE, - .dai_link = &da850_evm_dai, - .num_links = 1, - .drvdata = &da850_snd_soc_card_drvdata, -}; - -#if defined(CONFIG_OF) - -/* - * The struct is used as place holder. It will be completely - * filled with data from dt node. - */ -static struct snd_soc_dai_link evm_dai_tlv320aic3x = { - .name = "TLV320AIC3X", - .stream_name = "AIC3X", - .codec_dai_name = "tlv320aic3x-hifi", - .ops = &evm_ops, - .init = evm_aic3x_init, - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | - SND_SOC_DAIFMT_IB_NF, -}; - -static const struct of_device_id davinci_evm_dt_ids[] = { - { - .compatible = "ti,da830-evm-audio", - .data = (void *) &evm_dai_tlv320aic3x, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids); - -/* davinci evm audio machine driver */ -static struct snd_soc_card evm_soc_card = { - .owner = THIS_MODULE, - .num_links = 1, -}; - -static int davinci_evm_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - const struct of_device_id *match; - struct snd_soc_dai_link *dai; - struct snd_soc_card_drvdata_davinci *drvdata = NULL; - struct clk *mclk; - int ret = 0; - - match = of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); - if (!match) { - dev_err(&pdev->dev, "Error: No device match found\n"); - return -ENODEV; - } - - dai = (struct snd_soc_dai_link *) match->data; - - evm_soc_card.dai_link = dai; - - dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); - if (!dai->codec_of_node) - return -EINVAL; - - dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0); - if (!dai->cpu_of_node) - return -EINVAL; - - dai->platform_of_node = dai->cpu_of_node; - - evm_soc_card.dev = &pdev->dev; - ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); - if (ret) - return ret; - - mclk = devm_clk_get(&pdev->dev, "mclk"); - if (PTR_ERR(mclk) == -EPROBE_DEFER) { - return -EPROBE_DEFER; - } else if (IS_ERR(mclk)) { - dev_dbg(&pdev->dev, "mclk not found.\n"); - mclk = NULL; - } - - drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); - if (!drvdata) - return -ENOMEM; - - drvdata->mclk = mclk; - - ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk); - - if (ret < 0) { - if (!drvdata->mclk) { - dev_err(&pdev->dev, - "No clock or clock rate defined.\n"); - return -EINVAL; - } - drvdata->sysclk = clk_get_rate(drvdata->mclk); - } else if (drvdata->mclk) { - unsigned int requestd_rate = drvdata->sysclk; - clk_set_rate(drvdata->mclk, drvdata->sysclk); - drvdata->sysclk = clk_get_rate(drvdata->mclk); - if (drvdata->sysclk != requestd_rate) - dev_warn(&pdev->dev, - "Could not get requested rate %u using %u.\n", - requestd_rate, drvdata->sysclk); - } - - snd_soc_card_set_drvdata(&evm_soc_card, drvdata); - ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); - - if (ret) - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - - return ret; -} - -static struct platform_driver davinci_evm_driver = { - .probe = davinci_evm_probe, - .driver = { - .name = "davinci_evm", - .pm = &snd_soc_pm_ops, - .of_match_table = of_match_ptr(davinci_evm_dt_ids), - }, -}; -#endif - -static struct platform_device *evm_snd_device; - -static int __init evm_init(void) -{ - struct snd_soc_card *evm_snd_dev_data; - int index; - int ret; - - /* - * If dtb is there, the devices will be created dynamically. - * Only register platfrom driver structure. - */ -#if defined(CONFIG_OF) - if (of_have_populated_dt()) - return platform_driver_register(&davinci_evm_driver); -#endif - - if (machine_is_davinci_evm()) { - evm_snd_dev_data = &dm6446_snd_soc_card_evm; - index = 0; - } else if (machine_is_davinci_dm355_evm()) { - evm_snd_dev_data = &dm355_snd_soc_card_evm; - index = 1; - } else if (machine_is_davinci_dm365_evm()) { - evm_snd_dev_data = &dm365_snd_soc_card_evm; - index = 0; - } else if (machine_is_davinci_dm6467_evm()) { - evm_snd_dev_data = &dm6467_snd_soc_card_evm; - index = 0; - } else if (machine_is_davinci_da830_evm()) { - evm_snd_dev_data = &da830_snd_soc_card; - index = 1; - } else if (machine_is_davinci_da850_evm()) { - evm_snd_dev_data = &da850_snd_soc_card; - index = 0; - } else - return -EINVAL; - - evm_snd_device = platform_device_alloc("soc-audio", index); - if (!evm_snd_device) - return -ENOMEM; - - platform_set_drvdata(evm_snd_device, evm_snd_dev_data); - ret = platform_device_add(evm_snd_device); - if (ret) - platform_device_put(evm_snd_device); - - return ret; -} - -static void __exit evm_exit(void) -{ -#if defined(CONFIG_OF) - if (of_have_populated_dt()) { - platform_driver_unregister(&davinci_evm_driver); - return; - } -#endif - - platform_device_unregister(evm_snd_device); -} - -module_init(evm_init); -module_exit(evm_exit); - -MODULE_AUTHOR("Vladimir Barinov"); -MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c deleted file mode 100644 index a3206e65e5e5..000000000000 --- a/sound/soc/davinci/davinci-i2s.c +++ /dev/null @@ -1,782 +0,0 @@ -/* - * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor - * - * Author: Vladimir Barinov, - * Copyright: (C) 2007 MontaVista Software, Inc., - * - * DT support (c) 2016 Petr Kulhavy, Barix AG - * based on davinci-mcasp.c DT support - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * TODO: - * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "edma-pcm.h" -#include "davinci-i2s.h" - -#define DRV_NAME "davinci-i2s" - -/* - * NOTE: terminology here is confusing. - * - * - This driver supports the "Audio Serial Port" (ASP), - * found on dm6446, dm355, and other DaVinci chips. - * - * - But it labels it a "Multi-channel Buffered Serial Port" - * (McBSP) as on older chips like the dm642 ... which was - * backward-compatible, possibly explaining that confusion. - * - * - OMAP chips have a controller called McBSP, which is - * incompatible with the DaVinci flavor of McBSP. - * - * - Newer DaVinci chips have a controller called McASP, - * incompatible with ASP and with either McBSP. - * - * In short: this uses ASP to implement I2S, not McBSP. - * And it won't be the only DaVinci implemention of I2S. - */ -#define DAVINCI_MCBSP_DRR_REG 0x00 -#define DAVINCI_MCBSP_DXR_REG 0x04 -#define DAVINCI_MCBSP_SPCR_REG 0x08 -#define DAVINCI_MCBSP_RCR_REG 0x0c -#define DAVINCI_MCBSP_XCR_REG 0x10 -#define DAVINCI_MCBSP_SRGR_REG 0x14 -#define DAVINCI_MCBSP_PCR_REG 0x24 - -#define DAVINCI_MCBSP_SPCR_RRST (1 << 0) -#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4) -#define DAVINCI_MCBSP_SPCR_XRST (1 << 16) -#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20) -#define DAVINCI_MCBSP_SPCR_GRST (1 << 22) -#define DAVINCI_MCBSP_SPCR_FRST (1 << 23) -#define DAVINCI_MCBSP_SPCR_FREE (1 << 25) - -#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5) -#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8) -#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) -#define DAVINCI_MCBSP_RCR_RFIG (1 << 18) -#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) -#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24) -#define DAVINCI_MCBSP_RCR_RPHASE BIT(31) - -#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) -#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) -#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) -#define DAVINCI_MCBSP_XCR_XFIG (1 << 18) -#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) -#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24) -#define DAVINCI_MCBSP_XCR_XPHASE BIT(31) - -#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) -#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) -#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) -#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29) - -#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) -#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) -#define DAVINCI_MCBSP_PCR_FSRP (1 << 2) -#define DAVINCI_MCBSP_PCR_FSXP (1 << 3) -#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7) -#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8) -#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9) -#define DAVINCI_MCBSP_PCR_FSRM (1 << 10) -#define DAVINCI_MCBSP_PCR_FSXM (1 << 11) - -enum { - DAVINCI_MCBSP_WORD_8 = 0, - DAVINCI_MCBSP_WORD_12, - DAVINCI_MCBSP_WORD_16, - DAVINCI_MCBSP_WORD_20, - DAVINCI_MCBSP_WORD_24, - DAVINCI_MCBSP_WORD_32, -}; - -static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = { - [SNDRV_PCM_FORMAT_S8] = 1, - [SNDRV_PCM_FORMAT_S16_LE] = 2, - [SNDRV_PCM_FORMAT_S32_LE] = 4, -}; - -static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = { - [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8, - [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16, - [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32, -}; - -static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { - [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE, - [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE, -}; - -struct davinci_mcbsp_dev { - struct device *dev; - struct snd_dmaengine_dai_dma_data dma_data[2]; - int dma_request[2]; - void __iomem *base; -#define MOD_DSP_A 0 -#define MOD_DSP_B 1 - int mode; - u32 pcr; - struct clk *clk; - /* - * Combining both channels into 1 element will at least double the - * amount of time between servicing the dma channel, increase - * effiency, and reduce the chance of overrun/underrun. But, - * it will result in the left & right channels being swapped. - * - * If relabeling the left and right channels is not possible, - * you may want to let the codec know to swap them back. - * - * It may allow x10 the amount of time to service dma requests, - * if the codec is master and is using an unnecessarily fast bit clock - * (ie. tlvaic23b), independent of the sample rate. So, having an - * entire frame at once means it can be serviced at the sample rate - * instead of the bit clock rate. - * - * In the now unlikely case that an underrun still - * occurs, both the left and right samples will be repeated - * so that no pops are heard, and the left and right channels - * won't end up being swapped because of the underrun. - */ - unsigned enable_channel_combine:1; - - unsigned int fmt; - int clk_div; - int clk_input_pin; - bool i2s_accurate_sck; -}; - -static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, - int reg, u32 val) -{ - __raw_writel(val, dev->base + reg); -} - -static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg) -{ - return __raw_readl(dev->base + reg); -} - -static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback) -{ - u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP; - /* The clock needs to toggle to complete reset. - * So, fake it by toggling the clk polarity. - */ - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr); -} - -static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - u32 spcr; - u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST; - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - if (spcr & mask) { - /* start off disabled */ - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, - spcr & ~mask); - toggle_clock(dev, playback); - } - if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM | - DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) { - /* Start the sample generator */ - spcr |= DAVINCI_MCBSP_SPCR_GRST; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - } - - if (playback) { - /* Stop the DMA to avoid data loss */ - /* while the transmitter is out of reset to handle XSYNCERR */ - if (component->driver->ops->trigger) { - int ret = component->driver->ops->trigger(substream, - SNDRV_PCM_TRIGGER_STOP); - if (ret < 0) - printk(KERN_DEBUG "Playback DMA stop failed\n"); - } - - /* Enable the transmitter */ - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - spcr |= DAVINCI_MCBSP_SPCR_XRST; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - - /* wait for any unexpected frame sync error to occur */ - udelay(100); - - /* Disable the transmitter to clear any outstanding XSYNCERR */ - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - spcr &= ~DAVINCI_MCBSP_SPCR_XRST; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - toggle_clock(dev, playback); - - /* Restart the DMA */ - if (component->driver->ops->trigger) { - int ret = component->driver->ops->trigger(substream, - SNDRV_PCM_TRIGGER_START); - if (ret < 0) - printk(KERN_DEBUG "Playback DMA start failed\n"); - } - } - - /* Enable transmitter or receiver */ - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - spcr |= mask; - - if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) { - /* Start frame sync */ - spcr |= DAVINCI_MCBSP_SPCR_FRST; - } - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); -} - -static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback) -{ - u32 spcr; - - /* Reset transmitter/receiver and sample rate/frame sync generators */ - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST); - spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - toggle_clock(dev, playback); -} - -#define DEFAULT_BITPERSAMPLE 16 - -static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int pcr; - unsigned int srgr; - bool inv_fs = false; - /* Attention srgr is updated by hw_params! */ - srgr = DAVINCI_MCBSP_SRGR_FSGM | - DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) | - DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1); - - dev->fmt = fmt; - /* set master/slave audio interface */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* cpu is master */ - pcr = DAVINCI_MCBSP_PCR_FSXM | - DAVINCI_MCBSP_PCR_FSRM | - DAVINCI_MCBSP_PCR_CLKXM | - DAVINCI_MCBSP_PCR_CLKRM; - break; - case SND_SOC_DAIFMT_CBM_CFS: - pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM; - /* - * Selection of the clock input pin that is the - * input for the Sample Rate Generator. - * McBSP FSR and FSX are driven by the Sample Rate - * Generator. - */ - switch (dev->clk_input_pin) { - case MCBSP_CLKS: - pcr |= DAVINCI_MCBSP_PCR_CLKXM | - DAVINCI_MCBSP_PCR_CLKRM; - break; - case MCBSP_CLKR: - pcr |= DAVINCI_MCBSP_PCR_SCLKME; - break; - default: - dev_err(dev->dev, "bad clk_input_pin\n"); - return -EINVAL; - } - - break; - case SND_SOC_DAIFMT_CBM_CFM: - /* codec is master */ - pcr = 0; - break; - default: - printk(KERN_ERR "%s:bad master\n", __func__); - return -EINVAL; - } - - /* interface format */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - /* Davinci doesn't support TRUE I2S, but some codecs will have - * the left and right channels contiguous. This allows - * dsp_a mode to be used with an inverted normal frame clk. - * If your codec is master and does not have contiguous - * channels, then you will have sound on only one channel. - * Try using a different mode, or codec as slave. - * - * The TLV320AIC33 is an example of a codec where this works. - * It has a variable bit clock frequency allowing it to have - * valid data on every bit clock. - * - * The TLV320AIC23 is an example of a codec where this does not - * work. It has a fixed bit clock frequency with progressively - * more empty bit clock slots between channels as the sample - * rate is lowered. - */ - inv_fs = true; - /* fall through */ - case SND_SOC_DAIFMT_DSP_A: - dev->mode = MOD_DSP_A; - break; - case SND_SOC_DAIFMT_DSP_B: - dev->mode = MOD_DSP_B; - break; - default: - printk(KERN_ERR "%s:bad format\n", __func__); - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - /* CLKRP Receive clock polarity, - * 1 - sampled on rising edge of CLKR - * valid on rising edge - * CLKXP Transmit clock polarity, - * 1 - clocked on falling edge of CLKX - * valid on rising edge - * FSRP Receive frame sync pol, 0 - active high - * FSXP Transmit frame sync pol, 0 - active high - */ - pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP); - break; - case SND_SOC_DAIFMT_IB_IF: - /* CLKRP Receive clock polarity, - * 0 - sampled on falling edge of CLKR - * valid on falling edge - * CLKXP Transmit clock polarity, - * 0 - clocked on rising edge of CLKX - * valid on falling edge - * FSRP Receive frame sync pol, 1 - active low - * FSXP Transmit frame sync pol, 1 - active low - */ - pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); - break; - case SND_SOC_DAIFMT_NB_IF: - /* CLKRP Receive clock polarity, - * 1 - sampled on rising edge of CLKR - * valid on rising edge - * CLKXP Transmit clock polarity, - * 1 - clocked on falling edge of CLKX - * valid on rising edge - * FSRP Receive frame sync pol, 1 - active low - * FSXP Transmit frame sync pol, 1 - active low - */ - pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP | - DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); - break; - case SND_SOC_DAIFMT_IB_NF: - /* CLKRP Receive clock polarity, - * 0 - sampled on falling edge of CLKR - * valid on falling edge - * CLKXP Transmit clock polarity, - * 0 - clocked on rising edge of CLKX - * valid on falling edge - * FSRP Receive frame sync pol, 0 - active high - * FSXP Transmit frame sync pol, 0 - active high - */ - break; - default: - return -EINVAL; - } - if (inv_fs == true) - pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); - dev->pcr = pcr; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr); - return 0; -} - -static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); - - if (div_id != DAVINCI_MCBSP_CLKGDV) - return -ENODEV; - - dev->clk_div = div; - return 0; -} - -static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - struct snd_interval *i = NULL; - int mcbsp_word_length, master; - unsigned int rcr, xcr, srgr, clk_div, freq, framesize; - u32 spcr; - snd_pcm_format_t fmt; - unsigned element_cnt = 1; - - /* general line settings */ - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - } else { - spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - } - - master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; - fmt = params_format(params); - mcbsp_word_length = asp_word_length[fmt]; - - switch (master) { - case SND_SOC_DAIFMT_CBS_CFS: - freq = clk_get_rate(dev->clk); - srgr = DAVINCI_MCBSP_SRGR_FSGM | - DAVINCI_MCBSP_SRGR_CLKSM; - srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * - 8 - 1); - if (dev->i2s_accurate_sck) { - clk_div = 256; - do { - framesize = (freq / (--clk_div)) / - params->rate_num * - params->rate_den; - } while (((framesize < 33) || (framesize > 4095)) && - (clk_div)); - clk_div--; - srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1); - } else { - /* symmetric waveforms */ - clk_div = freq / (mcbsp_word_length * 16) / - params->rate_num * params->rate_den; - srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * - 16 - 1); - } - clk_div &= 0xFF; - srgr |= clk_div; - break; - case SND_SOC_DAIFMT_CBM_CFS: - srgr = DAVINCI_MCBSP_SRGR_FSGM; - clk_div = dev->clk_div - 1; - srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1); - srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1); - clk_div &= 0xFF; - srgr |= clk_div; - break; - case SND_SOC_DAIFMT_CBM_CFM: - /* Clock and frame sync given from external sources */ - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - srgr = DAVINCI_MCBSP_SRGR_FSGM; - srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); - pr_debug("%s - %d FWID set: re-read srgr = %X\n", - __func__, __LINE__, snd_interval_value(i) - 1); - - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); - srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); - break; - default: - return -EINVAL; - } - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); - - rcr = DAVINCI_MCBSP_RCR_RFIG; - xcr = DAVINCI_MCBSP_XCR_XFIG; - if (dev->mode == MOD_DSP_B) { - rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0); - xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0); - } else { - rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1); - xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); - } - /* Determine xfer data type */ - fmt = params_format(params); - if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) { - printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); - return -EINVAL; - } - - if (params_channels(params) == 2) { - element_cnt = 2; - if (double_fmt[fmt] && dev->enable_channel_combine) { - element_cnt = 1; - fmt = double_fmt[fmt]; - } - switch (master) { - case SND_SOC_DAIFMT_CBS_CFS: - case SND_SOC_DAIFMT_CBS_CFM: - rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0); - rcr |= DAVINCI_MCBSP_RCR_RPHASE; - xcr |= DAVINCI_MCBSP_XCR_XPHASE; - break; - case SND_SOC_DAIFMT_CBM_CFM: - case SND_SOC_DAIFMT_CBM_CFS: - rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1); - break; - default: - return -EINVAL; - } - } - mcbsp_word_length = asp_word_length[fmt]; - - switch (master) { - case SND_SOC_DAIFMT_CBS_CFS: - case SND_SOC_DAIFMT_CBS_CFM: - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0); - break; - case SND_SOC_DAIFMT_CBM_CFM: - case SND_SOC_DAIFMT_CBM_CFS: - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); - break; - default: - return -EINVAL; - } - - rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | - DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); - xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | - DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); - else - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); - - pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr); - pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr); - pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr); - return 0; -} - -static int davinci_i2s_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - davinci_mcbsp_stop(dev, playback); - return 0; -} - -static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - int ret = 0; - int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - davinci_mcbsp_start(dev, substream); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - davinci_mcbsp_stop(dev, playback); - break; - default: - ret = -EINVAL; - } - return ret; -} - -static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - davinci_mcbsp_stop(dev, playback); -} - -#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 - -static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .shutdown = davinci_i2s_shutdown, - .prepare = davinci_i2s_prepare, - .trigger = davinci_i2s_trigger, - .hw_params = davinci_i2s_hw_params, - .set_fmt = davinci_i2s_set_dai_fmt, - .set_clkdiv = davinci_i2s_dai_set_clkdiv, - -}; - -static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) -{ - struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); - - dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - - return 0; -} - -static struct snd_soc_dai_driver davinci_i2s_dai = { - .probe = davinci_i2s_dai_probe, - .playback = { - .channels_min = 2, - .channels_max = 2, - .rates = DAVINCI_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 2, - .channels_max = 2, - .rates = DAVINCI_I2S_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = &davinci_i2s_dai_ops, - -}; - -static const struct snd_soc_component_driver davinci_i2s_component = { - .name = DRV_NAME, -}; - -static int davinci_i2s_probe(struct platform_device *pdev) -{ - struct snd_dmaengine_dai_dma_data *dma_data; - struct davinci_mcbsp_dev *dev; - struct resource *mem, *res; - void __iomem *io_base; - int *dma; - int ret; - - mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!mem) { - dev_warn(&pdev->dev, - "\"mpu\" mem resource not found, using index 0\n"); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - } - - io_base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(io_base)) - return PTR_ERR(io_base); - - dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev), - GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->base = io_base; - - /* setup DMA, first TX, then RX */ - dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); - - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res) { - dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; - *dma = res->start; - dma_data->filter_data = dma; - } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { - dma_data->filter_data = "tx"; - } else { - dev_err(&pdev->dev, "Missing DMA tx resource\n"); - return -ENODEV; - } - - dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); - - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res) { - dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE]; - *dma = res->start; - dma_data->filter_data = dma; - } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { - dma_data->filter_data = "rx"; - } else { - dev_err(&pdev->dev, "Missing DMA rx resource\n"); - return -ENODEV; - } - - dev->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(dev->clk)) - return -ENODEV; - clk_enable(dev->clk); - - dev->dev = &pdev->dev; - dev_set_drvdata(&pdev->dev, dev); - - ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component, - &davinci_i2s_dai, 1); - if (ret != 0) - goto err_release_clk; - - ret = edma_pcm_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - goto err_unregister_component; - } - - return 0; - -err_unregister_component: - snd_soc_unregister_component(&pdev->dev); -err_release_clk: - clk_disable(dev->clk); - clk_put(dev->clk); - return ret; -} - -static int davinci_i2s_remove(struct platform_device *pdev) -{ - struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev); - - snd_soc_unregister_component(&pdev->dev); - - clk_disable(dev->clk); - clk_put(dev->clk); - dev->clk = NULL; - - return 0; -} - -static const struct of_device_id davinci_i2s_match[] = { - { .compatible = "ti,da850-mcbsp" }, - {}, -}; -MODULE_DEVICE_TABLE(of, davinci_i2s_match); - -static struct platform_driver davinci_mcbsp_driver = { - .probe = davinci_i2s_probe, - .remove = davinci_i2s_remove, - .driver = { - .name = "davinci-mcbsp", - .of_match_table = of_match_ptr(davinci_i2s_match), - }, -}; - -module_platform_driver(davinci_mcbsp_driver); - -MODULE_AUTHOR("Vladimir Barinov"); -MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-i2s.h b/sound/soc/davinci/davinci-i2s.h deleted file mode 100644 index 48dac3e2521a..000000000000 --- a/sound/soc/davinci/davinci-i2s.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor - * - * Author: Vladimir Barinov, - * Copyright: (C) 2007 MontaVista Software, Inc., - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _DAVINCI_I2S_H -#define _DAVINCI_I2S_H - -/* McBSP dividers */ -enum davinci_mcbsp_div { - DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */ -}; - -#endif diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c deleted file mode 100644 index 40d3a916fb74..000000000000 --- a/sound/soc/davinci/davinci-mcasp.c +++ /dev/null @@ -1,2212 +0,0 @@ -/* - * ALSA SoC McASP Audio Layer for TI DAVINCI processor - * - * Multi-channel Audio Serial Port Driver - * - * Author: Nirmal Pandey , - * Suresh Rajashekara - * Steve Chen - * - * Copyright: (C) 2009 MontaVista Software, Inc., - * Copyright: (C) 2009 Texas Instruments, India - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "edma-pcm.h" -#include "../omap/sdma-pcm.h" -#include "davinci-mcasp.h" - -#define MCASP_MAX_AFIFO_DEPTH 64 - -static u32 context_regs[] = { - DAVINCI_MCASP_TXFMCTL_REG, - DAVINCI_MCASP_RXFMCTL_REG, - DAVINCI_MCASP_TXFMT_REG, - DAVINCI_MCASP_RXFMT_REG, - DAVINCI_MCASP_ACLKXCTL_REG, - DAVINCI_MCASP_ACLKRCTL_REG, - DAVINCI_MCASP_AHCLKXCTL_REG, - DAVINCI_MCASP_AHCLKRCTL_REG, - DAVINCI_MCASP_PDIR_REG, - DAVINCI_MCASP_RXMASK_REG, - DAVINCI_MCASP_TXMASK_REG, - DAVINCI_MCASP_RXTDM_REG, - DAVINCI_MCASP_TXTDM_REG, -}; - -struct davinci_mcasp_context { - u32 config_regs[ARRAY_SIZE(context_regs)]; - u32 afifo_regs[2]; /* for read/write fifo control registers */ - u32 *xrsr_regs; /* for serializer configuration */ - bool pm_state; -}; - -struct davinci_mcasp_ruledata { - struct davinci_mcasp *mcasp; - int serializers; -}; - -struct davinci_mcasp { - struct snd_dmaengine_dai_dma_data dma_data[2]; - void __iomem *base; - u32 fifo_base; - struct device *dev; - struct snd_pcm_substream *substreams[2]; - unsigned int dai_fmt; - - /* McASP specific data */ - int tdm_slots; - u32 tdm_mask[2]; - int slot_width; - u8 op_mode; - u8 dismod; - u8 num_serializer; - u8 *serial_dir; - u8 version; - u8 bclk_div; - int streams; - u32 irq_request[2]; - int dma_request[2]; - - int sysclk_freq; - bool bclk_master; - - unsigned long pdir; /* Pin direction bitfield */ - - /* McASP FIFO related */ - u8 txnumevt; - u8 rxnumevt; - - bool dat_port; - - /* Used for comstraint setting on the second stream */ - u32 channels; - -#ifdef CONFIG_PM_SLEEP - struct davinci_mcasp_context context; -#endif - - struct davinci_mcasp_ruledata ruledata[2]; - struct snd_pcm_hw_constraint_list chconstr[2]; -}; - -static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, - u32 val) -{ - void __iomem *reg = mcasp->base + offset; - __raw_writel(__raw_readl(reg) | val, reg); -} - -static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset, - u32 val) -{ - void __iomem *reg = mcasp->base + offset; - __raw_writel((__raw_readl(reg) & ~(val)), reg); -} - -static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset, - u32 val, u32 mask) -{ - void __iomem *reg = mcasp->base + offset; - __raw_writel((__raw_readl(reg) & ~mask) | val, reg); -} - -static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset, - u32 val) -{ - __raw_writel(val, mcasp->base + offset); -} - -static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset) -{ - return (u32)__raw_readl(mcasp->base + offset); -} - -static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val) -{ - int i = 0; - - mcasp_set_bits(mcasp, ctl_reg, val); - - /* programming GBLCTL needs to read back from GBLCTL and verfiy */ - /* loop count is to avoid the lock-up */ - for (i = 0; i < 1000; i++) { - if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val) - break; - } - - if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val)) - printk(KERN_ERR "GBLCTL write error\n"); -} - -static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) -{ - u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); - u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); - - return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; -} - -static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) -{ - u32 bit = PIN_BIT_AMUTE; - - for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) { - if (enable) - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); - else - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); - } -} - -static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) -{ - u32 bit; - - for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AFSR) { - if (enable) - mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); - else - mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); - } -} - -static void mcasp_start_rx(struct davinci_mcasp *mcasp) -{ - if (mcasp->rxnumevt) { /* enable FIFO */ - u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - mcasp_set_bits(mcasp, reg, FIFO_ENABLE); - } - - /* Start clocks */ - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); - /* - * When ASYNC == 0 the transmit and receive sections operate - * synchronously from the transmit clock and frame sync. We need to make - * sure that the TX signlas are enabled when starting reception. - */ - if (mcasp_is_synchronous(mcasp)) { - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); - } - - /* Activate serializer(s) */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); - /* Release RX state machine */ - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); - /* Release Frame Sync generator */ - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); - if (mcasp_is_synchronous(mcasp)) - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); - - /* enable receive IRQs */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, - mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); -} - -static void mcasp_start_tx(struct davinci_mcasp *mcasp) -{ - u32 cnt; - - if (mcasp->txnumevt) { /* enable FIFO */ - u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - mcasp_set_bits(mcasp, reg, FIFO_ENABLE); - } - - /* Start clocks */ - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); - mcasp_set_clk_pdir(mcasp, true); - - /* Activate serializer(s) */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); - - /* wait for XDATA to be cleared */ - cnt = 0; - while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) && - (cnt < 100000)) - cnt++; - - mcasp_set_axr_pdir(mcasp, true); - - /* Release TX state machine */ - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); - /* Release Frame Sync generator */ - mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); - - /* enable transmit IRQs */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, - mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); -} - -static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) -{ - mcasp->streams++; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - mcasp_start_tx(mcasp); - else - mcasp_start_rx(mcasp); -} - -static void mcasp_stop_rx(struct davinci_mcasp *mcasp) -{ - /* disable IRQ sources */ - mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, - mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); - - /* - * In synchronous mode stop the TX clocks if no other stream is - * running - */ - if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { - mcasp_set_clk_pdir(mcasp, false); - mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); - } - - mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); - - if (mcasp->rxnumevt) { /* disable FIFO */ - u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - } -} - -static void mcasp_stop_tx(struct davinci_mcasp *mcasp) -{ - u32 val = 0; - - /* disable IRQ sources */ - mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, - mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); - - /* - * In synchronous mode keep TX clocks running if the capture stream is - * still running. - */ - if (mcasp_is_synchronous(mcasp) && mcasp->streams) - val = TXHCLKRST | TXCLKRST | TXFSRST; - else - mcasp_set_clk_pdir(mcasp, false); - - - mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); - - if (mcasp->txnumevt) { /* disable FIFO */ - u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - - mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); - } - - mcasp_set_axr_pdir(mcasp, false); -} - -static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) -{ - mcasp->streams--; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) - mcasp_stop_tx(mcasp); - else - mcasp_stop_rx(mcasp); -} - -static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) -{ - struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; - struct snd_pcm_substream *substream; - u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]; - u32 handled_mask = 0; - u32 stat; - - stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG); - if (stat & XUNDRN & irq_mask) { - dev_warn(mcasp->dev, "Transmit buffer underflow\n"); - handled_mask |= XUNDRN; - - substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; - if (substream) - snd_pcm_stop_xrun(substream); - } - - if (!handled_mask) - dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n", - stat); - - if (stat & XRERR) - handled_mask |= XRERR; - - /* Ack the handled event only */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask); - - return IRQ_RETVAL(handled_mask); -} - -static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) -{ - struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; - struct snd_pcm_substream *substream; - u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]; - u32 handled_mask = 0; - u32 stat; - - stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG); - if (stat & ROVRN & irq_mask) { - dev_warn(mcasp->dev, "Receive buffer overflow\n"); - handled_mask |= ROVRN; - - substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; - if (substream) - snd_pcm_stop_xrun(substream); - } - - if (!handled_mask) - dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n", - stat); - - if (stat & XRERR) - handled_mask |= XRERR; - - /* Ack the handled event only */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask); - - return IRQ_RETVAL(handled_mask); -} - -static irqreturn_t davinci_mcasp_common_irq_handler(int irq, void *data) -{ - struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; - irqreturn_t ret = IRQ_NONE; - - if (mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]) - ret = davinci_mcasp_tx_irq_handler(irq, data); - - if (mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]) - ret |= davinci_mcasp_rx_irq_handler(irq, data); - - return ret; -} - -static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - int ret = 0; - u32 data_delay; - bool fs_pol_rising; - bool inv_fs = false; - - if (!fmt) - return 0; - - pm_runtime_get_sync(mcasp->dev); - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_DSP_A: - mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); - /* 1st data bit occur one ACLK cycle after the frame sync */ - data_delay = 1; - break; - case SND_SOC_DAIFMT_DSP_B: - case SND_SOC_DAIFMT_AC97: - mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); - /* No delay after FS */ - data_delay = 0; - break; - case SND_SOC_DAIFMT_I2S: - /* configure a full-word SYNC pulse (LRCLK) */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); - /* 1st data bit occur one ACLK cycle after the frame sync */ - data_delay = 1; - /* FS need to be inverted */ - inv_fs = true; - break; - case SND_SOC_DAIFMT_LEFT_J: - /* configure a full-word SYNC pulse (LRCLK) */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); - /* No delay after FS */ - data_delay = 0; - break; - default: - ret = -EINVAL; - goto out; - } - - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay), - FSXDLY(3)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay), - FSRDLY(3)); - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* codec is clock and frame slave */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - - /* BCLK */ - set_bit(PIN_BIT_ACLKX, &mcasp->pdir); - set_bit(PIN_BIT_ACLKR, &mcasp->pdir); - /* Frame Sync */ - set_bit(PIN_BIT_AFSX, &mcasp->pdir); - set_bit(PIN_BIT_AFSR, &mcasp->pdir); - - mcasp->bclk_master = 1; - break; - case SND_SOC_DAIFMT_CBS_CFM: - /* codec is clock slave and frame master */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - - /* BCLK */ - set_bit(PIN_BIT_ACLKX, &mcasp->pdir); - set_bit(PIN_BIT_ACLKR, &mcasp->pdir); - /* Frame Sync */ - clear_bit(PIN_BIT_AFSX, &mcasp->pdir); - clear_bit(PIN_BIT_AFSR, &mcasp->pdir); - - mcasp->bclk_master = 1; - break; - case SND_SOC_DAIFMT_CBM_CFS: - /* codec is clock master and frame slave */ - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - - /* BCLK */ - clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); - clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); - /* Frame Sync */ - set_bit(PIN_BIT_AFSX, &mcasp->pdir); - set_bit(PIN_BIT_AFSR, &mcasp->pdir); - - mcasp->bclk_master = 0; - break; - case SND_SOC_DAIFMT_CBM_CFM: - /* codec is clock and frame master */ - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); - - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); - - /* BCLK */ - clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); - clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); - /* Frame Sync */ - clear_bit(PIN_BIT_AFSX, &mcasp->pdir); - clear_bit(PIN_BIT_AFSR, &mcasp->pdir); - - mcasp->bclk_master = 0; - break; - default: - ret = -EINVAL; - goto out; - } - - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_IB_NF: - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - fs_pol_rising = true; - break; - case SND_SOC_DAIFMT_NB_IF: - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - fs_pol_rising = false; - break; - case SND_SOC_DAIFMT_IB_IF: - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - fs_pol_rising = false; - break; - case SND_SOC_DAIFMT_NB_NF: - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); - fs_pol_rising = true; - break; - default: - ret = -EINVAL; - goto out; - } - - if (inv_fs) - fs_pol_rising = !fs_pol_rising; - - if (fs_pol_rising) { - mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); - } else { - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); - } - - mcasp->dai_fmt = fmt; -out: - pm_runtime_put(mcasp->dev); - return ret; -} - -static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, - int div, bool explicit) -{ - pm_runtime_get_sync(mcasp->dev); - switch (div_id) { - case MCASP_CLKDIV_AUXCLK: /* MCLK divider */ - mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, - AHCLKXDIV(div - 1), AHCLKXDIV_MASK); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, - AHCLKRDIV(div - 1), AHCLKRDIV_MASK); - break; - - case MCASP_CLKDIV_BCLK: /* BCLK divider */ - mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, - ACLKXDIV(div - 1), ACLKXDIV_MASK); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, - ACLKRDIV(div - 1), ACLKRDIV_MASK); - if (explicit) - mcasp->bclk_div = div; - break; - - case MCASP_CLKDIV_BCLK_FS_RATIO: - /* - * BCLK/LRCLK ratio descries how many bit-clock cycles - * fit into one frame. The clock ratio is given for a - * full period of data (for I2S format both left and - * right channels), so it has to be divided by number - * of tdm-slots (for I2S - divided by 2). - * Instead of storing this ratio, we calculate a new - * tdm_slot width by dividing the the ratio by the - * number of configured tdm slots. - */ - mcasp->slot_width = div / mcasp->tdm_slots; - if (div % mcasp->tdm_slots) - dev_warn(mcasp->dev, - "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", - __func__, div, mcasp->tdm_slots); - break; - - default: - return -EINVAL; - } - - pm_runtime_put(mcasp->dev); - return 0; -} - -static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, - int div) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - - return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1); -} - -static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - - pm_runtime_get_sync(mcasp->dev); - if (dir == SND_SOC_CLOCK_OUT) { - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); - } else { - mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); - clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); - } - - mcasp->sysclk_freq = freq; - - pm_runtime_put(mcasp->dev); - return 0; -} - -/* All serializers must have equal number of channels */ -static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, - int serializers) -{ - struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; - unsigned int *list = (unsigned int *) cl->list; - int slots = mcasp->tdm_slots; - int i, count = 0; - - if (mcasp->tdm_mask[stream]) - slots = hweight32(mcasp->tdm_mask[stream]); - - for (i = 1; i <= slots; i++) - list[count++] = i; - - for (i = 2; i <= serializers; i++) - list[count++] = i*slots; - - cl->count = count; - - return 0; -} - -static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp) -{ - int rx_serializers = 0, tx_serializers = 0, ret, i; - - for (i = 0; i < mcasp->num_serializer; i++) - if (mcasp->serial_dir[i] == TX_MODE) - tx_serializers++; - else if (mcasp->serial_dir[i] == RX_MODE) - rx_serializers++; - - ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK, - tx_serializers); - if (ret) - return ret; - - ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE, - rx_serializers); - - return ret; -} - - -static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, - unsigned int tx_mask, - unsigned int rx_mask, - int slots, int slot_width) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - - dev_dbg(mcasp->dev, - "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n", - __func__, tx_mask, rx_mask, slots, slot_width); - - if (tx_mask >= (1<= (1<dev, - "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n", - tx_mask, rx_mask, slots); - return -EINVAL; - } - - if (slot_width && - (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) { - dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n", - __func__, slot_width); - return -EINVAL; - } - - mcasp->tdm_slots = slots; - mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; - mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; - mcasp->slot_width = slot_width; - - return davinci_mcasp_set_ch_constraints(mcasp); -} - -static int davinci_config_channel_size(struct davinci_mcasp *mcasp, - int sample_width) -{ - u32 fmt; - u32 tx_rotate = (sample_width / 4) & 0x7; - u32 mask = (1ULL << sample_width) - 1; - u32 slot_width = sample_width; - - /* - * For captured data we should not rotate, inversion and masking is - * enoguh to get the data to the right position: - * Format data from bus after reverse (XRBUF) - * S16_LE: |LSB|MSB|xxx|xxx| |xxx|xxx|MSB|LSB| - * S24_3LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB| - * S24_LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB| - * S32_LE: |LSB|DAT|DAT|MSB| |MSB|DAT|DAT|LSB| - */ - u32 rx_rotate = 0; - - /* - * Setting the tdm slot width either with set_clkdiv() or - * set_tdm_slot() allows us to for example send 32 bits per - * channel to the codec, while only 16 of them carry audio - * payload. - */ - if (mcasp->slot_width) { - /* - * When we have more bclk then it is needed for the - * data, we need to use the rotation to move the - * received samples to have correct alignment. - */ - slot_width = mcasp->slot_width; - rx_rotate = (slot_width - sample_width) / 4; - } - - /* mapping of the XSSZ bit-field as described in the datasheet */ - fmt = (slot_width >> 1) - 1; - - if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), - RXSSZ(0x0F)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), - TXSSZ(0x0F)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), - TXROT(7)); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), - RXROT(7)); - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); - } - - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); - - return 0; -} - -static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, - int period_words, int channels) -{ - struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream]; - int i; - u8 tx_ser = 0; - u8 rx_ser = 0; - u8 slots = mcasp->tdm_slots; - u8 max_active_serializers = (channels + slots - 1) / slots; - int active_serializers, numevt; - u32 reg; - /* Default configuration */ - if (mcasp->version < MCASP_VERSION_3) - mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); - - /* All PINS as McASP */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000); - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); - } else { - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); - mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS); - } - - for (i = 0; i < mcasp->num_serializer; i++) { - mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - mcasp->serial_dir[i]); - if (mcasp->serial_dir[i] == TX_MODE && - tx_ser < max_active_serializers) { - mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - mcasp->dismod, DISMOD_MASK); - set_bit(PIN_BIT_AXR(i), &mcasp->pdir); - tx_ser++; - } else if (mcasp->serial_dir[i] == RX_MODE && - rx_ser < max_active_serializers) { - clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); - rx_ser++; - } else if (mcasp->serial_dir[i] == INACTIVE_MODE) { - mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - SRMOD_INACTIVE, SRMOD_MASK); - clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); - } else if (mcasp->serial_dir[i] == TX_MODE) { - /* Unused TX pins, clear PDIR */ - mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - mcasp->dismod, DISMOD_MASK); - clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); - } - } - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - active_serializers = tx_ser; - numevt = mcasp->txnumevt; - reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - } else { - active_serializers = rx_ser; - numevt = mcasp->rxnumevt; - reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - } - - if (active_serializers < max_active_serializers) { - dev_warn(mcasp->dev, "stream has more channels (%d) than are " - "enabled in mcasp (%d)\n", channels, - active_serializers * slots); - return -EINVAL; - } - - /* AFIFO is not in use */ - if (!numevt) { - /* Configure the burst size for platform drivers */ - if (active_serializers > 1) { - /* - * If more than one serializers are in use we have one - * DMA request to provide data for all serializers. - * For example if three serializers are enabled the DMA - * need to transfer three words per DMA request. - */ - dma_data->maxburst = active_serializers; - } else { - dma_data->maxburst = 0; - } - return 0; - } - - if (period_words % active_serializers) { - dev_err(mcasp->dev, "Invalid combination of period words and " - "active serializers: %d, %d\n", period_words, - active_serializers); - return -EINVAL; - } - - /* - * Calculate the optimal AFIFO depth for platform side: - * The number of words for numevt need to be in steps of active - * serializers. - */ - numevt = (numevt / active_serializers) * active_serializers; - - while (period_words % numevt && numevt > 0) - numevt -= active_serializers; - if (numevt <= 0) - numevt = active_serializers; - - mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK); - mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK); - - /* Configure the burst size for platform drivers */ - if (numevt == 1) - numevt = 0; - dma_data->maxburst = numevt; - - return 0; -} - -static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, - int channels) -{ - int i, active_slots; - int total_slots; - int active_serializers; - u32 mask = 0; - u32 busel = 0; - - total_slots = mcasp->tdm_slots; - - /* - * If more than one serializer is needed, then use them with - * all the specified tdm_slots. Otherwise, one serializer can - * cope with the transaction using just as many slots as there - * are channels in the stream. - */ - if (mcasp->tdm_mask[stream]) { - active_slots = hweight32(mcasp->tdm_mask[stream]); - active_serializers = (channels + active_slots - 1) / - active_slots; - if (active_serializers == 1) { - active_slots = channels; - for (i = 0; i < total_slots; i++) { - if ((1 << i) & mcasp->tdm_mask[stream]) { - mask |= (1 << i); - if (--active_slots <= 0) - break; - } - } - } - } else { - active_serializers = (channels + total_slots - 1) / total_slots; - if (active_serializers == 1) - active_slots = channels; - else - active_slots = total_slots; - - for (i = 0; i < active_slots; i++) - mask |= (1 << i); - } - mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); - - if (!mcasp->dat_port) - busel = TXSEL; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(total_slots), FSXMOD(0x1FF)); - } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(total_slots), FSRMOD(0x1FF)); - /* - * If McASP is set to be TX/RX synchronous and the playback is - * not running already we need to configure the TX slots in - * order to have correct FSX on the bus - */ - if (mcasp_is_synchronous(mcasp) && !mcasp->channels) - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(total_slots), FSXMOD(0x1FF)); - } - - return 0; -} - -/* S/PDIF */ -static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, - unsigned int rate) -{ - u32 cs_value = 0; - u8 *cs_bytes = (u8*) &cs_value; - - /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0 - and LSB first */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15)); - - /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180)); - - /* Set the TX tdm : for all the slots */ - mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF); - - /* Set the TX clock controls : div = 1 and internal */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC); - - mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); - - /* Only 44100 and 48000 are valid, both have the same setting */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3)); - - /* Enable the DIT */ - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN); - - /* Set S/PDIF channel status bits */ - cs_bytes[0] = IEC958_AES0_CON_NOT_COPYRIGHT; - cs_bytes[1] = IEC958_AES1_CON_PCM_CODER; - - switch (rate) { - case 22050: - cs_bytes[3] |= IEC958_AES3_CON_FS_22050; - break; - case 24000: - cs_bytes[3] |= IEC958_AES3_CON_FS_24000; - break; - case 32000: - cs_bytes[3] |= IEC958_AES3_CON_FS_32000; - break; - case 44100: - cs_bytes[3] |= IEC958_AES3_CON_FS_44100; - break; - case 48000: - cs_bytes[3] |= IEC958_AES3_CON_FS_48000; - break; - case 88200: - cs_bytes[3] |= IEC958_AES3_CON_FS_88200; - break; - case 96000: - cs_bytes[3] |= IEC958_AES3_CON_FS_96000; - break; - case 176400: - cs_bytes[3] |= IEC958_AES3_CON_FS_176400; - break; - case 192000: - cs_bytes[3] |= IEC958_AES3_CON_FS_192000; - break; - default: - printk(KERN_WARNING "unsupported sampling rate: %d\n", rate); - return -EINVAL; - } - - mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRA_REG, cs_value); - mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRB_REG, cs_value); - - return 0; -} - -static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, - unsigned int bclk_freq, bool set) -{ - int error_ppm; - unsigned int sysclk_freq = mcasp->sysclk_freq; - u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG); - int div = sysclk_freq / bclk_freq; - int rem = sysclk_freq % bclk_freq; - int aux_div = 1; - - if (div > (ACLKXDIV_MASK + 1)) { - if (reg & AHCLKXE) { - aux_div = div / (ACLKXDIV_MASK + 1); - if (div % (ACLKXDIV_MASK + 1)) - aux_div++; - - sysclk_freq /= aux_div; - div = sysclk_freq / bclk_freq; - rem = sysclk_freq % bclk_freq; - } else if (set) { - dev_warn(mcasp->dev, "Too fast reference clock (%u)\n", - sysclk_freq); - } - } - - if (rem != 0) { - if (div == 0 || - ((sysclk_freq / div) - bclk_freq) > - (bclk_freq - (sysclk_freq / (div+1)))) { - div++; - rem = rem - bclk_freq; - } - } - error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem, - (int)bclk_freq)) / div - 1000000; - - if (set) { - if (error_ppm) - dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", - error_ppm); - - __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0); - if (reg & AHCLKXE) - __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK, - aux_div, 0); - } - - return error_ppm; -} - -static inline u32 davinci_mcasp_tx_delay(struct davinci_mcasp *mcasp) -{ - if (!mcasp->txnumevt) - return 0; - - return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_WFIFOSTS_OFFSET); -} - -static inline u32 davinci_mcasp_rx_delay(struct davinci_mcasp *mcasp) -{ - if (!mcasp->rxnumevt) - return 0; - - return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_RFIFOSTS_OFFSET); -} - -static snd_pcm_sframes_t davinci_mcasp_delay( - struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - u32 fifo_use; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fifo_use = davinci_mcasp_tx_delay(mcasp); - else - fifo_use = davinci_mcasp_rx_delay(mcasp); - - /* - * Divide the used locations with the channel count to get the - * FIFO usage in samples (don't care about partial samples in the - * buffer). - */ - return fifo_use / substream->runtime->channels; -} - -static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - int word_length; - int channels = params_channels(params); - int period_size = params_period_size(params); - int ret; - - ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt); - if (ret) - return ret; - - /* - * If mcasp is BCLK master, and a BCLK divider was not provided by - * the machine driver, we need to calculate the ratio. - */ - if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - int slots = mcasp->tdm_slots; - int rate = params_rate(params); - int sbits = params_width(params); - - if (mcasp->slot_width) - sbits = mcasp->slot_width; - - davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true); - } - - ret = mcasp_common_hw_param(mcasp, substream->stream, - period_size * channels, channels); - if (ret) - return ret; - - if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) - ret = mcasp_dit_hw_param(mcasp, params_rate(params)); - else - ret = mcasp_i2s_hw_param(mcasp, substream->stream, - channels); - - if (ret) - return ret; - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_U8: - case SNDRV_PCM_FORMAT_S8: - word_length = 8; - break; - - case SNDRV_PCM_FORMAT_U16_LE: - case SNDRV_PCM_FORMAT_S16_LE: - word_length = 16; - break; - - case SNDRV_PCM_FORMAT_U24_3LE: - case SNDRV_PCM_FORMAT_S24_3LE: - word_length = 24; - break; - - case SNDRV_PCM_FORMAT_U24_LE: - case SNDRV_PCM_FORMAT_S24_LE: - word_length = 24; - break; - - case SNDRV_PCM_FORMAT_U32_LE: - case SNDRV_PCM_FORMAT_S32_LE: - word_length = 32; - break; - - default: - printk(KERN_WARNING "davinci-mcasp: unsupported PCM format"); - return -EINVAL; - } - - davinci_config_channel_size(mcasp, word_length); - - if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) - mcasp->channels = channels; - - return 0; -} - -static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *cpu_dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - davinci_mcasp_start(mcasp, substream->stream); - break; - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - davinci_mcasp_stop(mcasp, substream->stream); - break; - - default: - ret = -EINVAL; - } - - return ret; -} - -static const unsigned int davinci_mcasp_dai_rates[] = { - 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, - 88200, 96000, 176400, 192000, -}; - -#define DAVINCI_MAX_RATE_ERROR_PPM 1000 - -static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct davinci_mcasp_ruledata *rd = rule->private; - struct snd_interval *ri = - hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); - int sbits = params_width(params); - int slots = rd->mcasp->tdm_slots; - struct snd_interval range; - int i; - - if (rd->mcasp->slot_width) - sbits = rd->mcasp->slot_width; - - snd_interval_any(&range); - range.empty = 1; - - for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) { - if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) { - uint bclk_freq = sbits*slots* - davinci_mcasp_dai_rates[i]; - int ppm; - - ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, - false); - if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { - if (range.empty) { - range.min = davinci_mcasp_dai_rates[i]; - range.empty = 0; - } - range.max = davinci_mcasp_dai_rates[i]; - } - } - } - - dev_dbg(rd->mcasp->dev, - "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n", - ri->min, ri->max, range.min, range.max, sbits, slots); - - return snd_interval_refine(hw_param_interval(params, rule->var), - &range); -} - -static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct davinci_mcasp_ruledata *rd = rule->private; - struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); - struct snd_mask nfmt; - int rate = params_rate(params); - int slots = rd->mcasp->tdm_slots; - int i, count = 0; - - snd_mask_none(&nfmt); - - for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { - if (snd_mask_test(fmt, i)) { - uint sbits = snd_pcm_format_width(i); - int ppm; - - if (rd->mcasp->slot_width) - sbits = rd->mcasp->slot_width; - - ppm = davinci_mcasp_calc_clk_div(rd->mcasp, - sbits * slots * rate, - false); - if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { - snd_mask_set(&nfmt, i); - count++; - } - } - } - dev_dbg(rd->mcasp->dev, - "%d possible sample format for %d Hz and %d tdm slots\n", - count, rate, slots); - - return snd_mask_refine(fmt, &nfmt); -} - -static int davinci_mcasp_hw_rule_min_periodsize( - struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) -{ - struct snd_interval *period_size = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE); - struct snd_interval frames; - - snd_interval_any(&frames); - frames.min = 64; - frames.integer = 1; - - return snd_interval_refine(period_size, &frames); -} - -static int davinci_mcasp_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - struct davinci_mcasp_ruledata *ruledata = - &mcasp->ruledata[substream->stream]; - u32 max_channels = 0; - int i, dir; - int tdm_slots = mcasp->tdm_slots; - - /* Do not allow more then one stream per direction */ - if (mcasp->substreams[substream->stream]) - return -EBUSY; - - mcasp->substreams[substream->stream] = substream; - - if (mcasp->tdm_mask[substream->stream]) - tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); - - if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) - return 0; - - /* - * Limit the maximum allowed channels for the first stream: - * number of serializers for the direction * tdm slots per serializer - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dir = TX_MODE; - else - dir = RX_MODE; - - for (i = 0; i < mcasp->num_serializer; i++) { - if (mcasp->serial_dir[i] == dir) - max_channels++; - } - ruledata->serializers = max_channels; - max_channels *= tdm_slots; - /* - * If the already active stream has less channels than the calculated - * limnit based on the seirializers * tdm_slots, we need to use that as - * a constraint for the second stream. - * Otherwise (first stream or less allowed channels) we use the - * calculated constraint. - */ - if (mcasp->channels && mcasp->channels < max_channels) - max_channels = mcasp->channels; - /* - * But we can always allow channels upto the amount of - * the available tdm_slots. - */ - if (max_channels < tdm_slots) - max_channels = tdm_slots; - - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_CHANNELS, - 0, max_channels); - - snd_pcm_hw_constraint_list(substream->runtime, - 0, SNDRV_PCM_HW_PARAM_CHANNELS, - &mcasp->chconstr[substream->stream]); - - if (mcasp->slot_width) - snd_pcm_hw_constraint_minmax(substream->runtime, - SNDRV_PCM_HW_PARAM_SAMPLE_BITS, - 8, mcasp->slot_width); - - /* - * If we rely on implicit BCLK divider setting we should - * set constraints based on what we can provide. - */ - if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { - int ret; - - ruledata->mcasp = mcasp; - - ret = snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_RATE, - davinci_mcasp_hw_rule_rate, - ruledata, - SNDRV_PCM_HW_PARAM_FORMAT, -1); - if (ret) - return ret; - ret = snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_FORMAT, - davinci_mcasp_hw_rule_format, - ruledata, - SNDRV_PCM_HW_PARAM_RATE, -1); - if (ret) - return ret; - } - - snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, - davinci_mcasp_hw_rule_min_periodsize, NULL, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); - - return 0; -} - -static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); - - mcasp->substreams[substream->stream] = NULL; - - if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) - return; - - if (!cpu_dai->active) - mcasp->channels = 0; -} - -static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { - .startup = davinci_mcasp_startup, - .shutdown = davinci_mcasp_shutdown, - .trigger = davinci_mcasp_trigger, - .delay = davinci_mcasp_delay, - .hw_params = davinci_mcasp_hw_params, - .set_fmt = davinci_mcasp_set_dai_fmt, - .set_clkdiv = davinci_mcasp_set_clkdiv, - .set_sysclk = davinci_mcasp_set_sysclk, - .set_tdm_slot = davinci_mcasp_set_tdm_slot, -}; - -static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - - dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int davinci_mcasp_suspend(struct snd_soc_dai *dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - struct davinci_mcasp_context *context = &mcasp->context; - u32 reg; - int i; - - context->pm_state = pm_runtime_active(mcasp->dev); - if (!context->pm_state) - pm_runtime_get_sync(mcasp->dev); - - for (i = 0; i < ARRAY_SIZE(context_regs); i++) - context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]); - - if (mcasp->txnumevt) { - reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - context->afifo_regs[0] = mcasp_get_reg(mcasp, reg); - } - if (mcasp->rxnumevt) { - reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - context->afifo_regs[1] = mcasp_get_reg(mcasp, reg); - } - - for (i = 0; i < mcasp->num_serializer; i++) - context->xrsr_regs[i] = mcasp_get_reg(mcasp, - DAVINCI_MCASP_XRSRCTL_REG(i)); - - pm_runtime_put_sync(mcasp->dev); - - return 0; -} - -static int davinci_mcasp_resume(struct snd_soc_dai *dai) -{ - struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); - struct davinci_mcasp_context *context = &mcasp->context; - u32 reg; - int i; - - pm_runtime_get_sync(mcasp->dev); - - for (i = 0; i < ARRAY_SIZE(context_regs); i++) - mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]); - - if (mcasp->txnumevt) { - reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; - mcasp_set_reg(mcasp, reg, context->afifo_regs[0]); - } - if (mcasp->rxnumevt) { - reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; - mcasp_set_reg(mcasp, reg, context->afifo_regs[1]); - } - - for (i = 0; i < mcasp->num_serializer; i++) - mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), - context->xrsr_regs[i]); - - if (!context->pm_state) - pm_runtime_put_sync(mcasp->dev); - - return 0; -} -#else -#define davinci_mcasp_suspend NULL -#define davinci_mcasp_resume NULL -#endif - -#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000 - -#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \ - SNDRV_PCM_FMTBIT_U8 | \ - SNDRV_PCM_FMTBIT_S16_LE | \ - SNDRV_PCM_FMTBIT_U16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | \ - SNDRV_PCM_FMTBIT_U24_LE | \ - SNDRV_PCM_FMTBIT_S24_3LE | \ - SNDRV_PCM_FMTBIT_U24_3LE | \ - SNDRV_PCM_FMTBIT_S32_LE | \ - SNDRV_PCM_FMTBIT_U32_LE) - -static struct snd_soc_dai_driver davinci_mcasp_dai[] = { - { - .name = "davinci-mcasp.0", - .probe = davinci_mcasp_dai_probe, - .suspend = davinci_mcasp_suspend, - .resume = davinci_mcasp_resume, - .playback = { - .channels_min = 1, - .channels_max = 32 * 16, - .rates = DAVINCI_MCASP_RATES, - .formats = DAVINCI_MCASP_PCM_FMTS, - }, - .capture = { - .channels_min = 1, - .channels_max = 32 * 16, - .rates = DAVINCI_MCASP_RATES, - .formats = DAVINCI_MCASP_PCM_FMTS, - }, - .ops = &davinci_mcasp_dai_ops, - - .symmetric_samplebits = 1, - .symmetric_rates = 1, - }, - { - .name = "davinci-mcasp.1", - .probe = davinci_mcasp_dai_probe, - .playback = { - .channels_min = 1, - .channels_max = 384, - .rates = DAVINCI_MCASP_RATES, - .formats = DAVINCI_MCASP_PCM_FMTS, - }, - .ops = &davinci_mcasp_dai_ops, - }, - -}; - -static const struct snd_soc_component_driver davinci_mcasp_component = { - .name = "davinci-mcasp", -}; - -/* Some HW specific values and defaults. The rest is filled in from DT. */ -static struct davinci_mcasp_pdata dm646x_mcasp_pdata = { - .tx_dma_offset = 0x400, - .rx_dma_offset = 0x400, - .version = MCASP_VERSION_1, -}; - -static struct davinci_mcasp_pdata da830_mcasp_pdata = { - .tx_dma_offset = 0x2000, - .rx_dma_offset = 0x2000, - .version = MCASP_VERSION_2, -}; - -static struct davinci_mcasp_pdata am33xx_mcasp_pdata = { - .tx_dma_offset = 0, - .rx_dma_offset = 0, - .version = MCASP_VERSION_3, -}; - -static struct davinci_mcasp_pdata dra7_mcasp_pdata = { - /* The CFG port offset will be calculated if it is needed */ - .tx_dma_offset = 0, - .rx_dma_offset = 0, - .version = MCASP_VERSION_4, -}; - -static const struct of_device_id mcasp_dt_ids[] = { - { - .compatible = "ti,dm646x-mcasp-audio", - .data = &dm646x_mcasp_pdata, - }, - { - .compatible = "ti,da830-mcasp-audio", - .data = &da830_mcasp_pdata, - }, - { - .compatible = "ti,am33xx-mcasp-audio", - .data = &am33xx_mcasp_pdata, - }, - { - .compatible = "ti,dra7-mcasp-audio", - .data = &dra7_mcasp_pdata, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, mcasp_dt_ids); - -static int mcasp_reparent_fck(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - struct clk *gfclk, *parent_clk; - const char *parent_name; - int ret; - - if (!node) - return 0; - - parent_name = of_get_property(node, "fck_parent", NULL); - if (!parent_name) - return 0; - - dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n"); - - gfclk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(gfclk)) { - dev_err(&pdev->dev, "failed to get fck\n"); - return PTR_ERR(gfclk); - } - - parent_clk = clk_get(NULL, parent_name); - if (IS_ERR(parent_clk)) { - dev_err(&pdev->dev, "failed to get parent clock\n"); - ret = PTR_ERR(parent_clk); - goto err1; - } - - ret = clk_set_parent(gfclk, parent_clk); - if (ret) { - dev_err(&pdev->dev, "failed to reparent fck\n"); - goto err2; - } - -err2: - clk_put(parent_clk); -err1: - clk_put(gfclk); - return ret; -} - -static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( - struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct davinci_mcasp_pdata *pdata = NULL; - const struct of_device_id *match = - of_match_device(mcasp_dt_ids, &pdev->dev); - struct of_phandle_args dma_spec; - - const u32 *of_serial_dir32; - u32 val; - int i, ret = 0; - - if (pdev->dev.platform_data) { - pdata = pdev->dev.platform_data; - pdata->dismod = DISMOD_LOW; - return pdata; - } else if (match) { - pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata), - GFP_KERNEL); - if (!pdata) { - ret = -ENOMEM; - return pdata; - } - } else { - /* control shouldn't reach here. something is wrong */ - ret = -EINVAL; - goto nodata; - } - - ret = of_property_read_u32(np, "op-mode", &val); - if (ret >= 0) - pdata->op_mode = val; - - ret = of_property_read_u32(np, "tdm-slots", &val); - if (ret >= 0) { - if (val < 2 || val > 32) { - dev_err(&pdev->dev, - "tdm-slots must be in rage [2-32]\n"); - ret = -EINVAL; - goto nodata; - } - - pdata->tdm_slots = val; - } - - of_serial_dir32 = of_get_property(np, "serial-dir", &val); - val /= sizeof(u32); - if (of_serial_dir32) { - u8 *of_serial_dir = devm_kzalloc(&pdev->dev, - (sizeof(*of_serial_dir) * val), - GFP_KERNEL); - if (!of_serial_dir) { - ret = -ENOMEM; - goto nodata; - } - - for (i = 0; i < val; i++) - of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]); - - pdata->num_serializer = val; - pdata->serial_dir = of_serial_dir; - } - - ret = of_property_match_string(np, "dma-names", "tx"); - if (ret < 0) - goto nodata; - - ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, - &dma_spec); - if (ret < 0) - goto nodata; - - pdata->tx_dma_channel = dma_spec.args[0]; - - /* RX is not valid in DIT mode */ - if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE) { - ret = of_property_match_string(np, "dma-names", "rx"); - if (ret < 0) - goto nodata; - - ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, - &dma_spec); - if (ret < 0) - goto nodata; - - pdata->rx_dma_channel = dma_spec.args[0]; - } - - ret = of_property_read_u32(np, "tx-num-evt", &val); - if (ret >= 0) - pdata->txnumevt = val; - - ret = of_property_read_u32(np, "rx-num-evt", &val); - if (ret >= 0) - pdata->rxnumevt = val; - - ret = of_property_read_u32(np, "sram-size-playback", &val); - if (ret >= 0) - pdata->sram_size_playback = val; - - ret = of_property_read_u32(np, "sram-size-capture", &val); - if (ret >= 0) - pdata->sram_size_capture = val; - - ret = of_property_read_u32(np, "dismod", &val); - if (ret >= 0) { - if (val == 0 || val == 2 || val == 3) { - pdata->dismod = DISMOD_VAL(val); - } else { - dev_warn(&pdev->dev, "Invalid dismod value: %u\n", val); - pdata->dismod = DISMOD_LOW; - } - } else { - pdata->dismod = DISMOD_LOW; - } - - return pdata; - -nodata: - if (ret < 0) { - dev_err(&pdev->dev, "Error populating platform data, err %d\n", - ret); - pdata = NULL; - } - return pdata; -} - -enum { - PCM_EDMA, - PCM_SDMA, -}; -static const char *sdma_prefix = "ti,omap"; - -static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) -{ - struct dma_chan *chan; - const char *tmp; - int ret = PCM_EDMA; - - if (!mcasp->dev->of_node) - return PCM_EDMA; - - tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; - chan = dma_request_slave_channel_reason(mcasp->dev, tmp); - if (IS_ERR(chan)) { - if (PTR_ERR(chan) != -EPROBE_DEFER) - dev_err(mcasp->dev, - "Can't verify DMA configuration (%ld)\n", - PTR_ERR(chan)); - return PTR_ERR(chan); - } - if (WARN_ON(!chan->device || !chan->device->dev)) - return -EINVAL; - - if (chan->device->dev->of_node) - ret = of_property_read_string(chan->device->dev->of_node, - "compatible", &tmp); - else - dev_dbg(mcasp->dev, "DMA controller has no of-node\n"); - - dma_release_channel(chan); - if (ret) - return ret; - - dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp); - if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix))) - return PCM_SDMA; - - return PCM_EDMA; -} - -static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata) -{ - int i; - u32 offset = 0; - - if (pdata->version != MCASP_VERSION_4) - return pdata->tx_dma_offset; - - for (i = 0; i < pdata->num_serializer; i++) { - if (pdata->serial_dir[i] == TX_MODE) { - if (!offset) { - offset = DAVINCI_MCASP_TXBUF_REG(i); - } else { - pr_err("%s: Only one serializer allowed!\n", - __func__); - break; - } - } - } - - return offset; -} - -static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata) -{ - int i; - u32 offset = 0; - - if (pdata->version != MCASP_VERSION_4) - return pdata->rx_dma_offset; - - for (i = 0; i < pdata->num_serializer; i++) { - if (pdata->serial_dir[i] == RX_MODE) { - if (!offset) { - offset = DAVINCI_MCASP_RXBUF_REG(i); - } else { - pr_err("%s: Only one serializer allowed!\n", - __func__); - break; - } - } - } - - return offset; -} - -static int davinci_mcasp_probe(struct platform_device *pdev) -{ - struct snd_dmaengine_dai_dma_data *dma_data; - struct resource *mem, *res, *dat; - struct davinci_mcasp_pdata *pdata; - struct davinci_mcasp *mcasp; - char *irq_name; - int *dma; - int irq; - int ret; - - if (!pdev->dev.platform_data && !pdev->dev.of_node) { - dev_err(&pdev->dev, "No platform data supplied\n"); - return -EINVAL; - } - - mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp), - GFP_KERNEL); - if (!mcasp) - return -ENOMEM; - - pdata = davinci_mcasp_set_pdata_from_of(pdev); - if (!pdata) { - dev_err(&pdev->dev, "no platform data\n"); - return -EINVAL; - } - - mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!mem) { - dev_warn(mcasp->dev, - "\"mpu\" mem resource not found, using index 0\n"); - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) { - dev_err(&pdev->dev, "no mem resource?\n"); - return -ENODEV; - } - } - - mcasp->base = devm_ioremap_resource(&pdev->dev, mem); - if (IS_ERR(mcasp->base)) - return PTR_ERR(mcasp->base); - - pm_runtime_enable(&pdev->dev); - - mcasp->op_mode = pdata->op_mode; - /* sanity check for tdm slots parameter */ - if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { - if (pdata->tdm_slots < 2) { - dev_err(&pdev->dev, "invalid tdm slots: %d\n", - pdata->tdm_slots); - mcasp->tdm_slots = 2; - } else if (pdata->tdm_slots > 32) { - dev_err(&pdev->dev, "invalid tdm slots: %d\n", - pdata->tdm_slots); - mcasp->tdm_slots = 32; - } else { - mcasp->tdm_slots = pdata->tdm_slots; - } - } - - mcasp->num_serializer = pdata->num_serializer; -#ifdef CONFIG_PM_SLEEP - mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev, - mcasp->num_serializer, sizeof(u32), - GFP_KERNEL); - if (!mcasp->context.xrsr_regs) { - ret = -ENOMEM; - goto err; - } -#endif - mcasp->serial_dir = pdata->serial_dir; - mcasp->version = pdata->version; - mcasp->txnumevt = pdata->txnumevt; - mcasp->rxnumevt = pdata->rxnumevt; - mcasp->dismod = pdata->dismod; - - mcasp->dev = &pdev->dev; - - irq = platform_get_irq_byname(pdev, "common"); - if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common", - dev_name(&pdev->dev)); - if (!irq_name) { - ret = -ENOMEM; - goto err; - } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - davinci_mcasp_common_irq_handler, - IRQF_ONESHOT | IRQF_SHARED, - irq_name, mcasp); - if (ret) { - dev_err(&pdev->dev, "common IRQ request failed\n"); - goto err; - } - - mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; - mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; - } - - irq = platform_get_irq_byname(pdev, "rx"); - if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx", - dev_name(&pdev->dev)); - if (!irq_name) { - ret = -ENOMEM; - goto err; - } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - davinci_mcasp_rx_irq_handler, - IRQF_ONESHOT, irq_name, mcasp); - if (ret) { - dev_err(&pdev->dev, "RX IRQ request failed\n"); - goto err; - } - - mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; - } - - irq = platform_get_irq_byname(pdev, "tx"); - if (irq >= 0) { - irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx", - dev_name(&pdev->dev)); - if (!irq_name) { - ret = -ENOMEM; - goto err; - } - ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, - davinci_mcasp_tx_irq_handler, - IRQF_ONESHOT, irq_name, mcasp); - if (ret) { - dev_err(&pdev->dev, "TX IRQ request failed\n"); - goto err; - } - - mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; - } - - dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); - if (dat) - mcasp->dat_port = true; - - dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - if (dat) - dma_data->addr = dat->start; - else - dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata); - - dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; - res = platform_get_resource(pdev, IORESOURCE_DMA, 0); - if (res) - *dma = res->start; - else - *dma = pdata->tx_dma_channel; - - /* dmaengine filter data for DT and non-DT boot */ - if (pdev->dev.of_node) - dma_data->filter_data = "tx"; - else - dma_data->filter_data = dma; - - /* RX is not valid in DIT mode */ - if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { - dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - if (dat) - dma_data->addr = dat->start; - else - dma_data->addr = - mem->start + davinci_mcasp_rxdma_offset(pdata); - - dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE]; - res = platform_get_resource(pdev, IORESOURCE_DMA, 1); - if (res) - *dma = res->start; - else - *dma = pdata->rx_dma_channel; - - /* dmaengine filter data for DT and non-DT boot */ - if (pdev->dev.of_node) - dma_data->filter_data = "rx"; - else - dma_data->filter_data = dma; - } - - if (mcasp->version < MCASP_VERSION_3) { - mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE; - /* dma_params->dma_addr is pointing to the data port address */ - mcasp->dat_port = true; - } else { - mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; - } - - /* Allocate memory for long enough list for all possible - * scenarios. Maximum number tdm slots is 32 and there cannot - * be more serializers than given in the configuration. The - * serializer directions could be taken into account, but it - * would make code much more complex and save only couple of - * bytes. - */ - mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = - devm_kcalloc(mcasp->dev, - 32 + mcasp->num_serializer - 1, - sizeof(unsigned int), - GFP_KERNEL); - - mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = - devm_kcalloc(mcasp->dev, - 32 + mcasp->num_serializer - 1, - sizeof(unsigned int), - GFP_KERNEL); - - if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || - !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) { - ret = -ENOMEM; - goto err; - } - - ret = davinci_mcasp_set_ch_constraints(mcasp); - if (ret) - goto err; - - dev_set_drvdata(&pdev->dev, mcasp); - - mcasp_reparent_fck(pdev); - - ret = devm_snd_soc_register_component(&pdev->dev, - &davinci_mcasp_component, - &davinci_mcasp_dai[pdata->op_mode], 1); - - if (ret != 0) - goto err; - - ret = davinci_mcasp_get_dma_type(mcasp); - switch (ret) { - case PCM_EDMA: -#if IS_BUILTIN(CONFIG_SND_EDMA_SOC) || \ - (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_EDMA_SOC)) - ret = edma_pcm_platform_register(&pdev->dev); -#else - dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n"); - ret = -EINVAL; - goto err; -#endif - break; - case PCM_SDMA: -#if IS_BUILTIN(CONFIG_SND_SDMA_SOC) || \ - (IS_MODULE(CONFIG_SND_DAVINCI_SOC_MCASP) && \ - IS_MODULE(CONFIG_SND_SDMA_SOC)) - ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL); -#else - dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); - ret = -EINVAL; - goto err; -#endif - break; - default: - dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); - case -EPROBE_DEFER: - goto err; - break; - } - - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - goto err; - } - - return 0; - -err: - pm_runtime_disable(&pdev->dev); - return ret; -} - -static int davinci_mcasp_remove(struct platform_device *pdev) -{ - pm_runtime_disable(&pdev->dev); - - return 0; -} - -static struct platform_driver davinci_mcasp_driver = { - .probe = davinci_mcasp_probe, - .remove = davinci_mcasp_remove, - .driver = { - .name = "davinci-mcasp", - .of_match_table = mcasp_dt_ids, - }, -}; - -module_platform_driver(davinci_mcasp_driver); - -MODULE_AUTHOR("Steve Chen"); -MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h deleted file mode 100644 index 5e4060d8fe56..000000000000 --- a/sound/soc/davinci/davinci-mcasp.h +++ /dev/null @@ -1,306 +0,0 @@ -/* - * ALSA SoC McASP Audio Layer for TI DAVINCI processor - * - * MCASP related definitions - * - * Author: Nirmal Pandey , - * Suresh Rajashekara - * Steve Chen - * - * Copyright: (C) 2009 MontaVista Software, Inc., - * Copyright: (C) 2009 Texas Instruments, India - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef DAVINCI_MCASP_H -#define DAVINCI_MCASP_H - -/* - * McASP register definitions - */ -#define DAVINCI_MCASP_PID_REG 0x00 -#define DAVINCI_MCASP_PWREMUMGT_REG 0x04 - -#define DAVINCI_MCASP_PFUNC_REG 0x10 -#define DAVINCI_MCASP_PDIR_REG 0x14 -#define DAVINCI_MCASP_PDOUT_REG 0x18 -#define DAVINCI_MCASP_PDSET_REG 0x1c - -#define DAVINCI_MCASP_PDCLR_REG 0x20 - -#define DAVINCI_MCASP_TLGC_REG 0x30 -#define DAVINCI_MCASP_TLMR_REG 0x34 - -#define DAVINCI_MCASP_GBLCTL_REG 0x44 -#define DAVINCI_MCASP_AMUTE_REG 0x48 -#define DAVINCI_MCASP_LBCTL_REG 0x4c - -#define DAVINCI_MCASP_TXDITCTL_REG 0x50 - -#define DAVINCI_MCASP_GBLCTLR_REG 0x60 -#define DAVINCI_MCASP_RXMASK_REG 0x64 -#define DAVINCI_MCASP_RXFMT_REG 0x68 -#define DAVINCI_MCASP_RXFMCTL_REG 0x6c - -#define DAVINCI_MCASP_ACLKRCTL_REG 0x70 -#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74 -#define DAVINCI_MCASP_RXTDM_REG 0x78 -#define DAVINCI_MCASP_EVTCTLR_REG 0x7c - -#define DAVINCI_MCASP_RXSTAT_REG 0x80 -#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84 -#define DAVINCI_MCASP_RXCLKCHK_REG 0x88 -#define DAVINCI_MCASP_REVTCTL_REG 0x8c - -#define DAVINCI_MCASP_GBLCTLX_REG 0xa0 -#define DAVINCI_MCASP_TXMASK_REG 0xa4 -#define DAVINCI_MCASP_TXFMT_REG 0xa8 -#define DAVINCI_MCASP_TXFMCTL_REG 0xac - -#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0 -#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4 -#define DAVINCI_MCASP_TXTDM_REG 0xb8 -#define DAVINCI_MCASP_EVTCTLX_REG 0xbc - -#define DAVINCI_MCASP_TXSTAT_REG 0xc0 -#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4 -#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8 -#define DAVINCI_MCASP_XEVTCTL_REG 0xcc - -/* Left(even TDM Slot) Channel Status Register File */ -#define DAVINCI_MCASP_DITCSRA_REG 0x100 -/* Right(odd TDM slot) Channel Status Register File */ -#define DAVINCI_MCASP_DITCSRB_REG 0x118 -/* Left(even TDM slot) User Data Register File */ -#define DAVINCI_MCASP_DITUDRA_REG 0x130 -/* Right(odd TDM Slot) User Data Register File */ -#define DAVINCI_MCASP_DITUDRB_REG 0x148 - -/* Serializer n Control Register */ -#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180 -#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \ - (n << 2)) - -/* Transmit Buffer for Serializer n */ -#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2)) -/* Receive Buffer for Serializer n */ -#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2)) - -/* McASP FIFO Registers */ -#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010) -#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000) - -/* FIFO register offsets from AFIFO base */ -#define MCASP_WFIFOCTL_OFFSET (0x0) -#define MCASP_WFIFOSTS_OFFSET (0x4) -#define MCASP_RFIFOCTL_OFFSET (0x8) -#define MCASP_RFIFOSTS_OFFSET (0xc) - -/* - * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management - * Register Bits - */ -#define MCASP_FREE BIT(0) -#define MCASP_SOFT BIT(1) - -/* - * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits - * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits - * DAVINCI_MCASP_PDOUT_REG - Pin output in GPIO mode - * DAVINCI_MCASP_PDSET_REG - Pin input in GPIO mode - */ -#define PIN_BIT_AXR(n) (n) -#define PIN_BIT_AMUTE 25 -#define PIN_BIT_ACLKX 26 -#define PIN_BIT_AHCLKX 27 -#define PIN_BIT_AFSX 28 -#define PIN_BIT_ACLKR 29 -#define PIN_BIT_AHCLKR 30 -#define PIN_BIT_AFSR 31 - -/* - * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits - */ -#define DITEN BIT(0) /* Transmit DIT mode enable/disable */ -#define VA BIT(2) -#define VB BIT(3) - -/* - * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits - */ -#define TXROT(val) (val) -#define TXSEL BIT(3) -#define TXSSZ(val) (val<<4) -#define TXPBIT(val) (val<<8) -#define TXPAD(val) (val<<13) -#define TXORD BIT(15) -#define FSXDLY(val) (val<<16) - -/* - * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits - */ -#define RXROT(val) (val) -#define RXSEL BIT(3) -#define RXSSZ(val) (val<<4) -#define RXPBIT(val) (val<<8) -#define RXPAD(val) (val<<13) -#define RXORD BIT(15) -#define FSRDLY(val) (val<<16) - -/* - * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits - */ -#define FSXPOL BIT(0) -#define AFSXE BIT(1) -#define FSXDUR BIT(4) -#define FSXMOD(val) (val<<7) - -/* - * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits - */ -#define FSRPOL BIT(0) -#define AFSRE BIT(1) -#define FSRDUR BIT(4) -#define FSRMOD(val) (val<<7) - -/* - * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits - */ -#define ACLKXDIV(val) (val) -#define ACLKXE BIT(5) -#define TX_ASYNC BIT(6) -#define ACLKXPOL BIT(7) -#define ACLKXDIV_MASK 0x1f - -/* - * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits - */ -#define ACLKRDIV(val) (val) -#define ACLKRE BIT(5) -#define RX_ASYNC BIT(6) -#define ACLKRPOL BIT(7) -#define ACLKRDIV_MASK 0x1f - -/* - * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control - * Register Bits - */ -#define AHCLKXDIV(val) (val) -#define AHCLKXPOL BIT(14) -#define AHCLKXE BIT(15) -#define AHCLKXDIV_MASK 0xfff - -/* - * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control - * Register Bits - */ -#define AHCLKRDIV(val) (val) -#define AHCLKRPOL BIT(14) -#define AHCLKRE BIT(15) -#define AHCLKRDIV_MASK 0xfff - -/* - * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits - */ -#define MODE(val) (val) -#define DISMOD_3STATE (0x0) -#define DISMOD_LOW (0x2 << 2) -#define DISMOD_HIGH (0x3 << 2) -#define DISMOD_VAL(x) ((x) << 2) -#define DISMOD_MASK DISMOD_HIGH -#define TXSTATE BIT(4) -#define RXSTATE BIT(5) -#define SRMOD_MASK 3 -#define SRMOD_INACTIVE 0 - -/* - * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits - */ -#define LBEN BIT(0) -#define LBORD BIT(1) -#define LBGENMODE(val) (val<<2) - -/* - * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration - */ -#define TXTDMS(n) (1< - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "edma-pcm.h" -#include "davinci-i2s.h" - -#define MOD_REG_BIT(val, mask, set) do { \ - if (set) { \ - val |= mask; \ - } else { \ - val &= ~mask; \ - } \ -} while (0) - -struct davinci_vcif_dev { - struct davinci_vc *davinci_vc; - struct snd_dmaengine_dai_dma_data dma_data[2]; - int dma_request[2]; -}; - -static void davinci_vcif_start(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_vcif_dev *davinci_vcif_dev = - snd_soc_dai_get_drvdata(rtd->cpu_dai); - struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; - u32 w; - - /* Start the sample generator and enable transmitter/receiver */ - w = readl(davinci_vc->base + DAVINCI_VC_CTRL); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); - else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); - - writel(w, davinci_vc->base + DAVINCI_VC_CTRL); -} - -static void davinci_vcif_stop(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct davinci_vcif_dev *davinci_vcif_dev = - snd_soc_dai_get_drvdata(rtd->cpu_dai); - struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; - u32 w; - - /* Reset transmitter/receiver and sample rate/frame sync generators */ - w = readl(davinci_vc->base + DAVINCI_VC_CTRL); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); - else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); - - writel(w, davinci_vc->base + DAVINCI_VC_CTRL); -} - -static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai); - struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; - u32 w; - - /* Restart the codec before setup */ - davinci_vcif_stop(substream); - davinci_vcif_start(substream); - - /* General line settings */ - writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL); - - writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR); - - writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN); - - w = readl(davinci_vc->base + DAVINCI_VC_CTRL); - - /* Determine xfer data type */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_U8: - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | - DAVINCI_VC_CTRL_RD_UNSIGNED | - DAVINCI_VC_CTRL_WD_BITS_8 | - DAVINCI_VC_CTRL_WD_UNSIGNED, 1); - break; - case SNDRV_PCM_FORMAT_S8: - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | - DAVINCI_VC_CTRL_WD_BITS_8, 1); - - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED | - DAVINCI_VC_CTRL_WD_UNSIGNED, 0); - break; - case SNDRV_PCM_FORMAT_S16_LE: - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | - DAVINCI_VC_CTRL_RD_UNSIGNED | - DAVINCI_VC_CTRL_WD_BITS_8 | - DAVINCI_VC_CTRL_WD_UNSIGNED, 0); - break; - default: - printk(KERN_WARNING "davinci-vcif: unsupported PCM format"); - return -EINVAL; - } - - writel(w, davinci_vc->base + DAVINCI_VC_CTRL); - - return 0; -} - -static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - davinci_vcif_start(substream); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - davinci_vcif_stop(substream); - break; - default: - ret = -EINVAL; - } - - return ret; -} - -#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000 - -static const struct snd_soc_dai_ops davinci_vcif_dai_ops = { - .trigger = davinci_vcif_trigger, - .hw_params = davinci_vcif_hw_params, -}; - -static int davinci_vcif_dai_probe(struct snd_soc_dai *dai) -{ - struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); - - dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; - dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; - - return 0; -} - -static struct snd_soc_dai_driver davinci_vcif_dai = { - .probe = davinci_vcif_dai_probe, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = DAVINCI_VCIF_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = DAVINCI_VCIF_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE,}, - .ops = &davinci_vcif_dai_ops, - -}; - -static const struct snd_soc_component_driver davinci_vcif_component = { - .name = "davinci-vcif", -}; - -static int davinci_vcif_probe(struct platform_device *pdev) -{ - struct davinci_vc *davinci_vc = pdev->dev.platform_data; - struct davinci_vcif_dev *davinci_vcif_dev; - int ret; - - davinci_vcif_dev = devm_kzalloc(&pdev->dev, - sizeof(struct davinci_vcif_dev), - GFP_KERNEL); - if (!davinci_vcif_dev) - return -ENOMEM; - - /* DMA tx params */ - davinci_vcif_dev->davinci_vc = davinci_vc; - davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = - &davinci_vc->davinci_vcif.dma_tx_channel; - davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = - davinci_vc->davinci_vcif.dma_tx_addr; - - /* DMA rx params */ - davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = - &davinci_vc->davinci_vcif.dma_rx_channel; - davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = - davinci_vc->davinci_vcif.dma_rx_addr; - - dev_set_drvdata(&pdev->dev, davinci_vcif_dev); - - ret = devm_snd_soc_register_component(&pdev->dev, - &davinci_vcif_component, - &davinci_vcif_dai, 1); - if (ret != 0) { - dev_err(&pdev->dev, "could not register dai\n"); - return ret; - } - - ret = edma_pcm_platform_register(&pdev->dev); - if (ret) { - dev_err(&pdev->dev, "register PCM failed: %d\n", ret); - return ret; - } - - return 0; -} - -static struct platform_driver davinci_vcif_driver = { - .probe = davinci_vcif_probe, - .driver = { - .name = "davinci-vcif", - }, -}; - -module_platform_driver(davinci_vcif_driver); - -MODULE_AUTHOR("Miguel Aguilar"); -MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/edma-pcm.c b/sound/soc/davinci/edma-pcm.c deleted file mode 100644 index 59e588abe54b..000000000000 --- a/sound/soc/davinci/edma-pcm.c +++ /dev/null @@ -1,59 +0,0 @@ -/* - * edma-pcm.c - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx - * - * Copyright (C) 2014 Texas Instruments, Inc. - * - * Author: Peter Ujfalusi - * - * Based on: sound/soc/tegra/tegra_pcm.c - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "edma-pcm.h" - -static const struct snd_pcm_hardware edma_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | - SNDRV_PCM_INFO_INTERLEAVED, - .buffer_bytes_max = 128 * 1024, - .period_bytes_min = 32, - .period_bytes_max = 64 * 1024, - .periods_min = 2, - .periods_max = 19, /* Limit by edma dmaengine driver */ -}; - -static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = { - .pcm_hardware = &edma_pcm_hardware, - .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, - .compat_filter_fn = edma_filter_fn, - .prealloc_buffer_size = 128 * 1024, -}; - -int edma_pcm_platform_register(struct device *dev) -{ - return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config, - SND_DMAENGINE_PCM_FLAG_COMPAT); -} -EXPORT_SYMBOL_GPL(edma_pcm_platform_register); - -MODULE_AUTHOR("Peter Ujfalusi "); -MODULE_DESCRIPTION("eDMA PCM ASoC platform driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/davinci/edma-pcm.h b/sound/soc/davinci/edma-pcm.h deleted file mode 100644 index b0957744851c..000000000000 --- a/sound/soc/davinci/edma-pcm.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx - * - * Copyright (C) 2014 Texas Instruments, Inc. - * - * Author: Peter Ujfalusi - * - * Based on: sound/soc/tegra/tegra_pcm.h - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#ifndef __EDMA_PCM_H__ -#define __EDMA_PCM_H__ - -#if IS_ENABLED(CONFIG_SND_EDMA_SOC) -int edma_pcm_platform_register(struct device *dev); -#else -static inline int edma_pcm_platform_register(struct device *dev) -{ - return 0; -} -#endif /* CONFIG_SND_EDMA_SOC */ - -#endif /* __EDMA_PCM_H__ */ diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig deleted file mode 100644 index 3d418482d242..000000000000 --- a/sound/soc/omap/Kconfig +++ /dev/null @@ -1,120 +0,0 @@ -config SND_OMAP_SOC - tristate "SoC Audio for Texas Instruments OMAP chips (deprecated)" - depends on (ARCH_OMAP && DMA_OMAP) || (ARM && COMPILE_TEST) - select SND_SDMA_SOC - -config SND_SDMA_SOC - tristate "SoC Audio for Texas Instruments chips using sDMA" - depends on DMA_OMAP || COMPILE_TEST - select SND_SOC_GENERIC_DMAENGINE_PCM - -config SND_OMAP_SOC_DMIC - tristate - -config SND_OMAP_SOC_MCBSP - tristate - -config SND_OMAP_SOC_MCPDM - tristate - -config SND_OMAP_SOC_HDMI_AUDIO - tristate "HDMI audio support for OMAP4+ based SoCs" - depends on SND_SDMA_SOC - help - For HDMI audio to work OMAPDSS HDMI support should be - enabled. - The hdmi audio driver implements cpu-dai component using the - callbacks provided by OMAPDSS and registers the component - under DSS HDMI device. Omap-pcm is registered for platform - component also under DSS HDMI device. Dummy codec is used as - as codec component. The hdmi audio driver implements also - the card and registers it under its own platform device. - The device for the driver is registered by OMAPDSS hdmi - driver. - -config SND_OMAP_SOC_N810 - tristate "SoC Audio support for Nokia N810" - depends on SND_SDMA_SOC && MACH_NOKIA_N810 && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC3X - help - Say Y if you want to add support for SoC audio on Nokia N810. - -config SND_OMAP_SOC_RX51 - tristate "SoC Audio support for Nokia N900 (RX-51)" - depends on SND_SDMA_SOC && ARM && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC3X - select SND_SOC_TPA6130A2 - depends on GPIOLIB - help - Say Y if you want to add support for SoC audio on Nokia N900 - cellphone. - -config SND_OMAP_SOC_AMS_DELTA - tristate "SoC Audio support for Amstrad E3 (Delta) videophone" - depends on SND_SDMA_SOC && MACH_AMS_DELTA && TTY - select SND_OMAP_SOC_MCBSP - select SND_SOC_CX20442 - help - Say Y if you want to add support for SoC audio device connected to - a handset and a speakerphone found on Amstrad E3 (Delta) videophone. - - Note that in order to get those devices fully supported, you have to - build the kernel with standard serial port driver included and - configured for at least 4 ports. Then, from userspace, you must load - a line discipline #19 on the modem (ttyS3) serial line. The simplest - way to achieve this is to install util-linux-ng and use the included - ldattach utility. This can be started automatically from udev, - a simple rule like this one should do the trick (it does for me): - ACTION=="add", KERNEL=="controlC0", \ - RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" - -config SND_OMAP_SOC_OSK5912 - tristate "SoC Audio support for omap osk5912" - depends on SND_SDMA_SOC && MACH_OMAP_OSK && I2C - select SND_OMAP_SOC_MCBSP - select SND_SOC_TLV320AIC23_I2C - help - Say Y if you want to add support for SoC audio on osk5912. - -config SND_OMAP_SOC_OMAP_TWL4030 - tristate "SoC Audio support for TI SoC based boards with twl4030 codec" - depends on TWL4030_CORE && SND_SDMA_SOC - select SND_OMAP_SOC_MCBSP - select SND_SOC_TWL4030 - help - Say Y if you want to add support for SoC audio on TI SoC based boards - using twl4030 as c codec. This driver currently supports: - - Beagleboard or Devkit8000 - - Gumstix Overo or CompuLab CM-T35/CM-T3730 - - IGEP v2 - - OMAP3EVM - - SDP3430 - - Zoom2 - -config SND_OMAP_SOC_OMAP_ABE_TWL6040 - tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && SND_SDMA_SOC && COMMON_CLK - depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST - select SND_OMAP_SOC_DMIC - select SND_OMAP_SOC_MCPDM - select SND_SOC_TWL6040 - select SND_SOC_DMIC - select COMMON_CLK_PALMAS if (SOC_OMAP5 && MFD_PALMAS) - select CLK_TWL6040 - help - Say Y if you want to add support for SoC audio on OMAP boards using - ABE and twl6040 codec. This driver currently supports: - - SDP4430/Blaze boards - - PandaBoard (4430) - - PandaBoardES (4460) - - omap5-uevm (5432) - -config SND_OMAP_SOC_OMAP3_PANDORA - tristate "SoC Audio support for OMAP3 Pandora" - depends on TWL4030_CORE && SND_SDMA_SOC && MACH_OMAP3_PANDORA - select SND_OMAP_SOC_MCBSP - select SND_SOC_TWL4030 - help - Say Y if you want to add support for SoC audio on the OMAP3 Pandora. diff --git a/sound/soc/omap/Makefile b/sound/soc/omap/Makefile deleted file mode 100644 index d005338dd13c..000000000000 --- a/sound/soc/omap/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# OMAP Platform Support -snd-soc-sdma-objs := sdma-pcm.o -snd-soc-omap-dmic-objs := omap-dmic.o -snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o -snd-soc-omap-mcpdm-objs := omap-mcpdm.o -snd-soc-omap-hdmi-audio-objs := omap-hdmi-audio.o - -obj-$(CONFIG_SND_SDMA_SOC) += snd-soc-sdma.o -obj-$(CONFIG_SND_OMAP_SOC_DMIC) += snd-soc-omap-dmic.o -obj-$(CONFIG_SND_OMAP_SOC_MCBSP) += snd-soc-omap-mcbsp.o -obj-$(CONFIG_SND_OMAP_SOC_MCPDM) += snd-soc-omap-mcpdm.o -obj-$(CONFIG_SND_OMAP_SOC_HDMI_AUDIO) += snd-soc-omap-hdmi-audio.o - -# OMAP Machine Support -snd-soc-n810-objs := n810.o -snd-soc-rx51-objs := rx51.o -snd-soc-ams-delta-objs := ams-delta.o -snd-soc-osk5912-objs := osk5912.o -snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o -snd-soc-omap-twl4030-objs := omap-twl4030.o -snd-soc-omap3pandora-objs := omap3pandora.o - -obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o -obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o -obj-$(CONFIG_SND_OMAP_SOC_AMS_DELTA) += snd-soc-ams-delta.o -obj-$(CONFIG_SND_OMAP_SOC_OSK5912) += snd-soc-osk5912.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP_TWL4030) += snd-soc-omap-twl4030.o -obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o diff --git a/sound/soc/omap/ams-delta.c b/sound/soc/omap/ams-delta.c deleted file mode 100644 index 4dce494dfbd3..000000000000 --- a/sound/soc/omap/ams-delta.c +++ /dev/null @@ -1,594 +0,0 @@ -/* - * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone - * - * Copyright (C) 2009 Janusz Krzysztofik - * - * Initially based on sound/soc/omap/osk5912.x - * Copyright (C) 2008 Mistral Solutions - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include "omap-mcbsp.h" -#include "../codecs/cx20442.h" - -/* Board specific DAPM widgets */ -static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = { - /* Handset */ - SND_SOC_DAPM_MIC("Mouthpiece", NULL), - SND_SOC_DAPM_HP("Earpiece", NULL), - /* Handsfree/Speakerphone */ - SND_SOC_DAPM_MIC("Microphone", NULL), - SND_SOC_DAPM_SPK("Speaker", NULL), -}; - -/* How they are connected to codec pins */ -static const struct snd_soc_dapm_route ams_delta_audio_map[] = { - {"TELIN", NULL, "Mouthpiece"}, - {"Earpiece", NULL, "TELOUT"}, - - {"MIC", NULL, "Microphone"}, - {"Speaker", NULL, "SPKOUT"}, -}; - -/* - * Controls, functional after the modem line discipline is activated. - */ - -/* Virtual switch: audio input/output constellations */ -static const char *ams_delta_audio_mode[] = - {"Mixed", "Handset", "Handsfree", "Speakerphone"}; - -/* Selection <-> pin translation */ -#define AMS_DELTA_MOUTHPIECE 0 -#define AMS_DELTA_EARPIECE 1 -#define AMS_DELTA_MICROPHONE 2 -#define AMS_DELTA_SPEAKER 3 -#define AMS_DELTA_AGC 4 - -#define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \ - (1 << AMS_DELTA_MICROPHONE)) -#define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \ - (1 << AMS_DELTA_EARPIECE)) -#define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \ - (1 << AMS_DELTA_SPEAKER)) -#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC)) - -static const unsigned short ams_delta_audio_mode_pins[] = { - AMS_DELTA_MIXED, - AMS_DELTA_HANDSET, - AMS_DELTA_HANDSFREE, - AMS_DELTA_SPEAKERPHONE, -}; - -static unsigned short ams_delta_audio_agc; - -/* - * Used for passing a codec structure pointer - * from the board initialization code to the tty line discipline. - */ -static struct snd_soc_component *cx20442_codec; - -static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_context *dapm = &card->dapm; - struct soc_enum *control = (struct soc_enum *)kcontrol->private_value; - unsigned short pins; - int pin, changed = 0; - - /* Refuse any mode changes if we are not able to control the codec. */ - if (!cx20442_codec->card->pop_time) - return -EUNATCH; - - if (ucontrol->value.enumerated.item[0] >= control->items) - return -EINVAL; - - snd_soc_dapm_mutex_lock(dapm); - - /* Translate selection to bitmap */ - pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]]; - - /* Setup pins after corresponding bits if changed */ - pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE)); - - if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) { - changed = 1; - if (pin) - snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece"); - } - pin = !!(pins & (1 << AMS_DELTA_EARPIECE)); - if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) { - changed = 1; - if (pin) - snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece"); - } - pin = !!(pins & (1 << AMS_DELTA_MICROPHONE)); - if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) { - changed = 1; - if (pin) - snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone"); - } - pin = !!(pins & (1 << AMS_DELTA_SPEAKER)); - if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) { - changed = 1; - if (pin) - snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); - } - pin = !!(pins & (1 << AMS_DELTA_AGC)); - if (pin != ams_delta_audio_agc) { - ams_delta_audio_agc = pin; - changed = 1; - if (pin) - snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN"); - } - - if (changed) - snd_soc_dapm_sync_unlocked(dapm); - - snd_soc_dapm_mutex_unlock(dapm); - - return changed; -} - -static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - struct snd_soc_dapm_context *dapm = &card->dapm; - unsigned short pins, mode; - - pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") << - AMS_DELTA_MOUTHPIECE) | - (snd_soc_dapm_get_pin_status(dapm, "Earpiece") << - AMS_DELTA_EARPIECE)); - if (pins) - pins |= (snd_soc_dapm_get_pin_status(dapm, "Microphone") << - AMS_DELTA_MICROPHONE); - else - pins = ((snd_soc_dapm_get_pin_status(dapm, "Microphone") << - AMS_DELTA_MICROPHONE) | - (snd_soc_dapm_get_pin_status(dapm, "Speaker") << - AMS_DELTA_SPEAKER) | - (ams_delta_audio_agc << AMS_DELTA_AGC)); - - for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++) - if (pins == ams_delta_audio_mode_pins[mode]) - break; - - if (mode >= ARRAY_SIZE(ams_delta_audio_mode)) - return -EINVAL; - - ucontrol->value.enumerated.item[0] = mode; - - return 0; -} - -static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum, - ams_delta_audio_mode); - -static const struct snd_kcontrol_new ams_delta_audio_controls[] = { - SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum, - ams_delta_get_audio_mode, ams_delta_set_audio_mode), -}; - -/* Hook switch */ -static struct snd_soc_jack ams_delta_hook_switch; -static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = { - { - .name = "hook_switch", - .report = SND_JACK_HEADSET, - .invert = 1, - .debounce_time = 150, - } -}; - -/* After we are able to control the codec over the modem, - * the hook switch can be used for dynamic DAPM reconfiguration. */ -static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = { - /* Handset */ - { - .pin = "Mouthpiece", - .mask = SND_JACK_MICROPHONE, - }, - { - .pin = "Earpiece", - .mask = SND_JACK_HEADPHONE, - }, - /* Handsfree */ - { - .pin = "Microphone", - .mask = SND_JACK_MICROPHONE, - .invert = 1, - }, - { - .pin = "Speaker", - .mask = SND_JACK_HEADPHONE, - .invert = 1, - }, -}; - - -/* - * Modem line discipline, required for making above controls functional. - * Activated from userspace with ldattach, possibly invoked from udev rule. - */ - -/* To actually apply any modem controlled configuration changes to the codec, - * we must connect codec DAI pins to the modem for a moment. Be careful not - * to interfere with our digital mute function that shares the same hardware. */ -static struct timer_list cx81801_timer; -static bool cx81801_cmd_pending; -static bool ams_delta_muted; -static DEFINE_SPINLOCK(ams_delta_lock); -static struct gpio_desc *gpiod_modem_codec; - -static void cx81801_timeout(struct timer_list *unused) -{ - int muted; - - spin_lock(&ams_delta_lock); - cx81801_cmd_pending = 0; - muted = ams_delta_muted; - spin_unlock(&ams_delta_lock); - - /* Reconnect the codec DAI back from the modem to the CPU DAI - * only if digital mute still off */ - if (!muted) - gpiod_set_value(gpiod_modem_codec, 0); -} - -/* Line discipline .open() */ -static int cx81801_open(struct tty_struct *tty) -{ - int ret; - - if (!cx20442_codec) - return -ENODEV; - - /* - * Pass the codec structure pointer for use by other ldisc callbacks, - * both the card and the codec specific parts. - */ - tty->disc_data = cx20442_codec; - - ret = v253_ops.open(tty); - - if (ret < 0) - tty->disc_data = NULL; - - return ret; -} - -/* Line discipline .close() */ -static void cx81801_close(struct tty_struct *tty) -{ - struct snd_soc_component *component = tty->disc_data; - struct snd_soc_dapm_context *dapm = &component->card->dapm; - - del_timer_sync(&cx81801_timer); - - /* Prevent the hook switch from further changing the DAPM pins */ - INIT_LIST_HEAD(&ams_delta_hook_switch.pins); - - if (!component) - return; - - v253_ops.close(tty); - - /* Revert back to default audio input/output constellation */ - snd_soc_dapm_mutex_lock(dapm); - - snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece"); - snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece"); - snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone"); - snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); - snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN"); - - snd_soc_dapm_sync_unlocked(dapm); - - snd_soc_dapm_mutex_unlock(dapm); -} - -/* Line discipline .hangup() */ -static int cx81801_hangup(struct tty_struct *tty) -{ - cx81801_close(tty); - return 0; -} - -/* Line discipline .receive_buf() */ -static void cx81801_receive(struct tty_struct *tty, - const unsigned char *cp, char *fp, int count) -{ - struct snd_soc_component *component = tty->disc_data; - const unsigned char *c; - int apply, ret; - - if (!component) - return; - - if (!component->card->pop_time) { - /* First modem response, complete setup procedure */ - - /* Initialize timer used for config pulse generation */ - timer_setup(&cx81801_timer, cx81801_timeout, 0); - - v253_ops.receive_buf(tty, cp, fp, count); - - /* Link hook switch to DAPM pins */ - ret = snd_soc_jack_add_pins(&ams_delta_hook_switch, - ARRAY_SIZE(ams_delta_hook_switch_pins), - ams_delta_hook_switch_pins); - if (ret) - dev_warn(component->dev, - "Failed to link hook switch to DAPM pins, " - "will continue with hook switch unlinked.\n"); - - return; - } - - v253_ops.receive_buf(tty, cp, fp, count); - - for (c = &cp[count - 1]; c >= cp; c--) { - if (*c != '\r') - continue; - /* Complete modem response received, apply config to codec */ - - spin_lock_bh(&ams_delta_lock); - mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150)); - apply = !ams_delta_muted && !cx81801_cmd_pending; - cx81801_cmd_pending = 1; - spin_unlock_bh(&ams_delta_lock); - - /* Apply config pulse by connecting the codec to the modem - * if not already done */ - if (apply) - gpiod_set_value(gpiod_modem_codec, 1); - break; - } -} - -/* Line discipline .write_wakeup() */ -static void cx81801_wakeup(struct tty_struct *tty) -{ - v253_ops.write_wakeup(tty); -} - -static struct tty_ldisc_ops cx81801_ops = { - .magic = TTY_LDISC_MAGIC, - .name = "cx81801", - .owner = THIS_MODULE, - .open = cx81801_open, - .close = cx81801_close, - .hangup = cx81801_hangup, - .receive_buf = cx81801_receive, - .write_wakeup = cx81801_wakeup, -}; - - -/* - * Even if not very useful, the sound card can still work without any of the - * above functonality activated. You can still control its audio input/output - * constellation and speakerphone gain from userspace by issuing AT commands - * over the modem port. - */ - -static struct snd_soc_ops ams_delta_ops; - - -/* Digital mute implemented using modem/CPU multiplexer. - * Shares hardware with codec config pulse generation */ -static bool ams_delta_muted = 1; - -static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute) -{ - int apply; - - if (ams_delta_muted == mute) - return 0; - - spin_lock_bh(&ams_delta_lock); - ams_delta_muted = mute; - apply = !cx81801_cmd_pending; - spin_unlock_bh(&ams_delta_lock); - - if (apply) - gpiod_set_value(gpiod_modem_codec, !!mute); - return 0; -} - -/* Our codec DAI probably doesn't have its own .ops structure */ -static const struct snd_soc_dai_ops ams_delta_dai_ops = { - .digital_mute = ams_delta_digital_mute, -}; - -/* Will be used if the codec ever has its own digital_mute function */ -static int ams_delta_startup(struct snd_pcm_substream *substream) -{ - return ams_delta_digital_mute(NULL, 0); -} - -static void ams_delta_shutdown(struct snd_pcm_substream *substream) -{ - ams_delta_digital_mute(NULL, 1); -} - - -/* - * Card initialization - */ - -static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_card *card = rtd->card; - struct snd_soc_dapm_context *dapm = &card->dapm; - int ret; - /* Codec is ready, now add/activate board specific controls */ - - /* Store a pointer to the codec structure for tty ldisc use */ - cx20442_codec = rtd->codec_dai->component; - - /* Add hook switch - can be used to control the codec from userspace - * even if line discipline fails */ - ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET, - &ams_delta_hook_switch, NULL, 0); - if (ret) - dev_warn(card->dev, - "Failed to allocate resources for hook switch, " - "will continue without one.\n"); - else { - ret = snd_soc_jack_add_gpiods(card->dev, &ams_delta_hook_switch, - ARRAY_SIZE(ams_delta_hook_switch_gpios), - ams_delta_hook_switch_gpios); - if (ret) - dev_warn(card->dev, - "Failed to set up hook switch GPIO line, " - "will continue with hook switch inactive.\n"); - } - - gpiod_modem_codec = devm_gpiod_get(card->dev, "modem_codec", - GPIOD_OUT_HIGH); - if (IS_ERR(gpiod_modem_codec)) { - dev_warn(card->dev, "Failed to obtain modem_codec GPIO\n"); - return 0; - } - - /* Set up digital mute if not provided by the codec */ - if (!codec_dai->driver->ops) { - codec_dai->driver->ops = &ams_delta_dai_ops; - } else { - ams_delta_ops.startup = ams_delta_startup; - ams_delta_ops.shutdown = ams_delta_shutdown; - } - - /* Register optional line discipline for over the modem control */ - ret = tty_register_ldisc(N_V253, &cx81801_ops); - if (ret) { - dev_warn(card->dev, - "Failed to register line discipline, " - "will continue without any controls.\n"); - return 0; - } - - /* Set up initial pin constellation */ - snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); - snd_soc_dapm_disable_pin(dapm, "Speaker"); - snd_soc_dapm_disable_pin(dapm, "AGCIN"); - snd_soc_dapm_disable_pin(dapm, "AGCOUT"); - - return 0; -} - -/* DAI glue - connects codec <--> CPU */ -static struct snd_soc_dai_link ams_delta_dai_link = { - .name = "CX20442", - .stream_name = "CX20442", - .cpu_dai_name = "omap-mcbsp.1", - .codec_dai_name = "cx20442-voice", - .init = ams_delta_cx20442_init, - .platform_name = "omap-mcbsp.1", - .codec_name = "cx20442-codec", - .ops = &ams_delta_ops, - .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, -}; - -/* Audio card driver */ -static struct snd_soc_card ams_delta_audio_card = { - .name = "AMS_DELTA", - .owner = THIS_MODULE, - .dai_link = &ams_delta_dai_link, - .num_links = 1, - - .controls = ams_delta_audio_controls, - .num_controls = ARRAY_SIZE(ams_delta_audio_controls), - .dapm_widgets = ams_delta_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(ams_delta_dapm_widgets), - .dapm_routes = ams_delta_audio_map, - .num_dapm_routes = ARRAY_SIZE(ams_delta_audio_map), -}; - -/* Module init/exit */ -static int ams_delta_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &ams_delta_audio_card; - int ret; - - card->dev = &pdev->dev; - - ret = snd_soc_register_card(card); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); - card->dev = NULL; - return ret; - } - return 0; -} - -static int ams_delta_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - if (tty_unregister_ldisc(N_V253) != 0) - dev_warn(&pdev->dev, - "failed to unregister V253 line discipline\n"); - - snd_soc_unregister_card(card); - card->dev = NULL; - return 0; -} - -#define DRV_NAME "ams-delta-audio" - -static struct platform_driver ams_delta_driver = { - .driver = { - .name = DRV_NAME, - }, - .probe = ams_delta_probe, - .remove = ams_delta_remove, -}; - -module_platform_driver(ams_delta_driver); - -MODULE_AUTHOR("Janusz Krzysztofik "); -MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c deleted file mode 100644 index 9cfefe44a75f..000000000000 --- a/sound/soc/omap/n810.c +++ /dev/null @@ -1,378 +0,0 @@ -/* - * n810.c -- SoC audio for Nokia N810 - * - * Copyright (C) 2008 Nokia Corporation - * - * Contact: Jarkko Nikula - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "omap-mcbsp.h" - -#define N810_HEADSET_AMP_GPIO 10 -#define N810_SPEAKER_AMP_GPIO 101 - -enum { - N810_JACK_DISABLED, - N810_JACK_HP, - N810_JACK_HS, - N810_JACK_MIC, -}; - -static struct clk *sys_clkout2; -static struct clk *sys_clkout2_src; -static struct clk *func96m_clk; - -static int n810_spk_func; -static int n810_jack_func; -static int n810_dmic_func; - -static void n810_ext_control(struct snd_soc_dapm_context *dapm) -{ - int hp = 0, line1l = 0; - - switch (n810_jack_func) { - case N810_JACK_HS: - line1l = 1; - case N810_JACK_HP: - hp = 1; - break; - case N810_JACK_MIC: - line1l = 1; - break; - } - - snd_soc_dapm_mutex_lock(dapm); - - if (n810_spk_func) - snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); - - if (hp) - snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); - if (line1l) - snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); - - if (n810_dmic_func) - snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); - - snd_soc_dapm_sync_unlocked(dapm); - - snd_soc_dapm_mutex_unlock(dapm); -} - -static int n810_startup(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - - snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); - - n810_ext_control(&rtd->card->dapm); - return clk_prepare_enable(sys_clkout2); -} - -static void n810_shutdown(struct snd_pcm_substream *substream) -{ - clk_disable_unprepare(sys_clkout2); -} - -static int n810_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int err; - - /* Set the codec system clock for DAC and ADC */ - err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000, - SND_SOC_CLOCK_IN); - - return err; -} - -static const struct snd_soc_ops n810_ops = { - .startup = n810_startup, - .hw_params = n810_hw_params, - .shutdown = n810_shutdown, -}; - -static int n810_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = n810_spk_func; - - return 0; -} - -static int n810_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - - if (n810_spk_func == ucontrol->value.enumerated.item[0]) - return 0; - - n810_spk_func = ucontrol->value.enumerated.item[0]; - n810_ext_control(&card->dapm); - - return 1; -} - -static int n810_get_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = n810_jack_func; - - return 0; -} - -static int n810_set_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - - if (n810_jack_func == ucontrol->value.enumerated.item[0]) - return 0; - - n810_jack_func = ucontrol->value.enumerated.item[0]; - n810_ext_control(&card->dapm); - - return 1; -} - -static int n810_get_input(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = n810_dmic_func; - - return 0; -} - -static int n810_set_input(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - - if (n810_dmic_func == ucontrol->value.enumerated.item[0]) - return 0; - - n810_dmic_func = ucontrol->value.enumerated.item[0]; - n810_ext_control(&card->dapm); - - return 1; -} - -static int n810_spk_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - if (SND_SOC_DAPM_EVENT_ON(event)) - gpio_set_value(N810_SPEAKER_AMP_GPIO, 1); - else - gpio_set_value(N810_SPEAKER_AMP_GPIO, 0); - - return 0; -} - -static int n810_jack_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - if (SND_SOC_DAPM_EVENT_ON(event)) - gpio_set_value(N810_HEADSET_AMP_GPIO, 1); - else - gpio_set_value(N810_HEADSET_AMP_GPIO, 0); - - return 0; -} - -static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event), - SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event), - SND_SOC_DAPM_MIC("DMic", NULL), - SND_SOC_DAPM_MIC("HS Mic", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - {"Headphone Jack", NULL, "HPLOUT"}, - {"Headphone Jack", NULL, "HPROUT"}, - - {"Ext Spk", NULL, "LLOUT"}, - {"Ext Spk", NULL, "RLOUT"}, - - {"DMic Rate 64", NULL, "DMic"}, - {"DMic", NULL, "Mic Bias"}, - - /* - * Note that the mic bias is coming from Retu/Vilma and we don't have - * control over it atm. The analog HS mic is not working. <- TODO - */ - {"LINE1L", NULL, "HS Mic"}, -}; - -static const char *spk_function[] = {"Off", "On"}; -static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"}; -static const char *input_function[] = {"ADC", "Digital Mic"}; -static const struct soc_enum n810_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), -}; - -static const struct snd_kcontrol_new aic33_n810_controls[] = { - SOC_ENUM_EXT("Speaker Function", n810_enum[0], - n810_get_spk, n810_set_spk), - SOC_ENUM_EXT("Jack Function", n810_enum[1], - n810_get_jack, n810_set_jack), - SOC_ENUM_EXT("Input Select", n810_enum[2], - n810_get_input, n810_set_input), -}; - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link n810_dai = { - .name = "TLV320AIC33", - .stream_name = "AIC33", - .cpu_dai_name = "48076000.mcbsp", - .platform_name = "48076000.mcbsp", - .codec_name = "tlv320aic3x-codec.1-0018", - .codec_dai_name = "tlv320aic3x-hifi", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &n810_ops, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_n810 = { - .name = "N810", - .owner = THIS_MODULE, - .dai_link = &n810_dai, - .num_links = 1, - - .controls = aic33_n810_controls, - .num_controls = ARRAY_SIZE(aic33_n810_controls), - .dapm_widgets = aic33_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), - .fully_routed = true, -}; - -static struct platform_device *n810_snd_device; - -static int __init n810_soc_init(void) -{ - int err; - struct device *dev; - - if (!of_have_populated_dt() || - (!of_machine_is_compatible("nokia,n810") && - !of_machine_is_compatible("nokia,n810-wimax"))) - return -ENODEV; - - n810_snd_device = platform_device_alloc("soc-audio", -1); - if (!n810_snd_device) - return -ENOMEM; - - platform_set_drvdata(n810_snd_device, &snd_soc_n810); - err = platform_device_add(n810_snd_device); - if (err) - goto err1; - - dev = &n810_snd_device->dev; - - sys_clkout2_src = clk_get(dev, "sys_clkout2_src"); - if (IS_ERR(sys_clkout2_src)) { - dev_err(dev, "Could not get sys_clkout2_src clock\n"); - err = PTR_ERR(sys_clkout2_src); - goto err2; - } - sys_clkout2 = clk_get(dev, "sys_clkout2"); - if (IS_ERR(sys_clkout2)) { - dev_err(dev, "Could not get sys_clkout2\n"); - err = PTR_ERR(sys_clkout2); - goto err3; - } - /* - * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use - * 96 MHz as its parent in order to get 12 MHz - */ - func96m_clk = clk_get(dev, "func_96m_ck"); - if (IS_ERR(func96m_clk)) { - dev_err(dev, "Could not get func 96M clock\n"); - err = PTR_ERR(func96m_clk); - goto err4; - } - clk_set_parent(sys_clkout2_src, func96m_clk); - clk_set_rate(sys_clkout2, 12000000); - - if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) || - (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) { - err = -EINVAL; - goto err4; - } - - gpio_direction_output(N810_HEADSET_AMP_GPIO, 0); - gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0); - - return 0; -err4: - clk_put(sys_clkout2); -err3: - clk_put(sys_clkout2_src); -err2: - platform_device_del(n810_snd_device); -err1: - platform_device_put(n810_snd_device); - - return err; -} - -static void __exit n810_soc_exit(void) -{ - gpio_free(N810_SPEAKER_AMP_GPIO); - gpio_free(N810_HEADSET_AMP_GPIO); - clk_put(sys_clkout2_src); - clk_put(sys_clkout2); - clk_put(func96m_clk); - - platform_device_unregister(n810_snd_device); -} - -module_init(n810_soc_init); -module_exit(n810_soc_exit); - -MODULE_AUTHOR("Jarkko Nikula "); -MODULE_DESCRIPTION("ALSA SoC Nokia N810"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c deleted file mode 100644 index fed45b41f9d3..000000000000 --- a/sound/soc/omap/omap-abe-twl6040.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * omap-abe-twl6040.c -- SoC audio for TI OMAP based boards with ABE and - * twl6040 codec - * - * Author: Misael Lopez Cruz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "omap-dmic.h" -#include "omap-mcpdm.h" -#include "../codecs/twl6040.h" - -struct abe_twl6040 { - struct snd_soc_card card; - struct snd_soc_dai_link dai_links[2]; - int jack_detection; /* board can detect jack events */ - int mclk_freq; /* MCLK frequency speed for twl6040 */ -}; - -static struct platform_device *dmic_codec_dev; - -static int omap_abe_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_card *card = rtd->card; - struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); - int clk_id, freq; - int ret; - - clk_id = twl6040_get_clk_id(codec_dai->component); - if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) - freq = priv->mclk_freq; - else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) - freq = 32768; - else - return -EINVAL; - - /* set the codec mclk */ - ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, - SND_SOC_CLOCK_IN); - if (ret) { - printk(KERN_ERR "can't set codec system clock\n"); - return ret; - } - return ret; -} - -static const struct snd_soc_ops omap_abe_ops = { - .hw_params = omap_abe_hw_params, -}; - -static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret = 0; - - ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS, - 19200000, SND_SOC_CLOCK_IN); - if (ret < 0) { - printk(KERN_ERR "can't set DMIC cpu system clock\n"); - return ret; - } - ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000, - SND_SOC_CLOCK_OUT); - if (ret < 0) { - printk(KERN_ERR "can't set DMIC output clock\n"); - return ret; - } - return 0; -} - -static struct snd_soc_ops omap_abe_dmic_ops = { - .hw_params = omap_abe_dmic_hw_params, -}; - -/* Headset jack */ -static struct snd_soc_jack hs_jack; - -/*Headset jack detection DAPM pins */ -static struct snd_soc_jack_pin hs_jack_pins[] = { - { - .pin = "Headset Mic", - .mask = SND_JACK_MICROPHONE, - }, - { - .pin = "Headset Stereophone", - .mask = SND_JACK_HEADPHONE, - }, -}; - -/* SDP4430 machine DAPM */ -static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { - /* Outputs */ - SND_SOC_DAPM_HP("Headset Stereophone", NULL), - SND_SOC_DAPM_SPK("Earphone Spk", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), - SND_SOC_DAPM_LINE("Line Out", NULL), - SND_SOC_DAPM_SPK("Vibrator", NULL), - - /* Inputs */ - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Main Handset Mic", NULL), - SND_SOC_DAPM_MIC("Sub Handset Mic", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), - - /* Digital microphones */ - SND_SOC_DAPM_MIC("Digital Mic", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* Routings for outputs */ - {"Headset Stereophone", NULL, "HSOL"}, - {"Headset Stereophone", NULL, "HSOR"}, - - {"Earphone Spk", NULL, "EP"}, - - {"Ext Spk", NULL, "HFL"}, - {"Ext Spk", NULL, "HFR"}, - - {"Line Out", NULL, "AUXL"}, - {"Line Out", NULL, "AUXR"}, - - {"Vibrator", NULL, "VIBRAL"}, - {"Vibrator", NULL, "VIBRAR"}, - - /* Routings for inputs */ - {"HSMIC", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "Headset Mic Bias"}, - - {"MAINMIC", NULL, "Main Handset Mic"}, - {"Main Handset Mic", NULL, "Main Mic Bias"}, - - {"SUBMIC", NULL, "Sub Handset Mic"}, - {"Sub Handset Mic", NULL, "Main Mic Bias"}, - - {"AFML", NULL, "Line In"}, - {"AFMR", NULL, "Line In"}, -}; - -static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = rtd->codec_dai->component; - struct snd_soc_card *card = rtd->card; - struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); - int hs_trim; - int ret = 0; - - /* - * Configure McPDM offset cancellation based on the HSOTRIM value from - * twl6040. - */ - hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM); - omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim), - TWL6040_HSF_TRIM_RIGHT(hs_trim)); - - /* Headset jack detection only if it is supported */ - if (priv->jack_detection) { - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET, &hs_jack, - hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); - if (ret) - return ret; - - twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET); - } - - return 0; -} - -static const struct snd_soc_dapm_route dmic_audio_map[] = { - {"DMic", NULL, "Digital Mic"}, - {"Digital Mic", NULL, "Digital Mic1 Bias"}, -}; - -static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dapm_context *dapm = &rtd->card->dapm; - - return snd_soc_dapm_add_routes(dapm, dmic_audio_map, - ARRAY_SIZE(dmic_audio_map)); -} - -static int omap_abe_probe(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - struct snd_soc_card *card; - struct device_node *dai_node; - struct abe_twl6040 *priv; - int num_links = 0; - int ret = 0; - - if (!node) { - dev_err(&pdev->dev, "of node is missing.\n"); - return -ENODEV; - } - - priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - - card = &priv->card; - card->dev = &pdev->dev; - card->owner = THIS_MODULE; - card->dapm_widgets = twl6040_dapm_widgets; - card->num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets); - card->dapm_routes = audio_map; - card->num_dapm_routes = ARRAY_SIZE(audio_map); - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); - if (ret) { - dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); - return ret; - } - - dai_node = of_parse_phandle(node, "ti,mcpdm", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McPDM node is not provided\n"); - return -EINVAL; - } - - priv->dai_links[0].name = "DMIC"; - priv->dai_links[0].stream_name = "TWL6040"; - priv->dai_links[0].cpu_of_node = dai_node; - priv->dai_links[0].platform_of_node = dai_node; - priv->dai_links[0].codec_dai_name = "twl6040-legacy"; - priv->dai_links[0].codec_name = "twl6040-codec"; - priv->dai_links[0].init = omap_abe_twl6040_init; - priv->dai_links[0].ops = &omap_abe_ops; - - dai_node = of_parse_phandle(node, "ti,dmic", 0); - if (dai_node) { - num_links = 2; - priv->dai_links[1].name = "TWL6040"; - priv->dai_links[1].stream_name = "DMIC Capture"; - priv->dai_links[1].cpu_of_node = dai_node; - priv->dai_links[1].platform_of_node = dai_node; - priv->dai_links[1].codec_dai_name = "dmic-hifi"; - priv->dai_links[1].codec_name = "dmic-codec"; - priv->dai_links[1].init = omap_abe_dmic_init; - priv->dai_links[1].ops = &omap_abe_dmic_ops; - } else { - num_links = 1; - } - - priv->jack_detection = of_property_read_bool(node, "ti,jack-detection"); - of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); - if (!priv->mclk_freq) { - dev_err(&pdev->dev, "MCLK frequency not provided\n"); - return -EINVAL; - } - - card->fully_routed = 1; - - if (!priv->mclk_freq) { - dev_err(&pdev->dev, "MCLK frequency missing\n"); - return -ENODEV; - } - - card->dai_link = priv->dai_links; - card->num_links = num_links; - - snd_soc_card_set_drvdata(card, priv); - - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); - - return ret; -} - -static const struct of_device_id omap_abe_of_match[] = { - {.compatible = "ti,abe-twl6040", }, - { }, -}; -MODULE_DEVICE_TABLE(of, omap_abe_of_match); - -static struct platform_driver omap_abe_driver = { - .driver = { - .name = "omap-abe-twl6040", - .pm = &snd_soc_pm_ops, - .of_match_table = omap_abe_of_match, - }, - .probe = omap_abe_probe, -}; - -static int __init omap_abe_init(void) -{ - int ret; - - dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL, - 0); - if (IS_ERR(dmic_codec_dev)) { - pr_err("%s: dmic-codec device registration failed\n", __func__); - return PTR_ERR(dmic_codec_dev); - } - - ret = platform_driver_register(&omap_abe_driver); - if (ret) { - pr_err("%s: platform driver registration failed\n", __func__); - platform_device_unregister(dmic_codec_dev); - } - - return ret; -} -module_init(omap_abe_init); - -static void __exit omap_abe_exit(void) -{ - platform_driver_unregister(&omap_abe_driver); - platform_device_unregister(dmic_codec_dev); -} -module_exit(omap_abe_exit); - -MODULE_AUTHOR("Misael Lopez Cruz "); -MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:omap-abe-twl6040"); diff --git a/sound/soc/omap/omap-dmic.c b/sound/soc/omap/omap-dmic.c deleted file mode 100644 index cba9645b6487..000000000000 --- a/sound/soc/omap/omap-dmic.c +++ /dev/null @@ -1,541 +0,0 @@ -/* - * omap-dmic.c -- OMAP ASoC DMIC DAI driver - * - * Copyright (C) 2010 - 2011 Texas Instruments - * - * Author: David Lambert - * Misael Lopez Cruz - * Liam Girdwood - * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "omap-dmic.h" -#include "sdma-pcm.h" - -struct omap_dmic { - struct device *dev; - void __iomem *io_base; - struct clk *fclk; - struct pm_qos_request pm_qos_req; - int latency; - int fclk_freq; - int out_freq; - int clk_div; - int sysclk; - int threshold; - u32 ch_enabled; - bool active; - struct mutex mutex; - - struct snd_dmaengine_dai_dma_data dma_data; -}; - -static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val) -{ - writel_relaxed(val, dmic->io_base + reg); -} - -static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg) -{ - return readl_relaxed(dmic->io_base + reg); -} - -static inline void omap_dmic_start(struct omap_dmic *dmic) -{ - u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG); - - /* Configure DMA controller */ - omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG, - OMAP_DMIC_DMA_ENABLE); - - omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled); -} - -static inline void omap_dmic_stop(struct omap_dmic *dmic) -{ - u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG); - omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, - ctrl & ~OMAP_DMIC_UP_ENABLE_MASK); - - /* Disable DMA request generation */ - omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG, - OMAP_DMIC_DMA_ENABLE); - -} - -static inline int dmic_is_enabled(struct omap_dmic *dmic) -{ - return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) & - OMAP_DMIC_UP_ENABLE_MASK; -} - -static int omap_dmic_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - int ret = 0; - - mutex_lock(&dmic->mutex); - - if (!dai->active) - dmic->active = 1; - else - ret = -EBUSY; - - mutex_unlock(&dmic->mutex); - - return ret; -} - -static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - - mutex_lock(&dmic->mutex); - - pm_qos_remove_request(&dmic->pm_qos_req); - - if (!dai->active) - dmic->active = 0; - - mutex_unlock(&dmic->mutex); -} - -static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate) -{ - int divider = -EINVAL; - - /* - * 192KHz rate is only supported with 19.2MHz/3.84MHz clock - * configuration. - */ - if (sample_rate == 192000) { - if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000) - divider = 0x6; /* Divider: 5 (192KHz sampling rate) */ - else - dev_err(dmic->dev, - "invalid clock configuration for 192KHz\n"); - - return divider; - } - - switch (dmic->out_freq) { - case 1536000: - if (dmic->fclk_freq != 24576000) - goto div_err; - divider = 0x4; /* Divider: 16 */ - break; - case 2400000: - switch (dmic->fclk_freq) { - case 12000000: - divider = 0x5; /* Divider: 5 */ - break; - case 19200000: - divider = 0x0; /* Divider: 8 */ - break; - case 24000000: - divider = 0x2; /* Divider: 10 */ - break; - default: - goto div_err; - } - break; - case 3072000: - if (dmic->fclk_freq != 24576000) - goto div_err; - divider = 0x3; /* Divider: 8 */ - break; - case 3840000: - if (dmic->fclk_freq != 19200000) - goto div_err; - divider = 0x1; /* Divider: 5 (96KHz sampling rate) */ - break; - default: - dev_err(dmic->dev, "invalid out frequency: %dHz\n", - dmic->out_freq); - break; - } - - return divider; - -div_err: - dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n", - dmic->out_freq, dmic->fclk_freq); - return -EINVAL; -} - -static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - struct snd_dmaengine_dai_dma_data *dma_data; - int channels; - - dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params)); - if (dmic->clk_div < 0) { - dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n", - dmic->out_freq, dmic->fclk_freq); - return -EINVAL; - } - - dmic->ch_enabled = 0; - channels = params_channels(params); - switch (channels) { - case 6: - dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE; - /* fall through */ - case 4: - dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE; - /* fall through */ - case 2: - dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE; - break; - default: - dev_err(dmic->dev, "invalid number of legacy channels\n"); - return -EINVAL; - } - - /* packet size is threshold * channels */ - dma_data = snd_soc_dai_get_dma_data(dai, substream); - dma_data->maxburst = dmic->threshold * channels; - dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC / - params_rate(params); - - return 0; -} - -static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - u32 ctrl; - - if (pm_qos_request_active(&dmic->pm_qos_req)) - pm_qos_update_request(&dmic->pm_qos_req, dmic->latency); - - /* Configure uplink threshold */ - omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold); - - ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG); - - /* Set dmic out format */ - ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK); - ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 | - OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3); - - /* Configure dmic clock divider */ - ctrl &= ~OMAP_DMIC_CLK_DIV_MASK; - ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div); - - omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl); - - omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, - ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 | - OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3); - - return 0; -} - -static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - omap_dmic_start(dmic); - break; - case SNDRV_PCM_TRIGGER_STOP: - omap_dmic_stop(dmic); - break; - default: - break; - } - - return 0; -} - -static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id, - unsigned int freq) -{ - struct clk *parent_clk, *mux; - char *parent_clk_name; - int ret = 0; - - switch (freq) { - case 12000000: - case 19200000: - case 24000000: - case 24576000: - break; - default: - dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq); - dmic->fclk_freq = 0; - return -EINVAL; - } - - if (dmic->sysclk == clk_id) { - dmic->fclk_freq = freq; - return 0; - } - - /* re-parent not allowed if a stream is ongoing */ - if (dmic->active && dmic_is_enabled(dmic)) { - dev_err(dmic->dev, "can't re-parent when DMIC active\n"); - return -EBUSY; - } - - switch (clk_id) { - case OMAP_DMIC_SYSCLK_PAD_CLKS: - parent_clk_name = "pad_clks_ck"; - break; - case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS: - parent_clk_name = "slimbus_clk"; - break; - case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS: - parent_clk_name = "dmic_sync_mux_ck"; - break; - default: - dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id); - return -EINVAL; - } - - parent_clk = clk_get(dmic->dev, parent_clk_name); - if (IS_ERR(parent_clk)) { - dev_err(dmic->dev, "can't get %s\n", parent_clk_name); - return -ENODEV; - } - - mux = clk_get_parent(dmic->fclk); - if (IS_ERR(mux)) { - dev_err(dmic->dev, "can't get fck mux parent\n"); - clk_put(parent_clk); - return -ENODEV; - } - - mutex_lock(&dmic->mutex); - if (dmic->active) { - /* disable clock while reparenting */ - pm_runtime_put_sync(dmic->dev); - ret = clk_set_parent(mux, parent_clk); - pm_runtime_get_sync(dmic->dev); - } else { - ret = clk_set_parent(mux, parent_clk); - } - mutex_unlock(&dmic->mutex); - - if (ret < 0) { - dev_err(dmic->dev, "re-parent failed\n"); - goto err_busy; - } - - dmic->sysclk = clk_id; - dmic->fclk_freq = freq; - -err_busy: - clk_put(mux); - clk_put(parent_clk); - - return ret; -} - -static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id, - unsigned int freq) -{ - int ret = 0; - - if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) { - dev_err(dmic->dev, "output clk_id (%d) not supported\n", - clk_id); - return -EINVAL; - } - - switch (freq) { - case 1536000: - case 2400000: - case 3072000: - case 3840000: - dmic->out_freq = freq; - break; - default: - dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq); - dmic->out_freq = 0; - ret = -EINVAL; - } - - return ret; -} - -static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, - unsigned int freq, int dir) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - - if (dir == SND_SOC_CLOCK_IN) - return omap_dmic_select_fclk(dmic, clk_id, freq); - else if (dir == SND_SOC_CLOCK_OUT) - return omap_dmic_select_outclk(dmic, clk_id, freq); - - dev_err(dmic->dev, "invalid clock direction (%d)\n", dir); - return -EINVAL; -} - -static const struct snd_soc_dai_ops omap_dmic_dai_ops = { - .startup = omap_dmic_dai_startup, - .shutdown = omap_dmic_dai_shutdown, - .hw_params = omap_dmic_dai_hw_params, - .prepare = omap_dmic_dai_prepare, - .trigger = omap_dmic_dai_trigger, - .set_sysclk = omap_dmic_set_dai_sysclk, -}; - -static int omap_dmic_probe(struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - - pm_runtime_enable(dmic->dev); - - /* Disable lines while request is ongoing */ - pm_runtime_get_sync(dmic->dev); - omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00); - pm_runtime_put_sync(dmic->dev); - - /* Configure DMIC threshold value */ - dmic->threshold = OMAP_DMIC_THRES_MAX - 3; - - snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data); - - return 0; -} - -static int omap_dmic_remove(struct snd_soc_dai *dai) -{ - struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); - - pm_runtime_disable(dmic->dev); - - return 0; -} - -static struct snd_soc_dai_driver omap_dmic_dai = { - .name = "omap-dmic", - .probe = omap_dmic_probe, - .remove = omap_dmic_remove, - .capture = { - .channels_min = 2, - .channels_max = 6, - .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, - .formats = SNDRV_PCM_FMTBIT_S32_LE, - .sig_bits = 24, - }, - .ops = &omap_dmic_dai_ops, -}; - -static const struct snd_soc_component_driver omap_dmic_component = { - .name = "omap-dmic", -}; - -static int asoc_dmic_probe(struct platform_device *pdev) -{ - struct omap_dmic *dmic; - struct resource *res; - int ret; - - dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL); - if (!dmic) - return -ENOMEM; - - platform_set_drvdata(pdev, dmic); - dmic->dev = &pdev->dev; - dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS; - - mutex_init(&dmic->mutex); - - dmic->fclk = devm_clk_get(dmic->dev, "fck"); - if (IS_ERR(dmic->fclk)) { - dev_err(dmic->dev, "cant get fck\n"); - return -ENODEV; - } - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (!res) { - dev_err(dmic->dev, "invalid dma memory resource\n"); - return -ENODEV; - } - dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG; - - dmic->dma_data.filter_data = "up_link"; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - dmic->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dmic->io_base)) - return PTR_ERR(dmic->io_base); - - - ret = devm_snd_soc_register_component(&pdev->dev, - &omap_dmic_component, - &omap_dmic_dai, 1); - if (ret) - return ret; - - ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link"); - if (ret) - return ret; - - return 0; -} - -static const struct of_device_id omap_dmic_of_match[] = { - { .compatible = "ti,omap4-dmic", }, - { } -}; -MODULE_DEVICE_TABLE(of, omap_dmic_of_match); - -static struct platform_driver asoc_dmic_driver = { - .driver = { - .name = "omap-dmic", - .of_match_table = omap_dmic_of_match, - }, - .probe = asoc_dmic_probe, -}; - -module_platform_driver(asoc_dmic_driver); - -MODULE_ALIAS("platform:omap-dmic"); -MODULE_AUTHOR("Peter Ujfalusi "); -MODULE_DESCRIPTION("OMAP DMIC ASoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/omap-dmic.h b/sound/soc/omap/omap-dmic.h deleted file mode 100644 index 231e728bff0e..000000000000 --- a/sound/soc/omap/omap-dmic.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * omap-dmic.h -- OMAP Digital Microphone Controller - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _OMAP_DMIC_H -#define _OMAP_DMIC_H - -#define OMAP_DMIC_REVISION_REG 0x00 -#define OMAP_DMIC_SYSCONFIG_REG 0x10 -#define OMAP_DMIC_IRQSTATUS_RAW_REG 0x24 -#define OMAP_DMIC_IRQSTATUS_REG 0x28 -#define OMAP_DMIC_IRQENABLE_SET_REG 0x2C -#define OMAP_DMIC_IRQENABLE_CLR_REG 0x30 -#define OMAP_DMIC_IRQWAKE_EN_REG 0x34 -#define OMAP_DMIC_DMAENABLE_SET_REG 0x38 -#define OMAP_DMIC_DMAENABLE_CLR_REG 0x3C -#define OMAP_DMIC_DMAWAKEEN_REG 0x40 -#define OMAP_DMIC_CTRL_REG 0x44 -#define OMAP_DMIC_DATA_REG 0x48 -#define OMAP_DMIC_FIFO_CTRL_REG 0x4C -#define OMAP_DMIC_FIFO_DMIC1R_DATA_REG 0x50 -#define OMAP_DMIC_FIFO_DMIC1L_DATA_REG 0x54 -#define OMAP_DMIC_FIFO_DMIC2R_DATA_REG 0x58 -#define OMAP_DMIC_FIFO_DMIC2L_DATA_REG 0x5C -#define OMAP_DMIC_FIFO_DMIC3R_DATA_REG 0x60 -#define OMAP_DMIC_FIFO_DMIC3L_DATA_REG 0x64 - -/* IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR bit fields */ -#define OMAP_DMIC_IRQ (1 << 0) -#define OMAP_DMIC_IRQ_FULL (1 << 1) -#define OMAP_DMIC_IRQ_ALMST_EMPTY (1 << 2) -#define OMAP_DMIC_IRQ_EMPTY (1 << 3) -#define OMAP_DMIC_IRQ_MASK 0x07 - -/* DMIC_DMAENABLE bit fields */ -#define OMAP_DMIC_DMA_ENABLE 0x1 - -/* DMIC_CTRL bit fields */ -#define OMAP_DMIC_UP1_ENABLE (1 << 0) -#define OMAP_DMIC_UP2_ENABLE (1 << 1) -#define OMAP_DMIC_UP3_ENABLE (1 << 2) -#define OMAP_DMIC_UP_ENABLE_MASK 0x7 -#define OMAP_DMIC_FORMAT (1 << 3) -#define OMAP_DMIC_POLAR1 (1 << 4) -#define OMAP_DMIC_POLAR2 (1 << 5) -#define OMAP_DMIC_POLAR3 (1 << 6) -#define OMAP_DMIC_POLAR_MASK (0x7 << 4) -#define OMAP_DMIC_CLK_DIV(x) (((x) & 0x7) << 7) -#define OMAP_DMIC_CLK_DIV_MASK (0x7 << 7) -#define OMAP_DMIC_RESET (1 << 10) - -#define OMAP_DMICOUTFORMAT_LJUST (0 << 3) -#define OMAP_DMICOUTFORMAT_RJUST (1 << 3) - -/* DMIC_FIFO_CTRL bit fields */ -#define OMAP_DMIC_THRES_MAX 0xF - -enum omap_dmic_clk { - OMAP_DMIC_SYSCLK_PAD_CLKS, /* PAD_CLKS */ - OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS, /* SLIMBUS_CLK */ - OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS, /* DMIC_SYNC_MUX_CLK */ - OMAP_DMIC_ABE_DMIC_CLK, /* abe_dmic_clk */ -}; - -#endif diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c deleted file mode 100644 index 673a9eb153b2..000000000000 --- a/sound/soc/omap/omap-hdmi-audio.c +++ /dev/null @@ -1,418 +0,0 @@ -/* - * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library - * - * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com - * - * Author: Jyri Sarha - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sdma-pcm.h" - -#define DRV_NAME "omap-hdmi-audio" - -struct hdmi_audio_data { - struct snd_soc_card *card; - - const struct omap_hdmi_audio_ops *ops; - struct device *dssdev; - struct snd_dmaengine_dai_dma_data dma_data; - struct omap_dss_audio dss_audio; - struct snd_aes_iec958 iec; - struct snd_cea_861_aud_if cea; - - struct mutex current_stream_lock; - struct snd_pcm_substream *current_stream; -}; - -static -struct hdmi_audio_data *card_drvdata_substream(struct snd_pcm_substream *ss) -{ - struct snd_soc_pcm_runtime *rtd = ss->private_data; - - return snd_soc_card_get_drvdata(rtd->card); -} - -static void hdmi_dai_abort(struct device *dev) -{ - struct hdmi_audio_data *ad = dev_get_drvdata(dev); - - mutex_lock(&ad->current_stream_lock); - if (ad->current_stream && ad->current_stream->runtime && - snd_pcm_running(ad->current_stream)) { - dev_err(dev, "HDMI display disabled, aborting playback\n"); - snd_pcm_stream_lock_irq(ad->current_stream); - snd_pcm_stop(ad->current_stream, SNDRV_PCM_STATE_DISCONNECTED); - snd_pcm_stream_unlock_irq(ad->current_stream); - } - mutex_unlock(&ad->current_stream_lock); -} - -static int hdmi_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct hdmi_audio_data *ad = card_drvdata_substream(substream); - int ret; - /* - * Make sure that the period bytes are multiple of the DMA packet size. - * Largest packet size we use is 32 32-bit words = 128 bytes - */ - ret = snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); - if (ret < 0) { - dev_err(dai->dev, "Could not apply period constraint: %d\n", - ret); - return ret; - } - ret = snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128); - if (ret < 0) { - dev_err(dai->dev, "Could not apply buffer constraint: %d\n", - ret); - return ret; - } - - snd_soc_dai_set_dma_data(dai, substream, &ad->dma_data); - - mutex_lock(&ad->current_stream_lock); - ad->current_stream = substream; - mutex_unlock(&ad->current_stream_lock); - - ret = ad->ops->audio_startup(ad->dssdev, hdmi_dai_abort); - - if (ret) { - mutex_lock(&ad->current_stream_lock); - ad->current_stream = NULL; - mutex_unlock(&ad->current_stream_lock); - } - - return ret; -} - -static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct hdmi_audio_data *ad = card_drvdata_substream(substream); - struct snd_aes_iec958 *iec = &ad->iec; - struct snd_cea_861_aud_if *cea = &ad->cea; - - WARN_ON(ad->current_stream != substream); - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - ad->dma_data.maxburst = 16; - break; - case SNDRV_PCM_FORMAT_S24_LE: - ad->dma_data.maxburst = 32; - break; - default: - dev_err(dai->dev, "format not supported!\n"); - return -EINVAL; - } - - ad->dss_audio.iec = iec; - ad->dss_audio.cea = cea; - /* - * fill the IEC-60958 channel status word - */ - /* initialize the word bytes */ - memset(iec->status, 0, sizeof(iec->status)); - - /* specify IEC-60958-3 (commercial use) */ - iec->status[0] &= ~IEC958_AES0_PROFESSIONAL; - - /* specify that the audio is LPCM*/ - iec->status[0] &= ~IEC958_AES0_NONAUDIO; - - iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; - - iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; - - iec->status[1] = IEC958_AES1_CON_GENERAL; - - iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; - - iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC; - - switch (params_rate(params)) { - case 32000: - iec->status[3] |= IEC958_AES3_CON_FS_32000; - break; - case 44100: - iec->status[3] |= IEC958_AES3_CON_FS_44100; - break; - case 48000: - iec->status[3] |= IEC958_AES3_CON_FS_48000; - break; - case 88200: - iec->status[3] |= IEC958_AES3_CON_FS_88200; - break; - case 96000: - iec->status[3] |= IEC958_AES3_CON_FS_96000; - break; - case 176400: - iec->status[3] |= IEC958_AES3_CON_FS_176400; - break; - case 192000: - iec->status[3] |= IEC958_AES3_CON_FS_192000; - break; - default: - dev_err(dai->dev, "rate not supported!\n"); - return -EINVAL; - } - - /* specify the clock accuracy */ - iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM; - - /* - * specify the word length. The same word length value can mean - * two different lengths. Hence, we need to specify the maximum - * word length as well. - */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16; - iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24; - break; - case SNDRV_PCM_FORMAT_S24_LE: - iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20; - iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24; - break; - default: - dev_err(dai->dev, "format not supported!\n"); - return -EINVAL; - } - - /* - * Fill the CEA-861 audio infoframe (see spec for details) - */ - - cea->db1_ct_cc = (params_channels(params) - 1) - & CEA861_AUDIO_INFOFRAME_DB1CC; - cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM; - - cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM; - cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM; - - cea->db3 = 0; /* not used, all zeros */ - - if (params_channels(params) == 2) - cea->db4_ca = 0x0; - else if (params_channels(params) == 6) - cea->db4_ca = 0xb; - else - cea->db4_ca = 0x13; - - if (cea->db4_ca == 0x00) - cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED; - else - cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED; - - /* the expression is trivial but makes clear what we are doing */ - cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV); - - return ad->ops->audio_config(ad->dssdev, &ad->dss_audio); -} - -static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct hdmi_audio_data *ad = card_drvdata_substream(substream); - int err = 0; - - WARN_ON(ad->current_stream != substream); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - err = ad->ops->audio_start(ad->dssdev); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ad->ops->audio_stop(ad->dssdev); - break; - default: - err = -EINVAL; - } - return err; -} - -static void hdmi_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct hdmi_audio_data *ad = card_drvdata_substream(substream); - - WARN_ON(ad->current_stream != substream); - - ad->ops->audio_shutdown(ad->dssdev); - - mutex_lock(&ad->current_stream_lock); - ad->current_stream = NULL; - mutex_unlock(&ad->current_stream_lock); -} - -static const struct snd_soc_dai_ops hdmi_dai_ops = { - .startup = hdmi_dai_startup, - .hw_params = hdmi_dai_hw_params, - .trigger = hdmi_dai_trigger, - .shutdown = hdmi_dai_shutdown, -}; - -static const struct snd_soc_component_driver omap_hdmi_component = { - .name = "omapdss_hdmi", -}; - -static struct snd_soc_dai_driver omap5_hdmi_dai = { - .name = "omap5-hdmi-dai", - .playback = { - .channels_min = 2, - .channels_max = 8, - .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &hdmi_dai_ops, -}; - -static struct snd_soc_dai_driver omap4_hdmi_dai = { - .name = "omap4-hdmi-dai", - .playback = { - .channels_min = 2, - .channels_max = 8, - .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | - SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | - SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | - SNDRV_PCM_RATE_192000), - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, - }, - .ops = &hdmi_dai_ops, -}; - -static int omap_hdmi_audio_probe(struct platform_device *pdev) -{ - struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data; - struct device *dev = &pdev->dev; - struct hdmi_audio_data *ad; - struct snd_soc_dai_driver *dai_drv; - struct snd_soc_card *card; - int ret; - - if (!ha) { - dev_err(dev, "No platform data\n"); - return -EINVAL; - } - - ad = devm_kzalloc(dev, sizeof(*ad), GFP_KERNEL); - if (!ad) - return -ENOMEM; - ad->dssdev = ha->dev; - ad->ops = ha->ops; - ad->dma_data.addr = ha->audio_dma_addr; - ad->dma_data.filter_data = "audio_tx"; - ad->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - mutex_init(&ad->current_stream_lock); - - switch (ha->version) { - case 4: - dai_drv = &omap4_hdmi_dai; - break; - case 5: - dai_drv = &omap5_hdmi_dai; - break; - default: - return -EINVAL; - } - ret = devm_snd_soc_register_component(ad->dssdev, &omap_hdmi_component, - dai_drv, 1); - if (ret) - return ret; - - ret = sdma_pcm_platform_register(ad->dssdev, "audio_tx", NULL); - if (ret) - return ret; - - card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); - if (!card) - return -ENOMEM; - - card->name = devm_kasprintf(dev, GFP_KERNEL, - "HDMI %s", dev_name(ad->dssdev)); - if (!card->name) - return -ENOMEM; - - card->owner = THIS_MODULE; - card->dai_link = - devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); - if (!card->dai_link) - return -ENOMEM; - card->dai_link->name = card->name; - card->dai_link->stream_name = card->name; - card->dai_link->cpu_dai_name = dev_name(ad->dssdev); - card->dai_link->platform_name = dev_name(ad->dssdev); - card->dai_link->codec_name = "snd-soc-dummy"; - card->dai_link->codec_dai_name = "snd-soc-dummy-dai"; - card->num_links = 1; - card->dev = dev; - - ret = snd_soc_register_card(card); - if (ret) { - dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); - return ret; - } - - ad->card = card; - snd_soc_card_set_drvdata(card, ad); - - dev_set_drvdata(dev, ad); - - return 0; -} - -static int omap_hdmi_audio_remove(struct platform_device *pdev) -{ - struct hdmi_audio_data *ad = platform_get_drvdata(pdev); - - snd_soc_unregister_card(ad->card); - return 0; -} - -static struct platform_driver hdmi_audio_driver = { - .driver = { - .name = DRV_NAME, - }, - .probe = omap_hdmi_audio_probe, - .remove = omap_hdmi_audio_remove, -}; - -module_platform_driver(hdmi_audio_driver); - -MODULE_AUTHOR("Jyri Sarha "); -MODULE_DESCRIPTION("OMAP HDMI Audio Driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/omap/omap-mcbsp-priv.h b/sound/soc/omap/omap-mcbsp-priv.h deleted file mode 100644 index 7865cda4bf0a..000000000000 --- a/sound/soc/omap/omap-mcbsp-priv.h +++ /dev/null @@ -1,324 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * OMAP Multi-Channel Buffered Serial Port - * - * Contact: Jarkko Nikula - * Peter Ujfalusi - */ - -#ifndef __OMAP_MCBSP_PRIV_H__ -#define __OMAP_MCBSP_PRIV_H__ - -#include - -#ifdef CONFIG_ARCH_OMAP1 -#define mcbsp_omap1() 1 -#else -#define mcbsp_omap1() 0 -#endif - -/* McBSP register numbers. Register address offset = num * reg_step */ -enum { - /* Common registers */ - OMAP_MCBSP_REG_SPCR2 = 4, - OMAP_MCBSP_REG_SPCR1, - OMAP_MCBSP_REG_RCR2, - OMAP_MCBSP_REG_RCR1, - OMAP_MCBSP_REG_XCR2, - OMAP_MCBSP_REG_XCR1, - OMAP_MCBSP_REG_SRGR2, - OMAP_MCBSP_REG_SRGR1, - OMAP_MCBSP_REG_MCR2, - OMAP_MCBSP_REG_MCR1, - OMAP_MCBSP_REG_RCERA, - OMAP_MCBSP_REG_RCERB, - OMAP_MCBSP_REG_XCERA, - OMAP_MCBSP_REG_XCERB, - OMAP_MCBSP_REG_PCR0, - OMAP_MCBSP_REG_RCERC, - OMAP_MCBSP_REG_RCERD, - OMAP_MCBSP_REG_XCERC, - OMAP_MCBSP_REG_XCERD, - OMAP_MCBSP_REG_RCERE, - OMAP_MCBSP_REG_RCERF, - OMAP_MCBSP_REG_XCERE, - OMAP_MCBSP_REG_XCERF, - OMAP_MCBSP_REG_RCERG, - OMAP_MCBSP_REG_RCERH, - OMAP_MCBSP_REG_XCERG, - OMAP_MCBSP_REG_XCERH, - - /* OMAP1-OMAP2420 registers */ - OMAP_MCBSP_REG_DRR2 = 0, - OMAP_MCBSP_REG_DRR1, - OMAP_MCBSP_REG_DXR2, - OMAP_MCBSP_REG_DXR1, - - /* OMAP2430 and onwards */ - OMAP_MCBSP_REG_DRR = 0, - OMAP_MCBSP_REG_DXR = 2, - OMAP_MCBSP_REG_SYSCON = 35, - OMAP_MCBSP_REG_THRSH2, - OMAP_MCBSP_REG_THRSH1, - OMAP_MCBSP_REG_IRQST = 40, - OMAP_MCBSP_REG_IRQEN, - OMAP_MCBSP_REG_WAKEUPEN, - OMAP_MCBSP_REG_XCCR, - OMAP_MCBSP_REG_RCCR, - OMAP_MCBSP_REG_XBUFFSTAT, - OMAP_MCBSP_REG_RBUFFSTAT, - OMAP_MCBSP_REG_SSELCR, -}; - -/************************** McBSP SPCR1 bit definitions ***********************/ -#define RRST BIT(0) -#define RRDY BIT(1) -#define RFULL BIT(2) -#define RSYNC_ERR BIT(3) -#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ -#define ABIS BIT(6) -#define DXENA BIT(7) -#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */ -#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */ -#define ALB BIT(15) -#define DLB BIT(15) - -/************************** McBSP SPCR2 bit definitions ***********************/ -#define XRST BIT(0) -#define XRDY BIT(1) -#define XEMPTY BIT(2) -#define XSYNC_ERR BIT(3) -#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ -#define GRST BIT(6) -#define FRST BIT(7) -#define SOFT BIT(8) -#define FREE BIT(9) - -/************************** McBSP PCR bit definitions *************************/ -#define CLKRP BIT(0) -#define CLKXP BIT(1) -#define FSRP BIT(2) -#define FSXP BIT(3) -#define DR_STAT BIT(4) -#define DX_STAT BIT(5) -#define CLKS_STAT BIT(6) -#define SCLKME BIT(7) -#define CLKRM BIT(8) -#define CLKXM BIT(9) -#define FSRM BIT(10) -#define FSXM BIT(11) -#define RIOEN BIT(12) -#define XIOEN BIT(13) -#define IDLE_EN BIT(14) - -/************************** McBSP RCR1 bit definitions ************************/ -#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ - -/************************** McBSP XCR1 bit definitions ************************/ -#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ - -/*************************** McBSP RCR2 bit definitions ***********************/ -#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ -#define RFIG BIT(2) -#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ -#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ -#define RPHASE BIT(15) - -/*************************** McBSP XCR2 bit definitions ***********************/ -#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ -#define XFIG BIT(2) -#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ -#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ -#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ -#define XPHASE BIT(15) - -/************************* McBSP SRGR1 bit definitions ************************/ -#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */ -#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */ - -/************************* McBSP SRGR2 bit definitions ************************/ -#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */ -#define FSGM BIT(12) -#define CLKSM BIT(13) -#define CLKSP BIT(14) -#define GSYNC BIT(15) - -/************************* McBSP MCR1 bit definitions *************************/ -#define RMCM BIT(0) -#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ -#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ -#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ - -/************************* McBSP MCR2 bit definitions *************************/ -#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */ -#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ -#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ -#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ - -/*********************** McBSP XCCR bit definitions *************************/ -#define XDISABLE BIT(0) -#define XDMAEN BIT(3) -#define DILB BIT(5) -#define XFULL_CYCLE BIT(11) -#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */ -#define PPCONNECT BIT(14) -#define EXTCLKGATE BIT(15) - -/********************** McBSP RCCR bit definitions *************************/ -#define RDISABLE BIT(0) -#define RDMAEN BIT(3) -#define RFULL_CYCLE BIT(11) - -/********************** McBSP SYSCONFIG bit definitions ********************/ -#define SOFTRST BIT(1) -#define ENAWAKEUP BIT(2) -#define SIDLEMODE(value) (((value) & 0x3) << 3) -#define CLOCKACTIVITY(value) (((value) & 0x3) << 8) - -/********************** McBSP DMA operating modes **************************/ -#define MCBSP_DMA_MODE_ELEMENT 0 -#define MCBSP_DMA_MODE_THRESHOLD 1 - -/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/ -#define RSYNCERREN BIT(0) -#define RFSREN BIT(1) -#define REOFEN BIT(2) -#define RRDYEN BIT(3) -#define RUNDFLEN BIT(4) -#define ROVFLEN BIT(5) -#define XSYNCERREN BIT(7) -#define XFSXEN BIT(8) -#define XEOFEN BIT(9) -#define XRDYEN BIT(10) -#define XUNDFLEN BIT(11) -#define XOVFLEN BIT(12) -#define XEMPTYEOFEN BIT(14) - -/* Clock signal muxing options */ -#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */ -#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */ -#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */ -#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */ - -/* McBSP functional clock sources */ -#define MCBSP_CLKS_PRCM_SRC 0 -#define MCBSP_CLKS_PAD_SRC 1 - -/* we don't do multichannel for now */ -struct omap_mcbsp_reg_cfg { - u16 spcr2; - u16 spcr1; - u16 rcr2; - u16 rcr1; - u16 xcr2; - u16 xcr1; - u16 srgr2; - u16 srgr1; - u16 mcr2; - u16 mcr1; - u16 pcr0; - u16 rcerc; - u16 rcerd; - u16 xcerc; - u16 xcerd; - u16 rcere; - u16 rcerf; - u16 xcere; - u16 xcerf; - u16 rcerg; - u16 rcerh; - u16 xcerg; - u16 xcerh; - u16 xccr; - u16 rccr; -}; - -struct omap_mcbsp_st_data; - -struct omap_mcbsp { - struct device *dev; - struct clk *fclk; - spinlock_t lock; - unsigned long phys_base; - unsigned long phys_dma_base; - void __iomem *io_base; - u8 id; - /* - * Flags indicating is the bus already activated and configured by - * another substream - */ - int active; - int configured; - u8 free; - - int irq; - int rx_irq; - int tx_irq; - - /* Protect the field .free, while checking if the mcbsp is in use */ - struct omap_mcbsp_platform_data *pdata; - struct omap_mcbsp_st_data *st_data; - struct omap_mcbsp_reg_cfg cfg_regs; - struct snd_dmaengine_dai_dma_data dma_data[2]; - unsigned int dma_req[2]; - int dma_op_mode; - u16 max_tx_thres; - u16 max_rx_thres; - void *reg_cache; - int reg_cache_size; - - unsigned int fmt; - unsigned int in_freq; - unsigned int latency[2]; - int clk_div; - int wlen; - - struct pm_qos_request pm_qos_req; -}; - -static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; - writew_relaxed((u16)val, addr); - } else { - ((u32 *)mcbsp->reg_cache)[reg] = val; - writel_relaxed(val, addr); - } -} - -static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, - bool from_cache) -{ - void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; - - if (mcbsp->pdata->reg_size == 2) { - return !from_cache ? readw_relaxed(addr) : - ((u16 *)mcbsp->reg_cache)[reg]; - } else { - return !from_cache ? readl_relaxed(addr) : - ((u32 *)mcbsp->reg_cache)[reg]; - } -} - -#define MCBSP_READ(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) -#define MCBSP_WRITE(mcbsp, reg, val) \ - omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) -#define MCBSP_READ_CACHE(mcbsp, reg) \ - omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) - - -/* Sidetone specific API */ -int omap_mcbsp_st_init(struct platform_device *pdev); -void omap_mcbsp_st_cleanup(struct platform_device *pdev); - -int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp); -int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp); - -#endif /* __OMAP_MCBSP_PRIV_H__ */ diff --git a/sound/soc/omap/omap-mcbsp-st.c b/sound/soc/omap/omap-mcbsp-st.c deleted file mode 100644 index 1a3fe854e856..000000000000 --- a/sound/soc/omap/omap-mcbsp-st.c +++ /dev/null @@ -1,516 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * McBSP Sidetone support - * - * Copyright (C) 2004 Nokia Corporation - * Author: Samuel Ortiz - * - * Contact: Jarkko Nikula - * Peter Ujfalusi - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "omap-mcbsp.h" -#include "omap-mcbsp-priv.h" - -/* OMAP3 sidetone control registers */ -#define OMAP_ST_REG_REV 0x00 -#define OMAP_ST_REG_SYSCONFIG 0x10 -#define OMAP_ST_REG_IRQSTATUS 0x18 -#define OMAP_ST_REG_IRQENABLE 0x1C -#define OMAP_ST_REG_SGAINCR 0x24 -#define OMAP_ST_REG_SFIRCR 0x28 -#define OMAP_ST_REG_SSELCR 0x2C - -/********************** McBSP SSELCR bit definitions ***********************/ -#define SIDETONEEN BIT(10) - -/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ -#define ST_AUTOIDLE BIT(0) - -/********************** McBSP Sidetone SGAINCR bit definitions *************/ -#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ -#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ - -/********************** McBSP Sidetone SFIRCR bit definitions **************/ -#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ - -/********************** McBSP Sidetone SSELCR bit definitions **************/ -#define ST_SIDETONEEN BIT(0) -#define ST_COEFFWREN BIT(1) -#define ST_COEFFWRDONE BIT(2) - -struct omap_mcbsp_st_data { - void __iomem *io_base_st; - struct clk *mcbsp_iclk; - bool running; - bool enabled; - s16 taps[128]; /* Sidetone filter coefficients */ - int nr_taps; /* Number of filter coefficients in use */ - s16 ch0gain; - s16 ch1gain; -}; - -static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) -{ - writel_relaxed(val, mcbsp->st_data->io_base_st + reg); -} - -static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) -{ - return readl_relaxed(mcbsp->st_data->io_base_st + reg); -} - -#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) -#define MCBSP_ST_WRITE(mcbsp, reg, val) \ - omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) - -static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); - - /* Disable Sidetone clock auto-gating for normal operation */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); - - /* Enable McBSP Sidetone */ - w = MCBSP_READ(mcbsp, SSELCR); - MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); - - /* Enable Sidetone from Sidetone Core */ - w = MCBSP_ST_READ(mcbsp, SSELCR); - MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); -} - -static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) -{ - unsigned int w; - - w = MCBSP_ST_READ(mcbsp, SSELCR); - MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); - - w = MCBSP_READ(mcbsp, SSELCR); - MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); - - /* Enable Sidetone clock auto-gating to reduce power consumption */ - w = MCBSP_ST_READ(mcbsp, SYSCONFIG); - MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); - - if (mcbsp->pdata->force_ick_on) - mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); -} - -static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) -{ - u16 val, i; - - val = MCBSP_ST_READ(mcbsp, SSELCR); - - if (val & ST_COEFFWREN) - MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); - - MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); - - for (i = 0; i < 128; i++) - MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); - - i = 0; - - val = MCBSP_ST_READ(mcbsp, SSELCR); - while (!(val & ST_COEFFWRDONE) && (++i < 1000)) - val = MCBSP_ST_READ(mcbsp, SSELCR); - - MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); - - if (i == 1000) - dev_err(mcbsp->dev, "McBSP FIR load error!\n"); -} - -static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) -{ - u16 w; - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - w = MCBSP_ST_READ(mcbsp, SSELCR); - - MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | - ST_CH1GAIN(st_data->ch1gain)); -} - -static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, - s16 chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENOENT; - - spin_lock_irq(&mcbsp->lock); - if (channel == 0) - st_data->ch0gain = chgain; - else if (channel == 1) - st_data->ch1gain = chgain; - else - ret = -EINVAL; - - if (st_data->enabled) - omap_mcbsp_st_chgain(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, - s16 *chgain) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENOENT; - - spin_lock_irq(&mcbsp->lock); - if (channel == 0) - *chgain = st_data->ch0gain; - else if (channel == 1) - *chgain = st_data->ch1gain; - else - ret = -EINVAL; - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - st_data->enabled = 1; - omap_mcbsp_st_start(mcbsp); - spin_unlock_irq(&mcbsp->lock); - - return 0; -} - -static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int ret = 0; - - if (!st_data) - return -ENODEV; - - spin_lock_irq(&mcbsp->lock); - omap_mcbsp_st_stop(mcbsp); - st_data->enabled = 0; - spin_unlock_irq(&mcbsp->lock); - - return ret; -} - -static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (!st_data) - return -ENODEV; - - return st_data->enabled; -} - -static ssize_t st_taps_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - ssize_t status = 0; - int i; - - spin_lock_irq(&mcbsp->lock); - for (i = 0; i < st_data->nr_taps; i++) - status += sprintf(&buf[status], (i ? ", %d" : "%d"), - st_data->taps[i]); - if (i) - status += sprintf(&buf[status], "\n"); - spin_unlock_irq(&mcbsp->lock); - - return status; -} - -static ssize_t st_taps_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t size) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - int val, tmp, status, i = 0; - - spin_lock_irq(&mcbsp->lock); - memset(st_data->taps, 0, sizeof(st_data->taps)); - st_data->nr_taps = 0; - - do { - status = sscanf(buf, "%d%n", &val, &tmp); - if (status < 0 || status == 0) { - size = -EINVAL; - goto out; - } - if (val < -32768 || val > 32767) { - size = -EINVAL; - goto out; - } - st_data->taps[i++] = val; - buf += tmp; - if (*buf != ',') - break; - buf++; - } while (1); - - st_data->nr_taps = i; - -out: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(st_taps); - -static const struct attribute *sidetone_attrs[] = { - &dev_attr_st_taps.attr, - NULL, -}; - -static const struct attribute_group sidetone_attr_group = { - .attrs = (struct attribute **)sidetone_attrs, -}; - -int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->enabled && !st_data->running) { - omap_mcbsp_st_fir_write(mcbsp, st_data->taps); - omap_mcbsp_st_chgain(mcbsp); - - if (!mcbsp->free) { - omap_mcbsp_st_on(mcbsp); - st_data->running = 1; - } - } - - return 0; -} - -int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) -{ - struct omap_mcbsp_st_data *st_data = mcbsp->st_data; - - if (st_data->running) { - if (!mcbsp->free) { - omap_mcbsp_st_off(mcbsp); - st_data->running = 0; - } - } - - return 0; -} - -int omap_mcbsp_st_init(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - struct omap_mcbsp_st_data *st_data; - struct resource *res; - int ret; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); - if (!res) - return 0; - - st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); - if (!st_data) - return -ENOMEM; - - st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); - if (IS_ERR(st_data->mcbsp_iclk)) { - dev_warn(mcbsp->dev, - "Failed to get ick, sidetone might be broken\n"); - st_data->mcbsp_iclk = NULL; - } - - st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, - resource_size(res)); - if (!st_data->io_base_st) - return -ENOMEM; - - ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); - if (ret) - return ret; - - mcbsp->st_data = st_data; - - return 0; -} - -void omap_mcbsp_st_cleanup(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - - if (mcbsp->st_data) { - sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); - clk_put(mcbsp->st_data->mcbsp_iclk); - } -} - -static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int max = mc->max; - int min = mc->min; - - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 1; - uinfo->value.integer.min = min; - uinfo->value.integer.max = max; - return 0; -} - -#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ -static int \ -omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - struct soc_mixer_control *mc = \ - (struct soc_mixer_control *)kc->private_value; \ - int max = mc->max; \ - int min = mc->min; \ - int val = uc->value.integer.value[0]; \ - \ - if (val < min || val > max) \ - return -EINVAL; \ - \ - /* OMAP McBSP implementation uses index values 0..4 */ \ - return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ -} \ - \ -static int \ -omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ - struct snd_ctl_elem_value *uc) \ -{ \ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ - s16 chgain; \ - \ - if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ - return -EAGAIN; \ - \ - uc->value.integer.value[0] = chgain; \ - return 0; \ -} - -OMAP_MCBSP_ST_CHANNEL_VOLUME(0) -OMAP_MCBSP_ST_CHANNEL_VOLUME(1) - -static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - u8 value = ucontrol->value.integer.value[0]; - - if (value == omap_mcbsp_st_is_enabled(mcbsp)) - return 0; - - if (value) - omap_mcbsp_st_enable(mcbsp); - else - omap_mcbsp_st_disable(mcbsp); - - return 1; -} - -static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); - return 0; -} - -#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ - xhandler_get, xhandler_put) \ -{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ - .info = omap_mcbsp_st_info_volsw, \ - .get = xhandler_get, .put = xhandler_put, \ - .private_value = (unsigned long)&(struct soc_mixer_control) \ - {.min = xmin, .max = xmax} } - -#define OMAP_MCBSP_ST_CONTROLS(port) \ -static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ -SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ - omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch0_volume, \ - omap_mcbsp_set_st_ch0_volume), \ -OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ - -32768, 32767, \ - omap_mcbsp_get_st_ch1_volume, \ - omap_mcbsp_set_st_ch1_volume), \ -} - -OMAP_MCBSP_ST_CONTROLS(2); -OMAP_MCBSP_ST_CONTROLS(3); - -int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) -{ - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - if (!mcbsp->st_data) { - dev_warn(mcbsp->dev, "No sidetone data for port\n"); - return 0; - } - - switch (port_id) { - case 2: /* McBSP 2 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp2_st_controls, - ARRAY_SIZE(omap_mcbsp2_st_controls)); - case 3: /* McBSP 3 */ - return snd_soc_add_dai_controls(cpu_dai, - omap_mcbsp3_st_controls, - ARRAY_SIZE(omap_mcbsp3_st_controls)); - default: - dev_err(mcbsp->dev, "Port %d not supported\n", port_id); - break; - } - - return -EINVAL; -} -EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c deleted file mode 100644 index a395598f1f20..000000000000 --- a/sound/soc/omap/omap-mcbsp.c +++ /dev/null @@ -1,1479 +0,0 @@ -/* - * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port - * - * Copyright (C) 2008 Nokia Corporation - * - * Contact: Jarkko Nikula - * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "omap-mcbsp-priv.h" -#include "omap-mcbsp.h" -#include "sdma-pcm.h" - -#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) - -enum { - OMAP_MCBSP_WORD_8 = 0, - OMAP_MCBSP_WORD_12, - OMAP_MCBSP_WORD_16, - OMAP_MCBSP_WORD_20, - OMAP_MCBSP_WORD_24, - OMAP_MCBSP_WORD_32, -}; - -static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) -{ - dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); - dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); - dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); - dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); - dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); - dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); - dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); - dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); - dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); - dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); - dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); - dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); - dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); - dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); - dev_dbg(mcbsp->dev, "***********************\n"); -} - -static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) -{ - struct clk *fck_src; - const char *src; - int r; - - if (fck_src_id == MCBSP_CLKS_PAD_SRC) - src = "pad_fck"; - else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) - src = "prcm_fck"; - else - return -EINVAL; - - fck_src = clk_get(mcbsp->dev, src); - if (IS_ERR(fck_src)) { - dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); - return -EINVAL; - } - - pm_runtime_put_sync(mcbsp->dev); - - r = clk_set_parent(mcbsp->fclk, fck_src); - if (r) { - dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", - src); - clk_put(fck_src); - return r; - } - - pm_runtime_get_sync(mcbsp->dev); - - clk_put(fck_src); - - return 0; -} - -static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst; - - irqst = MCBSP_READ(mcbsp, IRQST); - dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); - - if (irqst & RSYNCERREN) - dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); - if (irqst & RFSREN) - dev_dbg(mcbsp->dev, "RX Frame Sync\n"); - if (irqst & REOFEN) - dev_dbg(mcbsp->dev, "RX End Of Frame\n"); - if (irqst & RRDYEN) - dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); - if (irqst & RUNDFLEN) - dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); - if (irqst & ROVFLEN) - dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); - - if (irqst & XSYNCERREN) - dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); - if (irqst & XFSXEN) - dev_dbg(mcbsp->dev, "TX Frame Sync\n"); - if (irqst & XEOFEN) - dev_dbg(mcbsp->dev, "TX End Of Frame\n"); - if (irqst & XRDYEN) - dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); - if (irqst & XUNDFLEN) - dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); - if (irqst & XOVFLEN) - dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); - if (irqst & XEMPTYEOFEN) - dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); - - MCBSP_WRITE(mcbsp, IRQST, irqst); - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst_spcr2; - - irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); - dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); - - if (irqst_spcr2 & XSYNC_ERR) { - dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", - irqst_spcr2); - /* Writing zero to XSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); - } - - return IRQ_HANDLED; -} - -static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) -{ - struct omap_mcbsp *mcbsp = data; - u16 irqst_spcr1; - - irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); - dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); - - if (irqst_spcr1 & RSYNC_ERR) { - dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", - irqst_spcr1); - /* Writing zero to RSYNC_ERR clears the IRQ */ - MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); - } - - return IRQ_HANDLED; -} - -/* - * omap_mcbsp_config simply write a config to the - * appropriate McBSP. - * You either call this function or set the McBSP registers - * by yourself before calling omap_mcbsp_start(). - */ -static void omap_mcbsp_config(struct omap_mcbsp *mcbsp, - const struct omap_mcbsp_reg_cfg *config) -{ - dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", - mcbsp->id, mcbsp->phys_base); - - /* We write the given config */ - MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); - MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); - MCBSP_WRITE(mcbsp, RCR2, config->rcr2); - MCBSP_WRITE(mcbsp, RCR1, config->rcr1); - MCBSP_WRITE(mcbsp, XCR2, config->xcr2); - MCBSP_WRITE(mcbsp, XCR1, config->xcr1); - MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); - MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); - MCBSP_WRITE(mcbsp, MCR2, config->mcr2); - MCBSP_WRITE(mcbsp, MCR1, config->mcr1); - MCBSP_WRITE(mcbsp, PCR0, config->pcr0); - if (mcbsp->pdata->has_ccr) { - MCBSP_WRITE(mcbsp, XCCR, config->xccr); - MCBSP_WRITE(mcbsp, RCCR, config->rccr); - } - /* Enable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); - - /* Enable TX/RX sync error interrupts by default */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | - RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); -} - -/** - * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register - * @mcbsp: omap_mcbsp struct for the McBSP instance - * @stream: Stream direction (playback/capture) - * - * Returns the address of mcbsp data transmit register or data receive register - * to be used by DMA for transferring/receiving data - */ -static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, - unsigned int stream) -{ - int data_reg; - - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (mcbsp->pdata->reg_size == 2) - data_reg = OMAP_MCBSP_REG_DXR1; - else - data_reg = OMAP_MCBSP_REG_DXR; - } else { - if (mcbsp->pdata->reg_size == 2) - data_reg = OMAP_MCBSP_REG_DRR1; - else - data_reg = OMAP_MCBSP_REG_DRR; - } - - return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; -} - -/* - * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. - * The threshold parameter is 1 based, and it is converted (threshold - 1) - * for the THRSH2 register. - */ -static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (threshold && threshold <= mcbsp->max_tx_thres) - MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); -} - -/* - * omap_mcbsp_set_rx_threshold configures the receive threshold in words. - * The threshold parameter is 1 based, and it is converted (threshold - 1) - * for the THRSH1 register. - */ -static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) -{ - if (threshold && threshold <= mcbsp->max_rx_thres) - MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); -} - -/* - * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO - */ -static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat; - - /* Returns the number of free locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); - - /* Number of slots are different in McBSP ports */ - return mcbsp->pdata->buffer_size - buffstat; -} - -/* - * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO - * to reach the threshold value (when the DMA will be triggered to read it) - */ -static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) -{ - u16 buffstat, threshold; - - /* Returns the number of used locations in the buffer */ - buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); - /* RX threshold */ - threshold = MCBSP_READ(mcbsp, THRSH1); - - /* Return the number of location till we reach the threshold limit */ - if (threshold <= buffstat) - return 0; - else - return threshold - buffstat; -} - -static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - int err; - - reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); - if (!reg_cache) - return -ENOMEM; - - spin_lock(&mcbsp->lock); - if (!mcbsp->free) { - dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); - err = -EBUSY; - goto err_kfree; - } - - mcbsp->free = false; - mcbsp->reg_cache = reg_cache; - spin_unlock(&mcbsp->lock); - - if(mcbsp->pdata->ops && mcbsp->pdata->ops->request) - mcbsp->pdata->ops->request(mcbsp->id - 1); - - /* - * Make sure that transmitter, receiver and sample-rate generator are - * not running before activating IRQs. - */ - MCBSP_WRITE(mcbsp, SPCR1, 0); - MCBSP_WRITE(mcbsp, SPCR2, 0); - - if (mcbsp->irq) { - err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, - "McBSP", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request IRQ\n"); - goto err_clk_disable; - } - } else { - err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, - "McBSP TX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); - goto err_clk_disable; - } - - err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP RX", (void *)mcbsp); - if (err != 0) { - dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); - goto err_free_irq; - } - } - - return 0; -err_free_irq: - free_irq(mcbsp->tx_irq, (void *)mcbsp); -err_clk_disable: - if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - spin_lock(&mcbsp->lock); - mcbsp->free = true; - mcbsp->reg_cache = NULL; -err_kfree: - spin_unlock(&mcbsp->lock); - kfree(reg_cache); - - return err; -} - -static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) -{ - void *reg_cache; - - if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id - 1); - - /* Disable wakeup behavior */ - if (mcbsp->pdata->has_wakeup) - MCBSP_WRITE(mcbsp, WAKEUPEN, 0); - - /* Disable interrupt requests */ - if (mcbsp->irq) - MCBSP_WRITE(mcbsp, IRQEN, 0); - - if (mcbsp->irq) { - free_irq(mcbsp->irq, (void *)mcbsp); - } else { - free_irq(mcbsp->rx_irq, (void *)mcbsp); - free_irq(mcbsp->tx_irq, (void *)mcbsp); - } - - reg_cache = mcbsp->reg_cache; - - /* - * Select CLKS source from internal source unconditionally before - * marking the McBSP port as free. - * If the external clock source via MCBSP_CLKS pin has been selected the - * system will refuse to enter idle if the CLKS pin source is not reset - * back to internal source. - */ - if (!mcbsp_omap1()) - omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); - - spin_lock(&mcbsp->lock); - if (mcbsp->free) - dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); - else - mcbsp->free = true; - mcbsp->reg_cache = NULL; - spin_unlock(&mcbsp->lock); - - kfree(reg_cache); -} - -/* - * Here we start the McBSP, by enabling transmitter, receiver or both. - * If no transmitter or receiver is active prior calling, then sample-rate - * generator and frame sync are started. - */ -static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) -{ - int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); - int rx = !tx; - int enable_srg = 0; - u16 w; - - if (mcbsp->st_data) - omap_mcbsp_st_start(mcbsp); - - /* Only enable SRG, if McBSP is master */ - w = MCBSP_READ_CACHE(mcbsp, PCR0); - if (w & (FSXM | FSRM | CLKXM | CLKRM)) - enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (enable_srg) { - /* Start the sample generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); - } - - /* Enable transmitter and receiver */ - tx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | tx); - - rx &= 1; - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w | rx); - - /* - * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec - * REVISIT: 100us may give enough time for two CLKSRG, however - * due to some unknown PM related, clock gating etc. reason it - * is now at 500us. - */ - udelay(500); - - if (enable_srg) { - /* Start frame sync */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); - } - - if (mcbsp->pdata->has_ccr) { - /* Release the transmitter and receiver */ - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w &= ~(tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w &= ~(rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - - /* Dump McBSP Regs */ - omap_mcbsp_dump_reg(mcbsp); -} - -static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) -{ - int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); - int rx = !tx; - int idle; - u16 w; - - /* Reset transmitter */ - tx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, XCCR); - w |= (tx ? XDISABLE : 0); - MCBSP_WRITE(mcbsp, XCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); - - /* Reset receiver */ - rx &= 1; - if (mcbsp->pdata->has_ccr) { - w = MCBSP_READ_CACHE(mcbsp, RCCR); - w |= (rx ? RDISABLE : 0); - MCBSP_WRITE(mcbsp, RCCR, w); - } - w = MCBSP_READ_CACHE(mcbsp, SPCR1); - MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); - - idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | - MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); - - if (idle) { - /* Reset the sample rate generator */ - w = MCBSP_READ_CACHE(mcbsp, SPCR2); - MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); - } - - if (mcbsp->st_data) - omap_mcbsp_st_stop(mcbsp); -} - -#define max_thres(m) (mcbsp->pdata->buffer_size) -#define valid_threshold(m, val) ((val) <= max_thres(m)) -#define THRESHOLD_PROP_BUILDER(prop) \ -static ssize_t prop##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ - \ - return sprintf(buf, "%u\n", mcbsp->prop); \ -} \ - \ -static ssize_t prop##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t size) \ -{ \ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ - unsigned long val; \ - int status; \ - \ - status = kstrtoul(buf, 0, &val); \ - if (status) \ - return status; \ - \ - if (!valid_threshold(mcbsp, val)) \ - return -EDOM; \ - \ - mcbsp->prop = val; \ - return size; \ -} \ - \ -static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store) - -THRESHOLD_PROP_BUILDER(max_tx_thres); -THRESHOLD_PROP_BUILDER(max_rx_thres); - -static const char * const dma_op_modes[] = { - "element", "threshold", -}; - -static ssize_t dma_op_mode_show(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - int dma_op_mode, i = 0; - ssize_t len = 0; - const char * const *s; - - dma_op_mode = mcbsp->dma_op_mode; - - for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { - if (dma_op_mode == i) - len += sprintf(buf + len, "[%s] ", *s); - else - len += sprintf(buf + len, "%s ", *s); - } - len += sprintf(buf + len, "\n"); - - return len; -} - -static ssize_t dma_op_mode_store(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t size) -{ - struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); - int i; - - i = sysfs_match_string(dma_op_modes, buf); - if (i < 0) - return i; - - spin_lock_irq(&mcbsp->lock); - if (!mcbsp->free) { - size = -EBUSY; - goto unlock; - } - mcbsp->dma_op_mode = i; - -unlock: - spin_unlock_irq(&mcbsp->lock); - - return size; -} - -static DEVICE_ATTR_RW(dma_op_mode); - -static const struct attribute *additional_attrs[] = { - &dev_attr_max_tx_thres.attr, - &dev_attr_max_rx_thres.attr, - &dev_attr_dma_op_mode.attr, - NULL, -}; - -static const struct attribute_group additional_attr_group = { - .attrs = (struct attribute **)additional_attrs, -}; - -/* - * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. - * 730 has only 2 McBSP, and both of them are MPU peripherals. - */ -static int omap_mcbsp_init(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - struct resource *res; - int ret = 0; - - spin_lock_init(&mcbsp->lock); - mcbsp->free = true; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - if (!res) - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mcbsp->io_base)) - return PTR_ERR(mcbsp->io_base); - - mcbsp->phys_base = res->start; - mcbsp->reg_cache_size = resource_size(res); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (!res) - mcbsp->phys_dma_base = mcbsp->phys_base; - else - mcbsp->phys_dma_base = res->start; - - /* - * OMAP1, 2 uses two interrupt lines: TX, RX - * OMAP2430, OMAP3 SoC have combined IRQ line as well. - * OMAP4 and newer SoC only have the combined IRQ line. - * Use the combined IRQ if available since it gives better debugging - * possibilities. - */ - mcbsp->irq = platform_get_irq_byname(pdev, "common"); - if (mcbsp->irq == -ENXIO) { - mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); - - if (mcbsp->tx_irq == -ENXIO) { - mcbsp->irq = platform_get_irq(pdev, 0); - mcbsp->tx_irq = 0; - } else { - mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); - mcbsp->irq = 0; - } - } - - if (!pdev->dev.of_node) { - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); - if (!res) { - dev_err(&pdev->dev, "invalid tx DMA channel\n"); - return -ENODEV; - } - mcbsp->dma_req[0] = res->start; - mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); - if (!res) { - dev_err(&pdev->dev, "invalid rx DMA channel\n"); - return -ENODEV; - } - mcbsp->dma_req[1] = res->start; - mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; - } else { - mcbsp->dma_data[0].filter_data = "tx"; - mcbsp->dma_data[1].filter_data = "rx"; - } - - mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, - SNDRV_PCM_STREAM_PLAYBACK); - mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, - SNDRV_PCM_STREAM_CAPTURE); - - mcbsp->fclk = clk_get(&pdev->dev, "fck"); - if (IS_ERR(mcbsp->fclk)) { - ret = PTR_ERR(mcbsp->fclk); - dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); - return ret; - } - - mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; - if (mcbsp->pdata->buffer_size) { - /* - * Initially configure the maximum thresholds to a safe value. - * The McBSP FIFO usage with these values should not go under - * 16 locations. - * If the whole FIFO without safety buffer is used, than there - * is a possibility that the DMA will be not able to push the - * new data on time, causing channel shifts in runtime. - */ - mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; - mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; - - ret = sysfs_create_group(&mcbsp->dev->kobj, - &additional_attr_group); - if (ret) { - dev_err(mcbsp->dev, - "Unable to create additional controls\n"); - goto err_thres; - } - } - - ret = omap_mcbsp_st_init(pdev); - if (ret) - goto err_st; - - return 0; - -err_st: - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); -err_thres: - clk_put(mcbsp->fclk); - return ret; -} - -/* - * Stream DMA parameters. DMA request line and port address are set runtime - * since they are different between OMAP1 and later OMAPs - */ -static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, - unsigned int packet_size) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - int words; - - /* No need to proceed further if McBSP does not have FIFO */ - if (mcbsp->pdata->buffer_size == 0) - return; - - /* - * Configure McBSP threshold based on either: - * packet_size, when the sDMA is in packet mode, or based on the - * period size in THRESHOLD mode, otherwise use McBSP threshold = 1 - * for mono streams. - */ - if (packet_size) - words = packet_size; - else - words = 1; - - /* Configure McBSP internal buffer usage */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - omap_mcbsp_set_tx_threshold(mcbsp, words); - else - omap_mcbsp_set_rx_threshold(mcbsp, words); -} - -static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, - struct snd_pcm_hw_rule *rule) -{ - struct snd_interval *buffer_size = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE); - struct snd_interval *channels = hw_param_interval(params, - SNDRV_PCM_HW_PARAM_CHANNELS); - struct omap_mcbsp *mcbsp = rule->private; - struct snd_interval frames; - int size; - - snd_interval_any(&frames); - size = mcbsp->pdata->buffer_size; - - frames.min = size / channels->min; - frames.integer = 1; - return snd_interval_refine(buffer_size, &frames); -} - -static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - int err = 0; - - if (!cpu_dai->active) - err = omap_mcbsp_request(mcbsp); - - /* - * OMAP3 McBSP FIFO is word structured. - * McBSP2 has 1024 + 256 = 1280 word long buffer, - * McBSP1,3,4,5 has 128 word long buffer - * This means that the size of the FIFO depends on the sample format. - * For example on McBSP3: - * 16bit samples: size is 128 * 2 = 256 bytes - * 32bit samples: size is 128 * 4 = 512 bytes - * It is simpler to place constraint for buffer and period based on - * channels. - * McBSP3 as example again (16 or 32 bit samples): - * 1 channel (mono): size is 128 frames (128 words) - * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) - * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) - */ - if (mcbsp->pdata->buffer_size) { - /* - * Rule for the buffer size. We should not allow - * smaller buffer than the FIFO size to avoid underruns. - * This applies only for the playback stream. - */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - snd_pcm_hw_rule_add(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, - omap_mcbsp_hwrule_min_buffersize, - mcbsp, - SNDRV_PCM_HW_PARAM_CHANNELS, -1); - - /* Make sure, that the period size is always even */ - snd_pcm_hw_constraint_step(substream->runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); - } - - return err; -} - -static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - - if (mcbsp->latency[stream2]) - pm_qos_update_request(&mcbsp->pm_qos_req, - mcbsp->latency[stream2]); - else if (mcbsp->latency[stream1]) - pm_qos_remove_request(&mcbsp->pm_qos_req); - - mcbsp->latency[stream1] = 0; - - if (!cpu_dai->active) { - omap_mcbsp_free(mcbsp); - mcbsp->configured = 0; - } -} - -static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req; - int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - int latency = mcbsp->latency[stream2]; - - /* Prevent omap hardware from hitting off between FIFO fills */ - if (!latency || mcbsp->latency[stream1] < latency) - latency = mcbsp->latency[stream1]; - - if (pm_qos_request_active(pm_qos_req)) - pm_qos_update_request(pm_qos_req, latency); - else if (latency) - pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); - - return 0; -} - -static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *cpu_dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - mcbsp->active++; - omap_mcbsp_start(mcbsp, substream->stream); - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - omap_mcbsp_stop(mcbsp, substream->stream); - mcbsp->active--; - break; - default: - return -EINVAL; - } - - return 0; -} - -static snd_pcm_sframes_t omap_mcbsp_dai_delay( - struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - u16 fifo_use; - snd_pcm_sframes_t delay; - - /* No need to proceed further if McBSP does not have FIFO */ - if (mcbsp->pdata->buffer_size == 0) - return 0; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - fifo_use = omap_mcbsp_get_tx_delay(mcbsp); - else - fifo_use = omap_mcbsp_get_rx_delay(mcbsp); - - /* - * Divide the used locations with the channel count to get the - * FIFO usage in samples (don't care about partial samples in the - * buffer). - */ - delay = fifo_use / substream->runtime->channels; - - return delay; -} - -static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; - struct snd_dmaengine_dai_dma_data *dma_data; - int wlen, channels, wpf; - int pkt_size = 0; - unsigned int format, div, framesize, master; - unsigned int buffer_size = mcbsp->pdata->buffer_size; - - dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); - channels = params_channels(params); - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - wlen = 16; - break; - case SNDRV_PCM_FORMAT_S32_LE: - wlen = 32; - break; - default: - return -EINVAL; - } - if (buffer_size) { - int latency; - - if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { - int period_words, max_thrsh; - int divider = 0; - - period_words = params_period_bytes(params) / (wlen / 8); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - max_thrsh = mcbsp->max_tx_thres; - else - max_thrsh = mcbsp->max_rx_thres; - /* - * Use sDMA packet mode if McBSP is in threshold mode: - * If period words less than the FIFO size the packet - * size is set to the number of period words, otherwise - * Look for the biggest threshold value which divides - * the period size evenly. - */ - divider = period_words / max_thrsh; - if (period_words % max_thrsh) - divider++; - while (period_words % divider && - divider < period_words) - divider++; - if (divider == period_words) - return -EINVAL; - - pkt_size = period_words / divider; - } else if (channels > 1) { - /* Use packet mode for non mono streams */ - pkt_size = channels; - } - - latency = (buffer_size - pkt_size) / channels; - latency = latency * USEC_PER_SEC / - (params->rate_num / params->rate_den); - mcbsp->latency[substream->stream] = latency; - - omap_mcbsp_set_threshold(substream, pkt_size); - } - - dma_data->maxburst = pkt_size; - - if (mcbsp->configured) { - /* McBSP already configured by another stream */ - return 0; - } - - regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7)); - regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); - regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); - regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); - format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; - wpf = channels; - if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || - format == SND_SOC_DAIFMT_LEFT_J)) { - /* Use dual-phase frames */ - regs->rcr2 |= RPHASE; - regs->xcr2 |= XPHASE; - /* Set 1 word per (McBSP) frame for phase1 and phase2 */ - wpf--; - regs->rcr2 |= RFRLEN2(wpf - 1); - regs->xcr2 |= XFRLEN2(wpf - 1); - } - - regs->rcr1 |= RFRLEN1(wpf - 1); - regs->xcr1 |= XFRLEN1(wpf - 1); - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - /* Set word lengths */ - regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); - regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); - regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); - regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); - break; - case SNDRV_PCM_FORMAT_S32_LE: - /* Set word lengths */ - regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); - regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); - regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); - regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); - break; - default: - /* Unsupported PCM format */ - return -EINVAL; - } - - /* In McBSP master modes, FRAME (i.e. sample rate) is generated - * by _counting_ BCLKs. Calculate frame size in BCLKs */ - master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK; - if (master == SND_SOC_DAIFMT_CBS_CFS) { - div = mcbsp->clk_div ? mcbsp->clk_div : 1; - framesize = (mcbsp->in_freq / div) / params_rate(params); - - if (framesize < wlen * channels) { - printk(KERN_ERR "%s: not enough bandwidth for desired rate and " - "channels\n", __func__); - return -EINVAL; - } - } else - framesize = wlen * channels; - - /* Set FS period and length in terms of bit clock periods */ - regs->srgr2 &= ~FPER(0xfff); - regs->srgr1 &= ~FWID(0xff); - switch (format) { - case SND_SOC_DAIFMT_I2S: - case SND_SOC_DAIFMT_LEFT_J: - regs->srgr2 |= FPER(framesize - 1); - regs->srgr1 |= FWID((framesize >> 1) - 1); - break; - case SND_SOC_DAIFMT_DSP_A: - case SND_SOC_DAIFMT_DSP_B: - regs->srgr2 |= FPER(framesize - 1); - regs->srgr1 |= FWID(0); - break; - } - - omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs); - mcbsp->wlen = wlen; - mcbsp->configured = 1; - - return 0; -} - -/* - * This must be called before _set_clkdiv and _set_sysclk since McBSP register - * cache is initialized here - */ -static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, - unsigned int fmt) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; - bool inv_fs = false; - - if (mcbsp->configured) - return 0; - - mcbsp->fmt = fmt; - memset(regs, 0, sizeof(*regs)); - /* Generic McBSP register settings */ - regs->spcr2 |= XINTM(3) | FREE; - regs->spcr1 |= RINTM(3); - /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */ - if (!mcbsp->pdata->has_ccr) { - regs->rcr2 |= RFIG; - regs->xcr2 |= XFIG; - } - - /* Configure XCCR/RCCR only for revisions which have ccr registers */ - if (mcbsp->pdata->has_ccr) { - regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE; - regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE; - } - - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - /* 1-bit data delay */ - regs->rcr2 |= RDATDLY(1); - regs->xcr2 |= XDATDLY(1); - break; - case SND_SOC_DAIFMT_LEFT_J: - /* 0-bit data delay */ - regs->rcr2 |= RDATDLY(0); - regs->xcr2 |= XDATDLY(0); - regs->spcr1 |= RJUST(2); - /* Invert FS polarity configuration */ - inv_fs = true; - break; - case SND_SOC_DAIFMT_DSP_A: - /* 1-bit data delay */ - regs->rcr2 |= RDATDLY(1); - regs->xcr2 |= XDATDLY(1); - /* Invert FS polarity configuration */ - inv_fs = true; - break; - case SND_SOC_DAIFMT_DSP_B: - /* 0-bit data delay */ - regs->rcr2 |= RDATDLY(0); - regs->xcr2 |= XDATDLY(0); - /* Invert FS polarity configuration */ - inv_fs = true; - break; - default: - /* Unsupported data format */ - return -EINVAL; - } - - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBS_CFS: - /* McBSP master. Set FS and bit clocks as outputs */ - regs->pcr0 |= FSXM | FSRM | - CLKXM | CLKRM; - /* Sample rate generator drives the FS */ - regs->srgr2 |= FSGM; - break; - case SND_SOC_DAIFMT_CBM_CFS: - /* McBSP slave. FS clock as output */ - regs->srgr2 |= FSGM; - regs->pcr0 |= FSXM | FSRM; - break; - case SND_SOC_DAIFMT_CBM_CFM: - /* McBSP slave */ - break; - default: - /* Unsupported master/slave configuration */ - return -EINVAL; - } - - /* Set bit clock (CLKX/CLKR) and FS polarities */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_NF: - /* - * Normal BCLK + FS. - * FS active low. TX data driven on falling edge of bit clock - * and RX data sampled on rising edge of bit clock. - */ - regs->pcr0 |= FSXP | FSRP | - CLKXP | CLKRP; - break; - case SND_SOC_DAIFMT_NB_IF: - regs->pcr0 |= CLKXP | CLKRP; - break; - case SND_SOC_DAIFMT_IB_NF: - regs->pcr0 |= FSXP | FSRP; - break; - case SND_SOC_DAIFMT_IB_IF: - break; - default: - return -EINVAL; - } - if (inv_fs == true) - regs->pcr0 ^= FSXP | FSRP; - - return 0; -} - -static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; - - if (div_id != OMAP_MCBSP_CLKGDV) - return -ENODEV; - - mcbsp->clk_div = div; - regs->srgr1 &= ~CLKGDV(0xff); - regs->srgr1 |= CLKGDV(div - 1); - - return 0; -} - -static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, - int dir) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); - struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; - int err = 0; - - if (mcbsp->active) { - if (freq == mcbsp->in_freq) - return 0; - else - return -EBUSY; - } - - mcbsp->in_freq = freq; - regs->srgr2 &= ~CLKSM; - regs->pcr0 &= ~SCLKME; - - switch (clk_id) { - case OMAP_MCBSP_SYSCLK_CLK: - regs->srgr2 |= CLKSM; - break; - case OMAP_MCBSP_SYSCLK_CLKS_FCLK: - if (mcbsp_omap1()) { - err = -EINVAL; - break; - } - err = omap2_mcbsp_set_clks_src(mcbsp, - MCBSP_CLKS_PRCM_SRC); - break; - case OMAP_MCBSP_SYSCLK_CLKS_EXT: - if (mcbsp_omap1()) { - err = 0; - break; - } - err = omap2_mcbsp_set_clks_src(mcbsp, - MCBSP_CLKS_PAD_SRC); - break; - - case OMAP_MCBSP_SYSCLK_CLKX_EXT: - regs->srgr2 |= CLKSM; - regs->pcr0 |= SCLKME; - /* - * If McBSP is master but yet the CLKX/CLKR pin drives the SRG, - * disable output on those pins. This enables to inject the - * reference clock through CLKX/CLKR. For this to work - * set_dai_sysclk() _needs_ to be called after set_dai_fmt(). - */ - regs->pcr0 &= ~CLKXM; - break; - case OMAP_MCBSP_SYSCLK_CLKR_EXT: - regs->pcr0 |= SCLKME; - /* Disable ouput on CLKR pin in master mode */ - regs->pcr0 &= ~CLKRM; - break; - default: - err = -ENODEV; - } - - return err; -} - -static const struct snd_soc_dai_ops mcbsp_dai_ops = { - .startup = omap_mcbsp_dai_startup, - .shutdown = omap_mcbsp_dai_shutdown, - .prepare = omap_mcbsp_dai_prepare, - .trigger = omap_mcbsp_dai_trigger, - .delay = omap_mcbsp_dai_delay, - .hw_params = omap_mcbsp_dai_hw_params, - .set_fmt = omap_mcbsp_dai_set_dai_fmt, - .set_clkdiv = omap_mcbsp_dai_set_clkdiv, - .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, -}; - -static int omap_mcbsp_probe(struct snd_soc_dai *dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); - - pm_runtime_enable(mcbsp->dev); - - snd_soc_dai_init_dma_data(dai, - &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK], - &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]); - - return 0; -} - -static int omap_mcbsp_remove(struct snd_soc_dai *dai) -{ - struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); - - pm_runtime_disable(mcbsp->dev); - - return 0; -} - -static struct snd_soc_dai_driver omap_mcbsp_dai = { - .probe = omap_mcbsp_probe, - .remove = omap_mcbsp_remove, - .playback = { - .channels_min = 1, - .channels_max = 16, - .rates = OMAP_MCBSP_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, - }, - .capture = { - .channels_min = 1, - .channels_max = 16, - .rates = OMAP_MCBSP_RATES, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, - }, - .ops = &mcbsp_dai_ops, -}; - -static const struct snd_soc_component_driver omap_mcbsp_component = { - .name = "omap-mcbsp", -}; - -static struct omap_mcbsp_platform_data omap2420_pdata = { - .reg_step = 4, - .reg_size = 2, -}; - -static struct omap_mcbsp_platform_data omap2430_pdata = { - .reg_step = 4, - .reg_size = 4, - .has_ccr = true, -}; - -static struct omap_mcbsp_platform_data omap3_pdata = { - .reg_step = 4, - .reg_size = 4, - .has_ccr = true, - .has_wakeup = true, -}; - -static struct omap_mcbsp_platform_data omap4_pdata = { - .reg_step = 4, - .reg_size = 4, - .has_ccr = true, - .has_wakeup = true, -}; - -static const struct of_device_id omap_mcbsp_of_match[] = { - { - .compatible = "ti,omap2420-mcbsp", - .data = &omap2420_pdata, - }, - { - .compatible = "ti,omap2430-mcbsp", - .data = &omap2430_pdata, - }, - { - .compatible = "ti,omap3-mcbsp", - .data = &omap3_pdata, - }, - { - .compatible = "ti,omap4-mcbsp", - .data = &omap4_pdata, - }, - { }, -}; -MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match); - -static int asoc_mcbsp_probe(struct platform_device *pdev) -{ - struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct omap_mcbsp *mcbsp; - const struct of_device_id *match; - int ret; - - match = of_match_device(omap_mcbsp_of_match, &pdev->dev); - if (match) { - struct device_node *node = pdev->dev.of_node; - struct omap_mcbsp_platform_data *pdata_quirk = pdata; - int buffer_size; - - pdata = devm_kzalloc(&pdev->dev, - sizeof(struct omap_mcbsp_platform_data), - GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - memcpy(pdata, match->data, sizeof(*pdata)); - if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size)) - pdata->buffer_size = buffer_size; - if (pdata_quirk) - pdata->force_ick_on = pdata_quirk->force_ick_on; - } else if (!pdata) { - dev_err(&pdev->dev, "missing platform data.\n"); - return -EINVAL; - } - mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL); - if (!mcbsp) - return -ENOMEM; - - mcbsp->id = pdev->id; - mcbsp->pdata = pdata; - mcbsp->dev = &pdev->dev; - platform_set_drvdata(pdev, mcbsp); - - ret = omap_mcbsp_init(pdev); - if (ret) - return ret; - - if (mcbsp->pdata->reg_size == 2) { - omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE; - omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE; - } - - ret = devm_snd_soc_register_component(&pdev->dev, - &omap_mcbsp_component, - &omap_mcbsp_dai, 1); - if (ret) - return ret; - - return sdma_pcm_platform_register(&pdev->dev, NULL, NULL); -} - -static int asoc_mcbsp_remove(struct platform_device *pdev) -{ - struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); - - if (mcbsp->pdata->ops && mcbsp->pdata->ops->free) - mcbsp->pdata->ops->free(mcbsp->id); - - if (pm_qos_request_active(&mcbsp->pm_qos_req)) - pm_qos_remove_request(&mcbsp->pm_qos_req); - - if (mcbsp->pdata->buffer_size) - sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); - - omap_mcbsp_st_cleanup(pdev); - - clk_put(mcbsp->fclk); - - return 0; -} - -static struct platform_driver asoc_mcbsp_driver = { - .driver = { - .name = "omap-mcbsp", - .of_match_table = omap_mcbsp_of_match, - }, - - .probe = asoc_mcbsp_probe, - .remove = asoc_mcbsp_remove, -}; - -module_platform_driver(asoc_mcbsp_driver); - -MODULE_AUTHOR("Jarkko Nikula "); -MODULE_DESCRIPTION("OMAP I2S SoC Interface"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:omap-mcbsp"); diff --git a/sound/soc/omap/omap-mcbsp.h b/sound/soc/omap/omap-mcbsp.h deleted file mode 100644 index 7911d24898c9..000000000000 --- a/sound/soc/omap/omap-mcbsp.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * omap-mcbsp.h - * - * Copyright (C) 2008 Nokia Corporation - * - * Contact: Jarkko Nikula - * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __OMAP_MCBSP_H__ -#define __OMAP_MCBSP_H__ - -#include - -/* Source clocks for McBSP sample rate generator */ -enum omap_mcbsp_clksrg_clk { - OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */ - OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */ - OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */ - OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */ - OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */ -}; - -/* McBSP dividers */ -enum omap_mcbsp_div { - OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ -}; - -int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id); - -#endif /* __OMAP_MCBSP_H__ */ diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c deleted file mode 100644 index 7d5bdc5a2890..000000000000 --- a/sound/soc/omap/omap-mcpdm.c +++ /dev/null @@ -1,619 +0,0 @@ -/* - * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port - * - * Copyright (C) 2009 - 2011 Texas Instruments - * - * Author: Misael Lopez Cruz - * Contact: Jorge Eduardo Candelaria - * Margarita Olaya - * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "omap-mcpdm.h" -#include "sdma-pcm.h" - -struct mcpdm_link_config { - u32 link_mask; /* channel mask for the direction */ - u32 threshold; /* FIFO threshold */ -}; - -struct omap_mcpdm { - struct device *dev; - unsigned long phys_base; - void __iomem *io_base; - int irq; - struct pm_qos_request pm_qos_req; - int latency[2]; - - struct mutex mutex; - - /* Playback/Capture configuration */ - struct mcpdm_link_config config[2]; - - /* McPDM dn offsets for rx1, and 2 channels */ - u32 dn_rx_offset; - - /* McPDM needs to be restarted due to runtime reconfiguration */ - bool restart; - - /* pm state for suspend/resume handling */ - int pm_active_count; - - struct snd_dmaengine_dai_dma_data dma_data[2]; -}; - -/* - * Stream DMA parameters - */ - -static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val) -{ - writel_relaxed(val, mcpdm->io_base + reg); -} - -static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg) -{ - return readl_relaxed(mcpdm->io_base + reg); -} - -#ifdef DEBUG -static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) -{ - dev_dbg(mcpdm->dev, "***********************\n"); - dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW)); - dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS)); - dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET)); - dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR)); - dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN)); - dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET)); - dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR)); - dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN)); - dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL)); - dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA)); - dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA)); - dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN)); - dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", - omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP)); - dev_dbg(mcpdm->dev, "***********************\n"); -} -#else -static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {} -#endif - -/* - * Enables the transfer through the PDM interface to/from the Phoenix - * codec by enabling the corresponding UP or DN channels. - */ -static void omap_mcpdm_start(struct omap_mcpdm *mcpdm) -{ - u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); - u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask; - - ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); - - ctrl |= link_mask; - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); - - ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); -} - -/* - * Disables the transfer through the PDM interface to/from the Phoenix - * codec by disabling the corresponding UP or DN channels. - */ -static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm) -{ - u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); - u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK; - - ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); - - ctrl &= ~(link_mask); - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); - - ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); - -} - -/* - * Is the physical McPDM interface active. - */ -static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm) -{ - return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) & - (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK); -} - -/* - * Configures McPDM uplink, and downlink for audio. - * This function should be called before omap_mcpdm_start. - */ -static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm) -{ - u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); - - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN); - - omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET, - MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL | - MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); - - /* Enable DN RX1/2 offset cancellation feature, if configured */ - if (mcpdm->dn_rx_offset) { - u32 dn_offset = mcpdm->dn_rx_offset; - - omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); - dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN); - omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); - } - - omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, - mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold); - omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, - mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold); - - omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET, - MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE); -} - -/* - * Cleans McPDM uplink, and downlink configuration. - * This function should be called when the stream is closed. - */ -static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm) -{ - /* Disable irq request generation for downlink */ - omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, - MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL); - - /* Disable DMA request generation for downlink */ - omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE); - - /* Disable irq request generation for uplink */ - omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, - MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); - - /* Disable DMA request generation for uplink */ - omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE); - - /* Disable RX1/2 offset cancellation */ - if (mcpdm->dn_rx_offset) - omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0); -} - -static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) -{ - struct omap_mcpdm *mcpdm = dev_id; - int irq_status; - - irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS); - - /* Acknowledge irq event */ - omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status); - - if (irq_status & MCPDM_DN_IRQ_FULL) - dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n"); - - if (irq_status & MCPDM_DN_IRQ_EMPTY) - dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n"); - - if (irq_status & MCPDM_DN_IRQ) - dev_dbg(mcpdm->dev, "DN (playback) write request\n"); - - if (irq_status & MCPDM_UP_IRQ_FULL) - dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n"); - - if (irq_status & MCPDM_UP_IRQ_EMPTY) - dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n"); - - if (irq_status & MCPDM_UP_IRQ) - dev_dbg(mcpdm->dev, "UP (capture) write request\n"); - - return IRQ_HANDLED; -} - -static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - - mutex_lock(&mcpdm->mutex); - - if (!dai->active) - omap_mcpdm_open_streams(mcpdm); - - mutex_unlock(&mcpdm->mutex); - - return 0; -} - -static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - - mutex_lock(&mcpdm->mutex); - - if (!dai->active) { - if (omap_mcpdm_active(mcpdm)) { - omap_mcpdm_stop(mcpdm); - omap_mcpdm_close_streams(mcpdm); - mcpdm->config[0].link_mask = 0; - mcpdm->config[1].link_mask = 0; - } - } - - if (mcpdm->latency[stream2]) - pm_qos_update_request(&mcpdm->pm_qos_req, - mcpdm->latency[stream2]); - else if (mcpdm->latency[stream1]) - pm_qos_remove_request(&mcpdm->pm_qos_req); - - mcpdm->latency[stream1] = 0; - - mutex_unlock(&mcpdm->mutex); -} - -static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - int stream = substream->stream; - struct snd_dmaengine_dai_dma_data *dma_data; - u32 threshold; - int channels, latency; - int link_mask = 0; - - channels = params_channels(params); - switch (channels) { - case 5: - if (stream == SNDRV_PCM_STREAM_CAPTURE) - /* up to 3 channels for capture */ - return -EINVAL; - link_mask |= 1 << 4; - /* fall through */ - case 4: - if (stream == SNDRV_PCM_STREAM_CAPTURE) - /* up to 3 channels for capture */ - return -EINVAL; - link_mask |= 1 << 3; - /* fall through */ - case 3: - link_mask |= 1 << 2; - /* fall through */ - case 2: - link_mask |= 1 << 1; - /* fall through */ - case 1: - link_mask |= 1 << 0; - break; - default: - /* unsupported number of channels */ - return -EINVAL; - } - - dma_data = snd_soc_dai_get_dma_data(dai, substream); - - threshold = mcpdm->config[stream].threshold; - /* Configure McPDM channels, and DMA packet size */ - if (stream == SNDRV_PCM_STREAM_PLAYBACK) { - link_mask <<= 3; - - /* If capture is not running assume a stereo stream to come */ - if (!mcpdm->config[!stream].link_mask) - mcpdm->config[!stream].link_mask = 0x3; - - dma_data->maxburst = - (MCPDM_DN_THRES_MAX - threshold) * channels; - latency = threshold; - } else { - /* If playback is not running assume a stereo stream to come */ - if (!mcpdm->config[!stream].link_mask) - mcpdm->config[!stream].link_mask = (0x3 << 3); - - dma_data->maxburst = threshold * channels; - latency = (MCPDM_DN_THRES_MAX - threshold); - } - - /* - * The DMA must act to a DMA request within latency time (usec) to avoid - * under/overflow - */ - mcpdm->latency[stream] = latency * USEC_PER_SEC / params_rate(params); - - if (!mcpdm->latency[stream]) - mcpdm->latency[stream] = 10; - - /* Check if we need to restart McPDM with this stream */ - if (mcpdm->config[stream].link_mask && - mcpdm->config[stream].link_mask != link_mask) - mcpdm->restart = true; - - mcpdm->config[stream].link_mask = link_mask; - - return 0; -} - -static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - struct pm_qos_request *pm_qos_req = &mcpdm->pm_qos_req; - int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; - int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; - int latency = mcpdm->latency[stream2]; - - /* Prevent omap hardware from hitting off between FIFO fills */ - if (!latency || mcpdm->latency[stream1] < latency) - latency = mcpdm->latency[stream1]; - - if (pm_qos_request_active(pm_qos_req)) - pm_qos_update_request(pm_qos_req, latency); - else if (latency) - pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); - - if (!omap_mcpdm_active(mcpdm)) { - omap_mcpdm_start(mcpdm); - omap_mcpdm_reg_dump(mcpdm); - } else if (mcpdm->restart) { - omap_mcpdm_stop(mcpdm); - omap_mcpdm_start(mcpdm); - mcpdm->restart = false; - omap_mcpdm_reg_dump(mcpdm); - } - - return 0; -} - -static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { - .startup = omap_mcpdm_dai_startup, - .shutdown = omap_mcpdm_dai_shutdown, - .hw_params = omap_mcpdm_dai_hw_params, - .prepare = omap_mcpdm_prepare, -}; - -static int omap_mcpdm_probe(struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - int ret; - - pm_runtime_enable(mcpdm->dev); - - /* Disable lines while request is ongoing */ - pm_runtime_get_sync(mcpdm->dev); - omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00); - - ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM", - (void *)mcpdm); - - pm_runtime_put_sync(mcpdm->dev); - - if (ret) { - dev_err(mcpdm->dev, "Request for IRQ failed\n"); - pm_runtime_disable(mcpdm->dev); - } - - /* Configure McPDM threshold values */ - mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2; - mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold = - MCPDM_UP_THRES_MAX - 3; - - snd_soc_dai_init_dma_data(dai, - &mcpdm->dma_data[SNDRV_PCM_STREAM_PLAYBACK], - &mcpdm->dma_data[SNDRV_PCM_STREAM_CAPTURE]); - - return ret; -} - -static int omap_mcpdm_remove(struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - - free_irq(mcpdm->irq, (void *)mcpdm); - pm_runtime_disable(mcpdm->dev); - - if (pm_qos_request_active(&mcpdm->pm_qos_req)) - pm_qos_remove_request(&mcpdm->pm_qos_req); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int omap_mcpdm_suspend(struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - - if (dai->active) { - omap_mcpdm_stop(mcpdm); - omap_mcpdm_close_streams(mcpdm); - } - - mcpdm->pm_active_count = 0; - while (pm_runtime_active(mcpdm->dev)) { - pm_runtime_put_sync(mcpdm->dev); - mcpdm->pm_active_count++; - } - - return 0; -} - -static int omap_mcpdm_resume(struct snd_soc_dai *dai) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); - - if (mcpdm->pm_active_count) { - while (mcpdm->pm_active_count--) - pm_runtime_get_sync(mcpdm->dev); - - if (dai->active) { - omap_mcpdm_open_streams(mcpdm); - omap_mcpdm_start(mcpdm); - } - } - - - return 0; -} -#else -#define omap_mcpdm_suspend NULL -#define omap_mcpdm_resume NULL -#endif - -#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) -#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE - -static struct snd_soc_dai_driver omap_mcpdm_dai = { - .probe = omap_mcpdm_probe, - .remove = omap_mcpdm_remove, - .suspend = omap_mcpdm_suspend, - .resume = omap_mcpdm_resume, - .probe_order = SND_SOC_COMP_ORDER_LATE, - .remove_order = SND_SOC_COMP_ORDER_EARLY, - .playback = { - .channels_min = 1, - .channels_max = 5, - .rates = OMAP_MCPDM_RATES, - .formats = OMAP_MCPDM_FORMATS, - .sig_bits = 24, - }, - .capture = { - .channels_min = 1, - .channels_max = 3, - .rates = OMAP_MCPDM_RATES, - .formats = OMAP_MCPDM_FORMATS, - .sig_bits = 24, - }, - .ops = &omap_mcpdm_dai_ops, -}; - -static const struct snd_soc_component_driver omap_mcpdm_component = { - .name = "omap-mcpdm", -}; - -void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, - u8 rx1, u8 rx2) -{ - struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai); - - mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2); -} -EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets); - -static int asoc_mcpdm_probe(struct platform_device *pdev) -{ - struct omap_mcpdm *mcpdm; - struct resource *res; - int ret; - - mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL); - if (!mcpdm) - return -ENOMEM; - - platform_set_drvdata(pdev, mcpdm); - - mutex_init(&mcpdm->mutex); - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); - if (res == NULL) - return -ENOMEM; - - mcpdm->dma_data[0].addr = res->start + MCPDM_REG_DN_DATA; - mcpdm->dma_data[1].addr = res->start + MCPDM_REG_UP_DATA; - - mcpdm->dma_data[0].filter_data = "dn_link"; - mcpdm->dma_data[1].filter_data = "up_link"; - - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); - mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(mcpdm->io_base)) - return PTR_ERR(mcpdm->io_base); - - mcpdm->irq = platform_get_irq(pdev, 0); - if (mcpdm->irq < 0) - return mcpdm->irq; - - mcpdm->dev = &pdev->dev; - - ret = devm_snd_soc_register_component(&pdev->dev, - &omap_mcpdm_component, - &omap_mcpdm_dai, 1); - if (ret) - return ret; - - return sdma_pcm_platform_register(&pdev->dev, "dn_link", "up_link"); -} - -static const struct of_device_id omap_mcpdm_of_match[] = { - { .compatible = "ti,omap4-mcpdm", }, - { } -}; -MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match); - -static struct platform_driver asoc_mcpdm_driver = { - .driver = { - .name = "omap-mcpdm", - .of_match_table = omap_mcpdm_of_match, - }, - - .probe = asoc_mcpdm_probe, -}; - -module_platform_driver(asoc_mcpdm_driver); - -MODULE_ALIAS("platform:omap-mcpdm"); -MODULE_AUTHOR("Misael Lopez Cruz "); -MODULE_DESCRIPTION("OMAP PDM SoC Interface"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/omap-mcpdm.h b/sound/soc/omap/omap-mcpdm.h deleted file mode 100644 index de8cf26595b1..000000000000 --- a/sound/soc/omap/omap-mcpdm.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * omap-mcpdm.h - * - * Copyright (C) 2009 - 2011 Texas Instruments - * - * Contact: Misael Lopez Cruz - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#ifndef __OMAP_MCPDM_H__ -#define __OMAP_MCPDM_H__ - -#define MCPDM_REG_REVISION 0x00 -#define MCPDM_REG_SYSCONFIG 0x10 -#define MCPDM_REG_IRQSTATUS_RAW 0x24 -#define MCPDM_REG_IRQSTATUS 0x28 -#define MCPDM_REG_IRQENABLE_SET 0x2C -#define MCPDM_REG_IRQENABLE_CLR 0x30 -#define MCPDM_REG_IRQWAKE_EN 0x34 -#define MCPDM_REG_DMAENABLE_SET 0x38 -#define MCPDM_REG_DMAENABLE_CLR 0x3C -#define MCPDM_REG_DMAWAKEEN 0x40 -#define MCPDM_REG_CTRL 0x44 -#define MCPDM_REG_DN_DATA 0x48 -#define MCPDM_REG_UP_DATA 0x4C -#define MCPDM_REG_FIFO_CTRL_DN 0x50 -#define MCPDM_REG_FIFO_CTRL_UP 0x54 -#define MCPDM_REG_DN_OFFSET 0x58 - -/* - * MCPDM_IRQ bit fields - * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR - */ - -#define MCPDM_DN_IRQ (1 << 0) -#define MCPDM_DN_IRQ_EMPTY (1 << 1) -#define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2) -#define MCPDM_DN_IRQ_FULL (1 << 3) - -#define MCPDM_UP_IRQ (1 << 8) -#define MCPDM_UP_IRQ_EMPTY (1 << 9) -#define MCPDM_UP_IRQ_ALMST_FULL (1 << 10) -#define MCPDM_UP_IRQ_FULL (1 << 11) - -#define MCPDM_DOWNLINK_IRQ_MASK 0x00F -#define MCPDM_UPLINK_IRQ_MASK 0xF00 - -/* - * MCPDM_DMAENABLE bit fields - */ - -#define MCPDM_DMA_DN_ENABLE (1 << 0) -#define MCPDM_DMA_UP_ENABLE (1 << 1) - -/* - * MCPDM_CTRL bit fields - */ - -#define MCPDM_PDM_UPLINK_EN(x) (1 << (x - 1)) /* ch1 is at bit 0 */ -#define MCPDM_PDM_DOWNLINK_EN(x) (1 << (x + 2)) /* ch1 is at bit 3 */ -#define MCPDM_PDMOUTFORMAT (1 << 8) -#define MCPDM_CMD_INT (1 << 9) -#define MCPDM_STATUS_INT (1 << 10) -#define MCPDM_SW_UP_RST (1 << 11) -#define MCPDM_SW_DN_RST (1 << 12) -#define MCPDM_WD_EN (1 << 14) -#define MCPDM_PDM_UP_MASK 0x7 -#define MCPDM_PDM_DN_MASK (0x1f << 3) - - -#define MCPDM_PDMOUTFORMAT_LJUST (0 << 8) -#define MCPDM_PDMOUTFORMAT_RJUST (1 << 8) - -/* - * MCPDM_FIFO_CTRL bit fields - */ - -#define MCPDM_UP_THRES_MAX 0xF -#define MCPDM_DN_THRES_MAX 0xF - -/* - * MCPDM_DN_OFFSET bit fields - */ - -#define MCPDM_DN_OFST_RX1_EN (1 << 0) -#define MCPDM_DNOFST_RX1(x) ((x & 0x1f) << 1) -#define MCPDM_DN_OFST_RX2_EN (1 << 8) -#define MCPDM_DNOFST_RX2(x) ((x & 0x1f) << 9) - -void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, - u8 rx1, u8 rx2); - -#endif /* End of __OMAP_MCPDM_H__ */ diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c deleted file mode 100644 index cccc316743fa..000000000000 --- a/sound/soc/omap/omap-twl4030.c +++ /dev/null @@ -1,353 +0,0 @@ -/* - * omap-twl4030.c -- SoC audio for TI SoC based boards with twl4030 codec - * - * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com - * All rights reserved. - * - * Author: Peter Ujfalusi - * - * This driver replaces the following machine drivers: - * omap3beagle (Author: Steve Sakoman ) - * omap3evm (Author: Anuj Aggarwal ) - * overo (Author: Steve Sakoman ) - * igep0020 (Author: Enric Balletbo i Serra ) - * zoom2 (Author: Misael Lopez Cruz ) - * sdp3430 (Author: Misael Lopez Cruz ) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "omap-mcbsp.h" - -struct omap_twl4030 { - int jack_detect; /* board can detect jack events */ - struct snd_soc_jack hs_jack; -}; - -static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - unsigned int fmt; - - switch (params_channels(params)) { - case 2: /* Stereo I2S mode */ - fmt = SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; - break; - case 4: /* Four channel TDM mode */ - fmt = SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM; - break; - default: - return -EINVAL; - } - - return snd_soc_runtime_set_dai_fmt(rtd, fmt); -} - -static const struct snd_soc_ops omap_twl4030_ops = { - .hw_params = omap_twl4030_hw_params, -}; - -static const struct snd_soc_dapm_widget dapm_widgets[] = { - SND_SOC_DAPM_SPK("Earpiece Spk", NULL), - SND_SOC_DAPM_SPK("Handsfree Spk", NULL), - SND_SOC_DAPM_HP("Headset Stereophone", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), - SND_SOC_DAPM_SPK("Carkit Spk", NULL), - - SND_SOC_DAPM_MIC("Main Mic", NULL), - SND_SOC_DAPM_MIC("Sub Mic", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Carkit Mic", NULL), - SND_SOC_DAPM_MIC("Digital0 Mic", NULL), - SND_SOC_DAPM_MIC("Digital1 Mic", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - /* Headset Stereophone: HSOL, HSOR */ - {"Headset Stereophone", NULL, "HSOL"}, - {"Headset Stereophone", NULL, "HSOR"}, - /* External Speakers: HFL, HFR */ - {"Handsfree Spk", NULL, "HFL"}, - {"Handsfree Spk", NULL, "HFR"}, - /* External Speakers: PredrivL, PredrivR */ - {"Ext Spk", NULL, "PREDRIVEL"}, - {"Ext Spk", NULL, "PREDRIVER"}, - /* Carkit speakers: CARKITL, CARKITR */ - {"Carkit Spk", NULL, "CARKITL"}, - {"Carkit Spk", NULL, "CARKITR"}, - /* Earpiece */ - {"Earpiece Spk", NULL, "EARPIECE"}, - - /* External Mics: MAINMIC, SUBMIC with bias */ - {"MAINMIC", NULL, "Main Mic"}, - {"Main Mic", NULL, "Mic Bias 1"}, - {"SUBMIC", NULL, "Sub Mic"}, - {"Sub Mic", NULL, "Mic Bias 2"}, - /* Headset Mic: HSMIC with bias */ - {"HSMIC", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "Headset Mic Bias"}, - /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */ - {"DIGIMIC0", NULL, "Digital0 Mic"}, - {"Digital0 Mic", NULL, "Mic Bias 1"}, - {"DIGIMIC1", NULL, "Digital1 Mic"}, - {"Digital1 Mic", NULL, "Mic Bias 2"}, - /* Carkit In: CARKITMIC */ - {"CARKITMIC", NULL, "Carkit Mic"}, - /* Aux In: AUXL, AUXR */ - {"AUXL", NULL, "Line In"}, - {"AUXR", NULL, "Line In"}, -}; - -/* Headset jack detection DAPM pins */ -static struct snd_soc_jack_pin hs_jack_pins[] = { - { - .pin = "Headset Mic", - .mask = SND_JACK_MICROPHONE, - }, - { - .pin = "Headset Stereophone", - .mask = SND_JACK_HEADPHONE, - }, -}; - -/* Headset jack detection gpios */ -static struct snd_soc_jack_gpio hs_jack_gpios[] = { - { - .name = "hsdet-gpio", - .report = SND_JACK_HEADSET, - .debounce_time = 200, - }, -}; - -static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, - int connected, char *pin) -{ - if (!connected) - snd_soc_dapm_disable_pin(dapm, pin); -} - -static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - struct snd_soc_dapm_context *dapm = &card->dapm; - struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); - struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); - int ret = 0; - - /* Headset jack detection only if it is supported */ - if (priv->jack_detect > 0) { - hs_jack_gpios[0].gpio = priv->jack_detect; - - ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", - SND_JACK_HEADSET, &priv->hs_jack, - hs_jack_pins, - ARRAY_SIZE(hs_jack_pins)); - if (ret) - return ret; - - ret = snd_soc_jack_add_gpios(&priv->hs_jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); - if (ret) - return ret; - } - - /* - * NULL pdata means we booted with DT. In this case the routing is - * provided and the card is fully routed, no need to mark pins. - */ - if (!pdata || !pdata->custom_routing) - return ret; - - /* Disable not connected paths if not used */ - twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); - twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); - twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); - twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); - - twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); - twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); - twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); - twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); - twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); - - return ret; -} - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link omap_twl4030_dai_links[] = { - { - .name = "TWL4030 HiFi", - .stream_name = "TWL4030 HiFi", - .cpu_dai_name = "omap-mcbsp.2", - .codec_dai_name = "twl4030-hifi", - .platform_name = "omap-mcbsp.2", - .codec_name = "twl4030-codec", - .init = omap_twl4030_init, - .ops = &omap_twl4030_ops, - }, - { - .name = "TWL4030 Voice", - .stream_name = "TWL4030 Voice", - .cpu_dai_name = "omap-mcbsp.3", - .codec_dai_name = "twl4030-voice", - .platform_name = "omap-mcbsp.3", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, - }, -}; - -/* Audio machine driver */ -static struct snd_soc_card omap_twl4030_card = { - .owner = THIS_MODULE, - .dai_link = omap_twl4030_dai_links, - .num_links = ARRAY_SIZE(omap_twl4030_dai_links), - - .dapm_widgets = dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static int omap_twl4030_probe(struct platform_device *pdev) -{ - struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); - struct device_node *node = pdev->dev.of_node; - struct snd_soc_card *card = &omap_twl4030_card; - struct omap_twl4030 *priv; - int ret = 0; - - card->dev = &pdev->dev; - - priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL); - if (priv == NULL) - return -ENOMEM; - - if (node) { - struct device_node *dai_node; - struct property *prop; - - if (snd_soc_of_parse_card_name(card, "ti,model")) { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - dai_node = of_parse_phandle(node, "ti,mcbsp", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McBSP node is not provided\n"); - return -EINVAL; - } - omap_twl4030_dai_links[0].cpu_dai_name = NULL; - omap_twl4030_dai_links[0].cpu_of_node = dai_node; - - omap_twl4030_dai_links[0].platform_name = NULL; - omap_twl4030_dai_links[0].platform_of_node = dai_node; - - dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); - if (!dai_node) { - card->num_links = 1; - } else { - omap_twl4030_dai_links[1].cpu_dai_name = NULL; - omap_twl4030_dai_links[1].cpu_of_node = dai_node; - - omap_twl4030_dai_links[1].platform_name = NULL; - omap_twl4030_dai_links[1].platform_of_node = dai_node; - } - - priv->jack_detect = of_get_named_gpio(node, - "ti,jack-det-gpio", 0); - - /* Optional: audio routing can be provided */ - prop = of_find_property(node, "ti,audio-routing", NULL); - if (prop) { - ret = snd_soc_of_parse_audio_routing(card, - "ti,audio-routing"); - if (ret) - return ret; - - card->fully_routed = 1; - } - } else if (pdata) { - if (pdata->card_name) { - card->name = pdata->card_name; - } else { - dev_err(&pdev->dev, "Card name is not provided\n"); - return -ENODEV; - } - - if (!pdata->voice_connected) - card->num_links = 1; - - priv->jack_detect = pdata->jack_detect; - } else { - dev_err(&pdev->dev, "Missing pdata\n"); - return -ENODEV; - } - - snd_soc_card_set_drvdata(card, priv); - ret = devm_snd_soc_register_card(&pdev->dev, card); - if (ret) { - dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", - ret); - return ret; - } - - return 0; -} - -static const struct of_device_id omap_twl4030_of_match[] = { - {.compatible = "ti,omap-twl4030", }, - { }, -}; -MODULE_DEVICE_TABLE(of, omap_twl4030_of_match); - -static struct platform_driver omap_twl4030_driver = { - .driver = { - .name = "omap-twl4030", - .pm = &snd_soc_pm_ops, - .of_match_table = omap_twl4030_of_match, - }, - .probe = omap_twl4030_probe, -}; - -module_platform_driver(omap_twl4030_driver); - -MODULE_AUTHOR("Peter Ujfalusi "); -MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:omap-twl4030"); diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c deleted file mode 100644 index 4e3de712159c..000000000000 --- a/sound/soc/omap/omap3pandora.c +++ /dev/null @@ -1,315 +0,0 @@ -/* - * omap3pandora.c -- SoC audio for Pandora Handheld Console - * - * Author: GraÅžvydas Ignotas - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "omap-mcbsp.h" - -#define OMAP3_PANDORA_DAC_POWER_GPIO 118 -#define OMAP3_PANDORA_AMP_POWER_GPIO 14 - -#define PREFIX "ASoC omap3pandora: " - -static struct regulator *omap3pandora_dac_reg; - -static int omap3pandora_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; - - /* Set the codec system clock for DAC and ADC */ - ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, - SND_SOC_CLOCK_IN); - if (ret < 0) { - pr_err(PREFIX "can't set codec system clock\n"); - return ret; - } - - /* Set McBSP clock to external */ - ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, - 256 * params_rate(params), - SND_SOC_CLOCK_IN); - if (ret < 0) { - pr_err(PREFIX "can't set cpu system clock\n"); - return ret; - } - - ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8); - if (ret < 0) { - pr_err(PREFIX "can't set SRG clock divider\n"); - return ret; - } - - return 0; -} - -static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - int ret; - - /* - * The PCM1773 DAC datasheet requires 1ms delay between switching - * VCC power on/off and /PD pin high/low - */ - if (SND_SOC_DAPM_EVENT_ON(event)) { - ret = regulator_enable(omap3pandora_dac_reg); - if (ret) { - dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret); - return ret; - } - mdelay(1); - gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1); - } else { - gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0); - mdelay(1); - regulator_disable(omap3pandora_dac_reg); - } - - return 0; -} - -static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - if (SND_SOC_DAPM_EVENT_ON(event)) - gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1); - else - gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0); - - return 0; -} - -/* - * Audio paths on Pandora board: - * - * |O| ---> PCM DAC +-> AMP -> Headphone Jack - * |M| A +--------> Line Out - * |A| <~~clk~~+ - * |P| <--- TWL4030 <--------- Line In and MICs - */ -static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = { - SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM, - 0, 0, omap3pandora_dac_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM, - 0, 0, NULL, 0, omap3pandora_hp_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_LINE("Line Out", NULL), - - SND_SOC_DAPM_MIC("Mic (internal)", NULL), - SND_SOC_DAPM_MIC("Mic (external)", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), -}; - -static const struct snd_soc_dapm_route omap3pandora_map[] = { - {"PCM DAC", NULL, "APLL Enable"}, - {"Headphone Amplifier", NULL, "PCM DAC"}, - {"Line Out", NULL, "PCM DAC"}, - {"Headphone Jack", NULL, "Headphone Amplifier"}, - - {"AUXL", NULL, "Line In"}, - {"AUXR", NULL, "Line In"}, - - {"MAINMIC", NULL, "Mic (internal)"}, - {"Mic (internal)", NULL, "Mic Bias 1"}, - - {"SUBMIC", NULL, "Mic (external)"}, - {"Mic (external)", NULL, "Mic Bias 2"}, -}; - -static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dapm_context *dapm = &rtd->card->dapm; - - /* All TWL4030 output pins are floating */ - snd_soc_dapm_nc_pin(dapm, "EARPIECE"); - snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); - snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); - snd_soc_dapm_nc_pin(dapm, "HSOL"); - snd_soc_dapm_nc_pin(dapm, "HSOR"); - snd_soc_dapm_nc_pin(dapm, "CARKITL"); - snd_soc_dapm_nc_pin(dapm, "CARKITR"); - snd_soc_dapm_nc_pin(dapm, "HFL"); - snd_soc_dapm_nc_pin(dapm, "HFR"); - snd_soc_dapm_nc_pin(dapm, "VIBRA"); - - return 0; -} - -static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_dapm_context *dapm = &rtd->card->dapm; - - /* Not comnnected */ - snd_soc_dapm_nc_pin(dapm, "HSMIC"); - snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); - snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); - snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); - - return 0; -} - -static const struct snd_soc_ops omap3pandora_ops = { - .hw_params = omap3pandora_hw_params, -}; - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link omap3pandora_dai[] = { - { - .name = "PCM1773", - .stream_name = "HiFi Out", - .cpu_dai_name = "omap-mcbsp.2", - .codec_dai_name = "twl4030-hifi", - .platform_name = "omap-mcbsp.2", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &omap3pandora_ops, - .init = omap3pandora_out_init, - }, { - .name = "TWL4030", - .stream_name = "Line/Mic In", - .cpu_dai_name = "omap-mcbsp.4", - .codec_dai_name = "twl4030-hifi", - .platform_name = "omap-mcbsp.4", - .codec_name = "twl4030-codec", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .ops = &omap3pandora_ops, - .init = omap3pandora_in_init, - } -}; - -/* SoC card */ -static struct snd_soc_card snd_soc_card_omap3pandora = { - .name = "omap3pandora", - .owner = THIS_MODULE, - .dai_link = omap3pandora_dai, - .num_links = ARRAY_SIZE(omap3pandora_dai), - - .dapm_widgets = omap3pandora_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets), - .dapm_routes = omap3pandora_map, - .num_dapm_routes = ARRAY_SIZE(omap3pandora_map), -}; - -static struct platform_device *omap3pandora_snd_device; - -static int __init omap3pandora_soc_init(void) -{ - int ret; - - if (!machine_is_omap3_pandora()) - return -ENODEV; - - pr_info("OMAP3 Pandora SoC init\n"); - - ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power"); - if (ret) { - pr_err(PREFIX "Failed to get DAC power GPIO\n"); - return ret; - } - - ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0); - if (ret) { - pr_err(PREFIX "Failed to set DAC power GPIO direction\n"); - goto fail0; - } - - ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power"); - if (ret) { - pr_err(PREFIX "Failed to get amp power GPIO\n"); - goto fail0; - } - - ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0); - if (ret) { - pr_err(PREFIX "Failed to set amp power GPIO direction\n"); - goto fail1; - } - - omap3pandora_snd_device = platform_device_alloc("soc-audio", -1); - if (omap3pandora_snd_device == NULL) { - pr_err(PREFIX "Platform device allocation failed\n"); - ret = -ENOMEM; - goto fail1; - } - - platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora); - - ret = platform_device_add(omap3pandora_snd_device); - if (ret) { - pr_err(PREFIX "Unable to add platform device\n"); - goto fail2; - } - - omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc"); - if (IS_ERR(omap3pandora_dac_reg)) { - pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n", - dev_name(&omap3pandora_snd_device->dev), - PTR_ERR(omap3pandora_dac_reg)); - ret = PTR_ERR(omap3pandora_dac_reg); - goto fail3; - } - - return 0; - -fail3: - platform_device_del(omap3pandora_snd_device); -fail2: - platform_device_put(omap3pandora_snd_device); -fail1: - gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); -fail0: - gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); - return ret; -} -module_init(omap3pandora_soc_init); - -static void __exit omap3pandora_soc_exit(void) -{ - regulator_put(omap3pandora_dac_reg); - platform_device_unregister(omap3pandora_snd_device); - gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); - gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); -} -module_exit(omap3pandora_soc_exit); - -MODULE_AUTHOR("Grazvydas Ignotas "); -MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c deleted file mode 100644 index e4096779ca05..000000000000 --- a/sound/soc/omap/osk5912.c +++ /dev/null @@ -1,187 +0,0 @@ -/* - * osk5912.c -- SoC audio for OSK 5912 - * - * Copyright (C) 2008 Mistral Solutions - * - * Contact: Arun KS - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "omap-mcbsp.h" -#include "../codecs/tlv320aic23.h" - -#define CODEC_CLOCK 12000000 - -static struct clk *tlv320aic23_mclk; - -static int osk_startup(struct snd_pcm_substream *substream) -{ - return clk_enable(tlv320aic23_mclk); -} - -static void osk_shutdown(struct snd_pcm_substream *substream) -{ - clk_disable(tlv320aic23_mclk); -} - -static int osk_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - int err; - - /* Set the codec system clock for DAC and ADC */ - err = - snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); - - if (err < 0) { - printk(KERN_ERR "can't set codec system clock\n"); - return err; - } - - return err; -} - -static const struct snd_soc_ops osk_ops = { - .startup = osk_startup, - .hw_params = osk_hw_params, - .shutdown = osk_shutdown, -}; - -static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_LINE("Line In", NULL), - SND_SOC_DAPM_MIC("Mic Jack", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - {"Headphone Jack", NULL, "LHPOUT"}, - {"Headphone Jack", NULL, "RHPOUT"}, - - {"LLINEIN", NULL, "Line In"}, - {"RLINEIN", NULL, "Line In"}, - - {"MICIN", NULL, "Mic Jack"}, -}; - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link osk_dai = { - .name = "TLV320AIC23", - .stream_name = "AIC23", - .cpu_dai_name = "omap-mcbsp.1", - .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "omap-mcbsp.1", - .codec_name = "tlv320aic23-codec", - .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .ops = &osk_ops, -}; - -/* Audio machine driver */ -static struct snd_soc_card snd_soc_card_osk = { - .name = "OSK5912", - .owner = THIS_MODULE, - .dai_link = &osk_dai, - .num_links = 1, - - .dapm_widgets = tlv320aic23_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static struct platform_device *osk_snd_device; - -static int __init osk_soc_init(void) -{ - int err; - u32 curRate; - struct device *dev; - - if (!(machine_is_omap_osk())) - return -ENODEV; - - osk_snd_device = platform_device_alloc("soc-audio", -1); - if (!osk_snd_device) - return -ENOMEM; - - platform_set_drvdata(osk_snd_device, &snd_soc_card_osk); - err = platform_device_add(osk_snd_device); - if (err) - goto err1; - - dev = &osk_snd_device->dev; - - tlv320aic23_mclk = clk_get(dev, "mclk"); - if (IS_ERR(tlv320aic23_mclk)) { - printk(KERN_ERR "Could not get mclk clock\n"); - err = PTR_ERR(tlv320aic23_mclk); - goto err2; - } - - /* - * Configure 12 MHz output on MCLK. - */ - curRate = (uint) clk_get_rate(tlv320aic23_mclk); - if (curRate != CODEC_CLOCK) { - if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) { - printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n"); - err = -ECANCELED; - goto err3; - } - } - - printk(KERN_INFO "MCLK = %d [%d]\n", - (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); - - return 0; - -err3: - clk_put(tlv320aic23_mclk); -err2: - platform_device_del(osk_snd_device); -err1: - platform_device_put(osk_snd_device); - - return err; - -} - -static void __exit osk_soc_exit(void) -{ - clk_put(tlv320aic23_mclk); - platform_device_unregister(osk_snd_device); -} - -module_init(osk_soc_init); -module_exit(osk_soc_exit); - -MODULE_AUTHOR("Arun KS "); -MODULE_DESCRIPTION("ALSA SoC OSK 5912"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c deleted file mode 100644 index 57448bd5ad77..000000000000 --- a/sound/soc/omap/rx51.c +++ /dev/null @@ -1,493 +0,0 @@ -/* - * rx51.c -- SoC audio for Nokia RX-51 - * - * Copyright (C) 2008 - 2009 Nokia Corporation - * - * Contact: Peter Ujfalusi - * Eduardo Valentin - * Jarkko Nikula - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "omap-mcbsp.h" - -enum { - RX51_JACK_DISABLED, - RX51_JACK_TVOUT, /* tv-out with stereo output */ - RX51_JACK_HP, /* headphone: stereo output, no mic */ - RX51_JACK_HS, /* headset: stereo output with mic */ -}; - -struct rx51_audio_pdata { - struct gpio_desc *tvout_selection_gpio; - struct gpio_desc *jack_detection_gpio; - struct gpio_desc *eci_sw_gpio; - struct gpio_desc *speaker_amp_gpio; -}; - -static int rx51_spk_func; -static int rx51_dmic_func; -static int rx51_jack_func; - -static void rx51_ext_control(struct snd_soc_dapm_context *dapm) -{ - struct snd_soc_card *card = dapm->card; - struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); - int hp = 0, hs = 0, tvout = 0; - - switch (rx51_jack_func) { - case RX51_JACK_TVOUT: - tvout = 1; - hp = 1; - break; - case RX51_JACK_HS: - hs = 1; - case RX51_JACK_HP: - hp = 1; - break; - } - - snd_soc_dapm_mutex_lock(dapm); - - if (rx51_spk_func) - snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); - if (rx51_dmic_func) - snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); - if (hp) - snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); - if (hs) - snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); - else - snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); - - gpiod_set_value(pdata->tvout_selection_gpio, tvout); - - snd_soc_dapm_sync_unlocked(dapm); - - snd_soc_dapm_mutex_unlock(dapm); -} - -static int rx51_startup(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_card *card = rtd->card; - - snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); - rx51_ext_control(&card->dapm); - - return 0; -} - -static int rx51_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - - /* Set the codec system clock for DAC and ADC */ - return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000, - SND_SOC_CLOCK_IN); -} - -static const struct snd_soc_ops rx51_ops = { - .startup = rx51_startup, - .hw_params = rx51_hw_params, -}; - -static int rx51_get_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = rx51_spk_func; - - return 0; -} - -static int rx51_set_spk(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - - if (rx51_spk_func == ucontrol->value.enumerated.item[0]) - return 0; - - rx51_spk_func = ucontrol->value.enumerated.item[0]; - rx51_ext_control(&card->dapm); - - return 1; -} - -static int rx51_spk_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *k, int event) -{ - struct snd_soc_dapm_context *dapm = w->dapm; - struct snd_soc_card *card = dapm->card; - struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); - - gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio, - !!SND_SOC_DAPM_EVENT_ON(event)); - - return 0; -} - -static int rx51_get_input(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = rx51_dmic_func; - - return 0; -} - -static int rx51_set_input(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - - if (rx51_dmic_func == ucontrol->value.enumerated.item[0]) - return 0; - - rx51_dmic_func = ucontrol->value.enumerated.item[0]; - rx51_ext_control(&card->dapm); - - return 1; -} - -static int rx51_get_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - ucontrol->value.enumerated.item[0] = rx51_jack_func; - - return 0; -} - -static int rx51_set_jack(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); - - if (rx51_jack_func == ucontrol->value.enumerated.item[0]) - return 0; - - rx51_jack_func = ucontrol->value.enumerated.item[0]; - rx51_ext_control(&card->dapm); - - return 1; -} - -static struct snd_soc_jack rx51_av_jack; - -static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { - { - .name = "avdet-gpio", - .report = SND_JACK_HEADSET, - .invert = 1, - .debounce_time = 200, - }, -}; - -static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { - SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), - SND_SOC_DAPM_MIC("DMic", NULL), - SND_SOC_DAPM_HP("Headphone Jack", NULL), - SND_SOC_DAPM_MIC("HS Mic", NULL), - SND_SOC_DAPM_LINE("FM Transmitter", NULL), - SND_SOC_DAPM_SPK("Earphone", NULL), -}; - -static const struct snd_soc_dapm_route audio_map[] = { - {"Ext Spk", NULL, "HPLOUT"}, - {"Ext Spk", NULL, "HPROUT"}, - {"Ext Spk", NULL, "HPLCOM"}, - {"Ext Spk", NULL, "HPRCOM"}, - {"FM Transmitter", NULL, "LLOUT"}, - {"FM Transmitter", NULL, "RLOUT"}, - - {"Headphone Jack", NULL, "TPA6130A2 HPLEFT"}, - {"Headphone Jack", NULL, "TPA6130A2 HPRIGHT"}, - {"TPA6130A2 LEFTIN", NULL, "LLOUT"}, - {"TPA6130A2 RIGHTIN", NULL, "RLOUT"}, - - {"DMic Rate 64", NULL, "DMic"}, - {"DMic", NULL, "Mic Bias"}, - - {"b LINE2R", NULL, "MONO_LOUT"}, - {"Earphone", NULL, "b HPLOUT"}, - - {"LINE1L", NULL, "HS Mic"}, - {"HS Mic", NULL, "b Mic Bias"}, -}; - -static const char * const spk_function[] = {"Off", "On"}; -static const char * const input_function[] = {"ADC", "Digital Mic"}; -static const char * const jack_function[] = { - "Off", "TV-OUT", "Headphone", "Headset" -}; - -static const struct soc_enum rx51_enum[] = { - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), - SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), -}; - -static const struct snd_kcontrol_new aic34_rx51_controls[] = { - SOC_ENUM_EXT("Speaker Function", rx51_enum[0], - rx51_get_spk, rx51_set_spk), - SOC_ENUM_EXT("Input Select", rx51_enum[1], - rx51_get_input, rx51_set_input), - SOC_ENUM_EXT("Jack Function", rx51_enum[2], - rx51_get_jack, rx51_set_jack), - SOC_DAPM_PIN_SWITCH("FM Transmitter"), - SOC_DAPM_PIN_SWITCH("Earphone"), -}; - -static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); - int err; - - snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42); - - err = omap_mcbsp_st_add_controls(rtd, 2); - if (err < 0) { - dev_err(card->dev, "Failed to add MCBSP controls\n"); - return err; - } - - /* AV jack detection */ - err = snd_soc_card_jack_new(rtd->card, "AV Jack", - SND_JACK_HEADSET | SND_JACK_VIDEOOUT, - &rx51_av_jack, NULL, 0); - if (err) { - dev_err(card->dev, "Failed to add AV Jack\n"); - return err; - } - - /* prepare gpio for snd_soc_jack_add_gpios */ - rx51_av_jack_gpios[0].gpio = desc_to_gpio(pdata->jack_detection_gpio); - devm_gpiod_put(card->dev, pdata->jack_detection_gpio); - - err = snd_soc_jack_add_gpios(&rx51_av_jack, - ARRAY_SIZE(rx51_av_jack_gpios), - rx51_av_jack_gpios); - if (err) { - dev_err(card->dev, "Failed to add GPIOs\n"); - return err; - } - - return err; -} - -/* Digital audio interface glue - connects codec <--> CPU */ -static struct snd_soc_dai_link rx51_dai[] = { - { - .name = "TLV320AIC34", - .stream_name = "AIC34", - .cpu_dai_name = "omap-mcbsp.2", - .codec_dai_name = "tlv320aic3x-hifi", - .platform_name = "omap-mcbsp.2", - .codec_name = "tlv320aic3x-codec.2-0018", - .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | - SND_SOC_DAIFMT_CBM_CFM, - .init = rx51_aic34_init, - .ops = &rx51_ops, - }, -}; - -static struct snd_soc_aux_dev rx51_aux_dev[] = { - { - .name = "TLV320AIC34b", - .codec_name = "tlv320aic3x-codec.2-0019", - }, - { - .name = "TPA61320A2", - .codec_name = "tpa6130a2.2-0060", - }, -}; - -static struct snd_soc_codec_conf rx51_codec_conf[] = { - { - .dev_name = "tlv320aic3x-codec.2-0019", - .name_prefix = "b", - }, - { - .dev_name = "tpa6130a2.2-0060", - .name_prefix = "TPA6130A2", - }, -}; - -/* Audio card */ -static struct snd_soc_card rx51_sound_card = { - .name = "RX-51", - .owner = THIS_MODULE, - .dai_link = rx51_dai, - .num_links = ARRAY_SIZE(rx51_dai), - .aux_dev = rx51_aux_dev, - .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), - .codec_conf = rx51_codec_conf, - .num_configs = ARRAY_SIZE(rx51_codec_conf), - .fully_routed = true, - - .controls = aic34_rx51_controls, - .num_controls = ARRAY_SIZE(aic34_rx51_controls), - .dapm_widgets = aic34_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic34_dapm_widgets), - .dapm_routes = audio_map, - .num_dapm_routes = ARRAY_SIZE(audio_map), -}; - -static int rx51_soc_probe(struct platform_device *pdev) -{ - struct rx51_audio_pdata *pdata; - struct device_node *np = pdev->dev.of_node; - struct snd_soc_card *card = &rx51_sound_card; - int err; - - if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900")) - return -ENODEV; - - card->dev = &pdev->dev; - - if (np) { - struct device_node *dai_node; - - dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); - if (!dai_node) { - dev_err(&pdev->dev, "McBSP node is not provided\n"); - return -EINVAL; - } - rx51_dai[0].cpu_dai_name = NULL; - rx51_dai[0].platform_name = NULL; - rx51_dai[0].cpu_of_node = dai_node; - rx51_dai[0].platform_of_node = dai_node; - - dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); - if (!dai_node) { - dev_err(&pdev->dev, "Codec node is not provided\n"); - return -EINVAL; - } - rx51_dai[0].codec_name = NULL; - rx51_dai[0].codec_of_node = dai_node; - - dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); - if (!dai_node) { - dev_err(&pdev->dev, "Auxiliary Codec node is not provided\n"); - return -EINVAL; - } - rx51_aux_dev[0].codec_name = NULL; - rx51_aux_dev[0].codec_of_node = dai_node; - rx51_codec_conf[0].dev_name = NULL; - rx51_codec_conf[0].of_node = dai_node; - - dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); - if (!dai_node) { - dev_err(&pdev->dev, "Headphone amplifier node is not provided\n"); - return -EINVAL; - } - rx51_aux_dev[1].codec_name = NULL; - rx51_aux_dev[1].codec_of_node = dai_node; - rx51_codec_conf[1].dev_name = NULL; - rx51_codec_conf[1].of_node = dai_node; - } - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (pdata == NULL) - return -ENOMEM; - - snd_soc_card_set_drvdata(card, pdata); - - pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, - "tvout-selection", - GPIOD_OUT_LOW); - if (IS_ERR(pdata->tvout_selection_gpio)) { - dev_err(card->dev, "could not get tvout selection gpio\n"); - return PTR_ERR(pdata->tvout_selection_gpio); - } - - pdata->jack_detection_gpio = devm_gpiod_get(card->dev, - "jack-detection", - GPIOD_ASIS); - if (IS_ERR(pdata->jack_detection_gpio)) { - dev_err(card->dev, "could not get jack detection gpio\n"); - return PTR_ERR(pdata->jack_detection_gpio); - } - - pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", - GPIOD_OUT_HIGH); - if (IS_ERR(pdata->eci_sw_gpio)) { - dev_err(card->dev, "could not get eci switch gpio\n"); - return PTR_ERR(pdata->eci_sw_gpio); - } - - pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, - "speaker-amplifier", - GPIOD_OUT_LOW); - if (IS_ERR(pdata->speaker_amp_gpio)) { - dev_err(card->dev, "could not get speaker enable gpio\n"); - return PTR_ERR(pdata->speaker_amp_gpio); - } - - err = devm_snd_soc_register_card(card->dev, card); - if (err) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err); - return err; - } - - return 0; -} - -#if defined(CONFIG_OF) -static const struct of_device_id rx51_audio_of_match[] = { - { .compatible = "nokia,n900-audio", }, - {}, -}; -MODULE_DEVICE_TABLE(of, rx51_audio_of_match); -#endif - -static struct platform_driver rx51_soc_driver = { - .driver = { - .name = "rx51-audio", - .of_match_table = of_match_ptr(rx51_audio_of_match), - }, - .probe = rx51_soc_probe, -}; - -module_platform_driver(rx51_soc_driver); - -MODULE_AUTHOR("Nokia Corporation"); -MODULE_DESCRIPTION("ALSA SoC Nokia RX-51"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:rx51-audio"); diff --git a/sound/soc/omap/sdma-pcm.c b/sound/soc/omap/sdma-pcm.c deleted file mode 100644 index 21a9c2499d48..000000000000 --- a/sound/soc/omap/sdma-pcm.c +++ /dev/null @@ -1,74 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com - * Author: Peter Ujfalusi - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sdma-pcm.h" - -static const struct snd_pcm_hardware sdma_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | - SNDRV_PCM_INFO_INTERLEAVED, - .period_bytes_min = 32, - .period_bytes_max = 64 * 1024, - .buffer_bytes_max = 128 * 1024, - .periods_min = 2, - .periods_max = 255, -}; - -static const struct snd_dmaengine_pcm_config sdma_dmaengine_pcm_config = { - .pcm_hardware = &sdma_pcm_hardware, - .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, - .compat_filter_fn = omap_dma_filter_fn, - .prealloc_buffer_size = 128 * 1024, -}; - -int sdma_pcm_platform_register(struct device *dev, - char *txdmachan, char *rxdmachan) -{ - struct snd_dmaengine_pcm_config *config; - unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT; - - /* Standard names for the directions: 'tx' and 'rx' */ - if (!txdmachan && !rxdmachan) - return devm_snd_dmaengine_pcm_register(dev, - &sdma_dmaengine_pcm_config, - flags); - - config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); - if (!config) - return -ENOMEM; - - *config = sdma_dmaengine_pcm_config; - - if (!txdmachan || !rxdmachan) { - /* One direction only PCM */ - flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX; - if (!txdmachan) { - txdmachan = rxdmachan; - rxdmachan = NULL; - } - } - - config->chan_names[0] = txdmachan; - config->chan_names[1] = rxdmachan; - - return devm_snd_dmaengine_pcm_register(dev, config, flags); -} -EXPORT_SYMBOL_GPL(sdma_pcm_platform_register); - -MODULE_AUTHOR("Peter Ujfalusi "); -MODULE_DESCRIPTION("sDMA PCM ASoC platform driver"); -MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/omap/sdma-pcm.h b/sound/soc/omap/sdma-pcm.h deleted file mode 100644 index 34a7f90b2587..000000000000 --- a/sound/soc/omap/sdma-pcm.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com - * Author: Peter Ujfalusi - */ - -#ifndef __SDMA_PCM_H__ -#define __SDMA_PCM_H__ - -#if IS_ENABLED(CONFIG_SND_SDMA_SOC) -int sdma_pcm_platform_register(struct device *dev, - char *txdmachan, char *rxdmachan); -#else -static inline int sdma_pcm_platform_register(struct device *dev, - char *txdmachan, char *rxdmachan) -{ - return -ENODEV; -} -#endif /* CONFIG_SND_SDMA_SOC */ - -#endif /* __SDMA_PCM_H__ */ diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig new file mode 100644 index 000000000000..698810c770f0 --- /dev/null +++ b/sound/soc/ti/Kconfig @@ -0,0 +1,368 @@ +menu "Audio support for Texas Instruments SoCs" +depends on DMA_OMAP || TI_EDMA || COMPILE_TEST + +config SND_SOC_TI_EDMA_PCM + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + +config SND_SOC_TI_SDMA_PCM + tristate + select SND_SOC_GENERIC_DMAENGINE_PCM + +comment "Texas Instruments DAI support for:" +config SND_SOC_DAVINCI_ASP + tristate "daVinci Audio Serial Port (ASP) or McBSP suport" + depends on ARCH_DAVINCI || COMPILE_TEST + select SND_SOC_TI_EDMA_PCM + help + Say Y or M here if you want audio support via daVinci ASP or McBSP. + The driver only implements the ASP support which is a subset of + daVinci McBSP (w/o the multichannel support). + +config SND_SOC_DAVINCI_MCASP + tristate "Multichannel Audio Serial Port (McASP) support" + select SND_SOC_TI_EDMA_PCM if TI_EDMA + select SND_SOC_TI_SDMA_PCM if DMA_OMAP + help + Say Y or M here if you want to have support for McASP IP found in + various Texas Instruments SoCs like: + - daVinci devices + - Sitara line of SoCs (AM335x, AM438x, etc) + - DRA7x devices + - Keystone devices + +config SND_SOC_DAVINCI_VCIF + tristate "daVinci Voice Interface (VCIF) suport" + depends on ARCH_DAVINCI || COMPILE_TEST + select SND_SOC_TI_EDMA_PCM + help + Say Y or M here if you want audio support via daVinci VCIF. + +config SND_SOC_OMAP_DMIC + tristate "Digital Microphone Module (DMIC) support" + depends on ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + Say Y or M here if you want to have support for DMIC IP found in + OMAP4 and OMAP5. + +config SND_OMAP_SOC_MCBSP + tristate + +config SND_SOC_OMAP_MCBSP + tristate "Multichannel Buffered Serial Port (McBSP) support" + depends on ARCH_OMAP || ARCH_OMAP1 || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + select SND_OMAP_SOC_MCBSP + help + Say Y or M here if you want to have support for McBSP IP found in + Texas Instruments OMAP1/2/3/4/5 SoCs. + +config SND_SOC_OMAP_MCPDM + tristate "Multichannel PDM Controller (McPDM) support" + depends on ARCH_OMAP4 || SOC_OMAP5 || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + Say Y or M here if you want to have support for McPDM IP found in + OMAP4 and OMAP5. + +comment "Audio support for boards with Texas Instruments SoCs" +config SND_SOC_NOKIA_N810 + tristate "SoC Audio support for Nokia N810" + depends on MACH_NOKIA_N810 && I2C + select SND_SOC_OMAP_MCBSP + select SND_SOC_TLV320AIC3X + help + Say Y or M if you want to add support for SoC audio on Nokia N810. + +config SND_SOC_NOKIA_RX51 + tristate "SoC Audio support for Nokia RX-51" + depends on ARCH_OMAP3 && I2C && GPIOLIB + select SND_SOC_OMAP_MCBSP + select SND_SOC_TLV320AIC3X + select SND_SOC_TPA6130A2 + help + Say Y or M if you want to add support for SoC audio on Nokia RX-51 + hardware. This is also known as Nokia N900 product. + +config SND_SOC_OMAP3_PANDORA + tristate "SoC Audio support for OMAP3 Pandora" + depends on ARCH_OMAP3 + depends on TWL4030_CORE + select SND_SOC_OMAP_MCBSP + select SND_SOC_TWL4030 + help + Say Y or M if you want to add support for SoC audio on the OMAP3 Pandora. + +config SND_SOC_OMAP3_TWL4030 + tristate "SoC Audio support for OMAP3 based boards with twl4030 codec" + depends on ARCH_OMAP3 || COMPILE_TEST + depends on TWL4030_CORE + select SND_SOC_OMAP_MCBSP + select SND_SOC_TWL4030 + help + Say Y or M if you want to add support for SoC audio on OMAP3 based + boards using twl4030 as codec. This driver currently supports: + - Beagleboard or Devkit8000 + - Gumstix Overo or CompuLab CM-T35/CM-T3730 + - IGEP v2 + - OMAP3EVM + - SDP3430 + - Zoom2 + +config SND_SOC_OMAP_ABE_TWL6040 + tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" + depends on TWL6040_CORE && COMMON_CLK + depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST + select SND_SOC_OMAP_DMIC + select SND_SOC_OMAP_MCPDM + select SND_SOC_TWL6040 + help + Say Y or M if you want to add support for SoC audio on OMAP boards + using ABE and twl6040 codec. This driver currently supports: + - SDP4430/Blaze boards + - PandaBoard (4430) + - PandaBoardES (4460) + - OMAP5 uEVM + +config SND_SOC_OMAP_AMS_DELTA + tristate "SoC Audio support for Amstrad E3 (Delta) videophone" + depends on MACH_AMS_DELTA && TTY + select SND_SOC_OMAP_MCBSP + select SND_SOC_CX20442 + help + Say Y or M if you want to add support for SoC audio device + connected to a handset and a speakerphone found on Amstrad E3 (Delta) + videophone. + + Note that in order to get those devices fully supported, you have to + build the kernel with standard serial port driver included and + configured for at least 4 ports. Then, from userspace, you must load + a line discipline #19 on the modem (ttyS3) serial line. The simplest + way to achieve this is to install util-linux-ng and use the included + ldattach utility. This can be started automatically from udev, + a simple rule like this one should do the trick (it does for me): + ACTION=="add", KERNEL=="controlC0", \ + RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" + +config SND_SOC_OMAP_HDMI + tristate "OMAP4/5 HDMI audio support" + depends on OMAP4_DSS_HDMI || OMAP5_DSS_HDMI || COMPILE_TEST + select SND_SOC_TI_SDMA_PCM + help + For HDMI audio to work OMAPDSS HDMI support should be + enabled. + The hdmi audio driver implements cpu-dai component using the + callbacks provided by OMAPDSS and registers the component + under DSS HDMI device. Omap-pcm is registered for platform + component also under DSS HDMI device. Dummy codec is used as + as codec component. The hdmi audio driver implements also + the card and registers it under its own platform device. + The device for the driver is registered by OMAPDSS hdmi + driver. + +config SND_SOC_OMAP_OSK5912 + tristate "SoC Audio support for omap osk5912" + depends on MACH_OMAP_OSK && I2C + select SND_SOC_OMAP_MCBSP + select SND_SOC_TLV320AIC23_I2C + help + Say Y or M if you want to add support for SoC audio on osk5912. + +config SND_SOC_DAVINCI_EVM + tristate "SoC Audio support for DaVinci EVMs" + depends on ARCH_DAVINCI && I2C + select SND_SOC_DAVINCI_ASP if MACH_DAVINCI_DM355_EVM + select SND_SOC_DAVINCI_ASP if SND_SOC_DM365_AIC3X_CODEC + select SND_SOC_DAVINCI_VCIF if SND_SOC_DM365_VOICE_CODEC + select SND_SOC_DAVINCI_ASP if MACH_DAVINCI_EVM # DM6446 + select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DM6467_EVM + select SND_SOC_SPDIF if MACH_DAVINCI_DM6467_EVM + select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DA830_EVM + select SND_SOC_DAVINCI_MCASP if MACH_DAVINCI_DA850_EVM + select SND_SOC_TLV320AIC3X + help + Say Y if you want to add support for SoC audio on the following TI + DaVinci EVM platforms: + - DM355 + - DM365 + - DM6446 + - DM6447 + - DM830 + - DM850 + +choice + prompt "DM365 codec select" + depends on SND_SOC_DAVINCI_EVM + depends on MACH_DAVINCI_DM365_EVM + +config SND_SOC_DM365_AIC3X_CODEC + bool "Audio Codec - AIC3101" + help + Say Y if you want to add support for AIC3101 audio codec + +config SND_SOC_DM365_VOICE_CODEC + bool "Voice Codec - CQ93VC" + select MFD_DAVINCI_VOICECODEC + select SND_SOC_CQ0093VC + help + Say Y if you want to add support for SoC On-chip voice codec +endchoice + +menu "Deprecated kconfig options:" + +config SND_AM33XX_SOC_EVM + tristate "SoC Audio for the AM33XX chip based boards" + depends on SOC_AM33XX && I2C + select SND_SOC_TLV320AIC3X + select SND_SOC_DAVINCI_MCASP + help + Say Y or M if you want to add support for SoC audio on AM33XX + boards using McASP and TLV320AIC3X codec. For example AM335X-EVM, + AM335X-EVMSK, and BeagelBone with AudioCape boards have this + setup. + +config SND_DAVINCI_SOC_EVM + tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" + depends on ARCH_DAVINCI && I2C + depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM + select SND_SOC_DAVINCI_EVM + help + Say Y if you want to add support for SoC audio on TI + DaVinci DM6446, DM355 or DM365 EVM platforms. + +choice + prompt "DM365 codec select" + depends on SND_DAVINCI_SOC_EVM + depends on MACH_DAVINCI_DM365_EVM + +config SND_DM365_AIC3X_CODEC + tristate "Audio Codec - AIC3101" + select SND_SOC_DM365_AIC3X_CODEC + help + Say Y if you want to add support for AIC3101 audio codec + +config SND_DM365_VOICE_CODEC + tristate "Voice Codec - CQ93VC" + select SND_SOC_DM365_VOICE_CODEC + help + Say Y if you want to add support for SoC On-chip voice codec +endchoice + +config SND_DM6467_SOC_EVM + tristate "SoC Audio support for DaVinci DM6467 EVM" + depends on MACH_DAVINCI_DM6467_EVM && I2C + select SND_SOC_DAVINCI_EVM + + help + Say Y if you want to add support for SoC audio on TI + +config SND_DA830_SOC_EVM + tristate "SoC Audio support for DA830/OMAP-L137 EVM" + depends on MACH_DAVINCI_DA830_EVM && I2C + select SND_SOC_DAVINCI_EVM + + help + Say Y if you want to add support for SoC audio on TI + DA830/OMAP-L137 EVM + +config SND_DA850_SOC_EVM + tristate "SoC Audio support for DA850/OMAP-L138 EVM" + depends on MACH_DAVINCI_DA850_EVM && I2C + select SND_SOC_DAVINCI_EVM + help + Say Y if you want to add support for SoC audio on TI + DA850/OMAP-L138 EVM + +config SND_OMAP_SOC_HDMI_AUDIO + tristate "HDMI audio support for OMAP4+ based SoCs" + select SND_SOC_OMAP_HDMI + help + For HDMI audio to work OMAPDSS HDMI support should be + enabled. + The hdmi audio driver implements cpu-dai component using the + callbacks provided by OMAPDSS and registers the component + under DSS HDMI device. Omap-pcm is registered for platform + component also under DSS HDMI device. Dummy codec is used as + as codec component. The hdmi audio driver implements also + the card and registers it under its own platform device. + The device for the driver is registered by OMAPDSS hdmi + driver. + +config SND_OMAP_SOC_N810 + tristate "SoC Audio support for Nokia N810" + depends on MACH_NOKIA_N810 && I2C + select SND_SOC_NOKIA_N810 + help + Say Y if you want to add support for SoC audio on Nokia N810. + +config SND_OMAP_SOC_RX51 + tristate "SoC Audio support for Nokia N900 (RX-51)" + depends on ARM && I2C && GPIOLIB && ARCH_OMAP3 + select SND_SOC_NOKIA_RX51 + help + Say Y if you want to add support for SoC audio on Nokia N900 + cellphone. + +config SND_OMAP_SOC_AMS_DELTA + tristate "SoC Audio support for Amstrad E3 (Delta) videophone" + depends on MACH_AMS_DELTA && TTY + select SND_SOC_OMAP_AMS_DELTA + help + Say Y if you want to add support for SoC audio device connected to + a handset and a speakerphone found on Amstrad E3 (Delta) videophone. + + Note that in order to get those devices fully supported, you have to + build the kernel with standard serial port driver included and + configured for at least 4 ports. Then, from userspace, you must load + a line discipline #19 on the modem (ttyS3) serial line. The simplest + way to achieve this is to install util-linux-ng and use the included + ldattach utility. This can be started automatically from udev, + a simple rule like this one should do the trick (it does for me): + ACTION=="add", KERNEL=="controlC0", \ + RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" + +config SND_OMAP_SOC_OSK5912 + tristate "SoC Audio support for omap osk5912" + depends on MACH_OMAP_OSK && I2C + select SND_SOC_OMAP_OSK5912 + help + Say Y if you want to add support for SoC audio on osk5912. + +config SND_OMAP_SOC_OMAP_TWL4030 + tristate "SoC Audio support for TI SoC based boards with twl4030 codec" + depends on TWL4030_CORE + select SND_SOC_OMAP3_TWL4030 + help + Say Y if you want to add support for SoC audio on TI SoC based boards + using twl4030 as c codec. This driver currently supports: + - Beagleboard or Devkit8000 + - Gumstix Overo or CompuLab CM-T35/CM-T3730 + - IGEP v2 + - OMAP3EVM + - SDP3430 + - Zoom2 + +config SND_OMAP_SOC_OMAP_ABE_TWL6040 + tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" + depends on TWL6040_CORE && COMMON_CLK + depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST + select SND_SOC_OMAP_ABE_TWL6040 + help + Say Y if you want to add support for SoC audio on OMAP boards using + ABE and twl6040 codec. This driver currently supports: + - SDP4430/Blaze boards + - PandaBoard (4430) + - PandaBoardES (4460) + - omap5-uevm (5432) + +config SND_OMAP_SOC_OMAP3_PANDORA + tristate "SoC Audio support for OMAP3 Pandora" + depends on TWL4030_CORE + select SND_SOC_OMAP3_PANDORA + help + Say Y if you want to add support for SoC audio on the OMAP3 Pandora. +endmenu + +endmenu + diff --git a/sound/soc/ti/Makefile b/sound/soc/ti/Makefile new file mode 100644 index 000000000000..08c44d56ef3e --- /dev/null +++ b/sound/soc/ti/Makefile @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: GPL-2.0 + +# Platform drivers +snd-soc-ti-edma-objs := edma-pcm.o +snd-soc-ti-sdma-objs := sdma-pcm.o + +obj-$(CONFIG_SND_SOC_TI_EDMA_PCM) += snd-soc-ti-edma.o +obj-$(CONFIG_SND_SOC_TI_SDMA_PCM) += snd-soc-ti-sdma.o + +# CPU DAI drivers +snd-soc-davinci-asp-objs := davinci-i2s.o +snd-soc-davinci-mcasp-objs := davinci-mcasp.o +snd-soc-davinci-vcif-objs := davinci-vcif.o +snd-soc-omap-dmic-objs := omap-dmic.o +snd-soc-omap-mcbsp-objs := omap-mcbsp.o omap-mcbsp-st.o +snd-soc-omap-mcpdm-objs := omap-mcpdm.o + +obj-$(CONFIG_SND_SOC_DAVINCI_ASP) += snd-soc-davinci-asp.o +obj-$(CONFIG_SND_SOC_DAVINCI_MCASP) += snd-soc-davinci-mcasp.o +obj-$(CONFIG_SND_SOC_DAVINCI_VCIF) += snd-soc-davinci-vcif.o +obj-$(CONFIG_SND_SOC_OMAP_DMIC) += snd-soc-omap-dmic.o +obj-$(CONFIG_SND_SOC_OMAP_MCBSP) += snd-soc-omap-mcbsp.o +obj-$(CONFIG_SND_SOC_OMAP_MCPDM) += snd-soc-omap-mcpdm.o + +# Machine drivers +snd-soc-davinci-evm-objs := davinci-evm.o +snd-soc-n810-objs := n810.o +snd-soc-rx51-objs := rx51.o +snd-soc-omap3pandora-objs := omap3pandora.o +snd-soc-omap-twl4030-objs := omap-twl4030.o +snd-soc-omap-abe-twl6040-objs := omap-abe-twl6040.o +snd-soc-ams-delta-objs := ams-delta.o +snd-soc-omap-hdmi-objs := omap-hdmi.o +snd-soc-osk5912-objs := osk5912.o + +obj-$(CONFIG_SND_SOC_DAVINCI_EVM) += snd-soc-davinci-evm.o +obj-$(CONFIG_SND_SOC_NOKIA_N810) += snd-soc-n810.o +obj-$(CONFIG_SND_SOC_NOKIA_RX51) += snd-soc-rx51.o +obj-$(CONFIG_SND_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o +obj-$(CONFIG_SND_SOC_OMAP3_TWL4030) += snd-soc-omap-twl4030.o +obj-$(CONFIG_SND_SOC_OMAP_ABE_TWL6040) += snd-soc-omap-abe-twl6040.o +obj-$(CONFIG_SND_SOC_OMAP_AMS_DELTA) += snd-soc-ams-delta.o +obj-$(CONFIG_SND_SOC_OMAP_HDMI) += snd-soc-omap-hdmi.o +obj-$(CONFIG_SND_SOC_OMAP_OSK5912) += snd-soc-osk5912.o diff --git a/sound/soc/ti/ams-delta.c b/sound/soc/ti/ams-delta.c new file mode 100644 index 000000000000..4dce494dfbd3 --- /dev/null +++ b/sound/soc/ti/ams-delta.c @@ -0,0 +1,594 @@ +/* + * ams-delta.c -- SoC audio for Amstrad E3 (Delta) videophone + * + * Copyright (C) 2009 Janusz Krzysztofik + * + * Initially based on sound/soc/omap/osk5912.x + * Copyright (C) 2008 Mistral Solutions + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include "omap-mcbsp.h" +#include "../codecs/cx20442.h" + +/* Board specific DAPM widgets */ +static const struct snd_soc_dapm_widget ams_delta_dapm_widgets[] = { + /* Handset */ + SND_SOC_DAPM_MIC("Mouthpiece", NULL), + SND_SOC_DAPM_HP("Earpiece", NULL), + /* Handsfree/Speakerphone */ + SND_SOC_DAPM_MIC("Microphone", NULL), + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +/* How they are connected to codec pins */ +static const struct snd_soc_dapm_route ams_delta_audio_map[] = { + {"TELIN", NULL, "Mouthpiece"}, + {"Earpiece", NULL, "TELOUT"}, + + {"MIC", NULL, "Microphone"}, + {"Speaker", NULL, "SPKOUT"}, +}; + +/* + * Controls, functional after the modem line discipline is activated. + */ + +/* Virtual switch: audio input/output constellations */ +static const char *ams_delta_audio_mode[] = + {"Mixed", "Handset", "Handsfree", "Speakerphone"}; + +/* Selection <-> pin translation */ +#define AMS_DELTA_MOUTHPIECE 0 +#define AMS_DELTA_EARPIECE 1 +#define AMS_DELTA_MICROPHONE 2 +#define AMS_DELTA_SPEAKER 3 +#define AMS_DELTA_AGC 4 + +#define AMS_DELTA_MIXED ((1 << AMS_DELTA_EARPIECE) | \ + (1 << AMS_DELTA_MICROPHONE)) +#define AMS_DELTA_HANDSET ((1 << AMS_DELTA_MOUTHPIECE) | \ + (1 << AMS_DELTA_EARPIECE)) +#define AMS_DELTA_HANDSFREE ((1 << AMS_DELTA_MICROPHONE) | \ + (1 << AMS_DELTA_SPEAKER)) +#define AMS_DELTA_SPEAKERPHONE (AMS_DELTA_HANDSFREE | (1 << AMS_DELTA_AGC)) + +static const unsigned short ams_delta_audio_mode_pins[] = { + AMS_DELTA_MIXED, + AMS_DELTA_HANDSET, + AMS_DELTA_HANDSFREE, + AMS_DELTA_SPEAKERPHONE, +}; + +static unsigned short ams_delta_audio_agc; + +/* + * Used for passing a codec structure pointer + * from the board initialization code to the tty line discipline. + */ +static struct snd_soc_component *cx20442_codec; + +static int ams_delta_set_audio_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = &card->dapm; + struct soc_enum *control = (struct soc_enum *)kcontrol->private_value; + unsigned short pins; + int pin, changed = 0; + + /* Refuse any mode changes if we are not able to control the codec. */ + if (!cx20442_codec->card->pop_time) + return -EUNATCH; + + if (ucontrol->value.enumerated.item[0] >= control->items) + return -EINVAL; + + snd_soc_dapm_mutex_lock(dapm); + + /* Translate selection to bitmap */ + pins = ams_delta_audio_mode_pins[ucontrol->value.enumerated.item[0]]; + + /* Setup pins after corresponding bits if changed */ + pin = !!(pins & (1 << AMS_DELTA_MOUTHPIECE)); + + if (pin != snd_soc_dapm_get_pin_status(dapm, "Mouthpiece")) { + changed = 1; + if (pin) + snd_soc_dapm_enable_pin_unlocked(dapm, "Mouthpiece"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece"); + } + pin = !!(pins & (1 << AMS_DELTA_EARPIECE)); + if (pin != snd_soc_dapm_get_pin_status(dapm, "Earpiece")) { + changed = 1; + if (pin) + snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Earpiece"); + } + pin = !!(pins & (1 << AMS_DELTA_MICROPHONE)); + if (pin != snd_soc_dapm_get_pin_status(dapm, "Microphone")) { + changed = 1; + if (pin) + snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Microphone"); + } + pin = !!(pins & (1 << AMS_DELTA_SPEAKER)); + if (pin != snd_soc_dapm_get_pin_status(dapm, "Speaker")) { + changed = 1; + if (pin) + snd_soc_dapm_enable_pin_unlocked(dapm, "Speaker"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); + } + pin = !!(pins & (1 << AMS_DELTA_AGC)); + if (pin != ams_delta_audio_agc) { + ams_delta_audio_agc = pin; + changed = 1; + if (pin) + snd_soc_dapm_enable_pin_unlocked(dapm, "AGCIN"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN"); + } + + if (changed) + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); + + return changed; +} + +static int ams_delta_get_audio_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + struct snd_soc_dapm_context *dapm = &card->dapm; + unsigned short pins, mode; + + pins = ((snd_soc_dapm_get_pin_status(dapm, "Mouthpiece") << + AMS_DELTA_MOUTHPIECE) | + (snd_soc_dapm_get_pin_status(dapm, "Earpiece") << + AMS_DELTA_EARPIECE)); + if (pins) + pins |= (snd_soc_dapm_get_pin_status(dapm, "Microphone") << + AMS_DELTA_MICROPHONE); + else + pins = ((snd_soc_dapm_get_pin_status(dapm, "Microphone") << + AMS_DELTA_MICROPHONE) | + (snd_soc_dapm_get_pin_status(dapm, "Speaker") << + AMS_DELTA_SPEAKER) | + (ams_delta_audio_agc << AMS_DELTA_AGC)); + + for (mode = 0; mode < ARRAY_SIZE(ams_delta_audio_mode); mode++) + if (pins == ams_delta_audio_mode_pins[mode]) + break; + + if (mode >= ARRAY_SIZE(ams_delta_audio_mode)) + return -EINVAL; + + ucontrol->value.enumerated.item[0] = mode; + + return 0; +} + +static const SOC_ENUM_SINGLE_EXT_DECL(ams_delta_audio_enum, + ams_delta_audio_mode); + +static const struct snd_kcontrol_new ams_delta_audio_controls[] = { + SOC_ENUM_EXT("Audio Mode", ams_delta_audio_enum, + ams_delta_get_audio_mode, ams_delta_set_audio_mode), +}; + +/* Hook switch */ +static struct snd_soc_jack ams_delta_hook_switch; +static struct snd_soc_jack_gpio ams_delta_hook_switch_gpios[] = { + { + .name = "hook_switch", + .report = SND_JACK_HEADSET, + .invert = 1, + .debounce_time = 150, + } +}; + +/* After we are able to control the codec over the modem, + * the hook switch can be used for dynamic DAPM reconfiguration. */ +static struct snd_soc_jack_pin ams_delta_hook_switch_pins[] = { + /* Handset */ + { + .pin = "Mouthpiece", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Earpiece", + .mask = SND_JACK_HEADPHONE, + }, + /* Handsfree */ + { + .pin = "Microphone", + .mask = SND_JACK_MICROPHONE, + .invert = 1, + }, + { + .pin = "Speaker", + .mask = SND_JACK_HEADPHONE, + .invert = 1, + }, +}; + + +/* + * Modem line discipline, required for making above controls functional. + * Activated from userspace with ldattach, possibly invoked from udev rule. + */ + +/* To actually apply any modem controlled configuration changes to the codec, + * we must connect codec DAI pins to the modem for a moment. Be careful not + * to interfere with our digital mute function that shares the same hardware. */ +static struct timer_list cx81801_timer; +static bool cx81801_cmd_pending; +static bool ams_delta_muted; +static DEFINE_SPINLOCK(ams_delta_lock); +static struct gpio_desc *gpiod_modem_codec; + +static void cx81801_timeout(struct timer_list *unused) +{ + int muted; + + spin_lock(&ams_delta_lock); + cx81801_cmd_pending = 0; + muted = ams_delta_muted; + spin_unlock(&ams_delta_lock); + + /* Reconnect the codec DAI back from the modem to the CPU DAI + * only if digital mute still off */ + if (!muted) + gpiod_set_value(gpiod_modem_codec, 0); +} + +/* Line discipline .open() */ +static int cx81801_open(struct tty_struct *tty) +{ + int ret; + + if (!cx20442_codec) + return -ENODEV; + + /* + * Pass the codec structure pointer for use by other ldisc callbacks, + * both the card and the codec specific parts. + */ + tty->disc_data = cx20442_codec; + + ret = v253_ops.open(tty); + + if (ret < 0) + tty->disc_data = NULL; + + return ret; +} + +/* Line discipline .close() */ +static void cx81801_close(struct tty_struct *tty) +{ + struct snd_soc_component *component = tty->disc_data; + struct snd_soc_dapm_context *dapm = &component->card->dapm; + + del_timer_sync(&cx81801_timer); + + /* Prevent the hook switch from further changing the DAPM pins */ + INIT_LIST_HEAD(&ams_delta_hook_switch.pins); + + if (!component) + return; + + v253_ops.close(tty); + + /* Revert back to default audio input/output constellation */ + snd_soc_dapm_mutex_lock(dapm); + + snd_soc_dapm_disable_pin_unlocked(dapm, "Mouthpiece"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Earpiece"); + snd_soc_dapm_enable_pin_unlocked(dapm, "Microphone"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Speaker"); + snd_soc_dapm_disable_pin_unlocked(dapm, "AGCIN"); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); +} + +/* Line discipline .hangup() */ +static int cx81801_hangup(struct tty_struct *tty) +{ + cx81801_close(tty); + return 0; +} + +/* Line discipline .receive_buf() */ +static void cx81801_receive(struct tty_struct *tty, + const unsigned char *cp, char *fp, int count) +{ + struct snd_soc_component *component = tty->disc_data; + const unsigned char *c; + int apply, ret; + + if (!component) + return; + + if (!component->card->pop_time) { + /* First modem response, complete setup procedure */ + + /* Initialize timer used for config pulse generation */ + timer_setup(&cx81801_timer, cx81801_timeout, 0); + + v253_ops.receive_buf(tty, cp, fp, count); + + /* Link hook switch to DAPM pins */ + ret = snd_soc_jack_add_pins(&ams_delta_hook_switch, + ARRAY_SIZE(ams_delta_hook_switch_pins), + ams_delta_hook_switch_pins); + if (ret) + dev_warn(component->dev, + "Failed to link hook switch to DAPM pins, " + "will continue with hook switch unlinked.\n"); + + return; + } + + v253_ops.receive_buf(tty, cp, fp, count); + + for (c = &cp[count - 1]; c >= cp; c--) { + if (*c != '\r') + continue; + /* Complete modem response received, apply config to codec */ + + spin_lock_bh(&ams_delta_lock); + mod_timer(&cx81801_timer, jiffies + msecs_to_jiffies(150)); + apply = !ams_delta_muted && !cx81801_cmd_pending; + cx81801_cmd_pending = 1; + spin_unlock_bh(&ams_delta_lock); + + /* Apply config pulse by connecting the codec to the modem + * if not already done */ + if (apply) + gpiod_set_value(gpiod_modem_codec, 1); + break; + } +} + +/* Line discipline .write_wakeup() */ +static void cx81801_wakeup(struct tty_struct *tty) +{ + v253_ops.write_wakeup(tty); +} + +static struct tty_ldisc_ops cx81801_ops = { + .magic = TTY_LDISC_MAGIC, + .name = "cx81801", + .owner = THIS_MODULE, + .open = cx81801_open, + .close = cx81801_close, + .hangup = cx81801_hangup, + .receive_buf = cx81801_receive, + .write_wakeup = cx81801_wakeup, +}; + + +/* + * Even if not very useful, the sound card can still work without any of the + * above functonality activated. You can still control its audio input/output + * constellation and speakerphone gain from userspace by issuing AT commands + * over the modem port. + */ + +static struct snd_soc_ops ams_delta_ops; + + +/* Digital mute implemented using modem/CPU multiplexer. + * Shares hardware with codec config pulse generation */ +static bool ams_delta_muted = 1; + +static int ams_delta_digital_mute(struct snd_soc_dai *dai, int mute) +{ + int apply; + + if (ams_delta_muted == mute) + return 0; + + spin_lock_bh(&ams_delta_lock); + ams_delta_muted = mute; + apply = !cx81801_cmd_pending; + spin_unlock_bh(&ams_delta_lock); + + if (apply) + gpiod_set_value(gpiod_modem_codec, !!mute); + return 0; +} + +/* Our codec DAI probably doesn't have its own .ops structure */ +static const struct snd_soc_dai_ops ams_delta_dai_ops = { + .digital_mute = ams_delta_digital_mute, +}; + +/* Will be used if the codec ever has its own digital_mute function */ +static int ams_delta_startup(struct snd_pcm_substream *substream) +{ + return ams_delta_digital_mute(NULL, 0); +} + +static void ams_delta_shutdown(struct snd_pcm_substream *substream) +{ + ams_delta_digital_mute(NULL, 1); +} + + +/* + * Card initialization + */ + +static int ams_delta_cx20442_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = rtd->card; + struct snd_soc_dapm_context *dapm = &card->dapm; + int ret; + /* Codec is ready, now add/activate board specific controls */ + + /* Store a pointer to the codec structure for tty ldisc use */ + cx20442_codec = rtd->codec_dai->component; + + /* Add hook switch - can be used to control the codec from userspace + * even if line discipline fails */ + ret = snd_soc_card_jack_new(card, "hook_switch", SND_JACK_HEADSET, + &ams_delta_hook_switch, NULL, 0); + if (ret) + dev_warn(card->dev, + "Failed to allocate resources for hook switch, " + "will continue without one.\n"); + else { + ret = snd_soc_jack_add_gpiods(card->dev, &ams_delta_hook_switch, + ARRAY_SIZE(ams_delta_hook_switch_gpios), + ams_delta_hook_switch_gpios); + if (ret) + dev_warn(card->dev, + "Failed to set up hook switch GPIO line, " + "will continue with hook switch inactive.\n"); + } + + gpiod_modem_codec = devm_gpiod_get(card->dev, "modem_codec", + GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_modem_codec)) { + dev_warn(card->dev, "Failed to obtain modem_codec GPIO\n"); + return 0; + } + + /* Set up digital mute if not provided by the codec */ + if (!codec_dai->driver->ops) { + codec_dai->driver->ops = &ams_delta_dai_ops; + } else { + ams_delta_ops.startup = ams_delta_startup; + ams_delta_ops.shutdown = ams_delta_shutdown; + } + + /* Register optional line discipline for over the modem control */ + ret = tty_register_ldisc(N_V253, &cx81801_ops); + if (ret) { + dev_warn(card->dev, + "Failed to register line discipline, " + "will continue without any controls.\n"); + return 0; + } + + /* Set up initial pin constellation */ + snd_soc_dapm_disable_pin(dapm, "Mouthpiece"); + snd_soc_dapm_disable_pin(dapm, "Speaker"); + snd_soc_dapm_disable_pin(dapm, "AGCIN"); + snd_soc_dapm_disable_pin(dapm, "AGCOUT"); + + return 0; +} + +/* DAI glue - connects codec <--> CPU */ +static struct snd_soc_dai_link ams_delta_dai_link = { + .name = "CX20442", + .stream_name = "CX20442", + .cpu_dai_name = "omap-mcbsp.1", + .codec_dai_name = "cx20442-voice", + .init = ams_delta_cx20442_init, + .platform_name = "omap-mcbsp.1", + .codec_name = "cx20442-codec", + .ops = &ams_delta_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, +}; + +/* Audio card driver */ +static struct snd_soc_card ams_delta_audio_card = { + .name = "AMS_DELTA", + .owner = THIS_MODULE, + .dai_link = &ams_delta_dai_link, + .num_links = 1, + + .controls = ams_delta_audio_controls, + .num_controls = ARRAY_SIZE(ams_delta_audio_controls), + .dapm_widgets = ams_delta_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ams_delta_dapm_widgets), + .dapm_routes = ams_delta_audio_map, + .num_dapm_routes = ARRAY_SIZE(ams_delta_audio_map), +}; + +/* Module init/exit */ +static int ams_delta_probe(struct platform_device *pdev) +{ + struct snd_soc_card *card = &ams_delta_audio_card; + int ret; + + card->dev = &pdev->dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + card->dev = NULL; + return ret; + } + return 0; +} + +static int ams_delta_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + if (tty_unregister_ldisc(N_V253) != 0) + dev_warn(&pdev->dev, + "failed to unregister V253 line discipline\n"); + + snd_soc_unregister_card(card); + card->dev = NULL; + return 0; +} + +#define DRV_NAME "ams-delta-audio" + +static struct platform_driver ams_delta_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = ams_delta_probe, + .remove = ams_delta_remove, +}; + +module_platform_driver(ams_delta_driver); + +MODULE_AUTHOR("Janusz Krzysztofik "); +MODULE_DESCRIPTION("ALSA SoC driver for Amstrad E3 (Delta) videophone"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/ti/davinci-evm.c b/sound/soc/ti/davinci-evm.c new file mode 100644 index 000000000000..4869d6311510 --- /dev/null +++ b/sound/soc/ti/davinci-evm.c @@ -0,0 +1,511 @@ +/* + * ASoC driver for TI DAVINCI EVM platform + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct snd_soc_card_drvdata_davinci { + struct clk *mclk; + unsigned sysclk; +}; + +static int evm_startup(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->card; + struct snd_soc_card_drvdata_davinci *drvdata = + snd_soc_card_get_drvdata(soc_card); + + if (drvdata->mclk) + return clk_prepare_enable(drvdata->mclk); + + return 0; +} + +static void evm_shutdown(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *soc_card = rtd->card; + struct snd_soc_card_drvdata_davinci *drvdata = + snd_soc_card_get_drvdata(soc_card); + + if (drvdata->mclk) + clk_disable_unprepare(drvdata->mclk); +} + +static int evm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_card *soc_card = rtd->card; + int ret = 0; + unsigned sysclk = ((struct snd_soc_card_drvdata_davinci *) + snd_soc_card_get_drvdata(soc_card))->sysclk; + + /* set the codec system clock */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, sysclk, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + /* set the CPU system clock */ + ret = snd_soc_dai_set_sysclk(cpu_dai, 0, sysclk, SND_SOC_CLOCK_OUT); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_ops evm_ops = { + .startup = evm_startup, + .shutdown = evm_shutdown, + .hw_params = evm_hw_params, +}; + +/* davinci-evm machine dapm widgets */ +static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +/* davinci-evm machine audio_mapnections to the codec pins */ +static const struct snd_soc_dapm_route audio_map[] = { + /* Headphone connected to HPLOUT, HPROUT */ + {"Headphone Jack", NULL, "HPLOUT"}, + {"Headphone Jack", NULL, "HPROUT"}, + + /* Line Out connected to LLOUT, RLOUT */ + {"Line Out", NULL, "LLOUT"}, + {"Line Out", NULL, "RLOUT"}, + + /* Mic connected to (MIC3L | MIC3R) */ + {"MIC3L", NULL, "Mic Bias"}, + {"MIC3R", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Mic Jack"}, + + /* Line In connected to (LINE1L | LINE2L), (LINE1R | LINE2R) */ + {"LINE1L", NULL, "Line In"}, + {"LINE2L", NULL, "Line In"}, + {"LINE1R", NULL, "Line In"}, + {"LINE2R", NULL, "Line In"}, +}; + +/* Logic for a aic3x as connected on a davinci-evm */ +static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct device_node *np = card->dev->of_node; + int ret; + + /* Add davinci-evm specific widgets */ + snd_soc_dapm_new_controls(&card->dapm, aic3x_dapm_widgets, + ARRAY_SIZE(aic3x_dapm_widgets)); + + if (np) { + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) + return ret; + } else { + /* Set up davinci-evm specific audio path audio_map */ + snd_soc_dapm_add_routes(&card->dapm, audio_map, + ARRAY_SIZE(audio_map)); + } + + /* not connected */ + snd_soc_dapm_nc_pin(&card->dapm, "MONO_LOUT"); + snd_soc_dapm_nc_pin(&card->dapm, "HPLCOM"); + snd_soc_dapm_nc_pin(&card->dapm, "HPRCOM"); + + return 0; +} + +/* davinci-evm digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link dm6446_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcbsp", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-001b", + .platform_name = "davinci-mcbsp", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, +}; + +static struct snd_soc_dai_link dm355_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcbsp.1", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-001b", + .platform_name = "davinci-mcbsp.1", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, +}; + +static struct snd_soc_dai_link dm365_evm_dai = { +#ifdef CONFIG_SND_SOC_DM365_AIC3X_CODEC + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcbsp", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-0018", + .platform_name = "davinci-mcbsp", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, +#elif defined(CONFIG_SND_SOC_DM365_VOICE_CODEC) + .name = "Voice Codec - CQ93VC", + .stream_name = "CQ93", + .cpu_dai_name = "davinci-vcif", + .codec_dai_name = "cq93vc-hifi", + .codec_name = "cq93vc-codec", + .platform_name = "davinci-vcif", +#endif +}; + +static struct snd_soc_dai_link dm6467_evm_dai[] = { + { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name= "davinci-mcasp.0", + .codec_dai_name = "tlv320aic3x-hifi", + .platform_name = "davinci-mcasp.0", + .codec_name = "tlv320aic3x-codec.0-001a", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, + }, + { + .name = "McASP", + .stream_name = "spdif", + .cpu_dai_name= "davinci-mcasp.1", + .codec_dai_name = "dit-hifi", + .codec_name = "spdif_dit", + .platform_name = "davinci-mcasp.1", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, + }, +}; + +static struct snd_soc_dai_link da830_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name = "davinci-mcasp.1", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-0018", + .platform_name = "davinci-mcasp.1", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, +}; + +static struct snd_soc_dai_link da850_evm_dai = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .cpu_dai_name= "davinci-mcasp.0", + .codec_dai_name = "tlv320aic3x-hifi", + .codec_name = "tlv320aic3x-codec.1-0018", + .platform_name = "davinci-mcasp.0", + .init = evm_aic3x_init, + .ops = &evm_ops, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, +}; + +/* davinci dm6446 evm audio machine driver */ +/* + * ASP0 in DM6446 EVM is clocked by U55, as configured by + * board-dm644x-evm.c using GPIOs from U18. There are six + * options; here we "know" we use a 48 KHz sample rate. + */ +static struct snd_soc_card_drvdata_davinci dm6446_snd_soc_card_drvdata = { + .sysclk = 12288000, +}; + +static struct snd_soc_card dm6446_snd_soc_card_evm = { + .name = "DaVinci DM6446 EVM", + .owner = THIS_MODULE, + .dai_link = &dm6446_evm_dai, + .num_links = 1, + .drvdata = &dm6446_snd_soc_card_drvdata, +}; + +/* davinci dm355 evm audio machine driver */ +/* ASP1 on DM355 EVM is clocked by an external oscillator */ +static struct snd_soc_card_drvdata_davinci dm355_snd_soc_card_drvdata = { + .sysclk = 27000000, +}; + +static struct snd_soc_card dm355_snd_soc_card_evm = { + .name = "DaVinci DM355 EVM", + .owner = THIS_MODULE, + .dai_link = &dm355_evm_dai, + .num_links = 1, + .drvdata = &dm355_snd_soc_card_drvdata, +}; + +/* davinci dm365 evm audio machine driver */ +static struct snd_soc_card_drvdata_davinci dm365_snd_soc_card_drvdata = { + .sysclk = 27000000, +}; + +static struct snd_soc_card dm365_snd_soc_card_evm = { + .name = "DaVinci DM365 EVM", + .owner = THIS_MODULE, + .dai_link = &dm365_evm_dai, + .num_links = 1, + .drvdata = &dm365_snd_soc_card_drvdata, +}; + +/* davinci dm6467 evm audio machine driver */ +static struct snd_soc_card_drvdata_davinci dm6467_snd_soc_card_drvdata = { + .sysclk = 27000000, +}; + +static struct snd_soc_card dm6467_snd_soc_card_evm = { + .name = "DaVinci DM6467 EVM", + .owner = THIS_MODULE, + .dai_link = dm6467_evm_dai, + .num_links = ARRAY_SIZE(dm6467_evm_dai), + .drvdata = &dm6467_snd_soc_card_drvdata, +}; + +static struct snd_soc_card_drvdata_davinci da830_snd_soc_card_drvdata = { + .sysclk = 24576000, +}; + +static struct snd_soc_card da830_snd_soc_card = { + .name = "DA830/OMAP-L137 EVM", + .owner = THIS_MODULE, + .dai_link = &da830_evm_dai, + .num_links = 1, + .drvdata = &da830_snd_soc_card_drvdata, +}; + +static struct snd_soc_card_drvdata_davinci da850_snd_soc_card_drvdata = { + .sysclk = 24576000, +}; + +static struct snd_soc_card da850_snd_soc_card = { + .name = "DA850/OMAP-L138 EVM", + .owner = THIS_MODULE, + .dai_link = &da850_evm_dai, + .num_links = 1, + .drvdata = &da850_snd_soc_card_drvdata, +}; + +#if defined(CONFIG_OF) + +/* + * The struct is used as place holder. It will be completely + * filled with data from dt node. + */ +static struct snd_soc_dai_link evm_dai_tlv320aic3x = { + .name = "TLV320AIC3X", + .stream_name = "AIC3X", + .codec_dai_name = "tlv320aic3x-hifi", + .ops = &evm_ops, + .init = evm_aic3x_init, + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_CBM_CFM | + SND_SOC_DAIFMT_IB_NF, +}; + +static const struct of_device_id davinci_evm_dt_ids[] = { + { + .compatible = "ti,da830-evm-audio", + .data = (void *) &evm_dai_tlv320aic3x, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, davinci_evm_dt_ids); + +/* davinci evm audio machine driver */ +static struct snd_soc_card evm_soc_card = { + .owner = THIS_MODULE, + .num_links = 1, +}; + +static int davinci_evm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct snd_soc_dai_link *dai; + struct snd_soc_card_drvdata_davinci *drvdata = NULL; + struct clk *mclk; + int ret = 0; + + match = of_match_device(of_match_ptr(davinci_evm_dt_ids), &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + + dai = (struct snd_soc_dai_link *) match->data; + + evm_soc_card.dai_link = dai; + + dai->codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); + if (!dai->codec_of_node) + return -EINVAL; + + dai->cpu_of_node = of_parse_phandle(np, "ti,mcasp-controller", 0); + if (!dai->cpu_of_node) + return -EINVAL; + + dai->platform_of_node = dai->cpu_of_node; + + evm_soc_card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); + if (ret) + return ret; + + mclk = devm_clk_get(&pdev->dev, "mclk"); + if (PTR_ERR(mclk) == -EPROBE_DEFER) { + return -EPROBE_DEFER; + } else if (IS_ERR(mclk)) { + dev_dbg(&pdev->dev, "mclk not found.\n"); + mclk = NULL; + } + + drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->mclk = mclk; + + ret = of_property_read_u32(np, "ti,codec-clock-rate", &drvdata->sysclk); + + if (ret < 0) { + if (!drvdata->mclk) { + dev_err(&pdev->dev, + "No clock or clock rate defined.\n"); + return -EINVAL; + } + drvdata->sysclk = clk_get_rate(drvdata->mclk); + } else if (drvdata->mclk) { + unsigned int requestd_rate = drvdata->sysclk; + clk_set_rate(drvdata->mclk, drvdata->sysclk); + drvdata->sysclk = clk_get_rate(drvdata->mclk); + if (drvdata->sysclk != requestd_rate) + dev_warn(&pdev->dev, + "Could not get requested rate %u using %u.\n", + requestd_rate, drvdata->sysclk); + } + + snd_soc_card_set_drvdata(&evm_soc_card, drvdata); + ret = devm_snd_soc_register_card(&pdev->dev, &evm_soc_card); + + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + + return ret; +} + +static struct platform_driver davinci_evm_driver = { + .probe = davinci_evm_probe, + .driver = { + .name = "davinci_evm", + .pm = &snd_soc_pm_ops, + .of_match_table = of_match_ptr(davinci_evm_dt_ids), + }, +}; +#endif + +static struct platform_device *evm_snd_device; + +static int __init evm_init(void) +{ + struct snd_soc_card *evm_snd_dev_data; + int index; + int ret; + + /* + * If dtb is there, the devices will be created dynamically. + * Only register platfrom driver structure. + */ +#if defined(CONFIG_OF) + if (of_have_populated_dt()) + return platform_driver_register(&davinci_evm_driver); +#endif + + if (machine_is_davinci_evm()) { + evm_snd_dev_data = &dm6446_snd_soc_card_evm; + index = 0; + } else if (machine_is_davinci_dm355_evm()) { + evm_snd_dev_data = &dm355_snd_soc_card_evm; + index = 1; + } else if (machine_is_davinci_dm365_evm()) { + evm_snd_dev_data = &dm365_snd_soc_card_evm; + index = 0; + } else if (machine_is_davinci_dm6467_evm()) { + evm_snd_dev_data = &dm6467_snd_soc_card_evm; + index = 0; + } else if (machine_is_davinci_da830_evm()) { + evm_snd_dev_data = &da830_snd_soc_card; + index = 1; + } else if (machine_is_davinci_da850_evm()) { + evm_snd_dev_data = &da850_snd_soc_card; + index = 0; + } else + return -EINVAL; + + evm_snd_device = platform_device_alloc("soc-audio", index); + if (!evm_snd_device) + return -ENOMEM; + + platform_set_drvdata(evm_snd_device, evm_snd_dev_data); + ret = platform_device_add(evm_snd_device); + if (ret) + platform_device_put(evm_snd_device); + + return ret; +} + +static void __exit evm_exit(void) +{ +#if defined(CONFIG_OF) + if (of_have_populated_dt()) { + platform_driver_unregister(&davinci_evm_driver); + return; + } +#endif + + platform_device_unregister(evm_snd_device); +} + +module_init(evm_init); +module_exit(evm_exit); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI EVM ASoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c new file mode 100644 index 000000000000..a3206e65e5e5 --- /dev/null +++ b/sound/soc/ti/davinci-i2s.c @@ -0,0 +1,782 @@ +/* + * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * DT support (c) 2016 Petr Kulhavy, Barix AG + * based on davinci-mcasp.c DT support + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * TODO: + * on DA850 implement HW FIFOs instead of DMA into DXR and DRR registers + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "edma-pcm.h" +#include "davinci-i2s.h" + +#define DRV_NAME "davinci-i2s" + +/* + * NOTE: terminology here is confusing. + * + * - This driver supports the "Audio Serial Port" (ASP), + * found on dm6446, dm355, and other DaVinci chips. + * + * - But it labels it a "Multi-channel Buffered Serial Port" + * (McBSP) as on older chips like the dm642 ... which was + * backward-compatible, possibly explaining that confusion. + * + * - OMAP chips have a controller called McBSP, which is + * incompatible with the DaVinci flavor of McBSP. + * + * - Newer DaVinci chips have a controller called McASP, + * incompatible with ASP and with either McBSP. + * + * In short: this uses ASP to implement I2S, not McBSP. + * And it won't be the only DaVinci implemention of I2S. + */ +#define DAVINCI_MCBSP_DRR_REG 0x00 +#define DAVINCI_MCBSP_DXR_REG 0x04 +#define DAVINCI_MCBSP_SPCR_REG 0x08 +#define DAVINCI_MCBSP_RCR_REG 0x0c +#define DAVINCI_MCBSP_XCR_REG 0x10 +#define DAVINCI_MCBSP_SRGR_REG 0x14 +#define DAVINCI_MCBSP_PCR_REG 0x24 + +#define DAVINCI_MCBSP_SPCR_RRST (1 << 0) +#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4) +#define DAVINCI_MCBSP_SPCR_XRST (1 << 16) +#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20) +#define DAVINCI_MCBSP_SPCR_GRST (1 << 22) +#define DAVINCI_MCBSP_SPCR_FRST (1 << 23) +#define DAVINCI_MCBSP_SPCR_FREE (1 << 25) + +#define DAVINCI_MCBSP_RCR_RWDLEN1(v) ((v) << 5) +#define DAVINCI_MCBSP_RCR_RFRLEN1(v) ((v) << 8) +#define DAVINCI_MCBSP_RCR_RDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_RCR_RFIG (1 << 18) +#define DAVINCI_MCBSP_RCR_RWDLEN2(v) ((v) << 21) +#define DAVINCI_MCBSP_RCR_RFRLEN2(v) ((v) << 24) +#define DAVINCI_MCBSP_RCR_RPHASE BIT(31) + +#define DAVINCI_MCBSP_XCR_XWDLEN1(v) ((v) << 5) +#define DAVINCI_MCBSP_XCR_XFRLEN1(v) ((v) << 8) +#define DAVINCI_MCBSP_XCR_XDATDLY(v) ((v) << 16) +#define DAVINCI_MCBSP_XCR_XFIG (1 << 18) +#define DAVINCI_MCBSP_XCR_XWDLEN2(v) ((v) << 21) +#define DAVINCI_MCBSP_XCR_XFRLEN2(v) ((v) << 24) +#define DAVINCI_MCBSP_XCR_XPHASE BIT(31) + +#define DAVINCI_MCBSP_SRGR_FWID(v) ((v) << 8) +#define DAVINCI_MCBSP_SRGR_FPER(v) ((v) << 16) +#define DAVINCI_MCBSP_SRGR_FSGM (1 << 28) +#define DAVINCI_MCBSP_SRGR_CLKSM BIT(29) + +#define DAVINCI_MCBSP_PCR_CLKRP (1 << 0) +#define DAVINCI_MCBSP_PCR_CLKXP (1 << 1) +#define DAVINCI_MCBSP_PCR_FSRP (1 << 2) +#define DAVINCI_MCBSP_PCR_FSXP (1 << 3) +#define DAVINCI_MCBSP_PCR_SCLKME (1 << 7) +#define DAVINCI_MCBSP_PCR_CLKRM (1 << 8) +#define DAVINCI_MCBSP_PCR_CLKXM (1 << 9) +#define DAVINCI_MCBSP_PCR_FSRM (1 << 10) +#define DAVINCI_MCBSP_PCR_FSXM (1 << 11) + +enum { + DAVINCI_MCBSP_WORD_8 = 0, + DAVINCI_MCBSP_WORD_12, + DAVINCI_MCBSP_WORD_16, + DAVINCI_MCBSP_WORD_20, + DAVINCI_MCBSP_WORD_24, + DAVINCI_MCBSP_WORD_32, +}; + +static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = 1, + [SNDRV_PCM_FORMAT_S16_LE] = 2, + [SNDRV_PCM_FORMAT_S32_LE] = 4, +}; + +static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8, + [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16, + [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32, +}; + +static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE, + [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE, +}; + +struct davinci_mcbsp_dev { + struct device *dev; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int dma_request[2]; + void __iomem *base; +#define MOD_DSP_A 0 +#define MOD_DSP_B 1 + int mode; + u32 pcr; + struct clk *clk; + /* + * Combining both channels into 1 element will at least double the + * amount of time between servicing the dma channel, increase + * effiency, and reduce the chance of overrun/underrun. But, + * it will result in the left & right channels being swapped. + * + * If relabeling the left and right channels is not possible, + * you may want to let the codec know to swap them back. + * + * It may allow x10 the amount of time to service dma requests, + * if the codec is master and is using an unnecessarily fast bit clock + * (ie. tlvaic23b), independent of the sample rate. So, having an + * entire frame at once means it can be serviced at the sample rate + * instead of the bit clock rate. + * + * In the now unlikely case that an underrun still + * occurs, both the left and right samples will be repeated + * so that no pops are heard, and the left and right channels + * won't end up being swapped because of the underrun. + */ + unsigned enable_channel_combine:1; + + unsigned int fmt; + int clk_div; + int clk_input_pin; + bool i2s_accurate_sck; +}; + +static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, + int reg, u32 val) +{ + __raw_writel(val, dev->base + reg); +} + +static inline u32 davinci_mcbsp_read_reg(struct davinci_mcbsp_dev *dev, int reg) +{ + return __raw_readl(dev->base + reg); +} + +static void toggle_clock(struct davinci_mcbsp_dev *dev, int playback) +{ + u32 m = playback ? DAVINCI_MCBSP_PCR_CLKXP : DAVINCI_MCBSP_PCR_CLKRP; + /* The clock needs to toggle to complete reset. + * So, fake it by toggling the clk polarity. + */ + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr ^ m); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, dev->pcr); +} + +static void davinci_mcbsp_start(struct davinci_mcbsp_dev *dev, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + u32 spcr; + u32 mask = playback ? DAVINCI_MCBSP_SPCR_XRST : DAVINCI_MCBSP_SPCR_RRST; + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + if (spcr & mask) { + /* start off disabled */ + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, + spcr & ~mask); + toggle_clock(dev, playback); + } + if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM | + DAVINCI_MCBSP_PCR_CLKXM | DAVINCI_MCBSP_PCR_CLKRM)) { + /* Start the sample generator */ + spcr |= DAVINCI_MCBSP_SPCR_GRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + } + + if (playback) { + /* Stop the DMA to avoid data loss */ + /* while the transmitter is out of reset to handle XSYNCERR */ + if (component->driver->ops->trigger) { + int ret = component->driver->ops->trigger(substream, + SNDRV_PCM_TRIGGER_STOP); + if (ret < 0) + printk(KERN_DEBUG "Playback DMA stop failed\n"); + } + + /* Enable the transmitter */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr |= DAVINCI_MCBSP_SPCR_XRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + + /* wait for any unexpected frame sync error to occur */ + udelay(100); + + /* Disable the transmitter to clear any outstanding XSYNCERR */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr &= ~DAVINCI_MCBSP_SPCR_XRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + toggle_clock(dev, playback); + + /* Restart the DMA */ + if (component->driver->ops->trigger) { + int ret = component->driver->ops->trigger(substream, + SNDRV_PCM_TRIGGER_START); + if (ret < 0) + printk(KERN_DEBUG "Playback DMA start failed\n"); + } + } + + /* Enable transmitter or receiver */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr |= mask; + + if (dev->pcr & (DAVINCI_MCBSP_PCR_FSXM | DAVINCI_MCBSP_PCR_FSRM)) { + /* Start frame sync */ + spcr |= DAVINCI_MCBSP_SPCR_FRST; + } + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); +} + +static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback) +{ + u32 spcr; + + /* Reset transmitter/receiver and sample rate/frame sync generators */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr &= ~(DAVINCI_MCBSP_SPCR_GRST | DAVINCI_MCBSP_SPCR_FRST); + spcr &= playback ? ~DAVINCI_MCBSP_SPCR_XRST : ~DAVINCI_MCBSP_SPCR_RRST; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + toggle_clock(dev, playback); +} + +#define DEFAULT_BITPERSAMPLE 16 + +static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + unsigned int pcr; + unsigned int srgr; + bool inv_fs = false; + /* Attention srgr is updated by hw_params! */ + srgr = DAVINCI_MCBSP_SRGR_FSGM | + DAVINCI_MCBSP_SRGR_FPER(DEFAULT_BITPERSAMPLE * 2 - 1) | + DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1); + + dev->fmt = fmt; + /* set master/slave audio interface */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* cpu is master */ + pcr = DAVINCI_MCBSP_PCR_FSXM | + DAVINCI_MCBSP_PCR_FSRM | + DAVINCI_MCBSP_PCR_CLKXM | + DAVINCI_MCBSP_PCR_CLKRM; + break; + case SND_SOC_DAIFMT_CBM_CFS: + pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM; + /* + * Selection of the clock input pin that is the + * input for the Sample Rate Generator. + * McBSP FSR and FSX are driven by the Sample Rate + * Generator. + */ + switch (dev->clk_input_pin) { + case MCBSP_CLKS: + pcr |= DAVINCI_MCBSP_PCR_CLKXM | + DAVINCI_MCBSP_PCR_CLKRM; + break; + case MCBSP_CLKR: + pcr |= DAVINCI_MCBSP_PCR_SCLKME; + break; + default: + dev_err(dev->dev, "bad clk_input_pin\n"); + return -EINVAL; + } + + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* codec is master */ + pcr = 0; + break; + default: + printk(KERN_ERR "%s:bad master\n", __func__); + return -EINVAL; + } + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* Davinci doesn't support TRUE I2S, but some codecs will have + * the left and right channels contiguous. This allows + * dsp_a mode to be used with an inverted normal frame clk. + * If your codec is master and does not have contiguous + * channels, then you will have sound on only one channel. + * Try using a different mode, or codec as slave. + * + * The TLV320AIC33 is an example of a codec where this works. + * It has a variable bit clock frequency allowing it to have + * valid data on every bit clock. + * + * The TLV320AIC23 is an example of a codec where this does not + * work. It has a fixed bit clock frequency with progressively + * more empty bit clock slots between channels as the sample + * rate is lowered. + */ + inv_fs = true; + /* fall through */ + case SND_SOC_DAIFMT_DSP_A: + dev->mode = MOD_DSP_A; + break; + case SND_SOC_DAIFMT_DSP_B: + dev->mode = MOD_DSP_B; + break; + default: + printk(KERN_ERR "%s:bad format\n", __func__); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* CLKRP Receive clock polarity, + * 1 - sampled on rising edge of CLKR + * valid on rising edge + * CLKXP Transmit clock polarity, + * 1 - clocked on falling edge of CLKX + * valid on rising edge + * FSRP Receive frame sync pol, 0 - active high + * FSXP Transmit frame sync pol, 0 - active high + */ + pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP); + break; + case SND_SOC_DAIFMT_IB_IF: + /* CLKRP Receive clock polarity, + * 0 - sampled on falling edge of CLKR + * valid on falling edge + * CLKXP Transmit clock polarity, + * 0 - clocked on rising edge of CLKX + * valid on falling edge + * FSRP Receive frame sync pol, 1 - active low + * FSXP Transmit frame sync pol, 1 - active low + */ + pcr |= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); + break; + case SND_SOC_DAIFMT_NB_IF: + /* CLKRP Receive clock polarity, + * 1 - sampled on rising edge of CLKR + * valid on rising edge + * CLKXP Transmit clock polarity, + * 1 - clocked on falling edge of CLKX + * valid on rising edge + * FSRP Receive frame sync pol, 1 - active low + * FSXP Transmit frame sync pol, 1 - active low + */ + pcr |= (DAVINCI_MCBSP_PCR_CLKXP | DAVINCI_MCBSP_PCR_CLKRP | + DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); + break; + case SND_SOC_DAIFMT_IB_NF: + /* CLKRP Receive clock polarity, + * 0 - sampled on falling edge of CLKR + * valid on falling edge + * CLKXP Transmit clock polarity, + * 0 - clocked on rising edge of CLKX + * valid on falling edge + * FSRP Receive frame sync pol, 0 - active high + * FSXP Transmit frame sync pol, 0 - active high + */ + break; + default: + return -EINVAL; + } + if (inv_fs == true) + pcr ^= (DAVINCI_MCBSP_PCR_FSXP | DAVINCI_MCBSP_PCR_FSRP); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); + dev->pcr = pcr; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_PCR_REG, pcr); + return 0; +} + +static int davinci_i2s_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); + + if (div_id != DAVINCI_MCBSP_CLKGDV) + return -ENODEV; + + dev->clk_div = div; + return 0; +} + +static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + struct snd_interval *i = NULL; + int mcbsp_word_length, master; + unsigned int rcr, xcr, srgr, clk_div, freq, framesize; + u32 spcr; + snd_pcm_format_t fmt; + unsigned element_cnt = 1; + + /* general line settings */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + } else { + spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE; + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); + } + + master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK; + fmt = params_format(params); + mcbsp_word_length = asp_word_length[fmt]; + + switch (master) { + case SND_SOC_DAIFMT_CBS_CFS: + freq = clk_get_rate(dev->clk); + srgr = DAVINCI_MCBSP_SRGR_FSGM | + DAVINCI_MCBSP_SRGR_CLKSM; + srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * + 8 - 1); + if (dev->i2s_accurate_sck) { + clk_div = 256; + do { + framesize = (freq / (--clk_div)) / + params->rate_num * + params->rate_den; + } while (((framesize < 33) || (framesize > 4095)) && + (clk_div)); + clk_div--; + srgr |= DAVINCI_MCBSP_SRGR_FPER(framesize - 1); + } else { + /* symmetric waveforms */ + clk_div = freq / (mcbsp_word_length * 16) / + params->rate_num * params->rate_den; + srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * + 16 - 1); + } + clk_div &= 0xFF; + srgr |= clk_div; + break; + case SND_SOC_DAIFMT_CBM_CFS: + srgr = DAVINCI_MCBSP_SRGR_FSGM; + clk_div = dev->clk_div - 1; + srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length * 8 - 1); + srgr |= DAVINCI_MCBSP_SRGR_FPER(mcbsp_word_length * 16 - 1); + clk_div &= 0xFF; + srgr |= clk_div; + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* Clock and frame sync given from external sources */ + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); + srgr = DAVINCI_MCBSP_SRGR_FSGM; + srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); + pr_debug("%s - %d FWID set: re-read srgr = %X\n", + __func__, __LINE__, snd_interval_value(i) - 1); + + i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); + srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); + break; + default: + return -EINVAL; + } + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); + + rcr = DAVINCI_MCBSP_RCR_RFIG; + xcr = DAVINCI_MCBSP_XCR_XFIG; + if (dev->mode == MOD_DSP_B) { + rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0); + xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0); + } else { + rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1); + xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); + } + /* Determine xfer data type */ + fmt = params_format(params); + if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) { + printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); + return -EINVAL; + } + + if (params_channels(params) == 2) { + element_cnt = 2; + if (double_fmt[fmt] && dev->enable_channel_combine) { + element_cnt = 1; + fmt = double_fmt[fmt]; + } + switch (master) { + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(0); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(0); + rcr |= DAVINCI_MCBSP_RCR_RPHASE; + xcr |= DAVINCI_MCBSP_XCR_XPHASE; + break; + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + rcr |= DAVINCI_MCBSP_RCR_RFRLEN2(element_cnt - 1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN2(element_cnt - 1); + break; + default: + return -EINVAL; + } + } + mcbsp_word_length = asp_word_length[fmt]; + + switch (master) { + case SND_SOC_DAIFMT_CBS_CFS: + case SND_SOC_DAIFMT_CBS_CFM: + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0); + break; + case SND_SOC_DAIFMT_CBM_CFM: + case SND_SOC_DAIFMT_CBM_CFS: + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); + break; + default: + return -EINVAL; + } + + rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); + xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | + DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); + else + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_RCR_REG, rcr); + + pr_debug("%s - %d srgr=%X\n", __func__, __LINE__, srgr); + pr_debug("%s - %d xcr=%X\n", __func__, __LINE__, xcr); + pr_debug("%s - %d rcr=%X\n", __func__, __LINE__, rcr); + return 0; +} + +static int davinci_i2s_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + davinci_mcbsp_stop(dev, playback); + return 0; +} + +static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + int ret = 0; + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_mcbsp_start(dev, substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_mcbsp_stop(dev, playback); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + int playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + davinci_mcbsp_stop(dev, playback); +} + +#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 + +static const struct snd_soc_dai_ops davinci_i2s_dai_ops = { + .shutdown = davinci_i2s_shutdown, + .prepare = davinci_i2s_prepare, + .trigger = davinci_i2s_trigger, + .hw_params = davinci_i2s_hw_params, + .set_fmt = davinci_i2s_set_dai_fmt, + .set_clkdiv = davinci_i2s_dai_set_clkdiv, + +}; + +static int davinci_i2s_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + +static struct snd_soc_dai_driver davinci_i2s_dai = { + .probe = davinci_i2s_dai_probe, + .playback = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .channels_min = 2, + .channels_max = 2, + .rates = DAVINCI_I2S_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &davinci_i2s_dai_ops, + +}; + +static const struct snd_soc_component_driver davinci_i2s_component = { + .name = DRV_NAME, +}; + +static int davinci_i2s_probe(struct platform_device *pdev) +{ + struct snd_dmaengine_dai_dma_data *dma_data; + struct davinci_mcbsp_dev *dev; + struct resource *mem, *res; + void __iomem *io_base; + int *dma; + int ret; + + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!mem) { + dev_warn(&pdev->dev, + "\"mpu\" mem resource not found, using index 0\n"); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + } + + io_base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(io_base)) + return PTR_ERR(io_base); + + dev = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcbsp_dev), + GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->base = io_base; + + /* setup DMA, first TX, then RX */ + dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG); + + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res) { + dma = &dev->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; + *dma = res->start; + dma_data->filter_data = dma; + } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + dma_data->filter_data = "tx"; + } else { + dev_err(&pdev->dev, "Missing DMA tx resource\n"); + return -ENODEV; + } + + dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DRR_REG); + + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res) { + dma = &dev->dma_request[SNDRV_PCM_STREAM_CAPTURE]; + *dma = res->start; + dma_data->filter_data = dma; + } else if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) { + dma_data->filter_data = "rx"; + } else { + dev_err(&pdev->dev, "Missing DMA rx resource\n"); + return -ENODEV; + } + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) + return -ENODEV; + clk_enable(dev->clk); + + dev->dev = &pdev->dev; + dev_set_drvdata(&pdev->dev, dev); + + ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component, + &davinci_i2s_dai, 1); + if (ret != 0) + goto err_release_clk; + + ret = edma_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + goto err_unregister_component; + } + + return 0; + +err_unregister_component: + snd_soc_unregister_component(&pdev->dev); +err_release_clk: + clk_disable(dev->clk); + clk_put(dev->clk); + return ret; +} + +static int davinci_i2s_remove(struct platform_device *pdev) +{ + struct davinci_mcbsp_dev *dev = dev_get_drvdata(&pdev->dev); + + snd_soc_unregister_component(&pdev->dev); + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + return 0; +} + +static const struct of_device_id davinci_i2s_match[] = { + { .compatible = "ti,da850-mcbsp" }, + {}, +}; +MODULE_DEVICE_TABLE(of, davinci_i2s_match); + +static struct platform_driver davinci_mcbsp_driver = { + .probe = davinci_i2s_probe, + .remove = davinci_i2s_remove, + .driver = { + .name = "davinci-mcbsp", + .of_match_table = of_match_ptr(davinci_i2s_match), + }, +}; + +module_platform_driver(davinci_mcbsp_driver); + +MODULE_AUTHOR("Vladimir Barinov"); +MODULE_DESCRIPTION("TI DAVINCI I2S (McBSP) SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/davinci-i2s.h b/sound/soc/ti/davinci-i2s.h new file mode 100644 index 000000000000..48dac3e2521a --- /dev/null +++ b/sound/soc/ti/davinci-i2s.h @@ -0,0 +1,20 @@ +/* + * ALSA SoC I2S (McBSP) Audio Layer for TI DAVINCI processor + * + * Author: Vladimir Barinov, + * Copyright: (C) 2007 MontaVista Software, Inc., + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _DAVINCI_I2S_H +#define _DAVINCI_I2S_H + +/* McBSP dividers */ +enum davinci_mcbsp_div { + DAVINCI_MCBSP_CLKGDV, /* Sample rate generator divider */ +}; + +#endif diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c new file mode 100644 index 000000000000..eeda6d5565bc --- /dev/null +++ b/sound/soc/ti/davinci-mcasp.c @@ -0,0 +1,2212 @@ +/* + * ALSA SoC McASP Audio Layer for TI DAVINCI processor + * + * Multi-channel Audio Serial Port Driver + * + * Author: Nirmal Pandey , + * Suresh Rajashekara + * Steve Chen + * + * Copyright: (C) 2009 MontaVista Software, Inc., + * Copyright: (C) 2009 Texas Instruments, India + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "edma-pcm.h" +#include "sdma-pcm.h" +#include "davinci-mcasp.h" + +#define MCASP_MAX_AFIFO_DEPTH 64 + +static u32 context_regs[] = { + DAVINCI_MCASP_TXFMCTL_REG, + DAVINCI_MCASP_RXFMCTL_REG, + DAVINCI_MCASP_TXFMT_REG, + DAVINCI_MCASP_RXFMT_REG, + DAVINCI_MCASP_ACLKXCTL_REG, + DAVINCI_MCASP_ACLKRCTL_REG, + DAVINCI_MCASP_AHCLKXCTL_REG, + DAVINCI_MCASP_AHCLKRCTL_REG, + DAVINCI_MCASP_PDIR_REG, + DAVINCI_MCASP_RXMASK_REG, + DAVINCI_MCASP_TXMASK_REG, + DAVINCI_MCASP_RXTDM_REG, + DAVINCI_MCASP_TXTDM_REG, +}; + +struct davinci_mcasp_context { + u32 config_regs[ARRAY_SIZE(context_regs)]; + u32 afifo_regs[2]; /* for read/write fifo control registers */ + u32 *xrsr_regs; /* for serializer configuration */ + bool pm_state; +}; + +struct davinci_mcasp_ruledata { + struct davinci_mcasp *mcasp; + int serializers; +}; + +struct davinci_mcasp { + struct snd_dmaengine_dai_dma_data dma_data[2]; + void __iomem *base; + u32 fifo_base; + struct device *dev; + struct snd_pcm_substream *substreams[2]; + unsigned int dai_fmt; + + /* McASP specific data */ + int tdm_slots; + u32 tdm_mask[2]; + int slot_width; + u8 op_mode; + u8 dismod; + u8 num_serializer; + u8 *serial_dir; + u8 version; + u8 bclk_div; + int streams; + u32 irq_request[2]; + int dma_request[2]; + + int sysclk_freq; + bool bclk_master; + + unsigned long pdir; /* Pin direction bitfield */ + + /* McASP FIFO related */ + u8 txnumevt; + u8 rxnumevt; + + bool dat_port; + + /* Used for comstraint setting on the second stream */ + u32 channels; + +#ifdef CONFIG_PM_SLEEP + struct davinci_mcasp_context context; +#endif + + struct davinci_mcasp_ruledata ruledata[2]; + struct snd_pcm_hw_constraint_list chconstr[2]; +}; + +static inline void mcasp_set_bits(struct davinci_mcasp *mcasp, u32 offset, + u32 val) +{ + void __iomem *reg = mcasp->base + offset; + __raw_writel(__raw_readl(reg) | val, reg); +} + +static inline void mcasp_clr_bits(struct davinci_mcasp *mcasp, u32 offset, + u32 val) +{ + void __iomem *reg = mcasp->base + offset; + __raw_writel((__raw_readl(reg) & ~(val)), reg); +} + +static inline void mcasp_mod_bits(struct davinci_mcasp *mcasp, u32 offset, + u32 val, u32 mask) +{ + void __iomem *reg = mcasp->base + offset; + __raw_writel((__raw_readl(reg) & ~mask) | val, reg); +} + +static inline void mcasp_set_reg(struct davinci_mcasp *mcasp, u32 offset, + u32 val) +{ + __raw_writel(val, mcasp->base + offset); +} + +static inline u32 mcasp_get_reg(struct davinci_mcasp *mcasp, u32 offset) +{ + return (u32)__raw_readl(mcasp->base + offset); +} + +static void mcasp_set_ctl_reg(struct davinci_mcasp *mcasp, u32 ctl_reg, u32 val) +{ + int i = 0; + + mcasp_set_bits(mcasp, ctl_reg, val); + + /* programming GBLCTL needs to read back from GBLCTL and verfiy */ + /* loop count is to avoid the lock-up */ + for (i = 0; i < 1000; i++) { + if ((mcasp_get_reg(mcasp, ctl_reg) & val) == val) + break; + } + + if (i == 1000 && ((mcasp_get_reg(mcasp, ctl_reg) & val) != val)) + printk(KERN_ERR "GBLCTL write error\n"); +} + +static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp) +{ + u32 rxfmctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXFMCTL_REG); + u32 aclkxctl = mcasp_get_reg(mcasp, DAVINCI_MCASP_ACLKXCTL_REG); + + return !(aclkxctl & TX_ASYNC) && rxfmctl & AFSRE; +} + +static inline void mcasp_set_clk_pdir(struct davinci_mcasp *mcasp, bool enable) +{ + u32 bit = PIN_BIT_AMUTE; + + for_each_set_bit_from(bit, &mcasp->pdir, PIN_BIT_AFSR + 1) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + +static inline void mcasp_set_axr_pdir(struct davinci_mcasp *mcasp, bool enable) +{ + u32 bit; + + for_each_set_bit(bit, &mcasp->pdir, PIN_BIT_AFSR) { + if (enable) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + else + mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDIR_REG, BIT(bit)); + } +} + +static void mcasp_start_rx(struct davinci_mcasp *mcasp) +{ + if (mcasp->rxnumevt) { /* enable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); + } + + /* Start clocks */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST); + /* + * When ASYNC == 0 the transmit and receive sections operate + * synchronously from the transmit clock and frame sync. We need to make + * sure that the TX signlas are enabled when starting reception. + */ + if (mcasp_is_synchronous(mcasp)) { + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + } + + /* Activate serializer(s) */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR); + /* Release RX state machine */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST); + /* Release Frame Sync generator */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST); + if (mcasp_is_synchronous(mcasp)) + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + + /* enable receive IRQs */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); +} + +static void mcasp_start_tx(struct davinci_mcasp *mcasp) +{ + u32 cnt; + + if (mcasp->txnumevt) { /* enable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + mcasp_set_bits(mcasp, reg, FIFO_ENABLE); + } + + /* Start clocks */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST); + mcasp_set_clk_pdir(mcasp, true); + + /* Activate serializer(s) */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR); + + /* wait for XDATA to be cleared */ + cnt = 0; + while ((mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) & XRDATA) && + (cnt < 100000)) + cnt++; + + mcasp_set_axr_pdir(mcasp, true); + + /* Release TX state machine */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST); + /* Release Frame Sync generator */ + mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST); + + /* enable transmit IRQs */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); +} + +static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream) +{ + mcasp->streams++; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + mcasp_start_tx(mcasp); + else + mcasp_start_rx(mcasp); +} + +static void mcasp_stop_rx(struct davinci_mcasp *mcasp) +{ + /* disable IRQ sources */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]); + + /* + * In synchronous mode stop the TX clocks if no other stream is + * running + */ + if (mcasp_is_synchronous(mcasp) && !mcasp->streams) { + mcasp_set_clk_pdir(mcasp, false); + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, 0); + } + + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + + if (mcasp->rxnumevt) { /* disable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + } +} + +static void mcasp_stop_tx(struct davinci_mcasp *mcasp) +{ + u32 val = 0; + + /* disable IRQ sources */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG, + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]); + + /* + * In synchronous mode keep TX clocks running if the capture stream is + * still running. + */ + if (mcasp_is_synchronous(mcasp) && mcasp->streams) + val = TXHCLKRST | TXCLKRST | TXFSRST; + else + mcasp_set_clk_pdir(mcasp, false); + + + mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val); + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + + if (mcasp->txnumevt) { /* disable FIFO */ + u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + + mcasp_clr_bits(mcasp, reg, FIFO_ENABLE); + } + + mcasp_set_axr_pdir(mcasp, false); +} + +static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream) +{ + mcasp->streams--; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) + mcasp_stop_tx(mcasp); + else + mcasp_stop_rx(mcasp); +} + +static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + struct snd_pcm_substream *substream; + u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]; + u32 handled_mask = 0; + u32 stat; + + stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG); + if (stat & XUNDRN & irq_mask) { + dev_warn(mcasp->dev, "Transmit buffer underflow\n"); + handled_mask |= XUNDRN; + + substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]; + if (substream) + snd_pcm_stop_xrun(substream); + } + + if (!handled_mask) + dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n", + stat); + + if (stat & XRERR) + handled_mask |= XRERR; + + /* Ack the handled event only */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask); + + return IRQ_RETVAL(handled_mask); +} + +static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + struct snd_pcm_substream *substream; + u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]; + u32 handled_mask = 0; + u32 stat; + + stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG); + if (stat & ROVRN & irq_mask) { + dev_warn(mcasp->dev, "Receive buffer overflow\n"); + handled_mask |= ROVRN; + + substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]; + if (substream) + snd_pcm_stop_xrun(substream); + } + + if (!handled_mask) + dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n", + stat); + + if (stat & XRERR) + handled_mask |= XRERR; + + /* Ack the handled event only */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask); + + return IRQ_RETVAL(handled_mask); +} + +static irqreturn_t davinci_mcasp_common_irq_handler(int irq, void *data) +{ + struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data; + irqreturn_t ret = IRQ_NONE; + + if (mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK]) + ret = davinci_mcasp_tx_irq_handler(irq, data); + + if (mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE]) + ret |= davinci_mcasp_rx_irq_handler(irq, data); + + return ret; +} + +static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + u32 data_delay; + bool fs_pol_rising; + bool inv_fs = false; + + if (!fmt) + return 0; + + pm_runtime_get_sync(mcasp->dev); + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + /* 1st data bit occur one ACLK cycle after the frame sync */ + data_delay = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_AC97: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + /* No delay after FS */ + data_delay = 0; + break; + case SND_SOC_DAIFMT_I2S: + /* configure a full-word SYNC pulse (LRCLK) */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + /* 1st data bit occur one ACLK cycle after the frame sync */ + data_delay = 1; + /* FS need to be inverted */ + inv_fs = true; + break; + case SND_SOC_DAIFMT_LEFT_J: + /* configure a full-word SYNC pulse (LRCLK) */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXDUR); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRDUR); + /* No delay after FS */ + data_delay = 0; + break; + default: + ret = -EINVAL; + goto out; + } + + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, FSXDLY(data_delay), + FSXDLY(3)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, FSRDLY(data_delay), + FSRDLY(3)); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* codec is clock and frame slave */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + /* BCLK */ + set_bit(PIN_BIT_ACLKX, &mcasp->pdir); + set_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + set_bit(PIN_BIT_AFSX, &mcasp->pdir); + set_bit(PIN_BIT_AFSR, &mcasp->pdir); + + mcasp->bclk_master = 1; + break; + case SND_SOC_DAIFMT_CBS_CFM: + /* codec is clock slave and frame master */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + /* BCLK */ + set_bit(PIN_BIT_ACLKX, &mcasp->pdir); + set_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + clear_bit(PIN_BIT_AFSX, &mcasp->pdir); + clear_bit(PIN_BIT_AFSR, &mcasp->pdir); + + mcasp->bclk_master = 1; + break; + case SND_SOC_DAIFMT_CBM_CFS: + /* codec is clock master and frame slave */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + /* BCLK */ + clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); + clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + set_bit(PIN_BIT_AFSX, &mcasp->pdir); + set_bit(PIN_BIT_AFSR, &mcasp->pdir); + + mcasp->bclk_master = 0; + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* codec is clock and frame master */ + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE); + + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, AFSRE); + + /* BCLK */ + clear_bit(PIN_BIT_ACLKX, &mcasp->pdir); + clear_bit(PIN_BIT_ACLKR, &mcasp->pdir); + /* Frame Sync */ + clear_bit(PIN_BIT_AFSX, &mcasp->pdir); + clear_bit(PIN_BIT_AFSR, &mcasp->pdir); + + mcasp->bclk_master = 0; + break; + default: + ret = -EINVAL; + goto out; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + fs_pol_rising = true; + break; + case SND_SOC_DAIFMT_NB_IF: + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + fs_pol_rising = false; + break; + case SND_SOC_DAIFMT_IB_IF: + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + fs_pol_rising = false; + break; + case SND_SOC_DAIFMT_NB_NF: + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, ACLKRPOL); + fs_pol_rising = true; + break; + default: + ret = -EINVAL; + goto out; + } + + if (inv_fs) + fs_pol_rising = !fs_pol_rising; + + if (fs_pol_rising) { + mcasp_clr_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + } else { + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL); + } + + mcasp->dai_fmt = fmt; +out: + pm_runtime_put(mcasp->dev); + return ret; +} + +static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, + int div, bool explicit) +{ + pm_runtime_get_sync(mcasp->dev); + switch (div_id) { + case MCASP_CLKDIV_AUXCLK: /* MCLK divider */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, + AHCLKXDIV(div - 1), AHCLKXDIV_MASK); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, + AHCLKRDIV(div - 1), AHCLKRDIV_MASK); + break; + + case MCASP_CLKDIV_BCLK: /* BCLK divider */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, + ACLKXDIV(div - 1), ACLKXDIV_MASK); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_ACLKRCTL_REG, + ACLKRDIV(div - 1), ACLKRDIV_MASK); + if (explicit) + mcasp->bclk_div = div; + break; + + case MCASP_CLKDIV_BCLK_FS_RATIO: + /* + * BCLK/LRCLK ratio descries how many bit-clock cycles + * fit into one frame. The clock ratio is given for a + * full period of data (for I2S format both left and + * right channels), so it has to be divided by number + * of tdm-slots (for I2S - divided by 2). + * Instead of storing this ratio, we calculate a new + * tdm_slot width by dividing the the ratio by the + * number of configured tdm slots. + */ + mcasp->slot_width = div / mcasp->tdm_slots; + if (div % mcasp->tdm_slots) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", + __func__, div, mcasp->tdm_slots); + break; + + default: + return -EINVAL; + } + + pm_runtime_put(mcasp->dev); + return 0; +} + +static int davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, + int div) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + return __davinci_mcasp_set_clkdiv(mcasp, div_id, div, 1); +} + +static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + pm_runtime_get_sync(mcasp->dev); + if (dir == SND_SOC_CLOCK_OUT) { + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); + set_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + } else { + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXE); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_AHCLKRCTL_REG, AHCLKRE); + clear_bit(PIN_BIT_AHCLKX, &mcasp->pdir); + } + + mcasp->sysclk_freq = freq; + + pm_runtime_put(mcasp->dev); + return 0; +} + +/* All serializers must have equal number of channels */ +static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, + int serializers) +{ + struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; + unsigned int *list = (unsigned int *) cl->list; + int slots = mcasp->tdm_slots; + int i, count = 0; + + if (mcasp->tdm_mask[stream]) + slots = hweight32(mcasp->tdm_mask[stream]); + + for (i = 1; i <= slots; i++) + list[count++] = i; + + for (i = 2; i <= serializers; i++) + list[count++] = i*slots; + + cl->count = count; + + return 0; +} + +static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp) +{ + int rx_serializers = 0, tx_serializers = 0, ret, i; + + for (i = 0; i < mcasp->num_serializer; i++) + if (mcasp->serial_dir[i] == TX_MODE) + tx_serializers++; + else if (mcasp->serial_dir[i] == RX_MODE) + rx_serializers++; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK, + tx_serializers); + if (ret) + return ret; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE, + rx_serializers); + + return ret; +} + + +static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(mcasp->dev, + "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + if (tx_mask >= (1<= (1<dev, + "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n", + tx_mask, rx_mask, slots); + return -EINVAL; + } + + if (slot_width && + (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) { + dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + mcasp->tdm_slots = slots; + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + mcasp->slot_width = slot_width; + + return davinci_mcasp_set_ch_constraints(mcasp); +} + +static int davinci_config_channel_size(struct davinci_mcasp *mcasp, + int sample_width) +{ + u32 fmt; + u32 tx_rotate = (sample_width / 4) & 0x7; + u32 mask = (1ULL << sample_width) - 1; + u32 slot_width = sample_width; + + /* + * For captured data we should not rotate, inversion and masking is + * enoguh to get the data to the right position: + * Format data from bus after reverse (XRBUF) + * S16_LE: |LSB|MSB|xxx|xxx| |xxx|xxx|MSB|LSB| + * S24_3LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB| + * S24_LE: |LSB|DAT|MSB|xxx| |xxx|MSB|DAT|LSB| + * S32_LE: |LSB|DAT|DAT|MSB| |MSB|DAT|DAT|LSB| + */ + u32 rx_rotate = 0; + + /* + * Setting the tdm slot width either with set_clkdiv() or + * set_tdm_slot() allows us to for example send 32 bits per + * channel to the codec, while only 16 of them carry audio + * payload. + */ + if (mcasp->slot_width) { + /* + * When we have more bclk then it is needed for the + * data, we need to use the rotation to move the + * received samples to have correct alignment. + */ + slot_width = mcasp->slot_width; + rx_rotate = (slot_width - sample_width) / 4; + } + + /* mapping of the XSSZ bit-field as described in the datasheet */ + fmt = (slot_width >> 1) - 1; + + if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), + RXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXSSZ(fmt), + TXSSZ(0x0F)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(tx_rotate), + TXROT(7)); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXROT(rx_rotate), + RXROT(7)); + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXMASK_REG, mask); + } + + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXMASK_REG, mask); + + return 0; +} + +static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream, + int period_words, int channels) +{ + struct snd_dmaengine_dai_dma_data *dma_data = &mcasp->dma_data[stream]; + int i; + u8 tx_ser = 0; + u8 rx_ser = 0; + u8 slots = mcasp->tdm_slots; + u8 max_active_serializers = (channels + slots - 1) / slots; + int active_serializers, numevt; + u32 reg; + /* Default configuration */ + if (mcasp->version < MCASP_VERSION_3) + mcasp_set_bits(mcasp, DAVINCI_MCASP_PWREMUMGT_REG, MCASP_SOFT); + + /* All PINS as McASP */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_PFUNC_REG, 0x00000000); + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); + } else { + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF); + mcasp_clr_bits(mcasp, DAVINCI_MCASP_REVTCTL_REG, RXDATADMADIS); + } + + for (i = 0; i < mcasp->num_serializer; i++) { + mcasp_set_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + mcasp->serial_dir[i]); + if (mcasp->serial_dir[i] == TX_MODE && + tx_ser < max_active_serializers) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + mcasp->dismod, DISMOD_MASK); + set_bit(PIN_BIT_AXR(i), &mcasp->pdir); + tx_ser++; + } else if (mcasp->serial_dir[i] == RX_MODE && + rx_ser < max_active_serializers) { + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); + rx_ser++; + } else if (mcasp->serial_dir[i] == INACTIVE_MODE) { + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + SRMOD_INACTIVE, SRMOD_MASK); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); + } else if (mcasp->serial_dir[i] == TX_MODE) { + /* Unused TX pins, clear PDIR */ + mcasp_mod_bits(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + mcasp->dismod, DISMOD_MASK); + clear_bit(PIN_BIT_AXR(i), &mcasp->pdir); + } + } + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + active_serializers = tx_ser; + numevt = mcasp->txnumevt; + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + } else { + active_serializers = rx_ser; + numevt = mcasp->rxnumevt; + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + } + + if (active_serializers < max_active_serializers) { + dev_warn(mcasp->dev, "stream has more channels (%d) than are " + "enabled in mcasp (%d)\n", channels, + active_serializers * slots); + return -EINVAL; + } + + /* AFIFO is not in use */ + if (!numevt) { + /* Configure the burst size for platform drivers */ + if (active_serializers > 1) { + /* + * If more than one serializers are in use we have one + * DMA request to provide data for all serializers. + * For example if three serializers are enabled the DMA + * need to transfer three words per DMA request. + */ + dma_data->maxburst = active_serializers; + } else { + dma_data->maxburst = 0; + } + return 0; + } + + if (period_words % active_serializers) { + dev_err(mcasp->dev, "Invalid combination of period words and " + "active serializers: %d, %d\n", period_words, + active_serializers); + return -EINVAL; + } + + /* + * Calculate the optimal AFIFO depth for platform side: + * The number of words for numevt need to be in steps of active + * serializers. + */ + numevt = (numevt / active_serializers) * active_serializers; + + while (period_words % numevt && numevt > 0) + numevt -= active_serializers; + if (numevt <= 0) + numevt = active_serializers; + + mcasp_mod_bits(mcasp, reg, active_serializers, NUMDMA_MASK); + mcasp_mod_bits(mcasp, reg, NUMEVT(numevt), NUMEVT_MASK); + + /* Configure the burst size for platform drivers */ + if (numevt == 1) + numevt = 0; + dma_data->maxburst = numevt; + + return 0; +} + +static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream, + int channels) +{ + int i, active_slots; + int total_slots; + int active_serializers; + u32 mask = 0; + u32 busel = 0; + + total_slots = mcasp->tdm_slots; + + /* + * If more than one serializer is needed, then use them with + * all the specified tdm_slots. Otherwise, one serializer can + * cope with the transaction using just as many slots as there + * are channels in the stream. + */ + if (mcasp->tdm_mask[stream]) { + active_slots = hweight32(mcasp->tdm_mask[stream]); + active_serializers = (channels + active_slots - 1) / + active_slots; + if (active_serializers == 1) { + active_slots = channels; + for (i = 0; i < total_slots; i++) { + if ((1 << i) & mcasp->tdm_mask[stream]) { + mask |= (1 << i); + if (--active_slots <= 0) + break; + } + } + } + } else { + active_serializers = (channels + total_slots - 1) / total_slots; + if (active_serializers == 1) + active_slots = channels; + else + active_slots = total_slots; + + for (i = 0; i < active_slots; i++) + mask |= (1 << i); + } + mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC); + + if (!mcasp->dat_port) + busel = TXSEL; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(total_slots), FSXMOD(0x1FF)); + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(total_slots), FSRMOD(0x1FF)); + /* + * If McASP is set to be TX/RX synchronous and the playback is + * not running already we need to configure the TX slots in + * order to have correct FSX on the bus + */ + if (mcasp_is_synchronous(mcasp) && !mcasp->channels) + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(total_slots), FSXMOD(0x1FF)); + } + + return 0; +} + +/* S/PDIF */ +static int mcasp_dit_hw_param(struct davinci_mcasp *mcasp, + unsigned int rate) +{ + u32 cs_value = 0; + u8 *cs_bytes = (u8*) &cs_value; + + /* Set the TX format : 24 bit right rotation, 32 bit slot, Pad 0 + and LSB first */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, TXROT(6) | TXSSZ(15)); + + /* Set TX frame synch : DIT Mode, 1 bit width, internal, rising edge */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXFMCTL_REG, AFSXE | FSXMOD(0x180)); + + /* Set the TX tdm : for all the slots */ + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, 0xFFFFFFFF); + + /* Set the TX clock controls : div = 1 and internal */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, ACLKXE | TX_ASYNC); + + mcasp_clr_bits(mcasp, DAVINCI_MCASP_XEVTCTL_REG, TXDATADMADIS); + + /* Only 44100 and 48000 are valid, both have the same setting */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG, AHCLKXDIV(3)); + + /* Enable the DIT */ + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXDITCTL_REG, DITEN); + + /* Set S/PDIF channel status bits */ + cs_bytes[0] = IEC958_AES0_CON_NOT_COPYRIGHT; + cs_bytes[1] = IEC958_AES1_CON_PCM_CODER; + + switch (rate) { + case 22050: + cs_bytes[3] |= IEC958_AES3_CON_FS_22050; + break; + case 24000: + cs_bytes[3] |= IEC958_AES3_CON_FS_24000; + break; + case 32000: + cs_bytes[3] |= IEC958_AES3_CON_FS_32000; + break; + case 44100: + cs_bytes[3] |= IEC958_AES3_CON_FS_44100; + break; + case 48000: + cs_bytes[3] |= IEC958_AES3_CON_FS_48000; + break; + case 88200: + cs_bytes[3] |= IEC958_AES3_CON_FS_88200; + break; + case 96000: + cs_bytes[3] |= IEC958_AES3_CON_FS_96000; + break; + case 176400: + cs_bytes[3] |= IEC958_AES3_CON_FS_176400; + break; + case 192000: + cs_bytes[3] |= IEC958_AES3_CON_FS_192000; + break; + default: + printk(KERN_WARNING "unsupported sampling rate: %d\n", rate); + return -EINVAL; + } + + mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRA_REG, cs_value); + mcasp_set_reg(mcasp, DAVINCI_MCASP_DITCSRB_REG, cs_value); + + return 0; +} + +static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, + unsigned int bclk_freq, bool set) +{ + int error_ppm; + unsigned int sysclk_freq = mcasp->sysclk_freq; + u32 reg = mcasp_get_reg(mcasp, DAVINCI_MCASP_AHCLKXCTL_REG); + int div = sysclk_freq / bclk_freq; + int rem = sysclk_freq % bclk_freq; + int aux_div = 1; + + if (div > (ACLKXDIV_MASK + 1)) { + if (reg & AHCLKXE) { + aux_div = div / (ACLKXDIV_MASK + 1); + if (div % (ACLKXDIV_MASK + 1)) + aux_div++; + + sysclk_freq /= aux_div; + div = sysclk_freq / bclk_freq; + rem = sysclk_freq % bclk_freq; + } else if (set) { + dev_warn(mcasp->dev, "Too fast reference clock (%u)\n", + sysclk_freq); + } + } + + if (rem != 0) { + if (div == 0 || + ((sysclk_freq / div) - bclk_freq) > + (bclk_freq - (sysclk_freq / (div+1)))) { + div++; + rem = rem - bclk_freq; + } + } + error_ppm = (div*1000000 + (int)div64_long(1000000LL*rem, + (int)bclk_freq)) / div - 1000000; + + if (set) { + if (error_ppm) + dev_info(mcasp->dev, "Sample-rate is off by %d PPM\n", + error_ppm); + + __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_BCLK, div, 0); + if (reg & AHCLKXE) + __davinci_mcasp_set_clkdiv(mcasp, MCASP_CLKDIV_AUXCLK, + aux_div, 0); + } + + return error_ppm; +} + +static inline u32 davinci_mcasp_tx_delay(struct davinci_mcasp *mcasp) +{ + if (!mcasp->txnumevt) + return 0; + + return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_WFIFOSTS_OFFSET); +} + +static inline u32 davinci_mcasp_rx_delay(struct davinci_mcasp *mcasp) +{ + if (!mcasp->rxnumevt) + return 0; + + return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_RFIFOSTS_OFFSET); +} + +static snd_pcm_sframes_t davinci_mcasp_delay( + struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + u32 fifo_use; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + fifo_use = davinci_mcasp_tx_delay(mcasp); + else + fifo_use = davinci_mcasp_rx_delay(mcasp); + + /* + * Divide the used locations with the channel count to get the + * FIFO usage in samples (don't care about partial samples in the + * buffer). + */ + return fifo_use / substream->runtime->channels; +} + +static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + int word_length; + int channels = params_channels(params); + int period_size = params_period_size(params); + int ret; + + ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt); + if (ret) + return ret; + + /* + * If mcasp is BCLK master, and a BCLK divider was not provided by + * the machine driver, we need to calculate the ratio. + */ + if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { + int slots = mcasp->tdm_slots; + int rate = params_rate(params); + int sbits = params_width(params); + + if (mcasp->slot_width) + sbits = mcasp->slot_width; + + davinci_mcasp_calc_clk_div(mcasp, rate * sbits * slots, true); + } + + ret = mcasp_common_hw_param(mcasp, substream->stream, + period_size * channels, channels); + if (ret) + return ret; + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + ret = mcasp_dit_hw_param(mcasp, params_rate(params)); + else + ret = mcasp_i2s_hw_param(mcasp, substream->stream, + channels); + + if (ret) + return ret; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + case SNDRV_PCM_FORMAT_S8: + word_length = 8; + break; + + case SNDRV_PCM_FORMAT_U16_LE: + case SNDRV_PCM_FORMAT_S16_LE: + word_length = 16; + break; + + case SNDRV_PCM_FORMAT_U24_3LE: + case SNDRV_PCM_FORMAT_S24_3LE: + word_length = 24; + break; + + case SNDRV_PCM_FORMAT_U24_LE: + case SNDRV_PCM_FORMAT_S24_LE: + word_length = 24; + break; + + case SNDRV_PCM_FORMAT_U32_LE: + case SNDRV_PCM_FORMAT_S32_LE: + word_length = 32; + break; + + default: + printk(KERN_WARNING "davinci-mcasp: unsupported PCM format"); + return -EINVAL; + } + + davinci_config_channel_size(mcasp, word_length); + + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) + mcasp->channels = channels; + + return 0; +} + +static int davinci_mcasp_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_mcasp_start(mcasp, substream->stream); + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_mcasp_stop(mcasp, substream->stream); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static const unsigned int davinci_mcasp_dai_rates[] = { + 8000, 11025, 16000, 22050, 32000, 44100, 48000, 64000, + 88200, 96000, 176400, 192000, +}; + +#define DAVINCI_MAX_RATE_ERROR_PPM 1000 + +static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + struct snd_interval *ri = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + int sbits = params_width(params); + int slots = rd->mcasp->tdm_slots; + struct snd_interval range; + int i; + + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + + snd_interval_any(&range); + range.empty = 1; + + for (i = 0; i < ARRAY_SIZE(davinci_mcasp_dai_rates); i++) { + if (snd_interval_test(ri, davinci_mcasp_dai_rates[i])) { + uint bclk_freq = sbits*slots* + davinci_mcasp_dai_rates[i]; + int ppm; + + ppm = davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, + false); + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + if (range.empty) { + range.min = davinci_mcasp_dai_rates[i]; + range.empty = 0; + } + range.max = davinci_mcasp_dai_rates[i]; + } + } + } + + dev_dbg(rd->mcasp->dev, + "Frequencies %d-%d -> %d-%d for %d sbits and %d tdm slots\n", + ri->min, ri->max, range.min, range.max, sbits, slots); + + return snd_interval_refine(hw_param_interval(params, rule->var), + &range); +} + +static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct davinci_mcasp_ruledata *rd = rule->private; + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_mask nfmt; + int rate = params_rate(params); + int slots = rd->mcasp->tdm_slots; + int i, count = 0; + + snd_mask_none(&nfmt); + + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + if (snd_mask_test(fmt, i)) { + uint sbits = snd_pcm_format_width(i); + int ppm; + + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + + ppm = davinci_mcasp_calc_clk_div(rd->mcasp, + sbits * slots * rate, + false); + if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { + snd_mask_set(&nfmt, i); + count++; + } + } + } + dev_dbg(rd->mcasp->dev, + "%d possible sample format for %d Hz and %d tdm slots\n", + count, rate, slots); + + return snd_mask_refine(fmt, &nfmt); +} + +static int davinci_mcasp_hw_rule_min_periodsize( + struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *period_size = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE); + struct snd_interval frames; + + snd_interval_any(&frames); + frames.min = 64; + frames.integer = 1; + + return snd_interval_refine(period_size, &frames); +} + +static int davinci_mcasp_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + struct davinci_mcasp_ruledata *ruledata = + &mcasp->ruledata[substream->stream]; + u32 max_channels = 0; + int i, dir; + int tdm_slots = mcasp->tdm_slots; + + /* Do not allow more then one stream per direction */ + if (mcasp->substreams[substream->stream]) + return -EBUSY; + + mcasp->substreams[substream->stream] = substream; + + if (mcasp->tdm_mask[substream->stream]) + tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]); + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + return 0; + + /* + * Limit the maximum allowed channels for the first stream: + * number of serializers for the direction * tdm slots per serializer + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dir = TX_MODE; + else + dir = RX_MODE; + + for (i = 0; i < mcasp->num_serializer; i++) { + if (mcasp->serial_dir[i] == dir) + max_channels++; + } + ruledata->serializers = max_channels; + max_channels *= tdm_slots; + /* + * If the already active stream has less channels than the calculated + * limnit based on the seirializers * tdm_slots, we need to use that as + * a constraint for the second stream. + * Otherwise (first stream or less allowed channels) we use the + * calculated constraint. + */ + if (mcasp->channels && mcasp->channels < max_channels) + max_channels = mcasp->channels; + /* + * But we can always allow channels upto the amount of + * the available tdm_slots. + */ + if (max_channels < tdm_slots) + max_channels = tdm_slots; + + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 0, max_channels); + + snd_pcm_hw_constraint_list(substream->runtime, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mcasp->chconstr[substream->stream]); + + if (mcasp->slot_width) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + 8, mcasp->slot_width); + + /* + * If we rely on implicit BCLK divider setting we should + * set constraints based on what we can provide. + */ + if (mcasp->bclk_master && mcasp->bclk_div == 0 && mcasp->sysclk_freq) { + int ret; + + ruledata->mcasp = mcasp; + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + davinci_mcasp_hw_rule_rate, + ruledata, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) + return ret; + ret = snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_FORMAT, + davinci_mcasp_hw_rule_format, + ruledata, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (ret) + return ret; + } + + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + davinci_mcasp_hw_rule_min_periodsize, NULL, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + + return 0; +} + +static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + + mcasp->substreams[substream->stream] = NULL; + + if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE) + return; + + if (!cpu_dai->active) + mcasp->channels = 0; +} + +static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { + .startup = davinci_mcasp_startup, + .shutdown = davinci_mcasp_shutdown, + .trigger = davinci_mcasp_trigger, + .delay = davinci_mcasp_delay, + .hw_params = davinci_mcasp_hw_params, + .set_fmt = davinci_mcasp_set_dai_fmt, + .set_clkdiv = davinci_mcasp_set_clkdiv, + .set_sysclk = davinci_mcasp_set_sysclk, + .set_tdm_slot = davinci_mcasp_set_tdm_slot, +}; + +static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int davinci_mcasp_suspend(struct snd_soc_dai *dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp_context *context = &mcasp->context; + u32 reg; + int i; + + context->pm_state = pm_runtime_active(mcasp->dev); + if (!context->pm_state) + pm_runtime_get_sync(mcasp->dev); + + for (i = 0; i < ARRAY_SIZE(context_regs); i++) + context->config_regs[i] = mcasp_get_reg(mcasp, context_regs[i]); + + if (mcasp->txnumevt) { + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + context->afifo_regs[0] = mcasp_get_reg(mcasp, reg); + } + if (mcasp->rxnumevt) { + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + context->afifo_regs[1] = mcasp_get_reg(mcasp, reg); + } + + for (i = 0; i < mcasp->num_serializer; i++) + context->xrsr_regs[i] = mcasp_get_reg(mcasp, + DAVINCI_MCASP_XRSRCTL_REG(i)); + + pm_runtime_put_sync(mcasp->dev); + + return 0; +} + +static int davinci_mcasp_resume(struct snd_soc_dai *dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + struct davinci_mcasp_context *context = &mcasp->context; + u32 reg; + int i; + + pm_runtime_get_sync(mcasp->dev); + + for (i = 0; i < ARRAY_SIZE(context_regs); i++) + mcasp_set_reg(mcasp, context_regs[i], context->config_regs[i]); + + if (mcasp->txnumevt) { + reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET; + mcasp_set_reg(mcasp, reg, context->afifo_regs[0]); + } + if (mcasp->rxnumevt) { + reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET; + mcasp_set_reg(mcasp, reg, context->afifo_regs[1]); + } + + for (i = 0; i < mcasp->num_serializer; i++) + mcasp_set_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(i), + context->xrsr_regs[i]); + + if (!context->pm_state) + pm_runtime_put_sync(mcasp->dev); + + return 0; +} +#else +#define davinci_mcasp_suspend NULL +#define davinci_mcasp_resume NULL +#endif + +#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000 + +#define DAVINCI_MCASP_PCM_FMTS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_U24_3LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE) + +static struct snd_soc_dai_driver davinci_mcasp_dai[] = { + { + .name = "davinci-mcasp.0", + .probe = davinci_mcasp_dai_probe, + .suspend = davinci_mcasp_suspend, + .resume = davinci_mcasp_resume, + .playback = { + .channels_min = 1, + .channels_max = 32 * 16, + .rates = DAVINCI_MCASP_RATES, + .formats = DAVINCI_MCASP_PCM_FMTS, + }, + .capture = { + .channels_min = 1, + .channels_max = 32 * 16, + .rates = DAVINCI_MCASP_RATES, + .formats = DAVINCI_MCASP_PCM_FMTS, + }, + .ops = &davinci_mcasp_dai_ops, + + .symmetric_samplebits = 1, + .symmetric_rates = 1, + }, + { + .name = "davinci-mcasp.1", + .probe = davinci_mcasp_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 384, + .rates = DAVINCI_MCASP_RATES, + .formats = DAVINCI_MCASP_PCM_FMTS, + }, + .ops = &davinci_mcasp_dai_ops, + }, + +}; + +static const struct snd_soc_component_driver davinci_mcasp_component = { + .name = "davinci-mcasp", +}; + +/* Some HW specific values and defaults. The rest is filled in from DT. */ +static struct davinci_mcasp_pdata dm646x_mcasp_pdata = { + .tx_dma_offset = 0x400, + .rx_dma_offset = 0x400, + .version = MCASP_VERSION_1, +}; + +static struct davinci_mcasp_pdata da830_mcasp_pdata = { + .tx_dma_offset = 0x2000, + .rx_dma_offset = 0x2000, + .version = MCASP_VERSION_2, +}; + +static struct davinci_mcasp_pdata am33xx_mcasp_pdata = { + .tx_dma_offset = 0, + .rx_dma_offset = 0, + .version = MCASP_VERSION_3, +}; + +static struct davinci_mcasp_pdata dra7_mcasp_pdata = { + /* The CFG port offset will be calculated if it is needed */ + .tx_dma_offset = 0, + .rx_dma_offset = 0, + .version = MCASP_VERSION_4, +}; + +static const struct of_device_id mcasp_dt_ids[] = { + { + .compatible = "ti,dm646x-mcasp-audio", + .data = &dm646x_mcasp_pdata, + }, + { + .compatible = "ti,da830-mcasp-audio", + .data = &da830_mcasp_pdata, + }, + { + .compatible = "ti,am33xx-mcasp-audio", + .data = &am33xx_mcasp_pdata, + }, + { + .compatible = "ti,dra7-mcasp-audio", + .data = &dra7_mcasp_pdata, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mcasp_dt_ids); + +static int mcasp_reparent_fck(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct clk *gfclk, *parent_clk; + const char *parent_name; + int ret; + + if (!node) + return 0; + + parent_name = of_get_property(node, "fck_parent", NULL); + if (!parent_name) + return 0; + + dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n"); + + gfclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(gfclk)) { + dev_err(&pdev->dev, "failed to get fck\n"); + return PTR_ERR(gfclk); + } + + parent_clk = clk_get(NULL, parent_name); + if (IS_ERR(parent_clk)) { + dev_err(&pdev->dev, "failed to get parent clock\n"); + ret = PTR_ERR(parent_clk); + goto err1; + } + + ret = clk_set_parent(gfclk, parent_clk); + if (ret) { + dev_err(&pdev->dev, "failed to reparent fck\n"); + goto err2; + } + +err2: + clk_put(parent_clk); +err1: + clk_put(gfclk); + return ret; +} + +static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of( + struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct davinci_mcasp_pdata *pdata = NULL; + const struct of_device_id *match = + of_match_device(mcasp_dt_ids, &pdev->dev); + struct of_phandle_args dma_spec; + + const u32 *of_serial_dir32; + u32 val; + int i, ret = 0; + + if (pdev->dev.platform_data) { + pdata = pdev->dev.platform_data; + pdata->dismod = DISMOD_LOW; + return pdata; + } else if (match) { + pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata), + GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + return pdata; + } + } else { + /* control shouldn't reach here. something is wrong */ + ret = -EINVAL; + goto nodata; + } + + ret = of_property_read_u32(np, "op-mode", &val); + if (ret >= 0) + pdata->op_mode = val; + + ret = of_property_read_u32(np, "tdm-slots", &val); + if (ret >= 0) { + if (val < 2 || val > 32) { + dev_err(&pdev->dev, + "tdm-slots must be in rage [2-32]\n"); + ret = -EINVAL; + goto nodata; + } + + pdata->tdm_slots = val; + } + + of_serial_dir32 = of_get_property(np, "serial-dir", &val); + val /= sizeof(u32); + if (of_serial_dir32) { + u8 *of_serial_dir = devm_kzalloc(&pdev->dev, + (sizeof(*of_serial_dir) * val), + GFP_KERNEL); + if (!of_serial_dir) { + ret = -ENOMEM; + goto nodata; + } + + for (i = 0; i < val; i++) + of_serial_dir[i] = be32_to_cpup(&of_serial_dir32[i]); + + pdata->num_serializer = val; + pdata->serial_dir = of_serial_dir; + } + + ret = of_property_match_string(np, "dma-names", "tx"); + if (ret < 0) + goto nodata; + + ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, + &dma_spec); + if (ret < 0) + goto nodata; + + pdata->tx_dma_channel = dma_spec.args[0]; + + /* RX is not valid in DIT mode */ + if (pdata->op_mode != DAVINCI_MCASP_DIT_MODE) { + ret = of_property_match_string(np, "dma-names", "rx"); + if (ret < 0) + goto nodata; + + ret = of_parse_phandle_with_args(np, "dmas", "#dma-cells", ret, + &dma_spec); + if (ret < 0) + goto nodata; + + pdata->rx_dma_channel = dma_spec.args[0]; + } + + ret = of_property_read_u32(np, "tx-num-evt", &val); + if (ret >= 0) + pdata->txnumevt = val; + + ret = of_property_read_u32(np, "rx-num-evt", &val); + if (ret >= 0) + pdata->rxnumevt = val; + + ret = of_property_read_u32(np, "sram-size-playback", &val); + if (ret >= 0) + pdata->sram_size_playback = val; + + ret = of_property_read_u32(np, "sram-size-capture", &val); + if (ret >= 0) + pdata->sram_size_capture = val; + + ret = of_property_read_u32(np, "dismod", &val); + if (ret >= 0) { + if (val == 0 || val == 2 || val == 3) { + pdata->dismod = DISMOD_VAL(val); + } else { + dev_warn(&pdev->dev, "Invalid dismod value: %u\n", val); + pdata->dismod = DISMOD_LOW; + } + } else { + pdata->dismod = DISMOD_LOW; + } + + return pdata; + +nodata: + if (ret < 0) { + dev_err(&pdev->dev, "Error populating platform data, err %d\n", + ret); + pdata = NULL; + } + return pdata; +} + +enum { + PCM_EDMA, + PCM_SDMA, +}; +static const char *sdma_prefix = "ti,omap"; + +static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp) +{ + struct dma_chan *chan; + const char *tmp; + int ret = PCM_EDMA; + + if (!mcasp->dev->of_node) + return PCM_EDMA; + + tmp = mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data; + chan = dma_request_slave_channel_reason(mcasp->dev, tmp); + if (IS_ERR(chan)) { + if (PTR_ERR(chan) != -EPROBE_DEFER) + dev_err(mcasp->dev, + "Can't verify DMA configuration (%ld)\n", + PTR_ERR(chan)); + return PTR_ERR(chan); + } + if (WARN_ON(!chan->device || !chan->device->dev)) + return -EINVAL; + + if (chan->device->dev->of_node) + ret = of_property_read_string(chan->device->dev->of_node, + "compatible", &tmp); + else + dev_dbg(mcasp->dev, "DMA controller has no of-node\n"); + + dma_release_channel(chan); + if (ret) + return ret; + + dev_dbg(mcasp->dev, "DMA controller compatible = \"%s\"\n", tmp); + if (!strncmp(tmp, sdma_prefix, strlen(sdma_prefix))) + return PCM_SDMA; + + return PCM_EDMA; +} + +static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata) +{ + int i; + u32 offset = 0; + + if (pdata->version != MCASP_VERSION_4) + return pdata->tx_dma_offset; + + for (i = 0; i < pdata->num_serializer; i++) { + if (pdata->serial_dir[i] == TX_MODE) { + if (!offset) { + offset = DAVINCI_MCASP_TXBUF_REG(i); + } else { + pr_err("%s: Only one serializer allowed!\n", + __func__); + break; + } + } + } + + return offset; +} + +static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata) +{ + int i; + u32 offset = 0; + + if (pdata->version != MCASP_VERSION_4) + return pdata->rx_dma_offset; + + for (i = 0; i < pdata->num_serializer; i++) { + if (pdata->serial_dir[i] == RX_MODE) { + if (!offset) { + offset = DAVINCI_MCASP_RXBUF_REG(i); + } else { + pr_err("%s: Only one serializer allowed!\n", + __func__); + break; + } + } + } + + return offset; +} + +static int davinci_mcasp_probe(struct platform_device *pdev) +{ + struct snd_dmaengine_dai_dma_data *dma_data; + struct resource *mem, *res, *dat; + struct davinci_mcasp_pdata *pdata; + struct davinci_mcasp *mcasp; + char *irq_name; + int *dma; + int irq; + int ret; + + if (!pdev->dev.platform_data && !pdev->dev.of_node) { + dev_err(&pdev->dev, "No platform data supplied\n"); + return -EINVAL; + } + + mcasp = devm_kzalloc(&pdev->dev, sizeof(struct davinci_mcasp), + GFP_KERNEL); + if (!mcasp) + return -ENOMEM; + + pdata = davinci_mcasp_set_pdata_from_of(pdev); + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!mem) { + dev_warn(mcasp->dev, + "\"mpu\" mem resource not found, using index 0\n"); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + } + + mcasp->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(mcasp->base)) + return PTR_ERR(mcasp->base); + + pm_runtime_enable(&pdev->dev); + + mcasp->op_mode = pdata->op_mode; + /* sanity check for tdm slots parameter */ + if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) { + if (pdata->tdm_slots < 2) { + dev_err(&pdev->dev, "invalid tdm slots: %d\n", + pdata->tdm_slots); + mcasp->tdm_slots = 2; + } else if (pdata->tdm_slots > 32) { + dev_err(&pdev->dev, "invalid tdm slots: %d\n", + pdata->tdm_slots); + mcasp->tdm_slots = 32; + } else { + mcasp->tdm_slots = pdata->tdm_slots; + } + } + + mcasp->num_serializer = pdata->num_serializer; +#ifdef CONFIG_PM_SLEEP + mcasp->context.xrsr_regs = devm_kcalloc(&pdev->dev, + mcasp->num_serializer, sizeof(u32), + GFP_KERNEL); + if (!mcasp->context.xrsr_regs) { + ret = -ENOMEM; + goto err; + } +#endif + mcasp->serial_dir = pdata->serial_dir; + mcasp->version = pdata->version; + mcasp->txnumevt = pdata->txnumevt; + mcasp->rxnumevt = pdata->rxnumevt; + mcasp->dismod = pdata->dismod; + + mcasp->dev = &pdev->dev; + + irq = platform_get_irq_byname(pdev, "common"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_common", + dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_common_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "common IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; + } + + irq = platform_get_irq_byname(pdev, "rx"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx", + dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_rx_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "RX IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN; + } + + irq = platform_get_irq_byname(pdev, "tx"); + if (irq >= 0) { + irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx", + dev_name(&pdev->dev)); + if (!irq_name) { + ret = -ENOMEM; + goto err; + } + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + davinci_mcasp_tx_irq_handler, + IRQF_ONESHOT, irq_name, mcasp); + if (ret) { + dev_err(&pdev->dev, "TX IRQ request failed\n"); + goto err; + } + + mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN; + } + + dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); + if (dat) + mcasp->dat_port = true; + + dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + if (dat) + dma_data->addr = dat->start; + else + dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata); + + dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK]; + res = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (res) + *dma = res->start; + else + *dma = pdata->tx_dma_channel; + + /* dmaengine filter data for DT and non-DT boot */ + if (pdev->dev.of_node) + dma_data->filter_data = "tx"; + else + dma_data->filter_data = dma; + + /* RX is not valid in DIT mode */ + if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { + dma_data = &mcasp->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + if (dat) + dma_data->addr = dat->start; + else + dma_data->addr = + mem->start + davinci_mcasp_rxdma_offset(pdata); + + dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE]; + res = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (res) + *dma = res->start; + else + *dma = pdata->rx_dma_channel; + + /* dmaengine filter data for DT and non-DT boot */ + if (pdev->dev.of_node) + dma_data->filter_data = "rx"; + else + dma_data->filter_data = dma; + } + + if (mcasp->version < MCASP_VERSION_3) { + mcasp->fifo_base = DAVINCI_MCASP_V2_AFIFO_BASE; + /* dma_params->dma_addr is pointing to the data port address */ + mcasp->dat_port = true; + } else { + mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; + } + + /* Allocate memory for long enough list for all possible + * scenarios. Maximum number tdm slots is 32 and there cannot + * be more serializers than given in the configuration. The + * serializer directions could be taken into account, but it + * would make code much more complex and save only couple of + * bytes. + */ + mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = + devm_kcalloc(mcasp->dev, + 32 + mcasp->num_serializer - 1, + sizeof(unsigned int), + GFP_KERNEL); + + mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = + devm_kcalloc(mcasp->dev, + 32 + mcasp->num_serializer - 1, + sizeof(unsigned int), + GFP_KERNEL); + + if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || + !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) { + ret = -ENOMEM; + goto err; + } + + ret = davinci_mcasp_set_ch_constraints(mcasp); + if (ret) + goto err; + + dev_set_drvdata(&pdev->dev, mcasp); + + mcasp_reparent_fck(pdev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &davinci_mcasp_component, + &davinci_mcasp_dai[pdata->op_mode], 1); + + if (ret != 0) + goto err; + + ret = davinci_mcasp_get_dma_type(mcasp); + switch (ret) { + case PCM_EDMA: +#if IS_BUILTIN(CONFIG_SND_SOC_TI_EDMA_PCM) || \ + (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \ + IS_MODULE(CONFIG_SND_SOC_TI_EDMA_PCM)) + ret = edma_pcm_platform_register(&pdev->dev); +#else + dev_err(&pdev->dev, "Missing SND_EDMA_SOC\n"); + ret = -EINVAL; + goto err; +#endif + break; + case PCM_SDMA: +#if IS_BUILTIN(CONFIG_SND_SOC_TI_SDMA_PCM) || \ + (IS_MODULE(CONFIG_SND_SOC_DAVINCI_MCASP) && \ + IS_MODULE(CONFIG_SND_SOC_TI_SDMA_PCM)) + ret = sdma_pcm_platform_register(&pdev->dev, NULL, NULL); +#else + dev_err(&pdev->dev, "Missing SND_SDMA_SOC\n"); + ret = -EINVAL; + goto err; +#endif + break; + default: + dev_err(&pdev->dev, "No DMA controller found (%d)\n", ret); + case -EPROBE_DEFER: + goto err; + break; + } + + if (ret) { + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + goto err; + } + + return 0; + +err: + pm_runtime_disable(&pdev->dev); + return ret; +} + +static int davinci_mcasp_remove(struct platform_device *pdev) +{ + pm_runtime_disable(&pdev->dev); + + return 0; +} + +static struct platform_driver davinci_mcasp_driver = { + .probe = davinci_mcasp_probe, + .remove = davinci_mcasp_remove, + .driver = { + .name = "davinci-mcasp", + .of_match_table = mcasp_dt_ids, + }, +}; + +module_platform_driver(davinci_mcasp_driver); + +MODULE_AUTHOR("Steve Chen"); +MODULE_DESCRIPTION("TI DAVINCI McASP SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/davinci-mcasp.h b/sound/soc/ti/davinci-mcasp.h new file mode 100644 index 000000000000..5e4060d8fe56 --- /dev/null +++ b/sound/soc/ti/davinci-mcasp.h @@ -0,0 +1,306 @@ +/* + * ALSA SoC McASP Audio Layer for TI DAVINCI processor + * + * MCASP related definitions + * + * Author: Nirmal Pandey , + * Suresh Rajashekara + * Steve Chen + * + * Copyright: (C) 2009 MontaVista Software, Inc., + * Copyright: (C) 2009 Texas Instruments, India + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef DAVINCI_MCASP_H +#define DAVINCI_MCASP_H + +/* + * McASP register definitions + */ +#define DAVINCI_MCASP_PID_REG 0x00 +#define DAVINCI_MCASP_PWREMUMGT_REG 0x04 + +#define DAVINCI_MCASP_PFUNC_REG 0x10 +#define DAVINCI_MCASP_PDIR_REG 0x14 +#define DAVINCI_MCASP_PDOUT_REG 0x18 +#define DAVINCI_MCASP_PDSET_REG 0x1c + +#define DAVINCI_MCASP_PDCLR_REG 0x20 + +#define DAVINCI_MCASP_TLGC_REG 0x30 +#define DAVINCI_MCASP_TLMR_REG 0x34 + +#define DAVINCI_MCASP_GBLCTL_REG 0x44 +#define DAVINCI_MCASP_AMUTE_REG 0x48 +#define DAVINCI_MCASP_LBCTL_REG 0x4c + +#define DAVINCI_MCASP_TXDITCTL_REG 0x50 + +#define DAVINCI_MCASP_GBLCTLR_REG 0x60 +#define DAVINCI_MCASP_RXMASK_REG 0x64 +#define DAVINCI_MCASP_RXFMT_REG 0x68 +#define DAVINCI_MCASP_RXFMCTL_REG 0x6c + +#define DAVINCI_MCASP_ACLKRCTL_REG 0x70 +#define DAVINCI_MCASP_AHCLKRCTL_REG 0x74 +#define DAVINCI_MCASP_RXTDM_REG 0x78 +#define DAVINCI_MCASP_EVTCTLR_REG 0x7c + +#define DAVINCI_MCASP_RXSTAT_REG 0x80 +#define DAVINCI_MCASP_RXTDMSLOT_REG 0x84 +#define DAVINCI_MCASP_RXCLKCHK_REG 0x88 +#define DAVINCI_MCASP_REVTCTL_REG 0x8c + +#define DAVINCI_MCASP_GBLCTLX_REG 0xa0 +#define DAVINCI_MCASP_TXMASK_REG 0xa4 +#define DAVINCI_MCASP_TXFMT_REG 0xa8 +#define DAVINCI_MCASP_TXFMCTL_REG 0xac + +#define DAVINCI_MCASP_ACLKXCTL_REG 0xb0 +#define DAVINCI_MCASP_AHCLKXCTL_REG 0xb4 +#define DAVINCI_MCASP_TXTDM_REG 0xb8 +#define DAVINCI_MCASP_EVTCTLX_REG 0xbc + +#define DAVINCI_MCASP_TXSTAT_REG 0xc0 +#define DAVINCI_MCASP_TXTDMSLOT_REG 0xc4 +#define DAVINCI_MCASP_TXCLKCHK_REG 0xc8 +#define DAVINCI_MCASP_XEVTCTL_REG 0xcc + +/* Left(even TDM Slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRA_REG 0x100 +/* Right(odd TDM slot) Channel Status Register File */ +#define DAVINCI_MCASP_DITCSRB_REG 0x118 +/* Left(even TDM slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRA_REG 0x130 +/* Right(odd TDM Slot) User Data Register File */ +#define DAVINCI_MCASP_DITUDRB_REG 0x148 + +/* Serializer n Control Register */ +#define DAVINCI_MCASP_XRSRCTL_BASE_REG 0x180 +#define DAVINCI_MCASP_XRSRCTL_REG(n) (DAVINCI_MCASP_XRSRCTL_BASE_REG + \ + (n << 2)) + +/* Transmit Buffer for Serializer n */ +#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2)) +/* Receive Buffer for Serializer n */ +#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2)) + +/* McASP FIFO Registers */ +#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010) +#define DAVINCI_MCASP_V3_AFIFO_BASE (0x1000) + +/* FIFO register offsets from AFIFO base */ +#define MCASP_WFIFOCTL_OFFSET (0x0) +#define MCASP_WFIFOSTS_OFFSET (0x4) +#define MCASP_RFIFOCTL_OFFSET (0x8) +#define MCASP_RFIFOSTS_OFFSET (0xc) + +/* + * DAVINCI_MCASP_PWREMUMGT_REG - Power Down and Emulation Management + * Register Bits + */ +#define MCASP_FREE BIT(0) +#define MCASP_SOFT BIT(1) + +/* + * DAVINCI_MCASP_PFUNC_REG - Pin Function / GPIO Enable Register Bits + * DAVINCI_MCASP_PDIR_REG - Pin Direction Register Bits + * DAVINCI_MCASP_PDOUT_REG - Pin output in GPIO mode + * DAVINCI_MCASP_PDSET_REG - Pin input in GPIO mode + */ +#define PIN_BIT_AXR(n) (n) +#define PIN_BIT_AMUTE 25 +#define PIN_BIT_ACLKX 26 +#define PIN_BIT_AHCLKX 27 +#define PIN_BIT_AFSX 28 +#define PIN_BIT_ACLKR 29 +#define PIN_BIT_AHCLKR 30 +#define PIN_BIT_AFSR 31 + +/* + * DAVINCI_MCASP_TXDITCTL_REG - Transmit DIT Control Register Bits + */ +#define DITEN BIT(0) /* Transmit DIT mode enable/disable */ +#define VA BIT(2) +#define VB BIT(3) + +/* + * DAVINCI_MCASP_TXFMT_REG - Transmit Bitstream Format Register Bits + */ +#define TXROT(val) (val) +#define TXSEL BIT(3) +#define TXSSZ(val) (val<<4) +#define TXPBIT(val) (val<<8) +#define TXPAD(val) (val<<13) +#define TXORD BIT(15) +#define FSXDLY(val) (val<<16) + +/* + * DAVINCI_MCASP_RXFMT_REG - Receive Bitstream Format Register Bits + */ +#define RXROT(val) (val) +#define RXSEL BIT(3) +#define RXSSZ(val) (val<<4) +#define RXPBIT(val) (val<<8) +#define RXPAD(val) (val<<13) +#define RXORD BIT(15) +#define FSRDLY(val) (val<<16) + +/* + * DAVINCI_MCASP_TXFMCTL_REG - Transmit Frame Control Register Bits + */ +#define FSXPOL BIT(0) +#define AFSXE BIT(1) +#define FSXDUR BIT(4) +#define FSXMOD(val) (val<<7) + +/* + * DAVINCI_MCASP_RXFMCTL_REG - Receive Frame Control Register Bits + */ +#define FSRPOL BIT(0) +#define AFSRE BIT(1) +#define FSRDUR BIT(4) +#define FSRMOD(val) (val<<7) + +/* + * DAVINCI_MCASP_ACLKXCTL_REG - Transmit Clock Control Register Bits + */ +#define ACLKXDIV(val) (val) +#define ACLKXE BIT(5) +#define TX_ASYNC BIT(6) +#define ACLKXPOL BIT(7) +#define ACLKXDIV_MASK 0x1f + +/* + * DAVINCI_MCASP_ACLKRCTL_REG Receive Clock Control Register Bits + */ +#define ACLKRDIV(val) (val) +#define ACLKRE BIT(5) +#define RX_ASYNC BIT(6) +#define ACLKRPOL BIT(7) +#define ACLKRDIV_MASK 0x1f + +/* + * DAVINCI_MCASP_AHCLKXCTL_REG - High Frequency Transmit Clock Control + * Register Bits + */ +#define AHCLKXDIV(val) (val) +#define AHCLKXPOL BIT(14) +#define AHCLKXE BIT(15) +#define AHCLKXDIV_MASK 0xfff + +/* + * DAVINCI_MCASP_AHCLKRCTL_REG - High Frequency Receive Clock Control + * Register Bits + */ +#define AHCLKRDIV(val) (val) +#define AHCLKRPOL BIT(14) +#define AHCLKRE BIT(15) +#define AHCLKRDIV_MASK 0xfff + +/* + * DAVINCI_MCASP_XRSRCTL_BASE_REG - Serializer Control Register Bits + */ +#define MODE(val) (val) +#define DISMOD_3STATE (0x0) +#define DISMOD_LOW (0x2 << 2) +#define DISMOD_HIGH (0x3 << 2) +#define DISMOD_VAL(x) ((x) << 2) +#define DISMOD_MASK DISMOD_HIGH +#define TXSTATE BIT(4) +#define RXSTATE BIT(5) +#define SRMOD_MASK 3 +#define SRMOD_INACTIVE 0 + +/* + * DAVINCI_MCASP_LBCTL_REG - Loop Back Control Register Bits + */ +#define LBEN BIT(0) +#define LBORD BIT(1) +#define LBGENMODE(val) (val<<2) + +/* + * DAVINCI_MCASP_TXTDMSLOT_REG - Transmit TDM Slot Register configuration + */ +#define TXTDMS(n) (1< + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "edma-pcm.h" +#include "davinci-i2s.h" + +#define MOD_REG_BIT(val, mask, set) do { \ + if (set) { \ + val |= mask; \ + } else { \ + val &= ~mask; \ + } \ +} while (0) + +struct davinci_vcif_dev { + struct davinci_vc *davinci_vc; + struct snd_dmaengine_dai_dma_data dma_data[2]; + int dma_request[2]; +}; + +static void davinci_vcif_start(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_vcif_dev *davinci_vcif_dev = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; + u32 w; + + /* Start the sample generator and enable transmitter/receiver */ + w = readl(davinci_vc->base + DAVINCI_VC_CTRL); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); + else + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); + + writel(w, davinci_vc->base + DAVINCI_VC_CTRL); +} + +static void davinci_vcif_stop(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct davinci_vcif_dev *davinci_vcif_dev = + snd_soc_dai_get_drvdata(rtd->cpu_dai); + struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; + u32 w; + + /* Reset transmitter/receiver and sample rate/frame sync generators */ + w = readl(davinci_vc->base + DAVINCI_VC_CTRL); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); + else + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); + + writel(w, davinci_vc->base + DAVINCI_VC_CTRL); +} + +static int davinci_vcif_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct davinci_vcif_dev *davinci_vcif_dev = snd_soc_dai_get_drvdata(dai); + struct davinci_vc *davinci_vc = davinci_vcif_dev->davinci_vc; + u32 w; + + /* Restart the codec before setup */ + davinci_vcif_stop(substream); + davinci_vcif_start(substream); + + /* General line settings */ + writel(DAVINCI_VC_CTRL_MASK, davinci_vc->base + DAVINCI_VC_CTRL); + + writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTCLR); + + writel(DAVINCI_VC_INT_MASK, davinci_vc->base + DAVINCI_VC_INTEN); + + w = readl(davinci_vc->base + DAVINCI_VC_CTRL); + + /* Determine xfer data type */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_U8: + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | + DAVINCI_VC_CTRL_RD_UNSIGNED | + DAVINCI_VC_CTRL_WD_BITS_8 | + DAVINCI_VC_CTRL_WD_UNSIGNED, 1); + break; + case SNDRV_PCM_FORMAT_S8: + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | + DAVINCI_VC_CTRL_WD_BITS_8, 1); + + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_UNSIGNED | + DAVINCI_VC_CTRL_WD_UNSIGNED, 0); + break; + case SNDRV_PCM_FORMAT_S16_LE: + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RD_BITS_8 | + DAVINCI_VC_CTRL_RD_UNSIGNED | + DAVINCI_VC_CTRL_WD_BITS_8 | + DAVINCI_VC_CTRL_WD_UNSIGNED, 0); + break; + default: + printk(KERN_WARNING "davinci-vcif: unsupported PCM format"); + return -EINVAL; + } + + writel(w, davinci_vc->base + DAVINCI_VC_CTRL); + + return 0; +} + +static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + davinci_vcif_start(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + davinci_vcif_stop(substream); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +#define DAVINCI_VCIF_RATES SNDRV_PCM_RATE_8000_48000 + +static const struct snd_soc_dai_ops davinci_vcif_dai_ops = { + .trigger = davinci_vcif_trigger, + .hw_params = davinci_vcif_hw_params, +}; + +static int davinci_vcif_dai_probe(struct snd_soc_dai *dai) +{ + struct davinci_vcif_dev *dev = snd_soc_dai_get_drvdata(dai); + + dai->playback_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK]; + dai->capture_dma_data = &dev->dma_data[SNDRV_PCM_STREAM_CAPTURE]; + + return 0; +} + +static struct snd_soc_dai_driver davinci_vcif_dai = { + .probe = davinci_vcif_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = DAVINCI_VCIF_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = DAVINCI_VCIF_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE,}, + .ops = &davinci_vcif_dai_ops, + +}; + +static const struct snd_soc_component_driver davinci_vcif_component = { + .name = "davinci-vcif", +}; + +static int davinci_vcif_probe(struct platform_device *pdev) +{ + struct davinci_vc *davinci_vc = pdev->dev.platform_data; + struct davinci_vcif_dev *davinci_vcif_dev; + int ret; + + davinci_vcif_dev = devm_kzalloc(&pdev->dev, + sizeof(struct davinci_vcif_dev), + GFP_KERNEL); + if (!davinci_vcif_dev) + return -ENOMEM; + + /* DMA tx params */ + davinci_vcif_dev->davinci_vc = davinci_vc; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].filter_data = + &davinci_vc->davinci_vcif.dma_tx_channel; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr = + davinci_vc->davinci_vcif.dma_tx_addr; + + /* DMA rx params */ + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].filter_data = + &davinci_vc->davinci_vcif.dma_rx_channel; + davinci_vcif_dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr = + davinci_vc->davinci_vcif.dma_rx_addr; + + dev_set_drvdata(&pdev->dev, davinci_vcif_dev); + + ret = devm_snd_soc_register_component(&pdev->dev, + &davinci_vcif_component, + &davinci_vcif_dai, 1); + if (ret != 0) { + dev_err(&pdev->dev, "could not register dai\n"); + return ret; + } + + ret = edma_pcm_platform_register(&pdev->dev); + if (ret) { + dev_err(&pdev->dev, "register PCM failed: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver davinci_vcif_driver = { + .probe = davinci_vcif_probe, + .driver = { + .name = "davinci-vcif", + }, +}; + +module_platform_driver(davinci_vcif_driver); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci ASoC Voice Codec Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/edma-pcm.c b/sound/soc/ti/edma-pcm.c new file mode 100644 index 000000000000..59e588abe54b --- /dev/null +++ b/sound/soc/ti/edma-pcm.c @@ -0,0 +1,59 @@ +/* + * edma-pcm.c - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx + * + * Copyright (C) 2014 Texas Instruments, Inc. + * + * Author: Peter Ujfalusi + * + * Based on: sound/soc/tegra/tegra_pcm.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "edma-pcm.h" + +static const struct snd_pcm_hardware edma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_INTERLEAVED, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 64 * 1024, + .periods_min = 2, + .periods_max = 19, /* Limit by edma dmaengine driver */ +}; + +static const struct snd_dmaengine_pcm_config edma_dmaengine_pcm_config = { + .pcm_hardware = &edma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = edma_filter_fn, + .prealloc_buffer_size = 128 * 1024, +}; + +int edma_pcm_platform_register(struct device *dev) +{ + return devm_snd_dmaengine_pcm_register(dev, &edma_dmaengine_pcm_config, + SND_DMAENGINE_PCM_FLAG_COMPAT); +} +EXPORT_SYMBOL_GPL(edma_pcm_platform_register); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("eDMA PCM ASoC platform driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/edma-pcm.h b/sound/soc/ti/edma-pcm.h new file mode 100644 index 000000000000..8058bdb0f032 --- /dev/null +++ b/sound/soc/ti/edma-pcm.h @@ -0,0 +1,32 @@ +/* + * edma-pcm.h - eDMA PCM driver using dmaengine for AM3xxx, AM4xxx + * + * Copyright (C) 2014 Texas Instruments, Inc. + * + * Author: Peter Ujfalusi + * + * Based on: sound/soc/tegra/tegra_pcm.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef __EDMA_PCM_H__ +#define __EDMA_PCM_H__ + +#if IS_ENABLED(CONFIG_SND_SOC_TI_EDMA_PCM) +int edma_pcm_platform_register(struct device *dev); +#else +static inline int edma_pcm_platform_register(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_SND_SOC_TI_EDMA_PCM */ + +#endif /* __EDMA_PCM_H__ */ diff --git a/sound/soc/ti/n810.c b/sound/soc/ti/n810.c new file mode 100644 index 000000000000..9cfefe44a75f --- /dev/null +++ b/sound/soc/ti/n810.c @@ -0,0 +1,378 @@ +/* + * n810.c -- SoC audio for Nokia N810 + * + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Jarkko Nikula + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-mcbsp.h" + +#define N810_HEADSET_AMP_GPIO 10 +#define N810_SPEAKER_AMP_GPIO 101 + +enum { + N810_JACK_DISABLED, + N810_JACK_HP, + N810_JACK_HS, + N810_JACK_MIC, +}; + +static struct clk *sys_clkout2; +static struct clk *sys_clkout2_src; +static struct clk *func96m_clk; + +static int n810_spk_func; +static int n810_jack_func; +static int n810_dmic_func; + +static void n810_ext_control(struct snd_soc_dapm_context *dapm) +{ + int hp = 0, line1l = 0; + + switch (n810_jack_func) { + case N810_JACK_HS: + line1l = 1; + case N810_JACK_HP: + hp = 1; + break; + case N810_JACK_MIC: + line1l = 1; + break; + } + + snd_soc_dapm_mutex_lock(dapm); + + if (n810_spk_func) + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); + + if (hp) + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + if (line1l) + snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); + + if (n810_dmic_func) + snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static int n810_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); + + n810_ext_control(&rtd->card->dapm); + return clk_prepare_enable(sys_clkout2); +} + +static void n810_shutdown(struct snd_pcm_substream *substream) +{ + clk_disable_unprepare(sys_clkout2); +} + +static int n810_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int err; + + /* Set the codec system clock for DAC and ADC */ + err = snd_soc_dai_set_sysclk(codec_dai, 0, 12000000, + SND_SOC_CLOCK_IN); + + return err; +} + +static const struct snd_soc_ops n810_ops = { + .startup = n810_startup, + .hw_params = n810_hw_params, + .shutdown = n810_shutdown, +}; + +static int n810_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = n810_spk_func; + + return 0; +} + +static int n810_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + + if (n810_spk_func == ucontrol->value.enumerated.item[0]) + return 0; + + n810_spk_func = ucontrol->value.enumerated.item[0]; + n810_ext_control(&card->dapm); + + return 1; +} + +static int n810_get_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = n810_jack_func; + + return 0; +} + +static int n810_set_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + + if (n810_jack_func == ucontrol->value.enumerated.item[0]) + return 0; + + n810_jack_func = ucontrol->value.enumerated.item[0]; + n810_ext_control(&card->dapm); + + return 1; +} + +static int n810_get_input(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = n810_dmic_func; + + return 0; +} + +static int n810_set_input(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + + if (n810_dmic_func == ucontrol->value.enumerated.item[0]) + return 0; + + n810_dmic_func = ucontrol->value.enumerated.item[0]; + n810_ext_control(&card->dapm); + + return 1; +} + +static int n810_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + gpio_set_value(N810_SPEAKER_AMP_GPIO, 1); + else + gpio_set_value(N810_SPEAKER_AMP_GPIO, 0); + + return 0; +} + +static int n810_jack_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + gpio_set_value(N810_HEADSET_AMP_GPIO, 1); + else + gpio_set_value(N810_HEADSET_AMP_GPIO, 0); + + return 0; +} + +static const struct snd_soc_dapm_widget aic33_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", n810_spk_event), + SND_SOC_DAPM_HP("Headphone Jack", n810_jack_event), + SND_SOC_DAPM_MIC("DMic", NULL), + SND_SOC_DAPM_MIC("HS Mic", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Headphone Jack", NULL, "HPLOUT"}, + {"Headphone Jack", NULL, "HPROUT"}, + + {"Ext Spk", NULL, "LLOUT"}, + {"Ext Spk", NULL, "RLOUT"}, + + {"DMic Rate 64", NULL, "DMic"}, + {"DMic", NULL, "Mic Bias"}, + + /* + * Note that the mic bias is coming from Retu/Vilma and we don't have + * control over it atm. The analog HS mic is not working. <- TODO + */ + {"LINE1L", NULL, "HS Mic"}, +}; + +static const char *spk_function[] = {"Off", "On"}; +static const char *jack_function[] = {"Off", "Headphone", "Headset", "Mic"}; +static const char *input_function[] = {"ADC", "Digital Mic"}; +static const struct soc_enum n810_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), +}; + +static const struct snd_kcontrol_new aic33_n810_controls[] = { + SOC_ENUM_EXT("Speaker Function", n810_enum[0], + n810_get_spk, n810_set_spk), + SOC_ENUM_EXT("Jack Function", n810_enum[1], + n810_get_jack, n810_set_jack), + SOC_ENUM_EXT("Input Select", n810_enum[2], + n810_get_input, n810_set_input), +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link n810_dai = { + .name = "TLV320AIC33", + .stream_name = "AIC33", + .cpu_dai_name = "48076000.mcbsp", + .platform_name = "48076000.mcbsp", + .codec_name = "tlv320aic3x-codec.1-0018", + .codec_dai_name = "tlv320aic3x-hifi", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .ops = &n810_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_n810 = { + .name = "N810", + .owner = THIS_MODULE, + .dai_link = &n810_dai, + .num_links = 1, + + .controls = aic33_n810_controls, + .num_controls = ARRAY_SIZE(aic33_n810_controls), + .dapm_widgets = aic33_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), + .fully_routed = true, +}; + +static struct platform_device *n810_snd_device; + +static int __init n810_soc_init(void) +{ + int err; + struct device *dev; + + if (!of_have_populated_dt() || + (!of_machine_is_compatible("nokia,n810") && + !of_machine_is_compatible("nokia,n810-wimax"))) + return -ENODEV; + + n810_snd_device = platform_device_alloc("soc-audio", -1); + if (!n810_snd_device) + return -ENOMEM; + + platform_set_drvdata(n810_snd_device, &snd_soc_n810); + err = platform_device_add(n810_snd_device); + if (err) + goto err1; + + dev = &n810_snd_device->dev; + + sys_clkout2_src = clk_get(dev, "sys_clkout2_src"); + if (IS_ERR(sys_clkout2_src)) { + dev_err(dev, "Could not get sys_clkout2_src clock\n"); + err = PTR_ERR(sys_clkout2_src); + goto err2; + } + sys_clkout2 = clk_get(dev, "sys_clkout2"); + if (IS_ERR(sys_clkout2)) { + dev_err(dev, "Could not get sys_clkout2\n"); + err = PTR_ERR(sys_clkout2); + goto err3; + } + /* + * Configure 12 MHz output on SYS_CLKOUT2. Therefore we must use + * 96 MHz as its parent in order to get 12 MHz + */ + func96m_clk = clk_get(dev, "func_96m_ck"); + if (IS_ERR(func96m_clk)) { + dev_err(dev, "Could not get func 96M clock\n"); + err = PTR_ERR(func96m_clk); + goto err4; + } + clk_set_parent(sys_clkout2_src, func96m_clk); + clk_set_rate(sys_clkout2, 12000000); + + if (WARN_ON((gpio_request(N810_HEADSET_AMP_GPIO, "hs_amp") < 0) || + (gpio_request(N810_SPEAKER_AMP_GPIO, "spk_amp") < 0))) { + err = -EINVAL; + goto err4; + } + + gpio_direction_output(N810_HEADSET_AMP_GPIO, 0); + gpio_direction_output(N810_SPEAKER_AMP_GPIO, 0); + + return 0; +err4: + clk_put(sys_clkout2); +err3: + clk_put(sys_clkout2_src); +err2: + platform_device_del(n810_snd_device); +err1: + platform_device_put(n810_snd_device); + + return err; +} + +static void __exit n810_soc_exit(void) +{ + gpio_free(N810_SPEAKER_AMP_GPIO); + gpio_free(N810_HEADSET_AMP_GPIO); + clk_put(sys_clkout2_src); + clk_put(sys_clkout2); + clk_put(func96m_clk); + + platform_device_unregister(n810_snd_device); +} + +module_init(n810_soc_init); +module_exit(n810_soc_exit); + +MODULE_AUTHOR("Jarkko Nikula "); +MODULE_DESCRIPTION("ALSA SoC Nokia N810"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/omap-abe-twl6040.c b/sound/soc/ti/omap-abe-twl6040.c new file mode 100644 index 000000000000..fed45b41f9d3 --- /dev/null +++ b/sound/soc/ti/omap-abe-twl6040.c @@ -0,0 +1,353 @@ +/* + * omap-abe-twl6040.c -- SoC audio for TI OMAP based boards with ABE and + * twl6040 codec + * + * Author: Misael Lopez Cruz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-dmic.h" +#include "omap-mcpdm.h" +#include "../codecs/twl6040.h" + +struct abe_twl6040 { + struct snd_soc_card card; + struct snd_soc_dai_link dai_links[2]; + int jack_detection; /* board can detect jack events */ + int mclk_freq; /* MCLK frequency speed for twl6040 */ +}; + +static struct platform_device *dmic_codec_dev; + +static int omap_abe_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_card *card = rtd->card; + struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); + int clk_id, freq; + int ret; + + clk_id = twl6040_get_clk_id(codec_dai->component); + if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) + freq = priv->mclk_freq; + else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) + freq = 32768; + else + return -EINVAL; + + /* set the codec mclk */ + ret = snd_soc_dai_set_sysclk(codec_dai, clk_id, freq, + SND_SOC_CLOCK_IN); + if (ret) { + printk(KERN_ERR "can't set codec system clock\n"); + return ret; + } + return ret; +} + +static const struct snd_soc_ops omap_abe_ops = { + .hw_params = omap_abe_hw_params, +}; + +static int omap_abe_dmic_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret = 0; + + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_SYSCLK_PAD_CLKS, + 19200000, SND_SOC_CLOCK_IN); + if (ret < 0) { + printk(KERN_ERR "can't set DMIC cpu system clock\n"); + return ret; + } + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_DMIC_ABE_DMIC_CLK, 2400000, + SND_SOC_CLOCK_OUT); + if (ret < 0) { + printk(KERN_ERR "can't set DMIC output clock\n"); + return ret; + } + return 0; +} + +static struct snd_soc_ops omap_abe_dmic_ops = { + .hw_params = omap_abe_dmic_hw_params, +}; + +/* Headset jack */ +static struct snd_soc_jack hs_jack; + +/*Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +/* SDP4430 machine DAPM */ +static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { + /* Outputs */ + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_SPK("Earphone Spk", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + SND_SOC_DAPM_SPK("Vibrator", NULL), + + /* Inputs */ + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Main Handset Mic", NULL), + SND_SOC_DAPM_MIC("Sub Handset Mic", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + + /* Digital microphones */ + SND_SOC_DAPM_MIC("Digital Mic", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Routings for outputs */ + {"Headset Stereophone", NULL, "HSOL"}, + {"Headset Stereophone", NULL, "HSOR"}, + + {"Earphone Spk", NULL, "EP"}, + + {"Ext Spk", NULL, "HFL"}, + {"Ext Spk", NULL, "HFR"}, + + {"Line Out", NULL, "AUXL"}, + {"Line Out", NULL, "AUXR"}, + + {"Vibrator", NULL, "VIBRAL"}, + {"Vibrator", NULL, "VIBRAR"}, + + /* Routings for inputs */ + {"HSMIC", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "Headset Mic Bias"}, + + {"MAINMIC", NULL, "Main Handset Mic"}, + {"Main Handset Mic", NULL, "Main Mic Bias"}, + + {"SUBMIC", NULL, "Sub Handset Mic"}, + {"Sub Handset Mic", NULL, "Main Mic Bias"}, + + {"AFML", NULL, "Line In"}, + {"AFMR", NULL, "Line In"}, +}; + +static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_card *card = rtd->card; + struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card); + int hs_trim; + int ret = 0; + + /* + * Configure McPDM offset cancellation based on the HSOTRIM value from + * twl6040. + */ + hs_trim = twl6040_get_trim_value(component, TWL6040_TRIM_HSOTRIM); + omap_mcpdm_configure_dn_offsets(rtd, TWL6040_HSF_TRIM_LEFT(hs_trim), + TWL6040_HSF_TRIM_RIGHT(hs_trim)); + + /* Headset jack detection only if it is supported */ + if (priv->jack_detection) { + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret) + return ret; + + twl6040_hs_jack_detect(component, &hs_jack, SND_JACK_HEADSET); + } + + return 0; +} + +static const struct snd_soc_dapm_route dmic_audio_map[] = { + {"DMic", NULL, "Digital Mic"}, + {"Digital Mic", NULL, "Digital Mic1 Bias"}, +}; + +static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; + + return snd_soc_dapm_add_routes(dapm, dmic_audio_map, + ARRAY_SIZE(dmic_audio_map)); +} + +static int omap_abe_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct snd_soc_card *card; + struct device_node *dai_node; + struct abe_twl6040 *priv; + int num_links = 0; + int ret = 0; + + if (!node) { + dev_err(&pdev->dev, "of node is missing.\n"); + return -ENODEV; + } + + priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + card = &priv->card; + card->dev = &pdev->dev; + card->owner = THIS_MODULE; + card->dapm_widgets = twl6040_dapm_widgets; + card->num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets); + card->dapm_routes = audio_map; + card->num_dapm_routes = ARRAY_SIZE(audio_map); + + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } + + ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing"); + if (ret) { + dev_err(&pdev->dev, "Error while parsing DAPM routing\n"); + return ret; + } + + dai_node = of_parse_phandle(node, "ti,mcpdm", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McPDM node is not provided\n"); + return -EINVAL; + } + + priv->dai_links[0].name = "DMIC"; + priv->dai_links[0].stream_name = "TWL6040"; + priv->dai_links[0].cpu_of_node = dai_node; + priv->dai_links[0].platform_of_node = dai_node; + priv->dai_links[0].codec_dai_name = "twl6040-legacy"; + priv->dai_links[0].codec_name = "twl6040-codec"; + priv->dai_links[0].init = omap_abe_twl6040_init; + priv->dai_links[0].ops = &omap_abe_ops; + + dai_node = of_parse_phandle(node, "ti,dmic", 0); + if (dai_node) { + num_links = 2; + priv->dai_links[1].name = "TWL6040"; + priv->dai_links[1].stream_name = "DMIC Capture"; + priv->dai_links[1].cpu_of_node = dai_node; + priv->dai_links[1].platform_of_node = dai_node; + priv->dai_links[1].codec_dai_name = "dmic-hifi"; + priv->dai_links[1].codec_name = "dmic-codec"; + priv->dai_links[1].init = omap_abe_dmic_init; + priv->dai_links[1].ops = &omap_abe_dmic_ops; + } else { + num_links = 1; + } + + priv->jack_detection = of_property_read_bool(node, "ti,jack-detection"); + of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq); + if (!priv->mclk_freq) { + dev_err(&pdev->dev, "MCLK frequency not provided\n"); + return -EINVAL; + } + + card->fully_routed = 1; + + if (!priv->mclk_freq) { + dev_err(&pdev->dev, "MCLK frequency missing\n"); + return -ENODEV; + } + + card->dai_link = priv->dai_links; + card->num_links = num_links; + + snd_soc_card_set_drvdata(card, priv); + + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) + dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", + ret); + + return ret; +} + +static const struct of_device_id omap_abe_of_match[] = { + {.compatible = "ti,abe-twl6040", }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_abe_of_match); + +static struct platform_driver omap_abe_driver = { + .driver = { + .name = "omap-abe-twl6040", + .pm = &snd_soc_pm_ops, + .of_match_table = omap_abe_of_match, + }, + .probe = omap_abe_probe, +}; + +static int __init omap_abe_init(void) +{ + int ret; + + dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL, + 0); + if (IS_ERR(dmic_codec_dev)) { + pr_err("%s: dmic-codec device registration failed\n", __func__); + return PTR_ERR(dmic_codec_dev); + } + + ret = platform_driver_register(&omap_abe_driver); + if (ret) { + pr_err("%s: platform driver registration failed\n", __func__); + platform_device_unregister(dmic_codec_dev); + } + + return ret; +} +module_init(omap_abe_init); + +static void __exit omap_abe_exit(void) +{ + platform_driver_unregister(&omap_abe_driver); + platform_device_unregister(dmic_codec_dev); +} +module_exit(omap_abe_exit); + +MODULE_AUTHOR("Misael Lopez Cruz "); +MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap-abe-twl6040"); diff --git a/sound/soc/ti/omap-dmic.c b/sound/soc/ti/omap-dmic.c new file mode 100644 index 000000000000..cba9645b6487 --- /dev/null +++ b/sound/soc/ti/omap-dmic.c @@ -0,0 +1,541 @@ +/* + * omap-dmic.c -- OMAP ASoC DMIC DAI driver + * + * Copyright (C) 2010 - 2011 Texas Instruments + * + * Author: David Lambert + * Misael Lopez Cruz + * Liam Girdwood + * Peter Ujfalusi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "omap-dmic.h" +#include "sdma-pcm.h" + +struct omap_dmic { + struct device *dev; + void __iomem *io_base; + struct clk *fclk; + struct pm_qos_request pm_qos_req; + int latency; + int fclk_freq; + int out_freq; + int clk_div; + int sysclk; + int threshold; + u32 ch_enabled; + bool active; + struct mutex mutex; + + struct snd_dmaengine_dai_dma_data dma_data; +}; + +static inline void omap_dmic_write(struct omap_dmic *dmic, u16 reg, u32 val) +{ + writel_relaxed(val, dmic->io_base + reg); +} + +static inline int omap_dmic_read(struct omap_dmic *dmic, u16 reg) +{ + return readl_relaxed(dmic->io_base + reg); +} + +static inline void omap_dmic_start(struct omap_dmic *dmic) +{ + u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG); + + /* Configure DMA controller */ + omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_SET_REG, + OMAP_DMIC_DMA_ENABLE); + + omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl | dmic->ch_enabled); +} + +static inline void omap_dmic_stop(struct omap_dmic *dmic) +{ + u32 ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG); + omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, + ctrl & ~OMAP_DMIC_UP_ENABLE_MASK); + + /* Disable DMA request generation */ + omap_dmic_write(dmic, OMAP_DMIC_DMAENABLE_CLR_REG, + OMAP_DMIC_DMA_ENABLE); + +} + +static inline int dmic_is_enabled(struct omap_dmic *dmic) +{ + return omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG) & + OMAP_DMIC_UP_ENABLE_MASK; +} + +static int omap_dmic_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + int ret = 0; + + mutex_lock(&dmic->mutex); + + if (!dai->active) + dmic->active = 1; + else + ret = -EBUSY; + + mutex_unlock(&dmic->mutex); + + return ret; +} + +static void omap_dmic_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + mutex_lock(&dmic->mutex); + + pm_qos_remove_request(&dmic->pm_qos_req); + + if (!dai->active) + dmic->active = 0; + + mutex_unlock(&dmic->mutex); +} + +static int omap_dmic_select_divider(struct omap_dmic *dmic, int sample_rate) +{ + int divider = -EINVAL; + + /* + * 192KHz rate is only supported with 19.2MHz/3.84MHz clock + * configuration. + */ + if (sample_rate == 192000) { + if (dmic->fclk_freq == 19200000 && dmic->out_freq == 3840000) + divider = 0x6; /* Divider: 5 (192KHz sampling rate) */ + else + dev_err(dmic->dev, + "invalid clock configuration for 192KHz\n"); + + return divider; + } + + switch (dmic->out_freq) { + case 1536000: + if (dmic->fclk_freq != 24576000) + goto div_err; + divider = 0x4; /* Divider: 16 */ + break; + case 2400000: + switch (dmic->fclk_freq) { + case 12000000: + divider = 0x5; /* Divider: 5 */ + break; + case 19200000: + divider = 0x0; /* Divider: 8 */ + break; + case 24000000: + divider = 0x2; /* Divider: 10 */ + break; + default: + goto div_err; + } + break; + case 3072000: + if (dmic->fclk_freq != 24576000) + goto div_err; + divider = 0x3; /* Divider: 8 */ + break; + case 3840000: + if (dmic->fclk_freq != 19200000) + goto div_err; + divider = 0x1; /* Divider: 5 (96KHz sampling rate) */ + break; + default: + dev_err(dmic->dev, "invalid out frequency: %dHz\n", + dmic->out_freq); + break; + } + + return divider; + +div_err: + dev_err(dmic->dev, "invalid out frequency %dHz for %dHz input\n", + dmic->out_freq, dmic->fclk_freq); + return -EINVAL; +} + +static int omap_dmic_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + struct snd_dmaengine_dai_dma_data *dma_data; + int channels; + + dmic->clk_div = omap_dmic_select_divider(dmic, params_rate(params)); + if (dmic->clk_div < 0) { + dev_err(dmic->dev, "no valid divider for %dHz from %dHz\n", + dmic->out_freq, dmic->fclk_freq); + return -EINVAL; + } + + dmic->ch_enabled = 0; + channels = params_channels(params); + switch (channels) { + case 6: + dmic->ch_enabled |= OMAP_DMIC_UP3_ENABLE; + /* fall through */ + case 4: + dmic->ch_enabled |= OMAP_DMIC_UP2_ENABLE; + /* fall through */ + case 2: + dmic->ch_enabled |= OMAP_DMIC_UP1_ENABLE; + break; + default: + dev_err(dmic->dev, "invalid number of legacy channels\n"); + return -EINVAL; + } + + /* packet size is threshold * channels */ + dma_data = snd_soc_dai_get_dma_data(dai, substream); + dma_data->maxburst = dmic->threshold * channels; + dmic->latency = (OMAP_DMIC_THRES_MAX - dmic->threshold) * USEC_PER_SEC / + params_rate(params); + + return 0; +} + +static int omap_dmic_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + u32 ctrl; + + if (pm_qos_request_active(&dmic->pm_qos_req)) + pm_qos_update_request(&dmic->pm_qos_req, dmic->latency); + + /* Configure uplink threshold */ + omap_dmic_write(dmic, OMAP_DMIC_FIFO_CTRL_REG, dmic->threshold); + + ctrl = omap_dmic_read(dmic, OMAP_DMIC_CTRL_REG); + + /* Set dmic out format */ + ctrl &= ~(OMAP_DMIC_FORMAT | OMAP_DMIC_POLAR_MASK); + ctrl |= (OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 | + OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3); + + /* Configure dmic clock divider */ + ctrl &= ~OMAP_DMIC_CLK_DIV_MASK; + ctrl |= OMAP_DMIC_CLK_DIV(dmic->clk_div); + + omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, ctrl); + + omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, + ctrl | OMAP_DMICOUTFORMAT_LJUST | OMAP_DMIC_POLAR1 | + OMAP_DMIC_POLAR2 | OMAP_DMIC_POLAR3); + + return 0; +} + +static int omap_dmic_dai_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + omap_dmic_start(dmic); + break; + case SNDRV_PCM_TRIGGER_STOP: + omap_dmic_stop(dmic); + break; + default: + break; + } + + return 0; +} + +static int omap_dmic_select_fclk(struct omap_dmic *dmic, int clk_id, + unsigned int freq) +{ + struct clk *parent_clk, *mux; + char *parent_clk_name; + int ret = 0; + + switch (freq) { + case 12000000: + case 19200000: + case 24000000: + case 24576000: + break; + default: + dev_err(dmic->dev, "invalid input frequency: %dHz\n", freq); + dmic->fclk_freq = 0; + return -EINVAL; + } + + if (dmic->sysclk == clk_id) { + dmic->fclk_freq = freq; + return 0; + } + + /* re-parent not allowed if a stream is ongoing */ + if (dmic->active && dmic_is_enabled(dmic)) { + dev_err(dmic->dev, "can't re-parent when DMIC active\n"); + return -EBUSY; + } + + switch (clk_id) { + case OMAP_DMIC_SYSCLK_PAD_CLKS: + parent_clk_name = "pad_clks_ck"; + break; + case OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS: + parent_clk_name = "slimbus_clk"; + break; + case OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS: + parent_clk_name = "dmic_sync_mux_ck"; + break; + default: + dev_err(dmic->dev, "fclk clk_id (%d) not supported\n", clk_id); + return -EINVAL; + } + + parent_clk = clk_get(dmic->dev, parent_clk_name); + if (IS_ERR(parent_clk)) { + dev_err(dmic->dev, "can't get %s\n", parent_clk_name); + return -ENODEV; + } + + mux = clk_get_parent(dmic->fclk); + if (IS_ERR(mux)) { + dev_err(dmic->dev, "can't get fck mux parent\n"); + clk_put(parent_clk); + return -ENODEV; + } + + mutex_lock(&dmic->mutex); + if (dmic->active) { + /* disable clock while reparenting */ + pm_runtime_put_sync(dmic->dev); + ret = clk_set_parent(mux, parent_clk); + pm_runtime_get_sync(dmic->dev); + } else { + ret = clk_set_parent(mux, parent_clk); + } + mutex_unlock(&dmic->mutex); + + if (ret < 0) { + dev_err(dmic->dev, "re-parent failed\n"); + goto err_busy; + } + + dmic->sysclk = clk_id; + dmic->fclk_freq = freq; + +err_busy: + clk_put(mux); + clk_put(parent_clk); + + return ret; +} + +static int omap_dmic_select_outclk(struct omap_dmic *dmic, int clk_id, + unsigned int freq) +{ + int ret = 0; + + if (clk_id != OMAP_DMIC_ABE_DMIC_CLK) { + dev_err(dmic->dev, "output clk_id (%d) not supported\n", + clk_id); + return -EINVAL; + } + + switch (freq) { + case 1536000: + case 2400000: + case 3072000: + case 3840000: + dmic->out_freq = freq; + break; + default: + dev_err(dmic->dev, "invalid out frequency: %dHz\n", freq); + dmic->out_freq = 0; + ret = -EINVAL; + } + + return ret; +} + +static int omap_dmic_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + if (dir == SND_SOC_CLOCK_IN) + return omap_dmic_select_fclk(dmic, clk_id, freq); + else if (dir == SND_SOC_CLOCK_OUT) + return omap_dmic_select_outclk(dmic, clk_id, freq); + + dev_err(dmic->dev, "invalid clock direction (%d)\n", dir); + return -EINVAL; +} + +static const struct snd_soc_dai_ops omap_dmic_dai_ops = { + .startup = omap_dmic_dai_startup, + .shutdown = omap_dmic_dai_shutdown, + .hw_params = omap_dmic_dai_hw_params, + .prepare = omap_dmic_dai_prepare, + .trigger = omap_dmic_dai_trigger, + .set_sysclk = omap_dmic_set_dai_sysclk, +}; + +static int omap_dmic_probe(struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + pm_runtime_enable(dmic->dev); + + /* Disable lines while request is ongoing */ + pm_runtime_get_sync(dmic->dev); + omap_dmic_write(dmic, OMAP_DMIC_CTRL_REG, 0x00); + pm_runtime_put_sync(dmic->dev); + + /* Configure DMIC threshold value */ + dmic->threshold = OMAP_DMIC_THRES_MAX - 3; + + snd_soc_dai_init_dma_data(dai, NULL, &dmic->dma_data); + + return 0; +} + +static int omap_dmic_remove(struct snd_soc_dai *dai) +{ + struct omap_dmic *dmic = snd_soc_dai_get_drvdata(dai); + + pm_runtime_disable(dmic->dev); + + return 0; +} + +static struct snd_soc_dai_driver omap_dmic_dai = { + .name = "omap-dmic", + .probe = omap_dmic_probe, + .remove = omap_dmic_remove, + .capture = { + .channels_min = 2, + .channels_max = 6, + .rates = SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + .sig_bits = 24, + }, + .ops = &omap_dmic_dai_ops, +}; + +static const struct snd_soc_component_driver omap_dmic_component = { + .name = "omap-dmic", +}; + +static int asoc_dmic_probe(struct platform_device *pdev) +{ + struct omap_dmic *dmic; + struct resource *res; + int ret; + + dmic = devm_kzalloc(&pdev->dev, sizeof(struct omap_dmic), GFP_KERNEL); + if (!dmic) + return -ENOMEM; + + platform_set_drvdata(pdev, dmic); + dmic->dev = &pdev->dev; + dmic->sysclk = OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS; + + mutex_init(&dmic->mutex); + + dmic->fclk = devm_clk_get(dmic->dev, "fck"); + if (IS_ERR(dmic->fclk)) { + dev_err(dmic->dev, "cant get fck\n"); + return -ENODEV; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) { + dev_err(dmic->dev, "invalid dma memory resource\n"); + return -ENODEV; + } + dmic->dma_data.addr = res->start + OMAP_DMIC_DATA_REG; + + dmic->dma_data.filter_data = "up_link"; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + dmic->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dmic->io_base)) + return PTR_ERR(dmic->io_base); + + + ret = devm_snd_soc_register_component(&pdev->dev, + &omap_dmic_component, + &omap_dmic_dai, 1); + if (ret) + return ret; + + ret = sdma_pcm_platform_register(&pdev->dev, NULL, "up_link"); + if (ret) + return ret; + + return 0; +} + +static const struct of_device_id omap_dmic_of_match[] = { + { .compatible = "ti,omap4-dmic", }, + { } +}; +MODULE_DEVICE_TABLE(of, omap_dmic_of_match); + +static struct platform_driver asoc_dmic_driver = { + .driver = { + .name = "omap-dmic", + .of_match_table = omap_dmic_of_match, + }, + .probe = asoc_dmic_probe, +}; + +module_platform_driver(asoc_dmic_driver); + +MODULE_ALIAS("platform:omap-dmic"); +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("OMAP DMIC ASoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/omap-dmic.h b/sound/soc/ti/omap-dmic.h new file mode 100644 index 000000000000..231e728bff0e --- /dev/null +++ b/sound/soc/ti/omap-dmic.h @@ -0,0 +1,69 @@ +/* + * omap-dmic.h -- OMAP Digital Microphone Controller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _OMAP_DMIC_H +#define _OMAP_DMIC_H + +#define OMAP_DMIC_REVISION_REG 0x00 +#define OMAP_DMIC_SYSCONFIG_REG 0x10 +#define OMAP_DMIC_IRQSTATUS_RAW_REG 0x24 +#define OMAP_DMIC_IRQSTATUS_REG 0x28 +#define OMAP_DMIC_IRQENABLE_SET_REG 0x2C +#define OMAP_DMIC_IRQENABLE_CLR_REG 0x30 +#define OMAP_DMIC_IRQWAKE_EN_REG 0x34 +#define OMAP_DMIC_DMAENABLE_SET_REG 0x38 +#define OMAP_DMIC_DMAENABLE_CLR_REG 0x3C +#define OMAP_DMIC_DMAWAKEEN_REG 0x40 +#define OMAP_DMIC_CTRL_REG 0x44 +#define OMAP_DMIC_DATA_REG 0x48 +#define OMAP_DMIC_FIFO_CTRL_REG 0x4C +#define OMAP_DMIC_FIFO_DMIC1R_DATA_REG 0x50 +#define OMAP_DMIC_FIFO_DMIC1L_DATA_REG 0x54 +#define OMAP_DMIC_FIFO_DMIC2R_DATA_REG 0x58 +#define OMAP_DMIC_FIFO_DMIC2L_DATA_REG 0x5C +#define OMAP_DMIC_FIFO_DMIC3R_DATA_REG 0x60 +#define OMAP_DMIC_FIFO_DMIC3L_DATA_REG 0x64 + +/* IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR bit fields */ +#define OMAP_DMIC_IRQ (1 << 0) +#define OMAP_DMIC_IRQ_FULL (1 << 1) +#define OMAP_DMIC_IRQ_ALMST_EMPTY (1 << 2) +#define OMAP_DMIC_IRQ_EMPTY (1 << 3) +#define OMAP_DMIC_IRQ_MASK 0x07 + +/* DMIC_DMAENABLE bit fields */ +#define OMAP_DMIC_DMA_ENABLE 0x1 + +/* DMIC_CTRL bit fields */ +#define OMAP_DMIC_UP1_ENABLE (1 << 0) +#define OMAP_DMIC_UP2_ENABLE (1 << 1) +#define OMAP_DMIC_UP3_ENABLE (1 << 2) +#define OMAP_DMIC_UP_ENABLE_MASK 0x7 +#define OMAP_DMIC_FORMAT (1 << 3) +#define OMAP_DMIC_POLAR1 (1 << 4) +#define OMAP_DMIC_POLAR2 (1 << 5) +#define OMAP_DMIC_POLAR3 (1 << 6) +#define OMAP_DMIC_POLAR_MASK (0x7 << 4) +#define OMAP_DMIC_CLK_DIV(x) (((x) & 0x7) << 7) +#define OMAP_DMIC_CLK_DIV_MASK (0x7 << 7) +#define OMAP_DMIC_RESET (1 << 10) + +#define OMAP_DMICOUTFORMAT_LJUST (0 << 3) +#define OMAP_DMICOUTFORMAT_RJUST (1 << 3) + +/* DMIC_FIFO_CTRL bit fields */ +#define OMAP_DMIC_THRES_MAX 0xF + +enum omap_dmic_clk { + OMAP_DMIC_SYSCLK_PAD_CLKS, /* PAD_CLKS */ + OMAP_DMIC_SYSCLK_SLIMBLUS_CLKS, /* SLIMBUS_CLK */ + OMAP_DMIC_SYSCLK_SYNC_MUX_CLKS, /* DMIC_SYNC_MUX_CLK */ + OMAP_DMIC_ABE_DMIC_CLK, /* abe_dmic_clk */ +}; + +#endif diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c new file mode 100644 index 000000000000..673a9eb153b2 --- /dev/null +++ b/sound/soc/ti/omap-hdmi.c @@ -0,0 +1,418 @@ +/* + * omap-hdmi-audio.c -- OMAP4+ DSS HDMI audio support library + * + * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com + * + * Author: Jyri Sarha + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdma-pcm.h" + +#define DRV_NAME "omap-hdmi-audio" + +struct hdmi_audio_data { + struct snd_soc_card *card; + + const struct omap_hdmi_audio_ops *ops; + struct device *dssdev; + struct snd_dmaengine_dai_dma_data dma_data; + struct omap_dss_audio dss_audio; + struct snd_aes_iec958 iec; + struct snd_cea_861_aud_if cea; + + struct mutex current_stream_lock; + struct snd_pcm_substream *current_stream; +}; + +static +struct hdmi_audio_data *card_drvdata_substream(struct snd_pcm_substream *ss) +{ + struct snd_soc_pcm_runtime *rtd = ss->private_data; + + return snd_soc_card_get_drvdata(rtd->card); +} + +static void hdmi_dai_abort(struct device *dev) +{ + struct hdmi_audio_data *ad = dev_get_drvdata(dev); + + mutex_lock(&ad->current_stream_lock); + if (ad->current_stream && ad->current_stream->runtime && + snd_pcm_running(ad->current_stream)) { + dev_err(dev, "HDMI display disabled, aborting playback\n"); + snd_pcm_stream_lock_irq(ad->current_stream); + snd_pcm_stop(ad->current_stream, SNDRV_PCM_STATE_DISCONNECTED); + snd_pcm_stream_unlock_irq(ad->current_stream); + } + mutex_unlock(&ad->current_stream_lock); +} + +static int hdmi_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_audio_data *ad = card_drvdata_substream(substream); + int ret; + /* + * Make sure that the period bytes are multiple of the DMA packet size. + * Largest packet size we use is 32 32-bit words = 128 bytes + */ + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); + if (ret < 0) { + dev_err(dai->dev, "Could not apply period constraint: %d\n", + ret); + return ret; + } + ret = snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 128); + if (ret < 0) { + dev_err(dai->dev, "Could not apply buffer constraint: %d\n", + ret); + return ret; + } + + snd_soc_dai_set_dma_data(dai, substream, &ad->dma_data); + + mutex_lock(&ad->current_stream_lock); + ad->current_stream = substream; + mutex_unlock(&ad->current_stream_lock); + + ret = ad->ops->audio_startup(ad->dssdev, hdmi_dai_abort); + + if (ret) { + mutex_lock(&ad->current_stream_lock); + ad->current_stream = NULL; + mutex_unlock(&ad->current_stream_lock); + } + + return ret; +} + +static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct hdmi_audio_data *ad = card_drvdata_substream(substream); + struct snd_aes_iec958 *iec = &ad->iec; + struct snd_cea_861_aud_if *cea = &ad->cea; + + WARN_ON(ad->current_stream != substream); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + ad->dma_data.maxburst = 16; + break; + case SNDRV_PCM_FORMAT_S24_LE: + ad->dma_data.maxburst = 32; + break; + default: + dev_err(dai->dev, "format not supported!\n"); + return -EINVAL; + } + + ad->dss_audio.iec = iec; + ad->dss_audio.cea = cea; + /* + * fill the IEC-60958 channel status word + */ + /* initialize the word bytes */ + memset(iec->status, 0, sizeof(iec->status)); + + /* specify IEC-60958-3 (commercial use) */ + iec->status[0] &= ~IEC958_AES0_PROFESSIONAL; + + /* specify that the audio is LPCM*/ + iec->status[0] &= ~IEC958_AES0_NONAUDIO; + + iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT; + + iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; + + iec->status[1] = IEC958_AES1_CON_GENERAL; + + iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; + + iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC; + + switch (params_rate(params)) { + case 32000: + iec->status[3] |= IEC958_AES3_CON_FS_32000; + break; + case 44100: + iec->status[3] |= IEC958_AES3_CON_FS_44100; + break; + case 48000: + iec->status[3] |= IEC958_AES3_CON_FS_48000; + break; + case 88200: + iec->status[3] |= IEC958_AES3_CON_FS_88200; + break; + case 96000: + iec->status[3] |= IEC958_AES3_CON_FS_96000; + break; + case 176400: + iec->status[3] |= IEC958_AES3_CON_FS_176400; + break; + case 192000: + iec->status[3] |= IEC958_AES3_CON_FS_192000; + break; + default: + dev_err(dai->dev, "rate not supported!\n"); + return -EINVAL; + } + + /* specify the clock accuracy */ + iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM; + + /* + * specify the word length. The same word length value can mean + * two different lengths. Hence, we need to specify the maximum + * word length as well. + */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16; + iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24; + break; + case SNDRV_PCM_FORMAT_S24_LE: + iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20; + iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24; + break; + default: + dev_err(dai->dev, "format not supported!\n"); + return -EINVAL; + } + + /* + * Fill the CEA-861 audio infoframe (see spec for details) + */ + + cea->db1_ct_cc = (params_channels(params) - 1) + & CEA861_AUDIO_INFOFRAME_DB1CC; + cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM; + + cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM; + cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM; + + cea->db3 = 0; /* not used, all zeros */ + + if (params_channels(params) == 2) + cea->db4_ca = 0x0; + else if (params_channels(params) == 6) + cea->db4_ca = 0xb; + else + cea->db4_ca = 0x13; + + if (cea->db4_ca == 0x00) + cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED; + else + cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED; + + /* the expression is trivial but makes clear what we are doing */ + cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV); + + return ad->ops->audio_config(ad->dssdev, &ad->dss_audio); +} + +static int hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdmi_audio_data *ad = card_drvdata_substream(substream); + int err = 0; + + WARN_ON(ad->current_stream != substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + err = ad->ops->audio_start(ad->dssdev); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ad->ops->audio_stop(ad->dssdev); + break; + default: + err = -EINVAL; + } + return err; +} + +static void hdmi_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct hdmi_audio_data *ad = card_drvdata_substream(substream); + + WARN_ON(ad->current_stream != substream); + + ad->ops->audio_shutdown(ad->dssdev); + + mutex_lock(&ad->current_stream_lock); + ad->current_stream = NULL; + mutex_unlock(&ad->current_stream_lock); +} + +static const struct snd_soc_dai_ops hdmi_dai_ops = { + .startup = hdmi_dai_startup, + .hw_params = hdmi_dai_hw_params, + .trigger = hdmi_dai_trigger, + .shutdown = hdmi_dai_shutdown, +}; + +static const struct snd_soc_component_driver omap_hdmi_component = { + .name = "omapdss_hdmi", +}; + +static struct snd_soc_dai_driver omap5_hdmi_dai = { + .name = "omap5-hdmi-dai", + .playback = { + .channels_min = 2, + .channels_max = 8, + .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &hdmi_dai_ops, +}; + +static struct snd_soc_dai_driver omap4_hdmi_dai = { + .name = "omap4-hdmi-dai", + .playback = { + .channels_min = 2, + .channels_max = 8, + .rates = (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | + SNDRV_PCM_RATE_192000), + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &hdmi_dai_ops, +}; + +static int omap_hdmi_audio_probe(struct platform_device *pdev) +{ + struct omap_hdmi_audio_pdata *ha = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct hdmi_audio_data *ad; + struct snd_soc_dai_driver *dai_drv; + struct snd_soc_card *card; + int ret; + + if (!ha) { + dev_err(dev, "No platform data\n"); + return -EINVAL; + } + + ad = devm_kzalloc(dev, sizeof(*ad), GFP_KERNEL); + if (!ad) + return -ENOMEM; + ad->dssdev = ha->dev; + ad->ops = ha->ops; + ad->dma_data.addr = ha->audio_dma_addr; + ad->dma_data.filter_data = "audio_tx"; + ad->dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + mutex_init(&ad->current_stream_lock); + + switch (ha->version) { + case 4: + dai_drv = &omap4_hdmi_dai; + break; + case 5: + dai_drv = &omap5_hdmi_dai; + break; + default: + return -EINVAL; + } + ret = devm_snd_soc_register_component(ad->dssdev, &omap_hdmi_component, + dai_drv, 1); + if (ret) + return ret; + + ret = sdma_pcm_platform_register(ad->dssdev, "audio_tx", NULL); + if (ret) + return ret; + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->name = devm_kasprintf(dev, GFP_KERNEL, + "HDMI %s", dev_name(ad->dssdev)); + if (!card->name) + return -ENOMEM; + + card->owner = THIS_MODULE; + card->dai_link = + devm_kzalloc(dev, sizeof(*(card->dai_link)), GFP_KERNEL); + if (!card->dai_link) + return -ENOMEM; + card->dai_link->name = card->name; + card->dai_link->stream_name = card->name; + card->dai_link->cpu_dai_name = dev_name(ad->dssdev); + card->dai_link->platform_name = dev_name(ad->dssdev); + card->dai_link->codec_name = "snd-soc-dummy"; + card->dai_link->codec_dai_name = "snd-soc-dummy-dai"; + card->num_links = 1; + card->dev = dev; + + ret = snd_soc_register_card(card); + if (ret) { + dev_err(dev, "snd_soc_register_card failed (%d)\n", ret); + return ret; + } + + ad->card = card; + snd_soc_card_set_drvdata(card, ad); + + dev_set_drvdata(dev, ad); + + return 0; +} + +static int omap_hdmi_audio_remove(struct platform_device *pdev) +{ + struct hdmi_audio_data *ad = platform_get_drvdata(pdev); + + snd_soc_unregister_card(ad->card); + return 0; +} + +static struct platform_driver hdmi_audio_driver = { + .driver = { + .name = DRV_NAME, + }, + .probe = omap_hdmi_audio_probe, + .remove = omap_hdmi_audio_remove, +}; + +module_platform_driver(hdmi_audio_driver); + +MODULE_AUTHOR("Jyri Sarha "); +MODULE_DESCRIPTION("OMAP HDMI Audio Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/ti/omap-mcbsp-priv.h b/sound/soc/ti/omap-mcbsp-priv.h new file mode 100644 index 000000000000..7865cda4bf0a --- /dev/null +++ b/sound/soc/ti/omap-mcbsp-priv.h @@ -0,0 +1,324 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * OMAP Multi-Channel Buffered Serial Port + * + * Contact: Jarkko Nikula + * Peter Ujfalusi + */ + +#ifndef __OMAP_MCBSP_PRIV_H__ +#define __OMAP_MCBSP_PRIV_H__ + +#include + +#ifdef CONFIG_ARCH_OMAP1 +#define mcbsp_omap1() 1 +#else +#define mcbsp_omap1() 0 +#endif + +/* McBSP register numbers. Register address offset = num * reg_step */ +enum { + /* Common registers */ + OMAP_MCBSP_REG_SPCR2 = 4, + OMAP_MCBSP_REG_SPCR1, + OMAP_MCBSP_REG_RCR2, + OMAP_MCBSP_REG_RCR1, + OMAP_MCBSP_REG_XCR2, + OMAP_MCBSP_REG_XCR1, + OMAP_MCBSP_REG_SRGR2, + OMAP_MCBSP_REG_SRGR1, + OMAP_MCBSP_REG_MCR2, + OMAP_MCBSP_REG_MCR1, + OMAP_MCBSP_REG_RCERA, + OMAP_MCBSP_REG_RCERB, + OMAP_MCBSP_REG_XCERA, + OMAP_MCBSP_REG_XCERB, + OMAP_MCBSP_REG_PCR0, + OMAP_MCBSP_REG_RCERC, + OMAP_MCBSP_REG_RCERD, + OMAP_MCBSP_REG_XCERC, + OMAP_MCBSP_REG_XCERD, + OMAP_MCBSP_REG_RCERE, + OMAP_MCBSP_REG_RCERF, + OMAP_MCBSP_REG_XCERE, + OMAP_MCBSP_REG_XCERF, + OMAP_MCBSP_REG_RCERG, + OMAP_MCBSP_REG_RCERH, + OMAP_MCBSP_REG_XCERG, + OMAP_MCBSP_REG_XCERH, + + /* OMAP1-OMAP2420 registers */ + OMAP_MCBSP_REG_DRR2 = 0, + OMAP_MCBSP_REG_DRR1, + OMAP_MCBSP_REG_DXR2, + OMAP_MCBSP_REG_DXR1, + + /* OMAP2430 and onwards */ + OMAP_MCBSP_REG_DRR = 0, + OMAP_MCBSP_REG_DXR = 2, + OMAP_MCBSP_REG_SYSCON = 35, + OMAP_MCBSP_REG_THRSH2, + OMAP_MCBSP_REG_THRSH1, + OMAP_MCBSP_REG_IRQST = 40, + OMAP_MCBSP_REG_IRQEN, + OMAP_MCBSP_REG_WAKEUPEN, + OMAP_MCBSP_REG_XCCR, + OMAP_MCBSP_REG_RCCR, + OMAP_MCBSP_REG_XBUFFSTAT, + OMAP_MCBSP_REG_RBUFFSTAT, + OMAP_MCBSP_REG_SSELCR, +}; + +/************************** McBSP SPCR1 bit definitions ***********************/ +#define RRST BIT(0) +#define RRDY BIT(1) +#define RFULL BIT(2) +#define RSYNC_ERR BIT(3) +#define RINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ +#define ABIS BIT(6) +#define DXENA BIT(7) +#define CLKSTP(value) (((value) & 0x3) << 11) /* bits 11:12 */ +#define RJUST(value) (((value) & 0x3) << 13) /* bits 13:14 */ +#define ALB BIT(15) +#define DLB BIT(15) + +/************************** McBSP SPCR2 bit definitions ***********************/ +#define XRST BIT(0) +#define XRDY BIT(1) +#define XEMPTY BIT(2) +#define XSYNC_ERR BIT(3) +#define XINTM(value) (((value) & 0x3) << 4) /* bits 4:5 */ +#define GRST BIT(6) +#define FRST BIT(7) +#define SOFT BIT(8) +#define FREE BIT(9) + +/************************** McBSP PCR bit definitions *************************/ +#define CLKRP BIT(0) +#define CLKXP BIT(1) +#define FSRP BIT(2) +#define FSXP BIT(3) +#define DR_STAT BIT(4) +#define DX_STAT BIT(5) +#define CLKS_STAT BIT(6) +#define SCLKME BIT(7) +#define CLKRM BIT(8) +#define CLKXM BIT(9) +#define FSRM BIT(10) +#define FSXM BIT(11) +#define RIOEN BIT(12) +#define XIOEN BIT(13) +#define IDLE_EN BIT(14) + +/************************** McBSP RCR1 bit definitions ************************/ +#define RWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define RFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ + +/************************** McBSP XCR1 bit definitions ************************/ +#define XWDLEN1(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define XFRLEN1(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ + +/*************************** McBSP RCR2 bit definitions ***********************/ +#define RDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ +#define RFIG BIT(2) +#define RCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ +#define RWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define RFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ +#define RPHASE BIT(15) + +/*************************** McBSP XCR2 bit definitions ***********************/ +#define XDATDLY(value) ((value) & 0x3) /* Bits 0:1 */ +#define XFIG BIT(2) +#define XCOMPAND(value) (((value) & 0x3) << 3) /* Bits 3:4 */ +#define XWDLEN2(value) (((value) & 0x7) << 5) /* Bits 5:7 */ +#define XFRLEN2(value) (((value) & 0x7f) << 8) /* Bits 8:14 */ +#define XPHASE BIT(15) + +/************************* McBSP SRGR1 bit definitions ************************/ +#define CLKGDV(value) ((value) & 0x7f) /* Bits 0:7 */ +#define FWID(value) (((value) & 0xff) << 8) /* Bits 8:15 */ + +/************************* McBSP SRGR2 bit definitions ************************/ +#define FPER(value) ((value) & 0x0fff) /* Bits 0:11 */ +#define FSGM BIT(12) +#define CLKSM BIT(13) +#define CLKSP BIT(14) +#define GSYNC BIT(15) + +/************************* McBSP MCR1 bit definitions *************************/ +#define RMCM BIT(0) +#define RCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ +#define RPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ +#define RPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ + +/************************* McBSP MCR2 bit definitions *************************/ +#define XMCM(value) ((value) & 0x3) /* Bits 0:1 */ +#define XCBLK(value) (((value) & 0x7) << 2) /* Bits 2:4 */ +#define XPABLK(value) (((value) & 0x3) << 5) /* Bits 5:6 */ +#define XPBBLK(value) (((value) & 0x3) << 7) /* Bits 7:8 */ + +/*********************** McBSP XCCR bit definitions *************************/ +#define XDISABLE BIT(0) +#define XDMAEN BIT(3) +#define DILB BIT(5) +#define XFULL_CYCLE BIT(11) +#define DXENDLY(value) (((value) & 0x3) << 12) /* Bits 12:13 */ +#define PPCONNECT BIT(14) +#define EXTCLKGATE BIT(15) + +/********************** McBSP RCCR bit definitions *************************/ +#define RDISABLE BIT(0) +#define RDMAEN BIT(3) +#define RFULL_CYCLE BIT(11) + +/********************** McBSP SYSCONFIG bit definitions ********************/ +#define SOFTRST BIT(1) +#define ENAWAKEUP BIT(2) +#define SIDLEMODE(value) (((value) & 0x3) << 3) +#define CLOCKACTIVITY(value) (((value) & 0x3) << 8) + +/********************** McBSP DMA operating modes **************************/ +#define MCBSP_DMA_MODE_ELEMENT 0 +#define MCBSP_DMA_MODE_THRESHOLD 1 + +/********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/ +#define RSYNCERREN BIT(0) +#define RFSREN BIT(1) +#define REOFEN BIT(2) +#define RRDYEN BIT(3) +#define RUNDFLEN BIT(4) +#define ROVFLEN BIT(5) +#define XSYNCERREN BIT(7) +#define XFSXEN BIT(8) +#define XEOFEN BIT(9) +#define XRDYEN BIT(10) +#define XUNDFLEN BIT(11) +#define XOVFLEN BIT(12) +#define XEMPTYEOFEN BIT(14) + +/* Clock signal muxing options */ +#define CLKR_SRC_CLKR 0 /* CLKR signal is from the CLKR pin */ +#define CLKR_SRC_CLKX 1 /* CLKR signal is from the CLKX pin */ +#define FSR_SRC_FSR 2 /* FSR signal is from the FSR pin */ +#define FSR_SRC_FSX 3 /* FSR signal is from the FSX pin */ + +/* McBSP functional clock sources */ +#define MCBSP_CLKS_PRCM_SRC 0 +#define MCBSP_CLKS_PAD_SRC 1 + +/* we don't do multichannel for now */ +struct omap_mcbsp_reg_cfg { + u16 spcr2; + u16 spcr1; + u16 rcr2; + u16 rcr1; + u16 xcr2; + u16 xcr1; + u16 srgr2; + u16 srgr1; + u16 mcr2; + u16 mcr1; + u16 pcr0; + u16 rcerc; + u16 rcerd; + u16 xcerc; + u16 xcerd; + u16 rcere; + u16 rcerf; + u16 xcere; + u16 xcerf; + u16 rcerg; + u16 rcerh; + u16 xcerg; + u16 xcerh; + u16 xccr; + u16 rccr; +}; + +struct omap_mcbsp_st_data; + +struct omap_mcbsp { + struct device *dev; + struct clk *fclk; + spinlock_t lock; + unsigned long phys_base; + unsigned long phys_dma_base; + void __iomem *io_base; + u8 id; + /* + * Flags indicating is the bus already activated and configured by + * another substream + */ + int active; + int configured; + u8 free; + + int irq; + int rx_irq; + int tx_irq; + + /* Protect the field .free, while checking if the mcbsp is in use */ + struct omap_mcbsp_platform_data *pdata; + struct omap_mcbsp_st_data *st_data; + struct omap_mcbsp_reg_cfg cfg_regs; + struct snd_dmaengine_dai_dma_data dma_data[2]; + unsigned int dma_req[2]; + int dma_op_mode; + u16 max_tx_thres; + u16 max_rx_thres; + void *reg_cache; + int reg_cache_size; + + unsigned int fmt; + unsigned int in_freq; + unsigned int latency[2]; + int clk_div; + int wlen; + + struct pm_qos_request pm_qos_req; +}; + +static inline void omap_mcbsp_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + ((u16 *)mcbsp->reg_cache)[reg] = (u16)val; + writew_relaxed((u16)val, addr); + } else { + ((u32 *)mcbsp->reg_cache)[reg] = val; + writel_relaxed(val, addr); + } +} + +static inline int omap_mcbsp_read(struct omap_mcbsp *mcbsp, u16 reg, + bool from_cache) +{ + void __iomem *addr = mcbsp->io_base + reg * mcbsp->pdata->reg_step; + + if (mcbsp->pdata->reg_size == 2) { + return !from_cache ? readw_relaxed(addr) : + ((u16 *)mcbsp->reg_cache)[reg]; + } else { + return !from_cache ? readl_relaxed(addr) : + ((u32 *)mcbsp->reg_cache)[reg]; + } +} + +#define MCBSP_READ(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 0) +#define MCBSP_WRITE(mcbsp, reg, val) \ + omap_mcbsp_write(mcbsp, OMAP_MCBSP_REG_##reg, val) +#define MCBSP_READ_CACHE(mcbsp, reg) \ + omap_mcbsp_read(mcbsp, OMAP_MCBSP_REG_##reg, 1) + + +/* Sidetone specific API */ +int omap_mcbsp_st_init(struct platform_device *pdev); +void omap_mcbsp_st_cleanup(struct platform_device *pdev); + +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp); +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp); + +#endif /* __OMAP_MCBSP_PRIV_H__ */ diff --git a/sound/soc/ti/omap-mcbsp-st.c b/sound/soc/ti/omap-mcbsp-st.c new file mode 100644 index 000000000000..1a3fe854e856 --- /dev/null +++ b/sound/soc/ti/omap-mcbsp-st.c @@ -0,0 +1,516 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * McBSP Sidetone support + * + * Copyright (C) 2004 Nokia Corporation + * Author: Samuel Ortiz + * + * Contact: Jarkko Nikula + * Peter Ujfalusi + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "omap-mcbsp.h" +#include "omap-mcbsp-priv.h" + +/* OMAP3 sidetone control registers */ +#define OMAP_ST_REG_REV 0x00 +#define OMAP_ST_REG_SYSCONFIG 0x10 +#define OMAP_ST_REG_IRQSTATUS 0x18 +#define OMAP_ST_REG_IRQENABLE 0x1C +#define OMAP_ST_REG_SGAINCR 0x24 +#define OMAP_ST_REG_SFIRCR 0x28 +#define OMAP_ST_REG_SSELCR 0x2C + +/********************** McBSP SSELCR bit definitions ***********************/ +#define SIDETONEEN BIT(10) + +/********************** McBSP Sidetone SYSCONFIG bit definitions ***********/ +#define ST_AUTOIDLE BIT(0) + +/********************** McBSP Sidetone SGAINCR bit definitions *************/ +#define ST_CH0GAIN(value) ((value) & 0xffff) /* Bits 0:15 */ +#define ST_CH1GAIN(value) (((value) & 0xffff) << 16) /* Bits 16:31 */ + +/********************** McBSP Sidetone SFIRCR bit definitions **************/ +#define ST_FIRCOEFF(value) ((value) & 0xffff) /* Bits 0:15 */ + +/********************** McBSP Sidetone SSELCR bit definitions **************/ +#define ST_SIDETONEEN BIT(0) +#define ST_COEFFWREN BIT(1) +#define ST_COEFFWRDONE BIT(2) + +struct omap_mcbsp_st_data { + void __iomem *io_base_st; + struct clk *mcbsp_iclk; + bool running; + bool enabled; + s16 taps[128]; /* Sidetone filter coefficients */ + int nr_taps; /* Number of filter coefficients in use */ + s16 ch0gain; + s16 ch1gain; +}; + +static void omap_mcbsp_st_write(struct omap_mcbsp *mcbsp, u16 reg, u32 val) +{ + writel_relaxed(val, mcbsp->st_data->io_base_st + reg); +} + +static int omap_mcbsp_st_read(struct omap_mcbsp *mcbsp, u16 reg) +{ + return readl_relaxed(mcbsp->st_data->io_base_st + reg); +} + +#define MCBSP_ST_READ(mcbsp, reg) omap_mcbsp_st_read(mcbsp, OMAP_ST_REG_##reg) +#define MCBSP_ST_WRITE(mcbsp, reg, val) \ + omap_mcbsp_st_write(mcbsp, OMAP_ST_REG_##reg, val) + +static void omap_mcbsp_st_on(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true); + + /* Disable Sidetone clock auto-gating for normal operation */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w & ~(ST_AUTOIDLE)); + + /* Enable McBSP Sidetone */ + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w | SIDETONEEN); + + /* Enable Sidetone from Sidetone Core */ + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w | ST_SIDETONEEN); +} + +static void omap_mcbsp_st_off(struct omap_mcbsp *mcbsp) +{ + unsigned int w; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + MCBSP_ST_WRITE(mcbsp, SSELCR, w & ~(ST_SIDETONEEN)); + + w = MCBSP_READ(mcbsp, SSELCR); + MCBSP_WRITE(mcbsp, SSELCR, w & ~(SIDETONEEN)); + + /* Enable Sidetone clock auto-gating to reduce power consumption */ + w = MCBSP_ST_READ(mcbsp, SYSCONFIG); + MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE); + + if (mcbsp->pdata->force_ick_on) + mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false); +} + +static void omap_mcbsp_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir) +{ + u16 val, i; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + + if (val & ST_COEFFWREN) + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val | ST_COEFFWREN); + + for (i = 0; i < 128; i++) + MCBSP_ST_WRITE(mcbsp, SFIRCR, fir[i]); + + i = 0; + + val = MCBSP_ST_READ(mcbsp, SSELCR); + while (!(val & ST_COEFFWRDONE) && (++i < 1000)) + val = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SSELCR, val & ~(ST_COEFFWREN)); + + if (i == 1000) + dev_err(mcbsp->dev, "McBSP FIR load error!\n"); +} + +static void omap_mcbsp_st_chgain(struct omap_mcbsp *mcbsp) +{ + u16 w; + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + w = MCBSP_ST_READ(mcbsp, SSELCR); + + MCBSP_ST_WRITE(mcbsp, SGAINCR, ST_CH0GAIN(st_data->ch0gain) | + ST_CH1GAIN(st_data->ch1gain)); +} + +static int omap_mcbsp_st_set_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + st_data->ch0gain = chgain; + else if (channel == 1) + st_data->ch1gain = chgain; + else + ret = -EINVAL; + + if (st_data->enabled) + omap_mcbsp_st_chgain(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_get_chgain(struct omap_mcbsp *mcbsp, int channel, + s16 *chgain) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENOENT; + + spin_lock_irq(&mcbsp->lock); + if (channel == 0) + *chgain = st_data->ch0gain; + else if (channel == 1) + *chgain = st_data->ch1gain; + else + ret = -EINVAL; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_enable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + st_data->enabled = 1; + omap_mcbsp_st_start(mcbsp); + spin_unlock_irq(&mcbsp->lock); + + return 0; +} + +static int omap_mcbsp_st_disable(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int ret = 0; + + if (!st_data) + return -ENODEV; + + spin_lock_irq(&mcbsp->lock); + omap_mcbsp_st_stop(mcbsp); + st_data->enabled = 0; + spin_unlock_irq(&mcbsp->lock); + + return ret; +} + +static int omap_mcbsp_st_is_enabled(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (!st_data) + return -ENODEV; + + return st_data->enabled; +} + +static ssize_t st_taps_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + ssize_t status = 0; + int i; + + spin_lock_irq(&mcbsp->lock); + for (i = 0; i < st_data->nr_taps; i++) + status += sprintf(&buf[status], (i ? ", %d" : "%d"), + st_data->taps[i]); + if (i) + status += sprintf(&buf[status], "\n"); + spin_unlock_irq(&mcbsp->lock); + + return status; +} + +static ssize_t st_taps_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + int val, tmp, status, i = 0; + + spin_lock_irq(&mcbsp->lock); + memset(st_data->taps, 0, sizeof(st_data->taps)); + st_data->nr_taps = 0; + + do { + status = sscanf(buf, "%d%n", &val, &tmp); + if (status < 0 || status == 0) { + size = -EINVAL; + goto out; + } + if (val < -32768 || val > 32767) { + size = -EINVAL; + goto out; + } + st_data->taps[i++] = val; + buf += tmp; + if (*buf != ',') + break; + buf++; + } while (1); + + st_data->nr_taps = i; + +out: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(st_taps); + +static const struct attribute *sidetone_attrs[] = { + &dev_attr_st_taps.attr, + NULL, +}; + +static const struct attribute_group sidetone_attr_group = { + .attrs = (struct attribute **)sidetone_attrs, +}; + +int omap_mcbsp_st_start(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->enabled && !st_data->running) { + omap_mcbsp_st_fir_write(mcbsp, st_data->taps); + omap_mcbsp_st_chgain(mcbsp); + + if (!mcbsp->free) { + omap_mcbsp_st_on(mcbsp); + st_data->running = 1; + } + } + + return 0; +} + +int omap_mcbsp_st_stop(struct omap_mcbsp *mcbsp) +{ + struct omap_mcbsp_st_data *st_data = mcbsp->st_data; + + if (st_data->running) { + if (!mcbsp->free) { + omap_mcbsp_st_off(mcbsp); + st_data->running = 0; + } + } + + return 0; +} + +int omap_mcbsp_st_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct omap_mcbsp_st_data *st_data; + struct resource *res; + int ret; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sidetone"); + if (!res) + return 0; + + st_data = devm_kzalloc(mcbsp->dev, sizeof(*mcbsp->st_data), GFP_KERNEL); + if (!st_data) + return -ENOMEM; + + st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick"); + if (IS_ERR(st_data->mcbsp_iclk)) { + dev_warn(mcbsp->dev, + "Failed to get ick, sidetone might be broken\n"); + st_data->mcbsp_iclk = NULL; + } + + st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start, + resource_size(res)); + if (!st_data->io_base_st) + return -ENOMEM; + + ret = sysfs_create_group(&mcbsp->dev->kobj, &sidetone_attr_group); + if (ret) + return ret; + + mcbsp->st_data = st_data; + + return 0; +} + +void omap_mcbsp_st_cleanup(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + + if (mcbsp->st_data) { + sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group); + clk_put(mcbsp->st_data->mcbsp_iclk); + } +} + +static int omap_mcbsp_st_info_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int max = mc->max; + int min = mc->min; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = min; + uinfo->value.integer.max = max; + return 0; +} + +#define OMAP_MCBSP_ST_CHANNEL_VOLUME(channel) \ +static int \ +omap_mcbsp_set_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + struct soc_mixer_control *mc = \ + (struct soc_mixer_control *)kc->private_value; \ + int max = mc->max; \ + int min = mc->min; \ + int val = uc->value.integer.value[0]; \ + \ + if (val < min || val > max) \ + return -EINVAL; \ + \ + /* OMAP McBSP implementation uses index values 0..4 */ \ + return omap_mcbsp_st_set_chgain(mcbsp, channel, val); \ +} \ + \ +static int \ +omap_mcbsp_get_st_ch##channel##_volume(struct snd_kcontrol *kc, \ + struct snd_ctl_elem_value *uc) \ +{ \ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kc); \ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); \ + s16 chgain; \ + \ + if (omap_mcbsp_st_get_chgain(mcbsp, channel, &chgain)) \ + return -EAGAIN; \ + \ + uc->value.integer.value[0] = chgain; \ + return 0; \ +} + +OMAP_MCBSP_ST_CHANNEL_VOLUME(0) +OMAP_MCBSP_ST_CHANNEL_VOLUME(1) + +static int omap_mcbsp_st_put_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + u8 value = ucontrol->value.integer.value[0]; + + if (value == omap_mcbsp_st_is_enabled(mcbsp)) + return 0; + + if (value) + omap_mcbsp_st_enable(mcbsp); + else + omap_mcbsp_st_disable(mcbsp); + + return 1; +} + +static int omap_mcbsp_st_get_mode(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol); + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + ucontrol->value.integer.value[0] = omap_mcbsp_st_is_enabled(mcbsp); + return 0; +} + +#define OMAP_MCBSP_SOC_SINGLE_S16_EXT(xname, xmin, xmax, \ + xhandler_get, xhandler_put) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .info = omap_mcbsp_st_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(struct soc_mixer_control) \ + {.min = xmin, .max = xmax} } + +#define OMAP_MCBSP_ST_CONTROLS(port) \ +static const struct snd_kcontrol_new omap_mcbsp##port##_st_controls[] = { \ +SOC_SINGLE_EXT("McBSP" #port " Sidetone Switch", 1, 0, 1, 0, \ + omap_mcbsp_st_get_mode, omap_mcbsp_st_put_mode), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 0 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch0_volume, \ + omap_mcbsp_set_st_ch0_volume), \ +OMAP_MCBSP_SOC_SINGLE_S16_EXT("McBSP" #port " Sidetone Channel 1 Volume", \ + -32768, 32767, \ + omap_mcbsp_get_st_ch1_volume, \ + omap_mcbsp_set_st_ch1_volume), \ +} + +OMAP_MCBSP_ST_CONTROLS(2); +OMAP_MCBSP_ST_CONTROLS(3); + +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id) +{ + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + if (!mcbsp->st_data) { + dev_warn(mcbsp->dev, "No sidetone data for port\n"); + return 0; + } + + switch (port_id) { + case 2: /* McBSP 2 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp2_st_controls, + ARRAY_SIZE(omap_mcbsp2_st_controls)); + case 3: /* McBSP 3 */ + return snd_soc_add_dai_controls(cpu_dai, + omap_mcbsp3_st_controls, + ARRAY_SIZE(omap_mcbsp3_st_controls)); + default: + dev_err(mcbsp->dev, "Port %d not supported\n", port_id); + break; + } + + return -EINVAL; +} +EXPORT_SYMBOL_GPL(omap_mcbsp_st_add_controls); diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c new file mode 100644 index 000000000000..a395598f1f20 --- /dev/null +++ b/sound/soc/ti/omap-mcbsp.c @@ -0,0 +1,1479 @@ +/* + * omap-mcbsp.c -- OMAP ALSA SoC DAI driver using McBSP port + * + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Jarkko Nikula + * Peter Ujfalusi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "omap-mcbsp-priv.h" +#include "omap-mcbsp.h" +#include "sdma-pcm.h" + +#define OMAP_MCBSP_RATES (SNDRV_PCM_RATE_8000_96000) + +enum { + OMAP_MCBSP_WORD_8 = 0, + OMAP_MCBSP_WORD_12, + OMAP_MCBSP_WORD_16, + OMAP_MCBSP_WORD_20, + OMAP_MCBSP_WORD_24, + OMAP_MCBSP_WORD_32, +}; + +static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) +{ + dev_dbg(mcbsp->dev, "**** McBSP%d regs ****\n", mcbsp->id); + dev_dbg(mcbsp->dev, "DRR2: 0x%04x\n", MCBSP_READ(mcbsp, DRR2)); + dev_dbg(mcbsp->dev, "DRR1: 0x%04x\n", MCBSP_READ(mcbsp, DRR1)); + dev_dbg(mcbsp->dev, "DXR2: 0x%04x\n", MCBSP_READ(mcbsp, DXR2)); + dev_dbg(mcbsp->dev, "DXR1: 0x%04x\n", MCBSP_READ(mcbsp, DXR1)); + dev_dbg(mcbsp->dev, "SPCR2: 0x%04x\n", MCBSP_READ(mcbsp, SPCR2)); + dev_dbg(mcbsp->dev, "SPCR1: 0x%04x\n", MCBSP_READ(mcbsp, SPCR1)); + dev_dbg(mcbsp->dev, "RCR2: 0x%04x\n", MCBSP_READ(mcbsp, RCR2)); + dev_dbg(mcbsp->dev, "RCR1: 0x%04x\n", MCBSP_READ(mcbsp, RCR1)); + dev_dbg(mcbsp->dev, "XCR2: 0x%04x\n", MCBSP_READ(mcbsp, XCR2)); + dev_dbg(mcbsp->dev, "XCR1: 0x%04x\n", MCBSP_READ(mcbsp, XCR1)); + dev_dbg(mcbsp->dev, "SRGR2: 0x%04x\n", MCBSP_READ(mcbsp, SRGR2)); + dev_dbg(mcbsp->dev, "SRGR1: 0x%04x\n", MCBSP_READ(mcbsp, SRGR1)); + dev_dbg(mcbsp->dev, "PCR0: 0x%04x\n", MCBSP_READ(mcbsp, PCR0)); + dev_dbg(mcbsp->dev, "***********************\n"); +} + +static int omap2_mcbsp_set_clks_src(struct omap_mcbsp *mcbsp, u8 fck_src_id) +{ + struct clk *fck_src; + const char *src; + int r; + + if (fck_src_id == MCBSP_CLKS_PAD_SRC) + src = "pad_fck"; + else if (fck_src_id == MCBSP_CLKS_PRCM_SRC) + src = "prcm_fck"; + else + return -EINVAL; + + fck_src = clk_get(mcbsp->dev, src); + if (IS_ERR(fck_src)) { + dev_err(mcbsp->dev, "CLKS: could not clk_get() %s\n", src); + return -EINVAL; + } + + pm_runtime_put_sync(mcbsp->dev); + + r = clk_set_parent(mcbsp->fclk, fck_src); + if (r) { + dev_err(mcbsp->dev, "CLKS: could not clk_set_parent() to %s\n", + src); + clk_put(fck_src); + return r; + } + + pm_runtime_get_sync(mcbsp->dev); + + clk_put(fck_src); + + return 0; +} + +static irqreturn_t omap_mcbsp_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst; + + irqst = MCBSP_READ(mcbsp, IRQST); + dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst); + + if (irqst & RSYNCERREN) + dev_err(mcbsp->dev, "RX Frame Sync Error!\n"); + if (irqst & RFSREN) + dev_dbg(mcbsp->dev, "RX Frame Sync\n"); + if (irqst & REOFEN) + dev_dbg(mcbsp->dev, "RX End Of Frame\n"); + if (irqst & RRDYEN) + dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n"); + if (irqst & RUNDFLEN) + dev_err(mcbsp->dev, "RX Buffer Underflow!\n"); + if (irqst & ROVFLEN) + dev_err(mcbsp->dev, "RX Buffer Overflow!\n"); + + if (irqst & XSYNCERREN) + dev_err(mcbsp->dev, "TX Frame Sync Error!\n"); + if (irqst & XFSXEN) + dev_dbg(mcbsp->dev, "TX Frame Sync\n"); + if (irqst & XEOFEN) + dev_dbg(mcbsp->dev, "TX End Of Frame\n"); + if (irqst & XRDYEN) + dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n"); + if (irqst & XUNDFLEN) + dev_err(mcbsp->dev, "TX Buffer Underflow!\n"); + if (irqst & XOVFLEN) + dev_err(mcbsp->dev, "TX Buffer Overflow!\n"); + if (irqst & XEMPTYEOFEN) + dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n"); + + MCBSP_WRITE(mcbsp, IRQST, irqst); + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr2; + + irqst_spcr2 = MCBSP_READ(mcbsp, SPCR2); + dev_dbg(mcbsp->dev, "TX IRQ callback : 0x%x\n", irqst_spcr2); + + if (irqst_spcr2 & XSYNC_ERR) { + dev_err(mcbsp->dev, "TX Frame Sync Error! : 0x%x\n", + irqst_spcr2); + /* Writing zero to XSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR2, MCBSP_READ_CACHE(mcbsp, SPCR2)); + } + + return IRQ_HANDLED; +} + +static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *data) +{ + struct omap_mcbsp *mcbsp = data; + u16 irqst_spcr1; + + irqst_spcr1 = MCBSP_READ(mcbsp, SPCR1); + dev_dbg(mcbsp->dev, "RX IRQ callback : 0x%x\n", irqst_spcr1); + + if (irqst_spcr1 & RSYNC_ERR) { + dev_err(mcbsp->dev, "RX Frame Sync Error! : 0x%x\n", + irqst_spcr1); + /* Writing zero to RSYNC_ERR clears the IRQ */ + MCBSP_WRITE(mcbsp, SPCR1, MCBSP_READ_CACHE(mcbsp, SPCR1)); + } + + return IRQ_HANDLED; +} + +/* + * omap_mcbsp_config simply write a config to the + * appropriate McBSP. + * You either call this function or set the McBSP registers + * by yourself before calling omap_mcbsp_start(). + */ +static void omap_mcbsp_config(struct omap_mcbsp *mcbsp, + const struct omap_mcbsp_reg_cfg *config) +{ + dev_dbg(mcbsp->dev, "Configuring McBSP%d phys_base: 0x%08lx\n", + mcbsp->id, mcbsp->phys_base); + + /* We write the given config */ + MCBSP_WRITE(mcbsp, SPCR2, config->spcr2); + MCBSP_WRITE(mcbsp, SPCR1, config->spcr1); + MCBSP_WRITE(mcbsp, RCR2, config->rcr2); + MCBSP_WRITE(mcbsp, RCR1, config->rcr1); + MCBSP_WRITE(mcbsp, XCR2, config->xcr2); + MCBSP_WRITE(mcbsp, XCR1, config->xcr1); + MCBSP_WRITE(mcbsp, SRGR2, config->srgr2); + MCBSP_WRITE(mcbsp, SRGR1, config->srgr1); + MCBSP_WRITE(mcbsp, MCR2, config->mcr2); + MCBSP_WRITE(mcbsp, MCR1, config->mcr1); + MCBSP_WRITE(mcbsp, PCR0, config->pcr0); + if (mcbsp->pdata->has_ccr) { + MCBSP_WRITE(mcbsp, XCCR, config->xccr); + MCBSP_WRITE(mcbsp, RCCR, config->rccr); + } + /* Enable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); + + /* Enable TX/RX sync error interrupts by default */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN | + RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN); +} + +/** + * omap_mcbsp_dma_reg_params - returns the address of mcbsp data register + * @mcbsp: omap_mcbsp struct for the McBSP instance + * @stream: Stream direction (playback/capture) + * + * Returns the address of mcbsp data transmit register or data receive register + * to be used by DMA for transferring/receiving data + */ +static int omap_mcbsp_dma_reg_params(struct omap_mcbsp *mcbsp, + unsigned int stream) +{ + int data_reg; + + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DXR1; + else + data_reg = OMAP_MCBSP_REG_DXR; + } else { + if (mcbsp->pdata->reg_size == 2) + data_reg = OMAP_MCBSP_REG_DRR1; + else + data_reg = OMAP_MCBSP_REG_DRR; + } + + return mcbsp->phys_dma_base + data_reg * mcbsp->pdata->reg_step; +} + +/* + * omap_mcbsp_set_rx_threshold configures the transmit threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH2 register. + */ +static void omap_mcbsp_set_tx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_tx_thres) + MCBSP_WRITE(mcbsp, THRSH2, threshold - 1); +} + +/* + * omap_mcbsp_set_rx_threshold configures the receive threshold in words. + * The threshold parameter is 1 based, and it is converted (threshold - 1) + * for the THRSH1 register. + */ +static void omap_mcbsp_set_rx_threshold(struct omap_mcbsp *mcbsp, u16 threshold) +{ + if (threshold && threshold <= mcbsp->max_rx_thres) + MCBSP_WRITE(mcbsp, THRSH1, threshold - 1); +} + +/* + * omap_mcbsp_get_tx_delay returns the number of used slots in the McBSP FIFO + */ +static u16 omap_mcbsp_get_tx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat; + + /* Returns the number of free locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, XBUFFSTAT); + + /* Number of slots are different in McBSP ports */ + return mcbsp->pdata->buffer_size - buffstat; +} + +/* + * omap_mcbsp_get_rx_delay returns the number of free slots in the McBSP FIFO + * to reach the threshold value (when the DMA will be triggered to read it) + */ +static u16 omap_mcbsp_get_rx_delay(struct omap_mcbsp *mcbsp) +{ + u16 buffstat, threshold; + + /* Returns the number of used locations in the buffer */ + buffstat = MCBSP_READ(mcbsp, RBUFFSTAT); + /* RX threshold */ + threshold = MCBSP_READ(mcbsp, THRSH1); + + /* Return the number of location till we reach the threshold limit */ + if (threshold <= buffstat) + return 0; + else + return threshold - buffstat; +} + +static int omap_mcbsp_request(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + int err; + + reg_cache = kzalloc(mcbsp->reg_cache_size, GFP_KERNEL); + if (!reg_cache) + return -ENOMEM; + + spin_lock(&mcbsp->lock); + if (!mcbsp->free) { + dev_err(mcbsp->dev, "McBSP%d is currently in use\n", mcbsp->id); + err = -EBUSY; + goto err_kfree; + } + + mcbsp->free = false; + mcbsp->reg_cache = reg_cache; + spin_unlock(&mcbsp->lock); + + if(mcbsp->pdata->ops && mcbsp->pdata->ops->request) + mcbsp->pdata->ops->request(mcbsp->id - 1); + + /* + * Make sure that transmitter, receiver and sample-rate generator are + * not running before activating IRQs. + */ + MCBSP_WRITE(mcbsp, SPCR1, 0); + MCBSP_WRITE(mcbsp, SPCR2, 0); + + if (mcbsp->irq) { + err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0, + "McBSP", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request IRQ\n"); + goto err_clk_disable; + } + } else { + err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP TX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request TX IRQ\n"); + goto err_clk_disable; + } + + err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP RX", (void *)mcbsp); + if (err != 0) { + dev_err(mcbsp->dev, "Unable to request RX IRQ\n"); + goto err_free_irq; + } + } + + return 0; +err_free_irq: + free_irq(mcbsp->tx_irq, (void *)mcbsp); +err_clk_disable: + if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + spin_lock(&mcbsp->lock); + mcbsp->free = true; + mcbsp->reg_cache = NULL; +err_kfree: + spin_unlock(&mcbsp->lock); + kfree(reg_cache); + + return err; +} + +static void omap_mcbsp_free(struct omap_mcbsp *mcbsp) +{ + void *reg_cache; + + if(mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id - 1); + + /* Disable wakeup behavior */ + if (mcbsp->pdata->has_wakeup) + MCBSP_WRITE(mcbsp, WAKEUPEN, 0); + + /* Disable interrupt requests */ + if (mcbsp->irq) + MCBSP_WRITE(mcbsp, IRQEN, 0); + + if (mcbsp->irq) { + free_irq(mcbsp->irq, (void *)mcbsp); + } else { + free_irq(mcbsp->rx_irq, (void *)mcbsp); + free_irq(mcbsp->tx_irq, (void *)mcbsp); + } + + reg_cache = mcbsp->reg_cache; + + /* + * Select CLKS source from internal source unconditionally before + * marking the McBSP port as free. + * If the external clock source via MCBSP_CLKS pin has been selected the + * system will refuse to enter idle if the CLKS pin source is not reset + * back to internal source. + */ + if (!mcbsp_omap1()) + omap2_mcbsp_set_clks_src(mcbsp, MCBSP_CLKS_PRCM_SRC); + + spin_lock(&mcbsp->lock); + if (mcbsp->free) + dev_err(mcbsp->dev, "McBSP%d was not reserved\n", mcbsp->id); + else + mcbsp->free = true; + mcbsp->reg_cache = NULL; + spin_unlock(&mcbsp->lock); + + kfree(reg_cache); +} + +/* + * Here we start the McBSP, by enabling transmitter, receiver or both. + * If no transmitter or receiver is active prior calling, then sample-rate + * generator and frame sync are started. + */ +static void omap_mcbsp_start(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int enable_srg = 0; + u16 w; + + if (mcbsp->st_data) + omap_mcbsp_st_start(mcbsp); + + /* Only enable SRG, if McBSP is master */ + w = MCBSP_READ_CACHE(mcbsp, PCR0); + if (w & (FSXM | FSRM | CLKXM | CLKRM)) + enable_srg = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (enable_srg) { + /* Start the sample generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 6)); + } + + /* Enable transmitter and receiver */ + tx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | tx); + + rx &= 1; + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w | rx); + + /* + * Worst case: CLKSRG*2 = 8000khz: (1/8000) * 2 * 2 usec + * REVISIT: 100us may give enough time for two CLKSRG, however + * due to some unknown PM related, clock gating etc. reason it + * is now at 500us. + */ + udelay(500); + + if (enable_srg) { + /* Start frame sync */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w | (1 << 7)); + } + + if (mcbsp->pdata->has_ccr) { + /* Release the transmitter and receiver */ + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w &= ~(tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w &= ~(rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + + /* Dump McBSP Regs */ + omap_mcbsp_dump_reg(mcbsp); +} + +static void omap_mcbsp_stop(struct omap_mcbsp *mcbsp, int stream) +{ + int tx = (stream == SNDRV_PCM_STREAM_PLAYBACK); + int rx = !tx; + int idle; + u16 w; + + /* Reset transmitter */ + tx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, XCCR); + w |= (tx ? XDISABLE : 0); + MCBSP_WRITE(mcbsp, XCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~tx); + + /* Reset receiver */ + rx &= 1; + if (mcbsp->pdata->has_ccr) { + w = MCBSP_READ_CACHE(mcbsp, RCCR); + w |= (rx ? RDISABLE : 0); + MCBSP_WRITE(mcbsp, RCCR, w); + } + w = MCBSP_READ_CACHE(mcbsp, SPCR1); + MCBSP_WRITE(mcbsp, SPCR1, w & ~rx); + + idle = !((MCBSP_READ_CACHE(mcbsp, SPCR2) | + MCBSP_READ_CACHE(mcbsp, SPCR1)) & 1); + + if (idle) { + /* Reset the sample rate generator */ + w = MCBSP_READ_CACHE(mcbsp, SPCR2); + MCBSP_WRITE(mcbsp, SPCR2, w & ~(1 << 6)); + } + + if (mcbsp->st_data) + omap_mcbsp_st_stop(mcbsp); +} + +#define max_thres(m) (mcbsp->pdata->buffer_size) +#define valid_threshold(m, val) ((val) <= max_thres(m)) +#define THRESHOLD_PROP_BUILDER(prop) \ +static ssize_t prop##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + \ + return sprintf(buf, "%u\n", mcbsp->prop); \ +} \ + \ +static ssize_t prop##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t size) \ +{ \ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); \ + unsigned long val; \ + int status; \ + \ + status = kstrtoul(buf, 0, &val); \ + if (status) \ + return status; \ + \ + if (!valid_threshold(mcbsp, val)) \ + return -EDOM; \ + \ + mcbsp->prop = val; \ + return size; \ +} \ + \ +static DEVICE_ATTR(prop, 0644, prop##_show, prop##_store) + +THRESHOLD_PROP_BUILDER(max_tx_thres); +THRESHOLD_PROP_BUILDER(max_rx_thres); + +static const char * const dma_op_modes[] = { + "element", "threshold", +}; + +static ssize_t dma_op_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int dma_op_mode, i = 0; + ssize_t len = 0; + const char * const *s; + + dma_op_mode = mcbsp->dma_op_mode; + + for (s = &dma_op_modes[i]; i < ARRAY_SIZE(dma_op_modes); s++, i++) { + if (dma_op_mode == i) + len += sprintf(buf + len, "[%s] ", *s); + else + len += sprintf(buf + len, "%s ", *s); + } + len += sprintf(buf + len, "\n"); + + return len; +} + +static ssize_t dma_op_mode_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + struct omap_mcbsp *mcbsp = dev_get_drvdata(dev); + int i; + + i = sysfs_match_string(dma_op_modes, buf); + if (i < 0) + return i; + + spin_lock_irq(&mcbsp->lock); + if (!mcbsp->free) { + size = -EBUSY; + goto unlock; + } + mcbsp->dma_op_mode = i; + +unlock: + spin_unlock_irq(&mcbsp->lock); + + return size; +} + +static DEVICE_ATTR_RW(dma_op_mode); + +static const struct attribute *additional_attrs[] = { + &dev_attr_max_tx_thres.attr, + &dev_attr_max_rx_thres.attr, + &dev_attr_dma_op_mode.attr, + NULL, +}; + +static const struct attribute_group additional_attr_group = { + .attrs = (struct attribute **)additional_attrs, +}; + +/* + * McBSP1 and McBSP3 are directly mapped on 1610 and 1510. + * 730 has only 2 McBSP, and both of them are MPU peripherals. + */ +static int omap_mcbsp_init(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + struct resource *res; + int ret = 0; + + spin_lock_init(&mcbsp->lock); + mcbsp->free = true; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + if (!res) + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + mcbsp->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcbsp->io_base)) + return PTR_ERR(mcbsp->io_base); + + mcbsp->phys_base = res->start; + mcbsp->reg_cache_size = resource_size(res); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (!res) + mcbsp->phys_dma_base = mcbsp->phys_base; + else + mcbsp->phys_dma_base = res->start; + + /* + * OMAP1, 2 uses two interrupt lines: TX, RX + * OMAP2430, OMAP3 SoC have combined IRQ line as well. + * OMAP4 and newer SoC only have the combined IRQ line. + * Use the combined IRQ if available since it gives better debugging + * possibilities. + */ + mcbsp->irq = platform_get_irq_byname(pdev, "common"); + if (mcbsp->irq == -ENXIO) { + mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); + + if (mcbsp->tx_irq == -ENXIO) { + mcbsp->irq = platform_get_irq(pdev, 0); + mcbsp->tx_irq = 0; + } else { + mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); + mcbsp->irq = 0; + } + } + + if (!pdev->dev.of_node) { + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx"); + if (!res) { + dev_err(&pdev->dev, "invalid tx DMA channel\n"); + return -ENODEV; + } + mcbsp->dma_req[0] = res->start; + mcbsp->dma_data[0].filter_data = &mcbsp->dma_req[0]; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); + if (!res) { + dev_err(&pdev->dev, "invalid rx DMA channel\n"); + return -ENODEV; + } + mcbsp->dma_req[1] = res->start; + mcbsp->dma_data[1].filter_data = &mcbsp->dma_req[1]; + } else { + mcbsp->dma_data[0].filter_data = "tx"; + mcbsp->dma_data[1].filter_data = "rx"; + } + + mcbsp->dma_data[0].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_PLAYBACK); + mcbsp->dma_data[1].addr = omap_mcbsp_dma_reg_params(mcbsp, + SNDRV_PCM_STREAM_CAPTURE); + + mcbsp->fclk = clk_get(&pdev->dev, "fck"); + if (IS_ERR(mcbsp->fclk)) { + ret = PTR_ERR(mcbsp->fclk); + dev_err(mcbsp->dev, "unable to get fck: %d\n", ret); + return ret; + } + + mcbsp->dma_op_mode = MCBSP_DMA_MODE_ELEMENT; + if (mcbsp->pdata->buffer_size) { + /* + * Initially configure the maximum thresholds to a safe value. + * The McBSP FIFO usage with these values should not go under + * 16 locations. + * If the whole FIFO without safety buffer is used, than there + * is a possibility that the DMA will be not able to push the + * new data on time, causing channel shifts in runtime. + */ + mcbsp->max_tx_thres = max_thres(mcbsp) - 0x10; + mcbsp->max_rx_thres = max_thres(mcbsp) - 0x10; + + ret = sysfs_create_group(&mcbsp->dev->kobj, + &additional_attr_group); + if (ret) { + dev_err(mcbsp->dev, + "Unable to create additional controls\n"); + goto err_thres; + } + } + + ret = omap_mcbsp_st_init(pdev); + if (ret) + goto err_st; + + return 0; + +err_st: + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); +err_thres: + clk_put(mcbsp->fclk); + return ret; +} + +/* + * Stream DMA parameters. DMA request line and port address are set runtime + * since they are different between OMAP1 and later OMAPs + */ +static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream, + unsigned int packet_size) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + int words; + + /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return; + + /* + * Configure McBSP threshold based on either: + * packet_size, when the sDMA is in packet mode, or based on the + * period size in THRESHOLD mode, otherwise use McBSP threshold = 1 + * for mono streams. + */ + if (packet_size) + words = packet_size; + else + words = 1; + + /* Configure McBSP internal buffer usage */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + omap_mcbsp_set_tx_threshold(mcbsp, words); + else + omap_mcbsp_set_rx_threshold(mcbsp, words); +} + +static int omap_mcbsp_hwrule_min_buffersize(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *buffer_size = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct omap_mcbsp *mcbsp = rule->private; + struct snd_interval frames; + int size; + + snd_interval_any(&frames); + size = mcbsp->pdata->buffer_size; + + frames.min = size / channels->min; + frames.integer = 1; + return snd_interval_refine(buffer_size, &frames); +} + +static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + int err = 0; + + if (!cpu_dai->active) + err = omap_mcbsp_request(mcbsp); + + /* + * OMAP3 McBSP FIFO is word structured. + * McBSP2 has 1024 + 256 = 1280 word long buffer, + * McBSP1,3,4,5 has 128 word long buffer + * This means that the size of the FIFO depends on the sample format. + * For example on McBSP3: + * 16bit samples: size is 128 * 2 = 256 bytes + * 32bit samples: size is 128 * 4 = 512 bytes + * It is simpler to place constraint for buffer and period based on + * channels. + * McBSP3 as example again (16 or 32 bit samples): + * 1 channel (mono): size is 128 frames (128 words) + * 2 channels (stereo): size is 128 / 2 = 64 frames (2 * 64 words) + * 4 channels: size is 128 / 4 = 32 frames (4 * 32 words) + */ + if (mcbsp->pdata->buffer_size) { + /* + * Rule for the buffer size. We should not allow + * smaller buffer than the FIFO size to avoid underruns. + * This applies only for the playback stream. + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_pcm_hw_rule_add(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + omap_mcbsp_hwrule_min_buffersize, + mcbsp, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + + /* Make sure, that the period size is always even */ + snd_pcm_hw_constraint_step(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 2); + } + + return err; +} + +static void omap_mcbsp_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; + int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + + if (mcbsp->latency[stream2]) + pm_qos_update_request(&mcbsp->pm_qos_req, + mcbsp->latency[stream2]); + else if (mcbsp->latency[stream1]) + pm_qos_remove_request(&mcbsp->pm_qos_req); + + mcbsp->latency[stream1] = 0; + + if (!cpu_dai->active) { + omap_mcbsp_free(mcbsp); + mcbsp->configured = 0; + } +} + +static int omap_mcbsp_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct pm_qos_request *pm_qos_req = &mcbsp->pm_qos_req; + int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; + int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + int latency = mcbsp->latency[stream2]; + + /* Prevent omap hardware from hitting off between FIFO fills */ + if (!latency || mcbsp->latency[stream1] < latency) + latency = mcbsp->latency[stream1]; + + if (pm_qos_request_active(pm_qos_req)) + pm_qos_update_request(pm_qos_req, latency); + else if (latency) + pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); + + return 0; +} + +static int omap_mcbsp_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *cpu_dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + mcbsp->active++; + omap_mcbsp_start(mcbsp, substream->stream); + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + omap_mcbsp_stop(mcbsp, substream->stream); + mcbsp->active--; + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_sframes_t omap_mcbsp_dai_delay( + struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + u16 fifo_use; + snd_pcm_sframes_t delay; + + /* No need to proceed further if McBSP does not have FIFO */ + if (mcbsp->pdata->buffer_size == 0) + return 0; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + fifo_use = omap_mcbsp_get_tx_delay(mcbsp); + else + fifo_use = omap_mcbsp_get_rx_delay(mcbsp); + + /* + * Divide the used locations with the channel count to get the + * FIFO usage in samples (don't care about partial samples in the + * buffer). + */ + delay = fifo_use / substream->runtime->channels; + + return delay; +} + +static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; + struct snd_dmaengine_dai_dma_data *dma_data; + int wlen, channels, wpf; + int pkt_size = 0; + unsigned int format, div, framesize, master; + unsigned int buffer_size = mcbsp->pdata->buffer_size; + + dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); + channels = params_channels(params); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + wlen = 16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + wlen = 32; + break; + default: + return -EINVAL; + } + if (buffer_size) { + int latency; + + if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { + int period_words, max_thrsh; + int divider = 0; + + period_words = params_period_bytes(params) / (wlen / 8); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + max_thrsh = mcbsp->max_tx_thres; + else + max_thrsh = mcbsp->max_rx_thres; + /* + * Use sDMA packet mode if McBSP is in threshold mode: + * If period words less than the FIFO size the packet + * size is set to the number of period words, otherwise + * Look for the biggest threshold value which divides + * the period size evenly. + */ + divider = period_words / max_thrsh; + if (period_words % max_thrsh) + divider++; + while (period_words % divider && + divider < period_words) + divider++; + if (divider == period_words) + return -EINVAL; + + pkt_size = period_words / divider; + } else if (channels > 1) { + /* Use packet mode for non mono streams */ + pkt_size = channels; + } + + latency = (buffer_size - pkt_size) / channels; + latency = latency * USEC_PER_SEC / + (params->rate_num / params->rate_den); + mcbsp->latency[substream->stream] = latency; + + omap_mcbsp_set_threshold(substream, pkt_size); + } + + dma_data->maxburst = pkt_size; + + if (mcbsp->configured) { + /* McBSP already configured by another stream */ + return 0; + } + + regs->rcr2 &= ~(RPHASE | RFRLEN2(0x7f) | RWDLEN2(7)); + regs->xcr2 &= ~(RPHASE | XFRLEN2(0x7f) | XWDLEN2(7)); + regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); + regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); + format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; + wpf = channels; + if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || + format == SND_SOC_DAIFMT_LEFT_J)) { + /* Use dual-phase frames */ + regs->rcr2 |= RPHASE; + regs->xcr2 |= XPHASE; + /* Set 1 word per (McBSP) frame for phase1 and phase2 */ + wpf--; + regs->rcr2 |= RFRLEN2(wpf - 1); + regs->xcr2 |= XFRLEN2(wpf - 1); + } + + regs->rcr1 |= RFRLEN1(wpf - 1); + regs->xcr1 |= XFRLEN1(wpf - 1); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + /* Set word lengths */ + regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_16); + regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_16); + regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_16); + regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_16); + break; + case SNDRV_PCM_FORMAT_S32_LE: + /* Set word lengths */ + regs->rcr2 |= RWDLEN2(OMAP_MCBSP_WORD_32); + regs->rcr1 |= RWDLEN1(OMAP_MCBSP_WORD_32); + regs->xcr2 |= XWDLEN2(OMAP_MCBSP_WORD_32); + regs->xcr1 |= XWDLEN1(OMAP_MCBSP_WORD_32); + break; + default: + /* Unsupported PCM format */ + return -EINVAL; + } + + /* In McBSP master modes, FRAME (i.e. sample rate) is generated + * by _counting_ BCLKs. Calculate frame size in BCLKs */ + master = mcbsp->fmt & SND_SOC_DAIFMT_MASTER_MASK; + if (master == SND_SOC_DAIFMT_CBS_CFS) { + div = mcbsp->clk_div ? mcbsp->clk_div : 1; + framesize = (mcbsp->in_freq / div) / params_rate(params); + + if (framesize < wlen * channels) { + printk(KERN_ERR "%s: not enough bandwidth for desired rate and " + "channels\n", __func__); + return -EINVAL; + } + } else + framesize = wlen * channels; + + /* Set FS period and length in terms of bit clock periods */ + regs->srgr2 &= ~FPER(0xfff); + regs->srgr1 &= ~FWID(0xff); + switch (format) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + regs->srgr2 |= FPER(framesize - 1); + regs->srgr1 |= FWID((framesize >> 1) - 1); + break; + case SND_SOC_DAIFMT_DSP_A: + case SND_SOC_DAIFMT_DSP_B: + regs->srgr2 |= FPER(framesize - 1); + regs->srgr1 |= FWID(0); + break; + } + + omap_mcbsp_config(mcbsp, &mcbsp->cfg_regs); + mcbsp->wlen = wlen; + mcbsp->configured = 1; + + return 0; +} + +/* + * This must be called before _set_clkdiv and _set_sysclk since McBSP register + * cache is initialized here + */ +static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; + bool inv_fs = false; + + if (mcbsp->configured) + return 0; + + mcbsp->fmt = fmt; + memset(regs, 0, sizeof(*regs)); + /* Generic McBSP register settings */ + regs->spcr2 |= XINTM(3) | FREE; + regs->spcr1 |= RINTM(3); + /* RFIG and XFIG are not defined in 2430 and on OMAP3+ */ + if (!mcbsp->pdata->has_ccr) { + regs->rcr2 |= RFIG; + regs->xcr2 |= XFIG; + } + + /* Configure XCCR/RCCR only for revisions which have ccr registers */ + if (mcbsp->pdata->has_ccr) { + regs->xccr = DXENDLY(1) | XDMAEN | XDISABLE; + regs->rccr = RFULL_CYCLE | RDMAEN | RDISABLE; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* 1-bit data delay */ + regs->rcr2 |= RDATDLY(1); + regs->xcr2 |= XDATDLY(1); + break; + case SND_SOC_DAIFMT_LEFT_J: + /* 0-bit data delay */ + regs->rcr2 |= RDATDLY(0); + regs->xcr2 |= XDATDLY(0); + regs->spcr1 |= RJUST(2); + /* Invert FS polarity configuration */ + inv_fs = true; + break; + case SND_SOC_DAIFMT_DSP_A: + /* 1-bit data delay */ + regs->rcr2 |= RDATDLY(1); + regs->xcr2 |= XDATDLY(1); + /* Invert FS polarity configuration */ + inv_fs = true; + break; + case SND_SOC_DAIFMT_DSP_B: + /* 0-bit data delay */ + regs->rcr2 |= RDATDLY(0); + regs->xcr2 |= XDATDLY(0); + /* Invert FS polarity configuration */ + inv_fs = true; + break; + default: + /* Unsupported data format */ + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + /* McBSP master. Set FS and bit clocks as outputs */ + regs->pcr0 |= FSXM | FSRM | + CLKXM | CLKRM; + /* Sample rate generator drives the FS */ + regs->srgr2 |= FSGM; + break; + case SND_SOC_DAIFMT_CBM_CFS: + /* McBSP slave. FS clock as output */ + regs->srgr2 |= FSGM; + regs->pcr0 |= FSXM | FSRM; + break; + case SND_SOC_DAIFMT_CBM_CFM: + /* McBSP slave */ + break; + default: + /* Unsupported master/slave configuration */ + return -EINVAL; + } + + /* Set bit clock (CLKX/CLKR) and FS polarities */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + /* + * Normal BCLK + FS. + * FS active low. TX data driven on falling edge of bit clock + * and RX data sampled on rising edge of bit clock. + */ + regs->pcr0 |= FSXP | FSRP | + CLKXP | CLKRP; + break; + case SND_SOC_DAIFMT_NB_IF: + regs->pcr0 |= CLKXP | CLKRP; + break; + case SND_SOC_DAIFMT_IB_NF: + regs->pcr0 |= FSXP | FSRP; + break; + case SND_SOC_DAIFMT_IB_IF: + break; + default: + return -EINVAL; + } + if (inv_fs == true) + regs->pcr0 ^= FSXP | FSRP; + + return 0; +} + +static int omap_mcbsp_dai_set_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; + + if (div_id != OMAP_MCBSP_CLKGDV) + return -ENODEV; + + mcbsp->clk_div = div; + regs->srgr1 &= ~CLKGDV(0xff); + regs->srgr1 |= CLKGDV(div - 1); + + return 0; +} + +static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, + int dir) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(cpu_dai); + struct omap_mcbsp_reg_cfg *regs = &mcbsp->cfg_regs; + int err = 0; + + if (mcbsp->active) { + if (freq == mcbsp->in_freq) + return 0; + else + return -EBUSY; + } + + mcbsp->in_freq = freq; + regs->srgr2 &= ~CLKSM; + regs->pcr0 &= ~SCLKME; + + switch (clk_id) { + case OMAP_MCBSP_SYSCLK_CLK: + regs->srgr2 |= CLKSM; + break; + case OMAP_MCBSP_SYSCLK_CLKS_FCLK: + if (mcbsp_omap1()) { + err = -EINVAL; + break; + } + err = omap2_mcbsp_set_clks_src(mcbsp, + MCBSP_CLKS_PRCM_SRC); + break; + case OMAP_MCBSP_SYSCLK_CLKS_EXT: + if (mcbsp_omap1()) { + err = 0; + break; + } + err = omap2_mcbsp_set_clks_src(mcbsp, + MCBSP_CLKS_PAD_SRC); + break; + + case OMAP_MCBSP_SYSCLK_CLKX_EXT: + regs->srgr2 |= CLKSM; + regs->pcr0 |= SCLKME; + /* + * If McBSP is master but yet the CLKX/CLKR pin drives the SRG, + * disable output on those pins. This enables to inject the + * reference clock through CLKX/CLKR. For this to work + * set_dai_sysclk() _needs_ to be called after set_dai_fmt(). + */ + regs->pcr0 &= ~CLKXM; + break; + case OMAP_MCBSP_SYSCLK_CLKR_EXT: + regs->pcr0 |= SCLKME; + /* Disable ouput on CLKR pin in master mode */ + regs->pcr0 &= ~CLKRM; + break; + default: + err = -ENODEV; + } + + return err; +} + +static const struct snd_soc_dai_ops mcbsp_dai_ops = { + .startup = omap_mcbsp_dai_startup, + .shutdown = omap_mcbsp_dai_shutdown, + .prepare = omap_mcbsp_dai_prepare, + .trigger = omap_mcbsp_dai_trigger, + .delay = omap_mcbsp_dai_delay, + .hw_params = omap_mcbsp_dai_hw_params, + .set_fmt = omap_mcbsp_dai_set_dai_fmt, + .set_clkdiv = omap_mcbsp_dai_set_clkdiv, + .set_sysclk = omap_mcbsp_dai_set_dai_sysclk, +}; + +static int omap_mcbsp_probe(struct snd_soc_dai *dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); + + pm_runtime_enable(mcbsp->dev); + + snd_soc_dai_init_dma_data(dai, + &mcbsp->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &mcbsp->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return 0; +} + +static int omap_mcbsp_remove(struct snd_soc_dai *dai) +{ + struct omap_mcbsp *mcbsp = snd_soc_dai_get_drvdata(dai); + + pm_runtime_disable(mcbsp->dev); + + return 0; +} + +static struct snd_soc_dai_driver omap_mcbsp_dai = { + .probe = omap_mcbsp_probe, + .remove = omap_mcbsp_remove, + .playback = { + .channels_min = 1, + .channels_max = 16, + .rates = OMAP_MCBSP_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 16, + .rates = OMAP_MCBSP_RATES, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &mcbsp_dai_ops, +}; + +static const struct snd_soc_component_driver omap_mcbsp_component = { + .name = "omap-mcbsp", +}; + +static struct omap_mcbsp_platform_data omap2420_pdata = { + .reg_step = 4, + .reg_size = 2, +}; + +static struct omap_mcbsp_platform_data omap2430_pdata = { + .reg_step = 4, + .reg_size = 4, + .has_ccr = true, +}; + +static struct omap_mcbsp_platform_data omap3_pdata = { + .reg_step = 4, + .reg_size = 4, + .has_ccr = true, + .has_wakeup = true, +}; + +static struct omap_mcbsp_platform_data omap4_pdata = { + .reg_step = 4, + .reg_size = 4, + .has_ccr = true, + .has_wakeup = true, +}; + +static const struct of_device_id omap_mcbsp_of_match[] = { + { + .compatible = "ti,omap2420-mcbsp", + .data = &omap2420_pdata, + }, + { + .compatible = "ti,omap2430-mcbsp", + .data = &omap2430_pdata, + }, + { + .compatible = "ti,omap3-mcbsp", + .data = &omap3_pdata, + }, + { + .compatible = "ti,omap4-mcbsp", + .data = &omap4_pdata, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_mcbsp_of_match); + +static int asoc_mcbsp_probe(struct platform_device *pdev) +{ + struct omap_mcbsp_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct omap_mcbsp *mcbsp; + const struct of_device_id *match; + int ret; + + match = of_match_device(omap_mcbsp_of_match, &pdev->dev); + if (match) { + struct device_node *node = pdev->dev.of_node; + struct omap_mcbsp_platform_data *pdata_quirk = pdata; + int buffer_size; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct omap_mcbsp_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + memcpy(pdata, match->data, sizeof(*pdata)); + if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size)) + pdata->buffer_size = buffer_size; + if (pdata_quirk) + pdata->force_ick_on = pdata_quirk->force_ick_on; + } else if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + mcbsp = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcbsp), GFP_KERNEL); + if (!mcbsp) + return -ENOMEM; + + mcbsp->id = pdev->id; + mcbsp->pdata = pdata; + mcbsp->dev = &pdev->dev; + platform_set_drvdata(pdev, mcbsp); + + ret = omap_mcbsp_init(pdev); + if (ret) + return ret; + + if (mcbsp->pdata->reg_size == 2) { + omap_mcbsp_dai.playback.formats = SNDRV_PCM_FMTBIT_S16_LE; + omap_mcbsp_dai.capture.formats = SNDRV_PCM_FMTBIT_S16_LE; + } + + ret = devm_snd_soc_register_component(&pdev->dev, + &omap_mcbsp_component, + &omap_mcbsp_dai, 1); + if (ret) + return ret; + + return sdma_pcm_platform_register(&pdev->dev, NULL, NULL); +} + +static int asoc_mcbsp_remove(struct platform_device *pdev) +{ + struct omap_mcbsp *mcbsp = platform_get_drvdata(pdev); + + if (mcbsp->pdata->ops && mcbsp->pdata->ops->free) + mcbsp->pdata->ops->free(mcbsp->id); + + if (pm_qos_request_active(&mcbsp->pm_qos_req)) + pm_qos_remove_request(&mcbsp->pm_qos_req); + + if (mcbsp->pdata->buffer_size) + sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group); + + omap_mcbsp_st_cleanup(pdev); + + clk_put(mcbsp->fclk); + + return 0; +} + +static struct platform_driver asoc_mcbsp_driver = { + .driver = { + .name = "omap-mcbsp", + .of_match_table = omap_mcbsp_of_match, + }, + + .probe = asoc_mcbsp_probe, + .remove = asoc_mcbsp_remove, +}; + +module_platform_driver(asoc_mcbsp_driver); + +MODULE_AUTHOR("Jarkko Nikula "); +MODULE_DESCRIPTION("OMAP I2S SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap-mcbsp"); diff --git a/sound/soc/ti/omap-mcbsp.h b/sound/soc/ti/omap-mcbsp.h new file mode 100644 index 000000000000..7911d24898c9 --- /dev/null +++ b/sound/soc/ti/omap-mcbsp.h @@ -0,0 +1,46 @@ +/* + * omap-mcbsp.h + * + * Copyright (C) 2008 Nokia Corporation + * + * Contact: Jarkko Nikula + * Peter Ujfalusi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __OMAP_MCBSP_H__ +#define __OMAP_MCBSP_H__ + +#include + +/* Source clocks for McBSP sample rate generator */ +enum omap_mcbsp_clksrg_clk { + OMAP_MCBSP_SYSCLK_CLKS_FCLK, /* Internal FCLK */ + OMAP_MCBSP_SYSCLK_CLKS_EXT, /* External CLKS pin */ + OMAP_MCBSP_SYSCLK_CLK, /* Internal ICLK */ + OMAP_MCBSP_SYSCLK_CLKX_EXT, /* External CLKX pin */ + OMAP_MCBSP_SYSCLK_CLKR_EXT, /* External CLKR pin */ +}; + +/* McBSP dividers */ +enum omap_mcbsp_div { + OMAP_MCBSP_CLKGDV, /* Sample rate generator divider */ +}; + +int omap_mcbsp_st_add_controls(struct snd_soc_pcm_runtime *rtd, int port_id); + +#endif /* __OMAP_MCBSP_H__ */ diff --git a/sound/soc/ti/omap-mcpdm.c b/sound/soc/ti/omap-mcpdm.c new file mode 100644 index 000000000000..7d5bdc5a2890 --- /dev/null +++ b/sound/soc/ti/omap-mcpdm.c @@ -0,0 +1,619 @@ +/* + * omap-mcpdm.c -- OMAP ALSA SoC DAI driver using McPDM port + * + * Copyright (C) 2009 - 2011 Texas Instruments + * + * Author: Misael Lopez Cruz + * Contact: Jorge Eduardo Candelaria + * Margarita Olaya + * Peter Ujfalusi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "omap-mcpdm.h" +#include "sdma-pcm.h" + +struct mcpdm_link_config { + u32 link_mask; /* channel mask for the direction */ + u32 threshold; /* FIFO threshold */ +}; + +struct omap_mcpdm { + struct device *dev; + unsigned long phys_base; + void __iomem *io_base; + int irq; + struct pm_qos_request pm_qos_req; + int latency[2]; + + struct mutex mutex; + + /* Playback/Capture configuration */ + struct mcpdm_link_config config[2]; + + /* McPDM dn offsets for rx1, and 2 channels */ + u32 dn_rx_offset; + + /* McPDM needs to be restarted due to runtime reconfiguration */ + bool restart; + + /* pm state for suspend/resume handling */ + int pm_active_count; + + struct snd_dmaengine_dai_dma_data dma_data[2]; +}; + +/* + * Stream DMA parameters + */ + +static inline void omap_mcpdm_write(struct omap_mcpdm *mcpdm, u16 reg, u32 val) +{ + writel_relaxed(val, mcpdm->io_base + reg); +} + +static inline int omap_mcpdm_read(struct omap_mcpdm *mcpdm, u16 reg) +{ + return readl_relaxed(mcpdm->io_base + reg); +} + +#ifdef DEBUG +static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) +{ + dev_dbg(mcpdm->dev, "***********************\n"); + dev_dbg(mcpdm->dev, "IRQSTATUS_RAW: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS_RAW)); + dev_dbg(mcpdm->dev, "IRQSTATUS: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS)); + dev_dbg(mcpdm->dev, "IRQENABLE_SET: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_SET)); + dev_dbg(mcpdm->dev, "IRQENABLE_CLR: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_IRQENABLE_CLR)); + dev_dbg(mcpdm->dev, "IRQWAKE_EN: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_IRQWAKE_EN)); + dev_dbg(mcpdm->dev, "DMAENABLE_SET: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_SET)); + dev_dbg(mcpdm->dev, "DMAENABLE_CLR: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_DMAENABLE_CLR)); + dev_dbg(mcpdm->dev, "DMAWAKEEN: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_DMAWAKEEN)); + dev_dbg(mcpdm->dev, "CTRL: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL)); + dev_dbg(mcpdm->dev, "DN_DATA: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_DN_DATA)); + dev_dbg(mcpdm->dev, "UP_DATA: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_UP_DATA)); + dev_dbg(mcpdm->dev, "FIFO_CTRL_DN: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_DN)); + dev_dbg(mcpdm->dev, "FIFO_CTRL_UP: 0x%04x\n", + omap_mcpdm_read(mcpdm, MCPDM_REG_FIFO_CTRL_UP)); + dev_dbg(mcpdm->dev, "***********************\n"); +} +#else +static void omap_mcpdm_reg_dump(struct omap_mcpdm *mcpdm) {} +#endif + +/* + * Enables the transfer through the PDM interface to/from the Phoenix + * codec by enabling the corresponding UP or DN channels. + */ +static void omap_mcpdm_start(struct omap_mcpdm *mcpdm) +{ + u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); + u32 link_mask = mcpdm->config[0].link_mask | mcpdm->config[1].link_mask; + + ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); + + ctrl |= link_mask; + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); + + ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); +} + +/* + * Disables the transfer through the PDM interface to/from the Phoenix + * codec by disabling the corresponding UP or DN channels. + */ +static void omap_mcpdm_stop(struct omap_mcpdm *mcpdm) +{ + u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); + u32 link_mask = MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK; + + ctrl |= (MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); + + ctrl &= ~(link_mask); + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); + + ctrl &= ~(MCPDM_SW_DN_RST | MCPDM_SW_UP_RST); + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl); + +} + +/* + * Is the physical McPDM interface active. + */ +static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm) +{ + return omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL) & + (MCPDM_PDM_DN_MASK | MCPDM_PDM_UP_MASK); +} + +/* + * Configures McPDM uplink, and downlink for audio. + * This function should be called before omap_mcpdm_start. + */ +static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm) +{ + u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL); + + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN); + + omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET, + MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL | + MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); + + /* Enable DN RX1/2 offset cancellation feature, if configured */ + if (mcpdm->dn_rx_offset) { + u32 dn_offset = mcpdm->dn_rx_offset; + + omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); + dn_offset |= (MCPDM_DN_OFST_RX1_EN | MCPDM_DN_OFST_RX2_EN); + omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, dn_offset); + } + + omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_DN, + mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold); + omap_mcpdm_write(mcpdm, MCPDM_REG_FIFO_CTRL_UP, + mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold); + + omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_SET, + MCPDM_DMA_DN_ENABLE | MCPDM_DMA_UP_ENABLE); +} + +/* + * Cleans McPDM uplink, and downlink configuration. + * This function should be called when the stream is closed. + */ +static void omap_mcpdm_close_streams(struct omap_mcpdm *mcpdm) +{ + /* Disable irq request generation for downlink */ + omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, + MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL); + + /* Disable DMA request generation for downlink */ + omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_DN_ENABLE); + + /* Disable irq request generation for uplink */ + omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_CLR, + MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL); + + /* Disable DMA request generation for uplink */ + omap_mcpdm_write(mcpdm, MCPDM_REG_DMAENABLE_CLR, MCPDM_DMA_UP_ENABLE); + + /* Disable RX1/2 offset cancellation */ + if (mcpdm->dn_rx_offset) + omap_mcpdm_write(mcpdm, MCPDM_REG_DN_OFFSET, 0); +} + +static irqreturn_t omap_mcpdm_irq_handler(int irq, void *dev_id) +{ + struct omap_mcpdm *mcpdm = dev_id; + int irq_status; + + irq_status = omap_mcpdm_read(mcpdm, MCPDM_REG_IRQSTATUS); + + /* Acknowledge irq event */ + omap_mcpdm_write(mcpdm, MCPDM_REG_IRQSTATUS, irq_status); + + if (irq_status & MCPDM_DN_IRQ_FULL) + dev_dbg(mcpdm->dev, "DN (playback) FIFO Full\n"); + + if (irq_status & MCPDM_DN_IRQ_EMPTY) + dev_dbg(mcpdm->dev, "DN (playback) FIFO Empty\n"); + + if (irq_status & MCPDM_DN_IRQ) + dev_dbg(mcpdm->dev, "DN (playback) write request\n"); + + if (irq_status & MCPDM_UP_IRQ_FULL) + dev_dbg(mcpdm->dev, "UP (capture) FIFO Full\n"); + + if (irq_status & MCPDM_UP_IRQ_EMPTY) + dev_dbg(mcpdm->dev, "UP (capture) FIFO Empty\n"); + + if (irq_status & MCPDM_UP_IRQ) + dev_dbg(mcpdm->dev, "UP (capture) write request\n"); + + return IRQ_HANDLED; +} + +static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + + mutex_lock(&mcpdm->mutex); + + if (!dai->active) + omap_mcpdm_open_streams(mcpdm); + + mutex_unlock(&mcpdm->mutex); + + return 0; +} + +static void omap_mcpdm_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; + int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + + mutex_lock(&mcpdm->mutex); + + if (!dai->active) { + if (omap_mcpdm_active(mcpdm)) { + omap_mcpdm_stop(mcpdm); + omap_mcpdm_close_streams(mcpdm); + mcpdm->config[0].link_mask = 0; + mcpdm->config[1].link_mask = 0; + } + } + + if (mcpdm->latency[stream2]) + pm_qos_update_request(&mcpdm->pm_qos_req, + mcpdm->latency[stream2]); + else if (mcpdm->latency[stream1]) + pm_qos_remove_request(&mcpdm->pm_qos_req); + + mcpdm->latency[stream1] = 0; + + mutex_unlock(&mcpdm->mutex); +} + +static int omap_mcpdm_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + int stream = substream->stream; + struct snd_dmaengine_dai_dma_data *dma_data; + u32 threshold; + int channels, latency; + int link_mask = 0; + + channels = params_channels(params); + switch (channels) { + case 5: + if (stream == SNDRV_PCM_STREAM_CAPTURE) + /* up to 3 channels for capture */ + return -EINVAL; + link_mask |= 1 << 4; + /* fall through */ + case 4: + if (stream == SNDRV_PCM_STREAM_CAPTURE) + /* up to 3 channels for capture */ + return -EINVAL; + link_mask |= 1 << 3; + /* fall through */ + case 3: + link_mask |= 1 << 2; + /* fall through */ + case 2: + link_mask |= 1 << 1; + /* fall through */ + case 1: + link_mask |= 1 << 0; + break; + default: + /* unsupported number of channels */ + return -EINVAL; + } + + dma_data = snd_soc_dai_get_dma_data(dai, substream); + + threshold = mcpdm->config[stream].threshold; + /* Configure McPDM channels, and DMA packet size */ + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + link_mask <<= 3; + + /* If capture is not running assume a stereo stream to come */ + if (!mcpdm->config[!stream].link_mask) + mcpdm->config[!stream].link_mask = 0x3; + + dma_data->maxburst = + (MCPDM_DN_THRES_MAX - threshold) * channels; + latency = threshold; + } else { + /* If playback is not running assume a stereo stream to come */ + if (!mcpdm->config[!stream].link_mask) + mcpdm->config[!stream].link_mask = (0x3 << 3); + + dma_data->maxburst = threshold * channels; + latency = (MCPDM_DN_THRES_MAX - threshold); + } + + /* + * The DMA must act to a DMA request within latency time (usec) to avoid + * under/overflow + */ + mcpdm->latency[stream] = latency * USEC_PER_SEC / params_rate(params); + + if (!mcpdm->latency[stream]) + mcpdm->latency[stream] = 10; + + /* Check if we need to restart McPDM with this stream */ + if (mcpdm->config[stream].link_mask && + mcpdm->config[stream].link_mask != link_mask) + mcpdm->restart = true; + + mcpdm->config[stream].link_mask = link_mask; + + return 0; +} + +static int omap_mcpdm_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + struct pm_qos_request *pm_qos_req = &mcpdm->pm_qos_req; + int tx = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); + int stream1 = tx ? SNDRV_PCM_STREAM_PLAYBACK : SNDRV_PCM_STREAM_CAPTURE; + int stream2 = tx ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK; + int latency = mcpdm->latency[stream2]; + + /* Prevent omap hardware from hitting off between FIFO fills */ + if (!latency || mcpdm->latency[stream1] < latency) + latency = mcpdm->latency[stream1]; + + if (pm_qos_request_active(pm_qos_req)) + pm_qos_update_request(pm_qos_req, latency); + else if (latency) + pm_qos_add_request(pm_qos_req, PM_QOS_CPU_DMA_LATENCY, latency); + + if (!omap_mcpdm_active(mcpdm)) { + omap_mcpdm_start(mcpdm); + omap_mcpdm_reg_dump(mcpdm); + } else if (mcpdm->restart) { + omap_mcpdm_stop(mcpdm); + omap_mcpdm_start(mcpdm); + mcpdm->restart = false; + omap_mcpdm_reg_dump(mcpdm); + } + + return 0; +} + +static const struct snd_soc_dai_ops omap_mcpdm_dai_ops = { + .startup = omap_mcpdm_dai_startup, + .shutdown = omap_mcpdm_dai_shutdown, + .hw_params = omap_mcpdm_dai_hw_params, + .prepare = omap_mcpdm_prepare, +}; + +static int omap_mcpdm_probe(struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + int ret; + + pm_runtime_enable(mcpdm->dev); + + /* Disable lines while request is ongoing */ + pm_runtime_get_sync(mcpdm->dev); + omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00); + + ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM", + (void *)mcpdm); + + pm_runtime_put_sync(mcpdm->dev); + + if (ret) { + dev_err(mcpdm->dev, "Request for IRQ failed\n"); + pm_runtime_disable(mcpdm->dev); + } + + /* Configure McPDM threshold values */ + mcpdm->config[SNDRV_PCM_STREAM_PLAYBACK].threshold = 2; + mcpdm->config[SNDRV_PCM_STREAM_CAPTURE].threshold = + MCPDM_UP_THRES_MAX - 3; + + snd_soc_dai_init_dma_data(dai, + &mcpdm->dma_data[SNDRV_PCM_STREAM_PLAYBACK], + &mcpdm->dma_data[SNDRV_PCM_STREAM_CAPTURE]); + + return ret; +} + +static int omap_mcpdm_remove(struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + + free_irq(mcpdm->irq, (void *)mcpdm); + pm_runtime_disable(mcpdm->dev); + + if (pm_qos_request_active(&mcpdm->pm_qos_req)) + pm_qos_remove_request(&mcpdm->pm_qos_req); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int omap_mcpdm_suspend(struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + + if (dai->active) { + omap_mcpdm_stop(mcpdm); + omap_mcpdm_close_streams(mcpdm); + } + + mcpdm->pm_active_count = 0; + while (pm_runtime_active(mcpdm->dev)) { + pm_runtime_put_sync(mcpdm->dev); + mcpdm->pm_active_count++; + } + + return 0; +} + +static int omap_mcpdm_resume(struct snd_soc_dai *dai) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai); + + if (mcpdm->pm_active_count) { + while (mcpdm->pm_active_count--) + pm_runtime_get_sync(mcpdm->dev); + + if (dai->active) { + omap_mcpdm_open_streams(mcpdm); + omap_mcpdm_start(mcpdm); + } + } + + + return 0; +} +#else +#define omap_mcpdm_suspend NULL +#define omap_mcpdm_resume NULL +#endif + +#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) +#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE + +static struct snd_soc_dai_driver omap_mcpdm_dai = { + .probe = omap_mcpdm_probe, + .remove = omap_mcpdm_remove, + .suspend = omap_mcpdm_suspend, + .resume = omap_mcpdm_resume, + .probe_order = SND_SOC_COMP_ORDER_LATE, + .remove_order = SND_SOC_COMP_ORDER_EARLY, + .playback = { + .channels_min = 1, + .channels_max = 5, + .rates = OMAP_MCPDM_RATES, + .formats = OMAP_MCPDM_FORMATS, + .sig_bits = 24, + }, + .capture = { + .channels_min = 1, + .channels_max = 3, + .rates = OMAP_MCPDM_RATES, + .formats = OMAP_MCPDM_FORMATS, + .sig_bits = 24, + }, + .ops = &omap_mcpdm_dai_ops, +}; + +static const struct snd_soc_component_driver omap_mcpdm_component = { + .name = "omap-mcpdm", +}; + +void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, + u8 rx1, u8 rx2) +{ + struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(rtd->cpu_dai); + + mcpdm->dn_rx_offset = MCPDM_DNOFST_RX1(rx1) | MCPDM_DNOFST_RX2(rx2); +} +EXPORT_SYMBOL_GPL(omap_mcpdm_configure_dn_offsets); + +static int asoc_mcpdm_probe(struct platform_device *pdev) +{ + struct omap_mcpdm *mcpdm; + struct resource *res; + int ret; + + mcpdm = devm_kzalloc(&pdev->dev, sizeof(struct omap_mcpdm), GFP_KERNEL); + if (!mcpdm) + return -ENOMEM; + + platform_set_drvdata(pdev, mcpdm); + + mutex_init(&mcpdm->mutex); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dma"); + if (res == NULL) + return -ENOMEM; + + mcpdm->dma_data[0].addr = res->start + MCPDM_REG_DN_DATA; + mcpdm->dma_data[1].addr = res->start + MCPDM_REG_UP_DATA; + + mcpdm->dma_data[0].filter_data = "dn_link"; + mcpdm->dma_data[1].filter_data = "up_link"; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); + mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mcpdm->io_base)) + return PTR_ERR(mcpdm->io_base); + + mcpdm->irq = platform_get_irq(pdev, 0); + if (mcpdm->irq < 0) + return mcpdm->irq; + + mcpdm->dev = &pdev->dev; + + ret = devm_snd_soc_register_component(&pdev->dev, + &omap_mcpdm_component, + &omap_mcpdm_dai, 1); + if (ret) + return ret; + + return sdma_pcm_platform_register(&pdev->dev, "dn_link", "up_link"); +} + +static const struct of_device_id omap_mcpdm_of_match[] = { + { .compatible = "ti,omap4-mcpdm", }, + { } +}; +MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match); + +static struct platform_driver asoc_mcpdm_driver = { + .driver = { + .name = "omap-mcpdm", + .of_match_table = omap_mcpdm_of_match, + }, + + .probe = asoc_mcpdm_probe, +}; + +module_platform_driver(asoc_mcpdm_driver); + +MODULE_ALIAS("platform:omap-mcpdm"); +MODULE_AUTHOR("Misael Lopez Cruz "); +MODULE_DESCRIPTION("OMAP PDM SoC Interface"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/omap-mcpdm.h b/sound/soc/ti/omap-mcpdm.h new file mode 100644 index 000000000000..de8cf26595b1 --- /dev/null +++ b/sound/soc/ti/omap-mcpdm.h @@ -0,0 +1,107 @@ +/* + * omap-mcpdm.h + * + * Copyright (C) 2009 - 2011 Texas Instruments + * + * Contact: Misael Lopez Cruz + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __OMAP_MCPDM_H__ +#define __OMAP_MCPDM_H__ + +#define MCPDM_REG_REVISION 0x00 +#define MCPDM_REG_SYSCONFIG 0x10 +#define MCPDM_REG_IRQSTATUS_RAW 0x24 +#define MCPDM_REG_IRQSTATUS 0x28 +#define MCPDM_REG_IRQENABLE_SET 0x2C +#define MCPDM_REG_IRQENABLE_CLR 0x30 +#define MCPDM_REG_IRQWAKE_EN 0x34 +#define MCPDM_REG_DMAENABLE_SET 0x38 +#define MCPDM_REG_DMAENABLE_CLR 0x3C +#define MCPDM_REG_DMAWAKEEN 0x40 +#define MCPDM_REG_CTRL 0x44 +#define MCPDM_REG_DN_DATA 0x48 +#define MCPDM_REG_UP_DATA 0x4C +#define MCPDM_REG_FIFO_CTRL_DN 0x50 +#define MCPDM_REG_FIFO_CTRL_UP 0x54 +#define MCPDM_REG_DN_OFFSET 0x58 + +/* + * MCPDM_IRQ bit fields + * IRQSTATUS_RAW, IRQSTATUS, IRQENABLE_SET, IRQENABLE_CLR + */ + +#define MCPDM_DN_IRQ (1 << 0) +#define MCPDM_DN_IRQ_EMPTY (1 << 1) +#define MCPDM_DN_IRQ_ALMST_EMPTY (1 << 2) +#define MCPDM_DN_IRQ_FULL (1 << 3) + +#define MCPDM_UP_IRQ (1 << 8) +#define MCPDM_UP_IRQ_EMPTY (1 << 9) +#define MCPDM_UP_IRQ_ALMST_FULL (1 << 10) +#define MCPDM_UP_IRQ_FULL (1 << 11) + +#define MCPDM_DOWNLINK_IRQ_MASK 0x00F +#define MCPDM_UPLINK_IRQ_MASK 0xF00 + +/* + * MCPDM_DMAENABLE bit fields + */ + +#define MCPDM_DMA_DN_ENABLE (1 << 0) +#define MCPDM_DMA_UP_ENABLE (1 << 1) + +/* + * MCPDM_CTRL bit fields + */ + +#define MCPDM_PDM_UPLINK_EN(x) (1 << (x - 1)) /* ch1 is at bit 0 */ +#define MCPDM_PDM_DOWNLINK_EN(x) (1 << (x + 2)) /* ch1 is at bit 3 */ +#define MCPDM_PDMOUTFORMAT (1 << 8) +#define MCPDM_CMD_INT (1 << 9) +#define MCPDM_STATUS_INT (1 << 10) +#define MCPDM_SW_UP_RST (1 << 11) +#define MCPDM_SW_DN_RST (1 << 12) +#define MCPDM_WD_EN (1 << 14) +#define MCPDM_PDM_UP_MASK 0x7 +#define MCPDM_PDM_DN_MASK (0x1f << 3) + + +#define MCPDM_PDMOUTFORMAT_LJUST (0 << 8) +#define MCPDM_PDMOUTFORMAT_RJUST (1 << 8) + +/* + * MCPDM_FIFO_CTRL bit fields + */ + +#define MCPDM_UP_THRES_MAX 0xF +#define MCPDM_DN_THRES_MAX 0xF + +/* + * MCPDM_DN_OFFSET bit fields + */ + +#define MCPDM_DN_OFST_RX1_EN (1 << 0) +#define MCPDM_DNOFST_RX1(x) ((x & 0x1f) << 1) +#define MCPDM_DN_OFST_RX2_EN (1 << 8) +#define MCPDM_DNOFST_RX2(x) ((x & 0x1f) << 9) + +void omap_mcpdm_configure_dn_offsets(struct snd_soc_pcm_runtime *rtd, + u8 rx1, u8 rx2); + +#endif /* End of __OMAP_MCPDM_H__ */ diff --git a/sound/soc/ti/omap-twl4030.c b/sound/soc/ti/omap-twl4030.c new file mode 100644 index 000000000000..cccc316743fa --- /dev/null +++ b/sound/soc/ti/omap-twl4030.c @@ -0,0 +1,353 @@ +/* + * omap-twl4030.c -- SoC audio for TI SoC based boards with twl4030 codec + * + * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Author: Peter Ujfalusi + * + * This driver replaces the following machine drivers: + * omap3beagle (Author: Steve Sakoman ) + * omap3evm (Author: Anuj Aggarwal ) + * overo (Author: Steve Sakoman ) + * igep0020 (Author: Enric Balletbo i Serra ) + * zoom2 (Author: Misael Lopez Cruz ) + * sdp3430 (Author: Misael Lopez Cruz ) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-mcbsp.h" + +struct omap_twl4030 { + int jack_detect; /* board can detect jack events */ + struct snd_soc_jack hs_jack; +}; + +static int omap_twl4030_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + unsigned int fmt; + + switch (params_channels(params)) { + case 2: /* Stereo I2S mode */ + fmt = SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + break; + case 4: /* Four channel TDM mode */ + fmt = SND_SOC_DAIFMT_DSP_A | + SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM; + break; + default: + return -EINVAL; + } + + return snd_soc_runtime_set_dai_fmt(rtd, fmt); +} + +static const struct snd_soc_ops omap_twl4030_ops = { + .hw_params = omap_twl4030_hw_params, +}; + +static const struct snd_soc_dapm_widget dapm_widgets[] = { + SND_SOC_DAPM_SPK("Earpiece Spk", NULL), + SND_SOC_DAPM_SPK("Handsfree Spk", NULL), + SND_SOC_DAPM_HP("Headset Stereophone", NULL), + SND_SOC_DAPM_SPK("Ext Spk", NULL), + SND_SOC_DAPM_SPK("Carkit Spk", NULL), + + SND_SOC_DAPM_MIC("Main Mic", NULL), + SND_SOC_DAPM_MIC("Sub Mic", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_MIC("Carkit Mic", NULL), + SND_SOC_DAPM_MIC("Digital0 Mic", NULL), + SND_SOC_DAPM_MIC("Digital1 Mic", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + /* Headset Stereophone: HSOL, HSOR */ + {"Headset Stereophone", NULL, "HSOL"}, + {"Headset Stereophone", NULL, "HSOR"}, + /* External Speakers: HFL, HFR */ + {"Handsfree Spk", NULL, "HFL"}, + {"Handsfree Spk", NULL, "HFR"}, + /* External Speakers: PredrivL, PredrivR */ + {"Ext Spk", NULL, "PREDRIVEL"}, + {"Ext Spk", NULL, "PREDRIVER"}, + /* Carkit speakers: CARKITL, CARKITR */ + {"Carkit Spk", NULL, "CARKITL"}, + {"Carkit Spk", NULL, "CARKITR"}, + /* Earpiece */ + {"Earpiece Spk", NULL, "EARPIECE"}, + + /* External Mics: MAINMIC, SUBMIC with bias */ + {"MAINMIC", NULL, "Main Mic"}, + {"Main Mic", NULL, "Mic Bias 1"}, + {"SUBMIC", NULL, "Sub Mic"}, + {"Sub Mic", NULL, "Mic Bias 2"}, + /* Headset Mic: HSMIC with bias */ + {"HSMIC", NULL, "Headset Mic"}, + {"Headset Mic", NULL, "Headset Mic Bias"}, + /* Digital Mics: DIGIMIC0, DIGIMIC1 with bias */ + {"DIGIMIC0", NULL, "Digital0 Mic"}, + {"Digital0 Mic", NULL, "Mic Bias 1"}, + {"DIGIMIC1", NULL, "Digital1 Mic"}, + {"Digital1 Mic", NULL, "Mic Bias 2"}, + /* Carkit In: CARKITMIC */ + {"CARKITMIC", NULL, "Carkit Mic"}, + /* Aux In: AUXL, AUXR */ + {"AUXL", NULL, "Line In"}, + {"AUXR", NULL, "Line In"}, +}; + +/* Headset jack detection DAPM pins */ +static struct snd_soc_jack_pin hs_jack_pins[] = { + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, + { + .pin = "Headset Stereophone", + .mask = SND_JACK_HEADPHONE, + }, +}; + +/* Headset jack detection gpios */ +static struct snd_soc_jack_gpio hs_jack_gpios[] = { + { + .name = "hsdet-gpio", + .report = SND_JACK_HEADSET, + .debounce_time = 200, + }, +}; + +static inline void twl4030_disconnect_pin(struct snd_soc_dapm_context *dapm, + int connected, char *pin) +{ + if (!connected) + snd_soc_dapm_disable_pin(dapm, pin); +} + +static int omap_twl4030_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dapm_context *dapm = &card->dapm; + struct omap_tw4030_pdata *pdata = dev_get_platdata(card->dev); + struct omap_twl4030 *priv = snd_soc_card_get_drvdata(card); + int ret = 0; + + /* Headset jack detection only if it is supported */ + if (priv->jack_detect > 0) { + hs_jack_gpios[0].gpio = priv->jack_detect; + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET, &priv->hs_jack, + hs_jack_pins, + ARRAY_SIZE(hs_jack_pins)); + if (ret) + return ret; + + ret = snd_soc_jack_add_gpios(&priv->hs_jack, + ARRAY_SIZE(hs_jack_gpios), + hs_jack_gpios); + if (ret) + return ret; + } + + /* + * NULL pdata means we booted with DT. In this case the routing is + * provided and the card is fully routed, no need to mark pins. + */ + if (!pdata || !pdata->custom_routing) + return ret; + + /* Disable not connected paths if not used */ + twl4030_disconnect_pin(dapm, pdata->has_ear, "Earpiece Spk"); + twl4030_disconnect_pin(dapm, pdata->has_hf, "Handsfree Spk"); + twl4030_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone"); + twl4030_disconnect_pin(dapm, pdata->has_predriv, "Ext Spk"); + twl4030_disconnect_pin(dapm, pdata->has_carkit, "Carkit Spk"); + + twl4030_disconnect_pin(dapm, pdata->has_mainmic, "Main Mic"); + twl4030_disconnect_pin(dapm, pdata->has_submic, "Sub Mic"); + twl4030_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic"); + twl4030_disconnect_pin(dapm, pdata->has_carkitmic, "Carkit Mic"); + twl4030_disconnect_pin(dapm, pdata->has_digimic0, "Digital0 Mic"); + twl4030_disconnect_pin(dapm, pdata->has_digimic1, "Digital1 Mic"); + twl4030_disconnect_pin(dapm, pdata->has_linein, "Line In"); + + return ret; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link omap_twl4030_dai_links[] = { + { + .name = "TWL4030 HiFi", + .stream_name = "TWL4030 HiFi", + .cpu_dai_name = "omap-mcbsp.2", + .codec_dai_name = "twl4030-hifi", + .platform_name = "omap-mcbsp.2", + .codec_name = "twl4030-codec", + .init = omap_twl4030_init, + .ops = &omap_twl4030_ops, + }, + { + .name = "TWL4030 Voice", + .stream_name = "TWL4030 Voice", + .cpu_dai_name = "omap-mcbsp.3", + .codec_dai_name = "twl4030-voice", + .platform_name = "omap-mcbsp.3", + .codec_name = "twl4030-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM, + }, +}; + +/* Audio machine driver */ +static struct snd_soc_card omap_twl4030_card = { + .owner = THIS_MODULE, + .dai_link = omap_twl4030_dai_links, + .num_links = ARRAY_SIZE(omap_twl4030_dai_links), + + .dapm_widgets = dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static int omap_twl4030_probe(struct platform_device *pdev) +{ + struct omap_tw4030_pdata *pdata = dev_get_platdata(&pdev->dev); + struct device_node *node = pdev->dev.of_node; + struct snd_soc_card *card = &omap_twl4030_card; + struct omap_twl4030 *priv; + int ret = 0; + + card->dev = &pdev->dev; + + priv = devm_kzalloc(&pdev->dev, sizeof(struct omap_twl4030), GFP_KERNEL); + if (priv == NULL) + return -ENOMEM; + + if (node) { + struct device_node *dai_node; + struct property *prop; + + if (snd_soc_of_parse_card_name(card, "ti,model")) { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } + + dai_node = of_parse_phandle(node, "ti,mcbsp", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McBSP node is not provided\n"); + return -EINVAL; + } + omap_twl4030_dai_links[0].cpu_dai_name = NULL; + omap_twl4030_dai_links[0].cpu_of_node = dai_node; + + omap_twl4030_dai_links[0].platform_name = NULL; + omap_twl4030_dai_links[0].platform_of_node = dai_node; + + dai_node = of_parse_phandle(node, "ti,mcbsp-voice", 0); + if (!dai_node) { + card->num_links = 1; + } else { + omap_twl4030_dai_links[1].cpu_dai_name = NULL; + omap_twl4030_dai_links[1].cpu_of_node = dai_node; + + omap_twl4030_dai_links[1].platform_name = NULL; + omap_twl4030_dai_links[1].platform_of_node = dai_node; + } + + priv->jack_detect = of_get_named_gpio(node, + "ti,jack-det-gpio", 0); + + /* Optional: audio routing can be provided */ + prop = of_find_property(node, "ti,audio-routing", NULL); + if (prop) { + ret = snd_soc_of_parse_audio_routing(card, + "ti,audio-routing"); + if (ret) + return ret; + + card->fully_routed = 1; + } + } else if (pdata) { + if (pdata->card_name) { + card->name = pdata->card_name; + } else { + dev_err(&pdev->dev, "Card name is not provided\n"); + return -ENODEV; + } + + if (!pdata->voice_connected) + card->num_links = 1; + + priv->jack_detect = pdata->jack_detect; + } else { + dev_err(&pdev->dev, "Missing pdata\n"); + return -ENODEV; + } + + snd_soc_card_set_drvdata(card, priv); + ret = devm_snd_soc_register_card(&pdev->dev, card); + if (ret) { + dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n", + ret); + return ret; + } + + return 0; +} + +static const struct of_device_id omap_twl4030_of_match[] = { + {.compatible = "ti,omap-twl4030", }, + { }, +}; +MODULE_DEVICE_TABLE(of, omap_twl4030_of_match); + +static struct platform_driver omap_twl4030_driver = { + .driver = { + .name = "omap-twl4030", + .pm = &snd_soc_pm_ops, + .of_match_table = omap_twl4030_of_match, + }, + .probe = omap_twl4030_probe, +}; + +module_platform_driver(omap_twl4030_driver); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("ALSA SoC for TI SoC based boards with twl4030 codec"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:omap-twl4030"); diff --git a/sound/soc/ti/omap3pandora.c b/sound/soc/ti/omap3pandora.c new file mode 100644 index 000000000000..4e3de712159c --- /dev/null +++ b/sound/soc/ti/omap3pandora.c @@ -0,0 +1,315 @@ +/* + * omap3pandora.c -- SoC audio for Pandora Handheld Console + * + * Author: GraÅžvydas Ignotas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "omap-mcbsp.h" + +#define OMAP3_PANDORA_DAC_POWER_GPIO 118 +#define OMAP3_PANDORA_AMP_POWER_GPIO 14 + +#define PREFIX "ASoC omap3pandora: " + +static struct regulator *omap3pandora_dac_reg; + +static int omap3pandora_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + /* Set the codec system clock for DAC and ADC */ + ret = snd_soc_dai_set_sysclk(codec_dai, 0, 26000000, + SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err(PREFIX "can't set codec system clock\n"); + return ret; + } + + /* Set McBSP clock to external */ + ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_EXT, + 256 * params_rate(params), + SND_SOC_CLOCK_IN); + if (ret < 0) { + pr_err(PREFIX "can't set cpu system clock\n"); + return ret; + } + + ret = snd_soc_dai_set_clkdiv(cpu_dai, OMAP_MCBSP_CLKGDV, 8); + if (ret < 0) { + pr_err(PREFIX "can't set SRG clock divider\n"); + return ret; + } + + return 0; +} + +static int omap3pandora_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + int ret; + + /* + * The PCM1773 DAC datasheet requires 1ms delay between switching + * VCC power on/off and /PD pin high/low + */ + if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = regulator_enable(omap3pandora_dac_reg); + if (ret) { + dev_err(w->dapm->dev, "Failed to power DAC: %d\n", ret); + return ret; + } + mdelay(1); + gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 1); + } else { + gpio_set_value(OMAP3_PANDORA_DAC_POWER_GPIO, 0); + mdelay(1); + regulator_disable(omap3pandora_dac_reg); + } + + return 0; +} + +static int omap3pandora_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + if (SND_SOC_DAPM_EVENT_ON(event)) + gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 1); + else + gpio_set_value(OMAP3_PANDORA_AMP_POWER_GPIO, 0); + + return 0; +} + +/* + * Audio paths on Pandora board: + * + * |O| ---> PCM DAC +-> AMP -> Headphone Jack + * |M| A +--------> Line Out + * |A| <~~clk~~+ + * |P| <--- TWL4030 <--------- Line In and MICs + */ +static const struct snd_soc_dapm_widget omap3pandora_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("PCM DAC", "HiFi Playback", SND_SOC_NOPM, + 0, 0, omap3pandora_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_PGA_E("Headphone Amplifier", SND_SOC_NOPM, + 0, 0, NULL, 0, omap3pandora_hp_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line Out", NULL), + + SND_SOC_DAPM_MIC("Mic (internal)", NULL), + SND_SOC_DAPM_MIC("Mic (external)", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), +}; + +static const struct snd_soc_dapm_route omap3pandora_map[] = { + {"PCM DAC", NULL, "APLL Enable"}, + {"Headphone Amplifier", NULL, "PCM DAC"}, + {"Line Out", NULL, "PCM DAC"}, + {"Headphone Jack", NULL, "Headphone Amplifier"}, + + {"AUXL", NULL, "Line In"}, + {"AUXR", NULL, "Line In"}, + + {"MAINMIC", NULL, "Mic (internal)"}, + {"Mic (internal)", NULL, "Mic Bias 1"}, + + {"SUBMIC", NULL, "Mic (external)"}, + {"Mic (external)", NULL, "Mic Bias 2"}, +}; + +static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; + + /* All TWL4030 output pins are floating */ + snd_soc_dapm_nc_pin(dapm, "EARPIECE"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVEL"); + snd_soc_dapm_nc_pin(dapm, "PREDRIVER"); + snd_soc_dapm_nc_pin(dapm, "HSOL"); + snd_soc_dapm_nc_pin(dapm, "HSOR"); + snd_soc_dapm_nc_pin(dapm, "CARKITL"); + snd_soc_dapm_nc_pin(dapm, "CARKITR"); + snd_soc_dapm_nc_pin(dapm, "HFL"); + snd_soc_dapm_nc_pin(dapm, "HFR"); + snd_soc_dapm_nc_pin(dapm, "VIBRA"); + + return 0; +} + +static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm = &rtd->card->dapm; + + /* Not comnnected */ + snd_soc_dapm_nc_pin(dapm, "HSMIC"); + snd_soc_dapm_nc_pin(dapm, "CARKITMIC"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC0"); + snd_soc_dapm_nc_pin(dapm, "DIGIMIC1"); + + return 0; +} + +static const struct snd_soc_ops omap3pandora_ops = { + .hw_params = omap3pandora_hw_params, +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link omap3pandora_dai[] = { + { + .name = "PCM1773", + .stream_name = "HiFi Out", + .cpu_dai_name = "omap-mcbsp.2", + .codec_dai_name = "twl4030-hifi", + .platform_name = "omap-mcbsp.2", + .codec_name = "twl4030-codec", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &omap3pandora_ops, + .init = omap3pandora_out_init, + }, { + .name = "TWL4030", + .stream_name = "Line/Mic In", + .cpu_dai_name = "omap-mcbsp.4", + .codec_dai_name = "twl4030-hifi", + .platform_name = "omap-mcbsp.4", + .codec_name = "twl4030-codec", + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ops = &omap3pandora_ops, + .init = omap3pandora_in_init, + } +}; + +/* SoC card */ +static struct snd_soc_card snd_soc_card_omap3pandora = { + .name = "omap3pandora", + .owner = THIS_MODULE, + .dai_link = omap3pandora_dai, + .num_links = ARRAY_SIZE(omap3pandora_dai), + + .dapm_widgets = omap3pandora_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(omap3pandora_dapm_widgets), + .dapm_routes = omap3pandora_map, + .num_dapm_routes = ARRAY_SIZE(omap3pandora_map), +}; + +static struct platform_device *omap3pandora_snd_device; + +static int __init omap3pandora_soc_init(void) +{ + int ret; + + if (!machine_is_omap3_pandora()) + return -ENODEV; + + pr_info("OMAP3 Pandora SoC init\n"); + + ret = gpio_request(OMAP3_PANDORA_DAC_POWER_GPIO, "dac_power"); + if (ret) { + pr_err(PREFIX "Failed to get DAC power GPIO\n"); + return ret; + } + + ret = gpio_direction_output(OMAP3_PANDORA_DAC_POWER_GPIO, 0); + if (ret) { + pr_err(PREFIX "Failed to set DAC power GPIO direction\n"); + goto fail0; + } + + ret = gpio_request(OMAP3_PANDORA_AMP_POWER_GPIO, "amp_power"); + if (ret) { + pr_err(PREFIX "Failed to get amp power GPIO\n"); + goto fail0; + } + + ret = gpio_direction_output(OMAP3_PANDORA_AMP_POWER_GPIO, 0); + if (ret) { + pr_err(PREFIX "Failed to set amp power GPIO direction\n"); + goto fail1; + } + + omap3pandora_snd_device = platform_device_alloc("soc-audio", -1); + if (omap3pandora_snd_device == NULL) { + pr_err(PREFIX "Platform device allocation failed\n"); + ret = -ENOMEM; + goto fail1; + } + + platform_set_drvdata(omap3pandora_snd_device, &snd_soc_card_omap3pandora); + + ret = platform_device_add(omap3pandora_snd_device); + if (ret) { + pr_err(PREFIX "Unable to add platform device\n"); + goto fail2; + } + + omap3pandora_dac_reg = regulator_get(&omap3pandora_snd_device->dev, "vcc"); + if (IS_ERR(omap3pandora_dac_reg)) { + pr_err(PREFIX "Failed to get DAC regulator from %s: %ld\n", + dev_name(&omap3pandora_snd_device->dev), + PTR_ERR(omap3pandora_dac_reg)); + ret = PTR_ERR(omap3pandora_dac_reg); + goto fail3; + } + + return 0; + +fail3: + platform_device_del(omap3pandora_snd_device); +fail2: + platform_device_put(omap3pandora_snd_device); +fail1: + gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); +fail0: + gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); + return ret; +} +module_init(omap3pandora_soc_init); + +static void __exit omap3pandora_soc_exit(void) +{ + regulator_put(omap3pandora_dac_reg); + platform_device_unregister(omap3pandora_snd_device); + gpio_free(OMAP3_PANDORA_AMP_POWER_GPIO); + gpio_free(OMAP3_PANDORA_DAC_POWER_GPIO); +} +module_exit(omap3pandora_soc_exit); + +MODULE_AUTHOR("Grazvydas Ignotas "); +MODULE_DESCRIPTION("ALSA SoC OMAP3 Pandora"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/osk5912.c b/sound/soc/ti/osk5912.c new file mode 100644 index 000000000000..e4096779ca05 --- /dev/null +++ b/sound/soc/ti/osk5912.c @@ -0,0 +1,187 @@ +/* + * osk5912.c -- SoC audio for OSK 5912 + * + * Copyright (C) 2008 Mistral Solutions + * + * Contact: Arun KS + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-mcbsp.h" +#include "../codecs/tlv320aic23.h" + +#define CODEC_CLOCK 12000000 + +static struct clk *tlv320aic23_mclk; + +static int osk_startup(struct snd_pcm_substream *substream) +{ + return clk_enable(tlv320aic23_mclk); +} + +static void osk_shutdown(struct snd_pcm_substream *substream) +{ + clk_disable(tlv320aic23_mclk); +} + +static int osk_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + int err; + + /* Set the codec system clock for DAC and ADC */ + err = + snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN); + + if (err < 0) { + printk(KERN_ERR "can't set codec system clock\n"); + return err; + } + + return err; +} + +static const struct snd_soc_ops osk_ops = { + .startup = osk_startup, + .hw_params = osk_hw_params, + .shutdown = osk_shutdown, +}; + +static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_LINE("Line In", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + {"LLINEIN", NULL, "Line In"}, + {"RLINEIN", NULL, "Line In"}, + + {"MICIN", NULL, "Mic Jack"}, +}; + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link osk_dai = { + .name = "TLV320AIC23", + .stream_name = "AIC23", + .cpu_dai_name = "omap-mcbsp.1", + .codec_dai_name = "tlv320aic23-hifi", + .platform_name = "omap-mcbsp.1", + .codec_name = "tlv320aic23-codec", + .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .ops = &osk_ops, +}; + +/* Audio machine driver */ +static struct snd_soc_card snd_soc_card_osk = { + .name = "OSK5912", + .owner = THIS_MODULE, + .dai_link = &osk_dai, + .num_links = 1, + + .dapm_widgets = tlv320aic23_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static struct platform_device *osk_snd_device; + +static int __init osk_soc_init(void) +{ + int err; + u32 curRate; + struct device *dev; + + if (!(machine_is_omap_osk())) + return -ENODEV; + + osk_snd_device = platform_device_alloc("soc-audio", -1); + if (!osk_snd_device) + return -ENOMEM; + + platform_set_drvdata(osk_snd_device, &snd_soc_card_osk); + err = platform_device_add(osk_snd_device); + if (err) + goto err1; + + dev = &osk_snd_device->dev; + + tlv320aic23_mclk = clk_get(dev, "mclk"); + if (IS_ERR(tlv320aic23_mclk)) { + printk(KERN_ERR "Could not get mclk clock\n"); + err = PTR_ERR(tlv320aic23_mclk); + goto err2; + } + + /* + * Configure 12 MHz output on MCLK. + */ + curRate = (uint) clk_get_rate(tlv320aic23_mclk); + if (curRate != CODEC_CLOCK) { + if (clk_set_rate(tlv320aic23_mclk, CODEC_CLOCK)) { + printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n"); + err = -ECANCELED; + goto err3; + } + } + + printk(KERN_INFO "MCLK = %d [%d]\n", + (uint) clk_get_rate(tlv320aic23_mclk), CODEC_CLOCK); + + return 0; + +err3: + clk_put(tlv320aic23_mclk); +err2: + platform_device_del(osk_snd_device); +err1: + platform_device_put(osk_snd_device); + + return err; + +} + +static void __exit osk_soc_exit(void) +{ + clk_put(tlv320aic23_mclk); + platform_device_unregister(osk_snd_device); +} + +module_init(osk_soc_init); +module_exit(osk_soc_exit); + +MODULE_AUTHOR("Arun KS "); +MODULE_DESCRIPTION("ALSA SoC OSK 5912"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/ti/rx51.c b/sound/soc/ti/rx51.c new file mode 100644 index 000000000000..57448bd5ad77 --- /dev/null +++ b/sound/soc/ti/rx51.c @@ -0,0 +1,493 @@ +/* + * rx51.c -- SoC audio for Nokia RX-51 + * + * Copyright (C) 2008 - 2009 Nokia Corporation + * + * Contact: Peter Ujfalusi + * Eduardo Valentin + * Jarkko Nikula + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "omap-mcbsp.h" + +enum { + RX51_JACK_DISABLED, + RX51_JACK_TVOUT, /* tv-out with stereo output */ + RX51_JACK_HP, /* headphone: stereo output, no mic */ + RX51_JACK_HS, /* headset: stereo output with mic */ +}; + +struct rx51_audio_pdata { + struct gpio_desc *tvout_selection_gpio; + struct gpio_desc *jack_detection_gpio; + struct gpio_desc *eci_sw_gpio; + struct gpio_desc *speaker_amp_gpio; +}; + +static int rx51_spk_func; +static int rx51_dmic_func; +static int rx51_jack_func; + +static void rx51_ext_control(struct snd_soc_dapm_context *dapm) +{ + struct snd_soc_card *card = dapm->card; + struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); + int hp = 0, hs = 0, tvout = 0; + + switch (rx51_jack_func) { + case RX51_JACK_TVOUT: + tvout = 1; + hp = 1; + break; + case RX51_JACK_HS: + hs = 1; + case RX51_JACK_HP: + hp = 1; + break; + } + + snd_soc_dapm_mutex_lock(dapm); + + if (rx51_spk_func) + snd_soc_dapm_enable_pin_unlocked(dapm, "Ext Spk"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Ext Spk"); + if (rx51_dmic_func) + snd_soc_dapm_enable_pin_unlocked(dapm, "DMic"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "DMic"); + if (hp) + snd_soc_dapm_enable_pin_unlocked(dapm, "Headphone Jack"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "Headphone Jack"); + if (hs) + snd_soc_dapm_enable_pin_unlocked(dapm, "HS Mic"); + else + snd_soc_dapm_disable_pin_unlocked(dapm, "HS Mic"); + + gpiod_set_value(pdata->tvout_selection_gpio, tvout); + + snd_soc_dapm_sync_unlocked(dapm); + + snd_soc_dapm_mutex_unlock(dapm); +} + +static int rx51_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + + snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2); + rx51_ext_control(&card->dapm); + + return 0; +} + +static int rx51_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* Set the codec system clock for DAC and ADC */ + return snd_soc_dai_set_sysclk(codec_dai, 0, 19200000, + SND_SOC_CLOCK_IN); +} + +static const struct snd_soc_ops rx51_ops = { + .startup = rx51_startup, + .hw_params = rx51_hw_params, +}; + +static int rx51_get_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = rx51_spk_func; + + return 0; +} + +static int rx51_set_spk(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + + if (rx51_spk_func == ucontrol->value.enumerated.item[0]) + return 0; + + rx51_spk_func = ucontrol->value.enumerated.item[0]; + rx51_ext_control(&card->dapm); + + return 1; +} + +static int rx51_spk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); + + gpiod_set_raw_value_cansleep(pdata->speaker_amp_gpio, + !!SND_SOC_DAPM_EVENT_ON(event)); + + return 0; +} + +static int rx51_get_input(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = rx51_dmic_func; + + return 0; +} + +static int rx51_set_input(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + + if (rx51_dmic_func == ucontrol->value.enumerated.item[0]) + return 0; + + rx51_dmic_func = ucontrol->value.enumerated.item[0]; + rx51_ext_control(&card->dapm); + + return 1; +} + +static int rx51_get_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.enumerated.item[0] = rx51_jack_func; + + return 0; +} + +static int rx51_set_jack(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_card *card = snd_kcontrol_chip(kcontrol); + + if (rx51_jack_func == ucontrol->value.enumerated.item[0]) + return 0; + + rx51_jack_func = ucontrol->value.enumerated.item[0]; + rx51_ext_control(&card->dapm); + + return 1; +} + +static struct snd_soc_jack rx51_av_jack; + +static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = { + { + .name = "avdet-gpio", + .report = SND_JACK_HEADSET, + .invert = 1, + .debounce_time = 200, + }, +}; + +static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = { + SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event), + SND_SOC_DAPM_MIC("DMic", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("HS Mic", NULL), + SND_SOC_DAPM_LINE("FM Transmitter", NULL), + SND_SOC_DAPM_SPK("Earphone", NULL), +}; + +static const struct snd_soc_dapm_route audio_map[] = { + {"Ext Spk", NULL, "HPLOUT"}, + {"Ext Spk", NULL, "HPROUT"}, + {"Ext Spk", NULL, "HPLCOM"}, + {"Ext Spk", NULL, "HPRCOM"}, + {"FM Transmitter", NULL, "LLOUT"}, + {"FM Transmitter", NULL, "RLOUT"}, + + {"Headphone Jack", NULL, "TPA6130A2 HPLEFT"}, + {"Headphone Jack", NULL, "TPA6130A2 HPRIGHT"}, + {"TPA6130A2 LEFTIN", NULL, "LLOUT"}, + {"TPA6130A2 RIGHTIN", NULL, "RLOUT"}, + + {"DMic Rate 64", NULL, "DMic"}, + {"DMic", NULL, "Mic Bias"}, + + {"b LINE2R", NULL, "MONO_LOUT"}, + {"Earphone", NULL, "b HPLOUT"}, + + {"LINE1L", NULL, "HS Mic"}, + {"HS Mic", NULL, "b Mic Bias"}, +}; + +static const char * const spk_function[] = {"Off", "On"}; +static const char * const input_function[] = {"ADC", "Digital Mic"}; +static const char * const jack_function[] = { + "Off", "TV-OUT", "Headphone", "Headset" +}; + +static const struct soc_enum rx51_enum[] = { + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(spk_function), spk_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(input_function), input_function), + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(jack_function), jack_function), +}; + +static const struct snd_kcontrol_new aic34_rx51_controls[] = { + SOC_ENUM_EXT("Speaker Function", rx51_enum[0], + rx51_get_spk, rx51_set_spk), + SOC_ENUM_EXT("Input Select", rx51_enum[1], + rx51_get_input, rx51_set_input), + SOC_ENUM_EXT("Jack Function", rx51_enum[2], + rx51_get_jack, rx51_set_jack), + SOC_DAPM_PIN_SWITCH("FM Transmitter"), + SOC_DAPM_PIN_SWITCH("Earphone"), +}; + +static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card); + int err; + + snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42); + + err = omap_mcbsp_st_add_controls(rtd, 2); + if (err < 0) { + dev_err(card->dev, "Failed to add MCBSP controls\n"); + return err; + } + + /* AV jack detection */ + err = snd_soc_card_jack_new(rtd->card, "AV Jack", + SND_JACK_HEADSET | SND_JACK_VIDEOOUT, + &rx51_av_jack, NULL, 0); + if (err) { + dev_err(card->dev, "Failed to add AV Jack\n"); + return err; + } + + /* prepare gpio for snd_soc_jack_add_gpios */ + rx51_av_jack_gpios[0].gpio = desc_to_gpio(pdata->jack_detection_gpio); + devm_gpiod_put(card->dev, pdata->jack_detection_gpio); + + err = snd_soc_jack_add_gpios(&rx51_av_jack, + ARRAY_SIZE(rx51_av_jack_gpios), + rx51_av_jack_gpios); + if (err) { + dev_err(card->dev, "Failed to add GPIOs\n"); + return err; + } + + return err; +} + +/* Digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link rx51_dai[] = { + { + .name = "TLV320AIC34", + .stream_name = "AIC34", + .cpu_dai_name = "omap-mcbsp.2", + .codec_dai_name = "tlv320aic3x-hifi", + .platform_name = "omap-mcbsp.2", + .codec_name = "tlv320aic3x-codec.2-0018", + .dai_fmt = SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF | + SND_SOC_DAIFMT_CBM_CFM, + .init = rx51_aic34_init, + .ops = &rx51_ops, + }, +}; + +static struct snd_soc_aux_dev rx51_aux_dev[] = { + { + .name = "TLV320AIC34b", + .codec_name = "tlv320aic3x-codec.2-0019", + }, + { + .name = "TPA61320A2", + .codec_name = "tpa6130a2.2-0060", + }, +}; + +static struct snd_soc_codec_conf rx51_codec_conf[] = { + { + .dev_name = "tlv320aic3x-codec.2-0019", + .name_prefix = "b", + }, + { + .dev_name = "tpa6130a2.2-0060", + .name_prefix = "TPA6130A2", + }, +}; + +/* Audio card */ +static struct snd_soc_card rx51_sound_card = { + .name = "RX-51", + .owner = THIS_MODULE, + .dai_link = rx51_dai, + .num_links = ARRAY_SIZE(rx51_dai), + .aux_dev = rx51_aux_dev, + .num_aux_devs = ARRAY_SIZE(rx51_aux_dev), + .codec_conf = rx51_codec_conf, + .num_configs = ARRAY_SIZE(rx51_codec_conf), + .fully_routed = true, + + .controls = aic34_rx51_controls, + .num_controls = ARRAY_SIZE(aic34_rx51_controls), + .dapm_widgets = aic34_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic34_dapm_widgets), + .dapm_routes = audio_map, + .num_dapm_routes = ARRAY_SIZE(audio_map), +}; + +static int rx51_soc_probe(struct platform_device *pdev) +{ + struct rx51_audio_pdata *pdata; + struct device_node *np = pdev->dev.of_node; + struct snd_soc_card *card = &rx51_sound_card; + int err; + + if (!machine_is_nokia_rx51() && !of_machine_is_compatible("nokia,omap3-n900")) + return -ENODEV; + + card->dev = &pdev->dev; + + if (np) { + struct device_node *dai_node; + + dai_node = of_parse_phandle(np, "nokia,cpu-dai", 0); + if (!dai_node) { + dev_err(&pdev->dev, "McBSP node is not provided\n"); + return -EINVAL; + } + rx51_dai[0].cpu_dai_name = NULL; + rx51_dai[0].platform_name = NULL; + rx51_dai[0].cpu_of_node = dai_node; + rx51_dai[0].platform_of_node = dai_node; + + dai_node = of_parse_phandle(np, "nokia,audio-codec", 0); + if (!dai_node) { + dev_err(&pdev->dev, "Codec node is not provided\n"); + return -EINVAL; + } + rx51_dai[0].codec_name = NULL; + rx51_dai[0].codec_of_node = dai_node; + + dai_node = of_parse_phandle(np, "nokia,audio-codec", 1); + if (!dai_node) { + dev_err(&pdev->dev, "Auxiliary Codec node is not provided\n"); + return -EINVAL; + } + rx51_aux_dev[0].codec_name = NULL; + rx51_aux_dev[0].codec_of_node = dai_node; + rx51_codec_conf[0].dev_name = NULL; + rx51_codec_conf[0].of_node = dai_node; + + dai_node = of_parse_phandle(np, "nokia,headphone-amplifier", 0); + if (!dai_node) { + dev_err(&pdev->dev, "Headphone amplifier node is not provided\n"); + return -EINVAL; + } + rx51_aux_dev[1].codec_name = NULL; + rx51_aux_dev[1].codec_of_node = dai_node; + rx51_codec_conf[1].dev_name = NULL; + rx51_codec_conf[1].of_node = dai_node; + } + + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + if (pdata == NULL) + return -ENOMEM; + + snd_soc_card_set_drvdata(card, pdata); + + pdata->tvout_selection_gpio = devm_gpiod_get(card->dev, + "tvout-selection", + GPIOD_OUT_LOW); + if (IS_ERR(pdata->tvout_selection_gpio)) { + dev_err(card->dev, "could not get tvout selection gpio\n"); + return PTR_ERR(pdata->tvout_selection_gpio); + } + + pdata->jack_detection_gpio = devm_gpiod_get(card->dev, + "jack-detection", + GPIOD_ASIS); + if (IS_ERR(pdata->jack_detection_gpio)) { + dev_err(card->dev, "could not get jack detection gpio\n"); + return PTR_ERR(pdata->jack_detection_gpio); + } + + pdata->eci_sw_gpio = devm_gpiod_get(card->dev, "eci-switch", + GPIOD_OUT_HIGH); + if (IS_ERR(pdata->eci_sw_gpio)) { + dev_err(card->dev, "could not get eci switch gpio\n"); + return PTR_ERR(pdata->eci_sw_gpio); + } + + pdata->speaker_amp_gpio = devm_gpiod_get(card->dev, + "speaker-amplifier", + GPIOD_OUT_LOW); + if (IS_ERR(pdata->speaker_amp_gpio)) { + dev_err(card->dev, "could not get speaker enable gpio\n"); + return PTR_ERR(pdata->speaker_amp_gpio); + } + + err = devm_snd_soc_register_card(card->dev, card); + if (err) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", err); + return err; + } + + return 0; +} + +#if defined(CONFIG_OF) +static const struct of_device_id rx51_audio_of_match[] = { + { .compatible = "nokia,n900-audio", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rx51_audio_of_match); +#endif + +static struct platform_driver rx51_soc_driver = { + .driver = { + .name = "rx51-audio", + .of_match_table = of_match_ptr(rx51_audio_of_match), + }, + .probe = rx51_soc_probe, +}; + +module_platform_driver(rx51_soc_driver); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_DESCRIPTION("ALSA SoC Nokia RX-51"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rx51-audio"); diff --git a/sound/soc/ti/sdma-pcm.c b/sound/soc/ti/sdma-pcm.c new file mode 100644 index 000000000000..21a9c2499d48 --- /dev/null +++ b/sound/soc/ti/sdma-pcm.c @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Author: Peter Ujfalusi + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sdma-pcm.h" + +static const struct snd_pcm_hardware sdma_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | + SNDRV_PCM_INFO_INTERLEAVED, + .period_bytes_min = 32, + .period_bytes_max = 64 * 1024, + .buffer_bytes_max = 128 * 1024, + .periods_min = 2, + .periods_max = 255, +}; + +static const struct snd_dmaengine_pcm_config sdma_dmaengine_pcm_config = { + .pcm_hardware = &sdma_pcm_hardware, + .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, + .compat_filter_fn = omap_dma_filter_fn, + .prealloc_buffer_size = 128 * 1024, +}; + +int sdma_pcm_platform_register(struct device *dev, + char *txdmachan, char *rxdmachan) +{ + struct snd_dmaengine_pcm_config *config; + unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT; + + /* Standard names for the directions: 'tx' and 'rx' */ + if (!txdmachan && !rxdmachan) + return devm_snd_dmaengine_pcm_register(dev, + &sdma_dmaengine_pcm_config, + flags); + + config = devm_kzalloc(dev, sizeof(*config), GFP_KERNEL); + if (!config) + return -ENOMEM; + + *config = sdma_dmaengine_pcm_config; + + if (!txdmachan || !rxdmachan) { + /* One direction only PCM */ + flags |= SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX; + if (!txdmachan) { + txdmachan = rxdmachan; + rxdmachan = NULL; + } + } + + config->chan_names[0] = txdmachan; + config->chan_names[1] = rxdmachan; + + return devm_snd_dmaengine_pcm_register(dev, config, flags); +} +EXPORT_SYMBOL_GPL(sdma_pcm_platform_register); + +MODULE_AUTHOR("Peter Ujfalusi "); +MODULE_DESCRIPTION("sDMA PCM ASoC platform driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/ti/sdma-pcm.h b/sound/soc/ti/sdma-pcm.h new file mode 100644 index 000000000000..cb0627c8dd34 --- /dev/null +++ b/sound/soc/ti/sdma-pcm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com + * Author: Peter Ujfalusi + */ + +#ifndef __SDMA_PCM_H__ +#define __SDMA_PCM_H__ + +#if IS_ENABLED(CONFIG_SND_SOC_TI_SDMA_PCM) +int sdma_pcm_platform_register(struct device *dev, + char *txdmachan, char *rxdmachan); +#else +static inline int sdma_pcm_platform_register(struct device *dev, + char *txdmachan, char *rxdmachan) +{ + return -ENODEV; +} +#endif /* CONFIG_SND_SOC_TI_SDMA_PCM */ + +#endif /* __SDMA_PCM_H__ */ -- cgit v1.2.3-58-ga151 From 0f4967bc21de0bb9e107f9ad3ced852daf66acd2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 17 Dec 2018 14:21:42 +0200 Subject: ASoC: ti: Kconfig: Remove the deprecated options We no longer have these options used anywhere. Signed-off-by: Peter Ujfalusi Acked-by: Tony Lindgren Signed-off-by: Mark Brown --- sound/soc/ti/Kconfig | 159 --------------------------------------------------- 1 file changed, 159 deletions(-) (limited to 'sound') diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig index 698810c770f0..4bf3c15d4e51 100644 --- a/sound/soc/ti/Kconfig +++ b/sound/soc/ti/Kconfig @@ -46,14 +46,10 @@ config SND_SOC_OMAP_DMIC Say Y or M here if you want to have support for DMIC IP found in OMAP4 and OMAP5. -config SND_OMAP_SOC_MCBSP - tristate - config SND_SOC_OMAP_MCBSP tristate "Multichannel Buffered Serial Port (McBSP) support" depends on ARCH_OMAP || ARCH_OMAP1 || COMPILE_TEST select SND_SOC_TI_SDMA_PCM - select SND_OMAP_SOC_MCBSP help Say Y or M here if you want to have support for McBSP IP found in Texas Instruments OMAP1/2/3/4/5 SoCs. @@ -209,160 +205,5 @@ config SND_SOC_DM365_VOICE_CODEC Say Y if you want to add support for SoC On-chip voice codec endchoice -menu "Deprecated kconfig options:" - -config SND_AM33XX_SOC_EVM - tristate "SoC Audio for the AM33XX chip based boards" - depends on SOC_AM33XX && I2C - select SND_SOC_TLV320AIC3X - select SND_SOC_DAVINCI_MCASP - help - Say Y or M if you want to add support for SoC audio on AM33XX - boards using McASP and TLV320AIC3X codec. For example AM335X-EVM, - AM335X-EVMSK, and BeagelBone with AudioCape boards have this - setup. - -config SND_DAVINCI_SOC_EVM - tristate "SoC Audio support for DaVinci DM6446, DM355 or DM365 EVM" - depends on ARCH_DAVINCI && I2C - depends on MACH_DAVINCI_EVM || MACH_DAVINCI_DM355_EVM || MACH_DAVINCI_DM365_EVM - select SND_SOC_DAVINCI_EVM - help - Say Y if you want to add support for SoC audio on TI - DaVinci DM6446, DM355 or DM365 EVM platforms. - -choice - prompt "DM365 codec select" - depends on SND_DAVINCI_SOC_EVM - depends on MACH_DAVINCI_DM365_EVM - -config SND_DM365_AIC3X_CODEC - tristate "Audio Codec - AIC3101" - select SND_SOC_DM365_AIC3X_CODEC - help - Say Y if you want to add support for AIC3101 audio codec - -config SND_DM365_VOICE_CODEC - tristate "Voice Codec - CQ93VC" - select SND_SOC_DM365_VOICE_CODEC - help - Say Y if you want to add support for SoC On-chip voice codec -endchoice - -config SND_DM6467_SOC_EVM - tristate "SoC Audio support for DaVinci DM6467 EVM" - depends on MACH_DAVINCI_DM6467_EVM && I2C - select SND_SOC_DAVINCI_EVM - - help - Say Y if you want to add support for SoC audio on TI - -config SND_DA830_SOC_EVM - tristate "SoC Audio support for DA830/OMAP-L137 EVM" - depends on MACH_DAVINCI_DA830_EVM && I2C - select SND_SOC_DAVINCI_EVM - - help - Say Y if you want to add support for SoC audio on TI - DA830/OMAP-L137 EVM - -config SND_DA850_SOC_EVM - tristate "SoC Audio support for DA850/OMAP-L138 EVM" - depends on MACH_DAVINCI_DA850_EVM && I2C - select SND_SOC_DAVINCI_EVM - help - Say Y if you want to add support for SoC audio on TI - DA850/OMAP-L138 EVM - -config SND_OMAP_SOC_HDMI_AUDIO - tristate "HDMI audio support for OMAP4+ based SoCs" - select SND_SOC_OMAP_HDMI - help - For HDMI audio to work OMAPDSS HDMI support should be - enabled. - The hdmi audio driver implements cpu-dai component using the - callbacks provided by OMAPDSS and registers the component - under DSS HDMI device. Omap-pcm is registered for platform - component also under DSS HDMI device. Dummy codec is used as - as codec component. The hdmi audio driver implements also - the card and registers it under its own platform device. - The device for the driver is registered by OMAPDSS hdmi - driver. - -config SND_OMAP_SOC_N810 - tristate "SoC Audio support for Nokia N810" - depends on MACH_NOKIA_N810 && I2C - select SND_SOC_NOKIA_N810 - help - Say Y if you want to add support for SoC audio on Nokia N810. - -config SND_OMAP_SOC_RX51 - tristate "SoC Audio support for Nokia N900 (RX-51)" - depends on ARM && I2C && GPIOLIB && ARCH_OMAP3 - select SND_SOC_NOKIA_RX51 - help - Say Y if you want to add support for SoC audio on Nokia N900 - cellphone. - -config SND_OMAP_SOC_AMS_DELTA - tristate "SoC Audio support for Amstrad E3 (Delta) videophone" - depends on MACH_AMS_DELTA && TTY - select SND_SOC_OMAP_AMS_DELTA - help - Say Y if you want to add support for SoC audio device connected to - a handset and a speakerphone found on Amstrad E3 (Delta) videophone. - - Note that in order to get those devices fully supported, you have to - build the kernel with standard serial port driver included and - configured for at least 4 ports. Then, from userspace, you must load - a line discipline #19 on the modem (ttyS3) serial line. The simplest - way to achieve this is to install util-linux-ng and use the included - ldattach utility. This can be started automatically from udev, - a simple rule like this one should do the trick (it does for me): - ACTION=="add", KERNEL=="controlC0", \ - RUN+="/usr/sbin/ldattach 19 /dev/ttyS3" - -config SND_OMAP_SOC_OSK5912 - tristate "SoC Audio support for omap osk5912" - depends on MACH_OMAP_OSK && I2C - select SND_SOC_OMAP_OSK5912 - help - Say Y if you want to add support for SoC audio on osk5912. - -config SND_OMAP_SOC_OMAP_TWL4030 - tristate "SoC Audio support for TI SoC based boards with twl4030 codec" - depends on TWL4030_CORE - select SND_SOC_OMAP3_TWL4030 - help - Say Y if you want to add support for SoC audio on TI SoC based boards - using twl4030 as c codec. This driver currently supports: - - Beagleboard or Devkit8000 - - Gumstix Overo or CompuLab CM-T35/CM-T3730 - - IGEP v2 - - OMAP3EVM - - SDP3430 - - Zoom2 - -config SND_OMAP_SOC_OMAP_ABE_TWL6040 - tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec" - depends on TWL6040_CORE && COMMON_CLK - depends on ARCH_OMAP4 || (SOC_OMAP5 && MFD_PALMAS) || COMPILE_TEST - select SND_SOC_OMAP_ABE_TWL6040 - help - Say Y if you want to add support for SoC audio on OMAP boards using - ABE and twl6040 codec. This driver currently supports: - - SDP4430/Blaze boards - - PandaBoard (4430) - - PandaBoardES (4460) - - omap5-uevm (5432) - -config SND_OMAP_SOC_OMAP3_PANDORA - tristate "SoC Audio support for OMAP3 Pandora" - depends on TWL4030_CORE - select SND_SOC_OMAP3_PANDORA - help - Say Y if you want to add support for SoC audio on the OMAP3 Pandora. -endmenu - endmenu -- cgit v1.2.3-58-ga151