From 0b95aa8e8afa4bcd49c8fa36404e2deb02a947ed Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 3 Aug 2020 17:05:29 -0700 Subject: ASoC: rt5682: Use dev_dbg() in rt5682_clk_check() I see a spew of "sysclk/dai not set correctly" whenever I cat /sys/kernel/debug/clk/clk_summary on my device. This is because the master pointer isn't set yet in this driver. A user isn't going to be able to do much if this check is failing so this error message isn't really an error, it's more of a kernel debug message. Lower the priority to dev_dbg() so that it isn't so noisy. Signed-off-by: Stephen Boyd Cc: Cheng-Yi Chiang Cc: Shuming Fan Link: https://lore.kernel.org/r/20200804000531.920688-2-swboyd@chromium.org Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index fab066a75ce0..ed9475f24aec 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2482,7 +2482,7 @@ static int rt5682_set_bias_level(struct snd_soc_component *component, static bool rt5682_clk_check(struct rt5682_priv *rt5682) { if (!rt5682->master[RT5682_AIF1]) { - dev_err(rt5682->component->dev, "sysclk/dai not set correctly\n"); + dev_dbg(rt5682->component->dev, "sysclk/dai not set correctly\n"); return false; } return true; -- cgit From edbd24ea1e5c72980b37ae2d271696b05274d509 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 3 Aug 2020 17:05:30 -0700 Subject: ASoC: rt5682: Drop usage of __clk_get_name() The __clk_get_name() API is deprecated. Use clk_hw_get_name() or proper registration techniques to avoid it. Signed-off-by: Stephen Boyd Cc: Cheng-Yi Chiang Cc: Shuming Fan Link: https://lore.kernel.org/r/20200804000531.920688-3-swboyd@chromium.org Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index ed9475f24aec..d8a1973a4624 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2548,7 +2548,7 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw, container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); struct snd_soc_component *component = rt5682->component; - const char * const clk_name = __clk_get_name(hw->clk); + const char * const clk_name = clk_hw_get_name(hw); if (!rt5682_clk_check(rt5682)) return 0; @@ -2572,7 +2572,7 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate, container_of(hw, struct rt5682_priv, dai_clks_hw[RT5682_DAI_WCLK_IDX]); struct snd_soc_component *component = rt5682->component; - const char * const clk_name = __clk_get_name(hw->clk); + const char * const clk_name = clk_hw_get_name(hw); if (!rt5682_clk_check(rt5682)) return -EINVAL; @@ -2597,7 +2597,7 @@ static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate, dai_clks_hw[RT5682_DAI_WCLK_IDX]); struct snd_soc_component *component = rt5682->component; struct clk *parent_clk; - const char * const clk_name = __clk_get_name(hw->clk); + const char * const clk_name = clk_hw_get_name(hw); int pre_div; unsigned int clk_pll2_out; @@ -2755,33 +2755,31 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component) struct device *dev = component->dev; struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); struct rt5682_platform_data *pdata = &rt5682->pdata; - struct clk_init_data init; struct clk *dai_clk; struct clk_lookup *dai_clk_lookup; struct clk_hw *dai_clk_hw; - const char *parent_name; int i, ret; for (i = 0; i < RT5682_DAI_NUM_CLKS; ++i) { + struct clk_init_data init = { }; + dai_clk_hw = &rt5682->dai_clks_hw[i]; switch (i) { case RT5682_DAI_WCLK_IDX: /* Make MCLK the parent of WCLK */ if (rt5682->mclk) { - parent_name = __clk_get_name(rt5682->mclk); - init.parent_names = &parent_name; + init.parent_data = &(struct clk_parent_data){ + .fw_name = "mclk", + }; init.num_parents = 1; - } else { - init.parent_names = NULL; - init.num_parents = 0; } break; case RT5682_DAI_BCLK_IDX: /* Make WCLK the parent of BCLK */ - parent_name = __clk_get_name( - rt5682->dai_clks[RT5682_DAI_WCLK_IDX]); - init.parent_names = &parent_name; + init.parent_hws = &(const struct clk_hw *){ + &rt5682->dai_clks_hw[RT5682_DAI_WCLK_IDX] + }; init.num_parents = 1; break; default: -- cgit From 653bdab267bd8dbce9cbd16bec843ca9d20a8450 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 3 Aug 2020 17:05:31 -0700 Subject: ASoC: rt5682: Use clk_hw based APIs for registration The (new?) style of clk registration uses clk_hw based APIs so that we can more easily see the difference between clk providers and clk consumers. Use the clk_hw based APIs to do this and migrate to devm for the clkdev creation so that we can reduce the amount of code. Signed-off-by: Stephen Boyd Cc: Cheng-Yi Chiang Cc: Shuming Fan Link: https://lore.kernel.org/r/20200804000531.920688-4-swboyd@chromium.org Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 47 +++++++++++------------------------------------ sound/soc/codecs/rt5682.h | 2 -- 2 files changed, 11 insertions(+), 38 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index d8a1973a4624..bfb26fec7137 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2755,8 +2755,6 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component) struct device *dev = component->dev; struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); struct rt5682_platform_data *pdata = &rt5682->pdata; - struct clk *dai_clk; - struct clk_lookup *dai_clk_lookup; struct clk_hw *dai_clk_hw; int i, ret; @@ -2784,8 +2782,7 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component) break; default: dev_err(dev, "Invalid clock index\n"); - ret = -EINVAL; - goto err; + return -EINVAL; } init.name = pdata->dai_clk_names[i]; @@ -2793,39 +2790,26 @@ static int rt5682_register_dai_clks(struct snd_soc_component *component) init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; dai_clk_hw->init = &init; - dai_clk = devm_clk_register(dev, dai_clk_hw); - if (IS_ERR(dai_clk)) { - dev_warn(dev, "Failed to register %s: %ld\n", - init.name, PTR_ERR(dai_clk)); - ret = PTR_ERR(dai_clk); - goto err; + ret = devm_clk_hw_register(dev, dai_clk_hw); + if (ret) { + dev_warn(dev, "Failed to register %s: %d\n", + init.name, ret); + return ret; } - rt5682->dai_clks[i] = dai_clk; if (dev->of_node) { devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, dai_clk_hw); } else { - dai_clk_lookup = clkdev_create(dai_clk, init.name, - "%s", dev_name(dev)); - if (!dai_clk_lookup) { - ret = -ENOMEM; - goto err; - } else { - rt5682->dai_clks_lookup[i] = dai_clk_lookup; - } + ret = devm_clk_hw_register_clkdev(dev, dai_clk_hw, + init.name, + dev_name(dev)); + if (ret) + return ret; } } return 0; - -err: - do { - if (rt5682->dai_clks_lookup[i]) - clkdev_drop(rt5682->dai_clks_lookup[i]); - } while (i-- > 0); - - return ret; } #endif /* CONFIG_COMMON_CLK */ @@ -2882,15 +2866,6 @@ static void rt5682_remove(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); -#ifdef CONFIG_COMMON_CLK - int i; - - for (i = RT5682_DAI_NUM_CLKS - 1; i >= 0; --i) { - if (rt5682->dai_clks_lookup[i]) - clkdev_drop(rt5682->dai_clks_lookup[i]); - } -#endif - rt5682_reset(rt5682); } diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index 6d94327beae5..354acd735ef4 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1411,8 +1411,6 @@ struct rt5682_priv { #ifdef CONFIG_COMMON_CLK struct clk_hw dai_clks_hw[RT5682_DAI_NUM_CLKS]; - struct clk_lookup *dai_clks_lookup[RT5682_DAI_NUM_CLKS]; - struct clk *dai_clks[RT5682_DAI_NUM_CLKS]; struct clk *mclk; #endif -- cgit From 1220f6a76e77af8ac14fe67a11fcd7806764ea46 Mon Sep 17 00:00:00 2001 From: Ajit Pandey Date: Fri, 14 Aug 2020 16:22:57 +0530 Subject: ASoC: qcom: Add common array to initialize soc based core clocks LPASS variants have their own soc specific clocks that needs to be enabled for MI2S audio support. Added a common variable in drvdata to initialize such clocks using bulk clk api. Such clock names is defined in variants specific data and needs to fetched during init. Signed-off-by: Ajit Pandey Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-2-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 39 +++++++++++++++++++-------------------- sound/soc/qcom/lpass.h | 10 +++++++--- 2 files changed, 26 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index b3610d05b651..8210e371ecee 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -161,32 +161,27 @@ static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) static int apq8016_lpass_init(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *variant = drvdata->variant; struct device *dev = &pdev->dev; - int ret; + int ret, i; - drvdata->pcnoc_mport_clk = devm_clk_get(dev, "pcnoc-mport-clk"); - if (IS_ERR(drvdata->pcnoc_mport_clk)) { - dev_err(dev, "error getting pcnoc-mport-clk: %ld\n", - PTR_ERR(drvdata->pcnoc_mport_clk)); - return PTR_ERR(drvdata->pcnoc_mport_clk); - } - ret = clk_prepare_enable(drvdata->pcnoc_mport_clk); + drvdata->clks = devm_kcalloc(dev, variant->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + drvdata->num_clks = variant->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = variant->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); if (ret) { - dev_err(dev, "Error enabling pcnoc-mport-clk: %d\n", ret); + dev_err(dev, "Failed to get clocks %d\n", ret); return ret; } - drvdata->pcnoc_sway_clk = devm_clk_get(dev, "pcnoc-sway-clk"); - if (IS_ERR(drvdata->pcnoc_sway_clk)) { - dev_err(dev, "error getting pcnoc-sway-clk: %ld\n", - PTR_ERR(drvdata->pcnoc_sway_clk)); - return PTR_ERR(drvdata->pcnoc_sway_clk); - } - - ret = clk_prepare_enable(drvdata->pcnoc_sway_clk); + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); if (ret) { - dev_err(dev, "Error enabling pcnoc_sway_clk: %d\n", ret); + dev_err(dev, "apq8016 clk_enable failed\n"); return ret; } @@ -197,8 +192,7 @@ static int apq8016_lpass_exit(struct platform_device *pdev) { struct lpass_data *drvdata = platform_get_drvdata(pdev); - clk_disable_unprepare(drvdata->pcnoc_mport_clk); - clk_disable_unprepare(drvdata->pcnoc_sway_clk); + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); return 0; } @@ -219,6 +213,11 @@ static struct lpass_variant apq8016_data = { .wrdma_reg_stride = 0x1000, .wrdma_channel_start = 5, .wrdma_channels = 2, + .clk_name = (const char*[]) { + "pcnoc-mport-clk", + "pcnoc-sway-clk", + }, + .num_clks = 2, .dai_driver = apq8016_lpass_cpu_dai_driver, .num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver), .dai_osr_clk_names = (const char *[]) { diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index bd19ec57c73d..450020e5f302 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -51,9 +51,9 @@ struct lpass_data { /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; - /* 8016 specific */ - struct clk *pcnoc_mport_clk; - struct clk *pcnoc_sway_clk; + /* SOC specific clock list */ + struct clk_bulk_data *clks; + int num_clks; }; @@ -89,6 +89,10 @@ struct lpass_variant { int num_dai; const char * const *dai_osr_clk_names; const char * const *dai_bit_clk_names; + + /* SOC specific clocks configuration */ + const char **clk_name; + int num_clks; }; /* register the platform driver from the CPU DAI driver */ -- cgit From a503567d70eca91796a4ab23053d4c70df8e1e3e Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Aug 2020 16:22:58 +0530 Subject: ASoC: qcom: lpass-cpu: Move ahbix clk to platform specific function Ahbix clock is optional clock and not needed for all platforms. Move it to lpass-apq8016/ipq806x as it is not needed for sc7180. Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-3-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 27 ++++++++++++++++++++++++++ sound/soc/qcom/lpass-cpu.c | 40 ++++++++++----------------------------- sound/soc/qcom/lpass-ipq806x.c | 43 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 30 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 8210e371ecee..fe4c25847022 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -185,7 +185,33 @@ static int apq8016_lpass_init(struct platform_device *pdev) return ret; } + drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); + if (IS_ERR(drvdata->ahbix_clk)) { + dev_err(dev, "error getting ahbix-clk: %ld\n", + PTR_ERR(drvdata->ahbix_clk)); + ret = PTR_ERR(drvdata->ahbix_clk); + goto err_ahbix_clk; + } + + ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); + if (ret) { + dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + dev_dbg(dev, "set ahbix_clk rate to %lu\n", + clk_get_rate(drvdata->ahbix_clk)); + + ret = clk_prepare_enable(drvdata->ahbix_clk); + if (ret) { + dev_err(dev, "error enabling ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + return 0; + +err_ahbix_clk: + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + return ret; } static int apq8016_lpass_exit(struct platform_device *pdev) @@ -193,6 +219,7 @@ static int apq8016_lpass_exit(struct platform_device *pdev) struct lpass_data *drvdata = platform_get_drvdata(pdev); clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + clk_disable_unprepare(drvdata->ahbix_clk); return 0; } diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index e00a4af29c13..f0c7e93063b5 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -566,8 +566,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR(drvdata->lpaif_map); } - if (variant->init) - variant->init(pdev); + if (variant->init) { + ret = variant->init(pdev); + if (ret) { + dev_err(dev, "error initializing variant: %d\n", ret); + return ret; + } + } for (i = 0; i < variant->num_dai; i++) { dai_id = variant->dai_driver[i].id; @@ -594,46 +599,22 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) } } - drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); - if (IS_ERR(drvdata->ahbix_clk)) { - dev_err(dev, "error getting ahbix-clk: %ld\n", - PTR_ERR(drvdata->ahbix_clk)); - return PTR_ERR(drvdata->ahbix_clk); - } - - ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); - if (ret) { - dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); - return ret; - } - dev_dbg(dev, "set ahbix_clk rate to %lu\n", - clk_get_rate(drvdata->ahbix_clk)); - - ret = clk_prepare_enable(drvdata->ahbix_clk); - if (ret) { - dev_err(dev, "error enabling ahbix_clk: %d\n", ret); - return ret; - } - ret = devm_snd_soc_register_component(dev, &lpass_cpu_comp_driver, variant->dai_driver, variant->num_dai); if (ret) { dev_err(dev, "error registering cpu driver: %d\n", ret); - goto err_clk; + goto err; } ret = asoc_qcom_lpass_platform_register(pdev); if (ret) { dev_err(dev, "error registering platform driver: %d\n", ret); - goto err_clk; + goto err; } - return 0; - -err_clk: - clk_disable_unprepare(drvdata->ahbix_clk); +err: return ret; } EXPORT_SYMBOL_GPL(asoc_qcom_lpass_cpu_platform_probe); @@ -645,7 +626,6 @@ int asoc_qcom_lpass_cpu_platform_remove(struct platform_device *pdev) if (drvdata->variant->exit) drvdata->variant->exit(pdev); - clk_disable_unprepare(drvdata->ahbix_clk); return 0; } diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 1987605482f7..b7c05863d612 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -55,6 +55,47 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = { .ops = &asoc_qcom_lpass_cpu_dai_ops, }; +static int ipq806x_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + int ret; + + drvdata->ahbix_clk = devm_clk_get(dev, "ahbix-clk"); + if (IS_ERR(drvdata->ahbix_clk)) { + dev_err(dev, "error getting ahbix-clk: %ld\n", + PTR_ERR(drvdata->ahbix_clk)); + ret = PTR_ERR(drvdata->ahbix_clk); + goto err_ahbix_clk; + } + + ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); + if (ret) { + dev_err(dev, "error setting rate on ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + dev_dbg(dev, "set ahbix_clk rate to %lu\n", + clk_get_rate(drvdata->ahbix_clk)); + + ret = clk_prepare_enable(drvdata->ahbix_clk); + if (ret) { + dev_err(dev, "error enabling ahbix_clk: %d\n", ret); + goto err_ahbix_clk; + } + +err_ahbix_clk: + return ret; +} + +static int ipq806x_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_disable_unprepare(drvdata->ahbix_clk); + + return 0; +} + static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) { if (dir == SNDRV_PCM_STREAM_PLAYBACK) @@ -90,6 +131,8 @@ static struct lpass_variant ipq806x_data = { .dai_bit_clk_names = (const char *[]) { "mi2s-bit-clk", }, + .init = ipq806x_lpass_init, + .exit = ipq806x_lpass_exit, .alloc_dma_channel = ipq806x_lpass_alloc_dma_channel, .free_dma_channel = ipq806x_lpass_free_dma_channel, }; -- cgit From 784771863abae5f8878c62e9c624111c51bebe7c Mon Sep 17 00:00:00 2001 From: Ajit Pandey Date: Fri, 14 Aug 2020 16:22:59 +0530 Subject: ASoC: qcom: lpass-platform: Replace card->dev with component->dev We are allocating dma memory for component->dev but trying to mmap such memory for substream->pcm->card->dev. Replace device argument in mmap with component->dev to fix this. Signed-off-by: Ajit Pandey Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-4-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 01179bc0e5e5..3697f4ee63a0 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -400,9 +400,8 @@ static int lpass_platform_pcmops_mmap(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; - return dma_mmap_coherent(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, - runtime->dma_bytes); + return dma_mmap_coherent(component->dev, vma, runtime->dma_area, + runtime->dma_addr, runtime->dma_bytes); } static irqreturn_t lpass_dma_interrupt_handler( -- cgit From 5fd188215d4eb52703600d8986b22311099a5940 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Aug 2020 16:23:00 +0530 Subject: ASoC: qcom: lpass-platform: fix memory leak lpass_pcm_data is never freed. Free it in close ops to avoid memory leak. Fixes: 022d00ee0b55 ("ASoC: lpass-platform: Fix broken pcm data usage") Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-5-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 3697f4ee63a0..315feda414d9 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -61,7 +61,7 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, int ret, dma_ch, dir = substream->stream; struct lpass_pcm_data *data; - data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -118,6 +118,7 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component, if (v->free_dma_channel) v->free_dma_channel(drvdata, data->dma_ch); + kfree(data); return 0; } -- cgit From b5022a36d28f6a99c1a57f54246e8b566cf094d5 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Aug 2020 16:23:01 +0530 Subject: ASoC: qcom: lpass: Use regmap_field for i2sctl and dmactl registers I2SCTL and DMACTL registers has different bits alignment for newer LPASS variants of SC7180 soc. Use REG_FIELD_ID() to define the reg_fields in platform specific file and removed shifts and mask macros for such registers from header file. Signed-off-by: Rohit kumar Link: https://lore.kernel.org/r/1597402388-14112-6-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 24 ++++++ sound/soc/qcom/lpass-cpu.c | 156 ++++++++++++++++++++++++++------------ sound/soc/qcom/lpass-ipq806x.c | 24 ++++++ sound/soc/qcom/lpass-lpaif-reg.h | 157 +++++++++++++++++++++------------------ sound/soc/qcom/lpass-platform.c | 145 ++++++++++++++++++++++++++++-------- sound/soc/qcom/lpass.h | 53 +++++++++++++ 6 files changed, 407 insertions(+), 152 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index fe4c25847022..dd9e3dd014f6 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -240,6 +240,30 @@ static struct lpass_variant apq8016_data = { .wrdma_reg_stride = 0x1000, .wrdma_channel_start = 5, .wrdma_channels = 2, + .loopback = REG_FIELD_ID(0x1000, 15, 15, 4, 0x1000), + .spken = REG_FIELD_ID(0x1000, 14, 14, 4, 0x1000), + .spkmode = REG_FIELD_ID(0x1000, 10, 13, 4, 0x1000), + .spkmono = REG_FIELD_ID(0x1000, 9, 9, 4, 0x1000), + .micen = REG_FIELD_ID(0x1000, 8, 8, 4, 0x1000), + .micmode = REG_FIELD_ID(0x1000, 4, 7, 4, 0x1000), + .micmono = REG_FIELD_ID(0x1000, 3, 3, 4, 0x1000), + .wssrc = REG_FIELD_ID(0x1000, 2, 2, 4, 0x1000), + .bitwidth = REG_FIELD_ID(0x1000, 0, 0, 4, 0x1000), + + .rdma_dyncclk = REG_FIELD_ID(0x8400, 12, 12, 2, 0x1000), + .rdma_bursten = REG_FIELD_ID(0x8400, 11, 11, 2, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0x8400, 8, 10, 2, 0x1000), + .rdma_intf = REG_FIELD_ID(0x8400, 4, 7, 2, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0x8400, 1, 3, 2, 0x1000), + .rdma_enable = REG_FIELD_ID(0x8400, 0, 0, 2, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 2, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 2, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 2, 0x1000), + .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 2, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 2, 0x1000), + .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 2, 0x1000), + .clk_name = (const char*[]) { "pcnoc-mport-clk", "pcnoc-sway-clk", diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index f0c7e93063b5..6b86f16b6ee5 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -29,6 +29,32 @@ #define LPASS_CPU_I2S_SD0_1_2_MASK GENMASK(2, 0) #define LPASS_CPU_I2S_SD0_1_2_3_MASK GENMASK(3, 0) +static int lpass_cpu_init_i2sctl_bitfields(struct device *dev, + struct lpaif_i2sctl *i2sctl, struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + + i2sctl->loopback = devm_regmap_field_alloc(dev, map, v->loopback); + i2sctl->spken = devm_regmap_field_alloc(dev, map, v->spken); + i2sctl->spkmode = devm_regmap_field_alloc(dev, map, v->spkmode); + i2sctl->spkmono = devm_regmap_field_alloc(dev, map, v->spkmono); + i2sctl->micen = devm_regmap_field_alloc(dev, map, v->micen); + i2sctl->micmode = devm_regmap_field_alloc(dev, map, v->micmode); + i2sctl->micmono = devm_regmap_field_alloc(dev, map, v->micmono); + i2sctl->wssrc = devm_regmap_field_alloc(dev, map, v->wssrc); + i2sctl->bitwidth = devm_regmap_field_alloc(dev, map, v->bitwidth); + + if (IS_ERR(i2sctl->loopback) || IS_ERR(i2sctl->spken) || + IS_ERR(i2sctl->spkmode) || IS_ERR(i2sctl->spkmono) || + IS_ERR(i2sctl->micen) || IS_ERR(i2sctl->micmode) || + IS_ERR(i2sctl->micmono) || IS_ERR(i2sctl->wssrc) || + IS_ERR(i2sctl->bitwidth)) + return -EINVAL; + + return 0; +} + static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { @@ -79,6 +105,8 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; + unsigned int id = dai->driver->id; snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int rate = params_rate(params); @@ -92,28 +120,45 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return bitwidth; } - regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | - LPAIF_I2SCTL_WSSRC_INTERNAL; + ret = regmap_fields_write(i2sctl->loopback, id, + LPAIF_I2SCTL_LOOPBACK_DISABLE); + if (ret) { + dev_err(dai->dev, "error updating loopback field: %d\n", ret); + return ret; + } + + ret = regmap_fields_write(i2sctl->wssrc, id, + LPAIF_I2SCTL_WSSRC_INTERNAL); + if (ret) { + dev_err(dai->dev, "error updating wssrc field: %d\n", ret); + return ret; + } switch (bitwidth) { case 16: - regval |= LPAIF_I2SCTL_BITWIDTH_16; + regval = LPAIF_I2SCTL_BITWIDTH_16; break; case 24: - regval |= LPAIF_I2SCTL_BITWIDTH_24; + regval = LPAIF_I2SCTL_BITWIDTH_24; break; case 32: - regval |= LPAIF_I2SCTL_BITWIDTH_32; + regval = LPAIF_I2SCTL_BITWIDTH_32; break; default: dev_err(dai->dev, "invalid bitwidth given: %d\n", bitwidth); return -EINVAL; } + ret = regmap_fields_write(i2sctl->bitwidth, id, regval); + if (ret) { + dev_err(dai->dev, "error updating bitwidth field: %d\n", ret); + return ret; + } + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - mode = drvdata->mi2s_playback_sd_mode[dai->driver->id]; + mode = drvdata->mi2s_playback_sd_mode[id]; else - mode = drvdata->mi2s_capture_sd_mode[dai->driver->id]; + mode = drvdata->mi2s_capture_sd_mode[id]; if (!mode) { dev_err(dai->dev, "no line is assigned\n"); @@ -175,30 +220,42 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - regval |= LPAIF_I2SCTL_SPKMODE(mode); - + ret = regmap_fields_write(i2sctl->spkmode, id, + LPAIF_I2SCTL_SPKMODE(mode)); + if (ret) { + dev_err(dai->dev, "error writing to i2sctl spkr mode: %d\n", + ret); + return ret; + } if (channels >= 2) - regval |= LPAIF_I2SCTL_SPKMONO_STEREO; + ret = regmap_fields_write(i2sctl->spkmono, id, + LPAIF_I2SCTL_SPKMONO_STEREO); else - regval |= LPAIF_I2SCTL_SPKMONO_MONO; + ret = regmap_fields_write(i2sctl->spkmono, id, + LPAIF_I2SCTL_SPKMONO_MONO); } else { - regval |= LPAIF_I2SCTL_MICMODE(mode); - + ret = regmap_fields_write(i2sctl->micmode, id, + LPAIF_I2SCTL_MICMODE(mode)); + if (ret) { + dev_err(dai->dev, "error writing to i2sctl mic mode: %d\n", + ret); + return ret; + } if (channels >= 2) - regval |= LPAIF_I2SCTL_MICMONO_STEREO; + ret = regmap_fields_write(i2sctl->micmono, id, + LPAIF_I2SCTL_MICMONO_STEREO); else - regval |= LPAIF_I2SCTL_MICMONO_MONO; + ret = regmap_fields_write(i2sctl->micmono, id, + LPAIF_I2SCTL_MICMONO_MONO); } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - regval); if (ret) { - dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); + dev_err(dai->dev, "error writing to i2sctl channels mode: %d\n", + ret); return ret; } - ret = clk_set_rate(drvdata->mi2s_bit_clk[dai->driver->id], + ret = clk_set_rate(drvdata->mi2s_bit_clk[id], rate * bitwidth * 2); if (ret) { dev_err(dai->dev, "error setting mi2s bitclk to %u: %d\n", @@ -228,22 +285,20 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; + unsigned int id = dai->driver->id; int ret; - unsigned int val, mask; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val = LPAIF_I2SCTL_SPKEN_ENABLE; - mask = LPAIF_I2SCTL_SPKEN_MASK; - } else { - val = LPAIF_I2SCTL_MICEN_ENABLE; - mask = LPAIF_I2SCTL_MICEN_MASK; + ret = regmap_fields_write(i2sctl->spken, id, + LPAIF_I2SCTL_SPKEN_ENABLE); + } else { + ret = regmap_fields_write(i2sctl->micen, id, + LPAIF_I2SCTL_MICEN_ENABLE); } - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - mask, val); if (ret) - dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); + dev_err(dai->dev, "error writing to i2sctl enable: %d\n", ret); return ret; } @@ -252,25 +307,21 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct lpaif_i2sctl *i2sctl = drvdata->i2sctl; + unsigned int id = dai->driver->id; int ret = -EINVAL; - unsigned int val, mask; 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) { - val = LPAIF_I2SCTL_SPKEN_ENABLE; - mask = LPAIF_I2SCTL_SPKEN_MASK; + ret = regmap_fields_write(i2sctl->spken, id, + LPAIF_I2SCTL_SPKEN_ENABLE); } else { - val = LPAIF_I2SCTL_MICEN_ENABLE; - mask = LPAIF_I2SCTL_MICEN_MASK; + ret = regmap_fields_write(i2sctl->micen, id, + LPAIF_I2SCTL_MICEN_ENABLE); } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, - dai->driver->id), - mask, val); if (ret) dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); @@ -279,17 +330,12 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - val = LPAIF_I2SCTL_SPKEN_DISABLE; - mask = LPAIF_I2SCTL_SPKEN_MASK; + ret = regmap_fields_write(i2sctl->spken, id, + LPAIF_I2SCTL_SPKEN_DISABLE); } else { - val = LPAIF_I2SCTL_MICEN_DISABLE; - mask = LPAIF_I2SCTL_MICEN_MASK; + ret = regmap_fields_write(i2sctl->micen, id, + LPAIF_I2SCTL_MICEN_DISABLE); } - - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, - dai->driver->id), - mask, val); if (ret) dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); @@ -599,6 +645,18 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) } } + /* Allocation for i2sctl regmap fields */ + drvdata->i2sctl = devm_kzalloc(&pdev->dev, sizeof(struct lpaif_i2sctl), + GFP_KERNEL); + + /* Initialize bitfields for dai I2SCTL register */ + ret = lpass_cpu_init_i2sctl_bitfields(dev, drvdata->i2sctl, + drvdata->lpaif_map); + if (ret) { + dev_err(dev, "error init i2sctl field: %d\n", ret); + return ret; + } + ret = devm_snd_soc_register_component(dev, &lpass_cpu_comp_driver, variant->dai_driver, diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index b7c05863d612..72f09b3a4f6b 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -123,6 +123,30 @@ static struct lpass_variant ipq806x_data = { .wrdma_reg_stride = 0x1000, .wrdma_channel_start = 5, .wrdma_channels = 4, + .loopback = REG_FIELD_ID(0x0010, 15, 15, 5, 0x4), + .spken = REG_FIELD_ID(0x0010, 14, 14, 5, 0x4), + .spkmode = REG_FIELD_ID(0x0010, 10, 13, 5, 0x4), + .spkmono = REG_FIELD_ID(0x0010, 9, 9, 5, 0x4), + .micen = REG_FIELD_ID(0x0010, 8, 8, 5, 0x4), + .micmode = REG_FIELD_ID(0x0010, 4, 7, 5, 0x4), + .micmono = REG_FIELD_ID(0x0010, 3, 3, 5, 0x4), + .wssrc = REG_FIELD_ID(0x0010, 2, 2, 5, 0x4), + .bitwidth = REG_FIELD_ID(0x0010, 0, 0, 5, 0x4), + + .rdma_dyncclk = REG_FIELD_ID(0x6000, 12, 12, 4, 0x1000), + .rdma_bursten = REG_FIELD_ID(0x6000, 11, 11, 4, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0x6000, 8, 10, 4, 0x1000), + .rdma_intf = REG_FIELD_ID(0x6000, 4, 7, 4, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0x6000, 1, 3, 4, 0x1000), + .rdma_enable = REG_FIELD_ID(0x6000, 0, 0, 4, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0xB000, 12, 12, 4, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0xB000, 11, 11, 4, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0xB000, 8, 10, 4, 0x1000), + .wrdma_intf = REG_FIELD_ID(0xB000, 4, 7, 4, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0xB000, 1, 3, 4, 0x1000), + .wrdma_enable = REG_FIELD_ID(0xB000, 0, 0, 4, 0x1000), + .dai_driver = &ipq806x_lpass_cpu_dai_driver, .num_dai = 1, .dai_osr_clk_names = (const char *[]) { diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 72a3e2f69572..5258e60d3646 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -12,15 +12,12 @@ (v->i2sctrl_reg_base + (addr) + v->i2sctrl_reg_stride * (port)) #define LPAIF_I2SCTL_REG(v, port) LPAIF_I2SCTL_REG_ADDR(v, 0x0, (port)) -#define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000 -#define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 -#define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) -#define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT) -#define LPAIF_I2SCTL_SPKEN_MASK 0x4000 -#define LPAIF_I2SCTL_SPKEN_SHIFT 14 -#define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT) -#define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT) +#define LPAIF_I2SCTL_LOOPBACK_DISABLE 0 +#define LPAIF_I2SCTL_LOOPBACK_ENABLE 1 + +#define LPAIF_I2SCTL_SPKEN_DISABLE 0 +#define LPAIF_I2SCTL_SPKEN_ENABLE 1 #define LPAIF_I2SCTL_MODE_NONE 0 #define LPAIF_I2SCTL_MODE_SD0 1 @@ -31,40 +28,37 @@ #define LPAIF_I2SCTL_MODE_QUAD23 6 #define LPAIF_I2SCTL_MODE_6CH 7 #define LPAIF_I2SCTL_MODE_8CH 8 +#define LPAIF_I2SCTL_MODE_10CH 9 +#define LPAIF_I2SCTL_MODE_12CH 10 +#define LPAIF_I2SCTL_MODE_14CH 11 +#define LPAIF_I2SCTL_MODE_16CH 12 +#define LPAIF_I2SCTL_MODE_SD4 13 +#define LPAIF_I2SCTL_MODE_SD5 14 +#define LPAIF_I2SCTL_MODE_SD6 15 +#define LPAIF_I2SCTL_MODE_SD7 16 +#define LPAIF_I2SCTL_MODE_QUAD45 17 +#define LPAIF_I2SCTL_MODE_QUAD47 18 +#define LPAIF_I2SCTL_MODE_8CH_2 19 -#define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00 -#define LPAIF_I2SCTL_SPKMODE_SHIFT 10 -#define LPAIF_I2SCTL_SPKMODE(mode) ((mode) << LPAIF_I2SCTL_SPKMODE_SHIFT) +#define LPAIF_I2SCTL_SPKMODE(mode) mode -#define LPAIF_I2SCTL_SPKMONO_MASK 0x0200 -#define LPAIF_I2SCTL_SPKMONO_SHIFT 9 -#define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) -#define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) +#define LPAIF_I2SCTL_SPKMONO_STEREO 0 +#define LPAIF_I2SCTL_SPKMONO_MONO 1 -#define LPAIF_I2SCTL_MICEN_MASK GENMASK(8, 8) -#define LPAIF_I2SCTL_MICEN_SHIFT 8 -#define LPAIF_I2SCTL_MICEN_DISABLE (0 << LPAIF_I2SCTL_MICEN_SHIFT) -#define LPAIF_I2SCTL_MICEN_ENABLE (1 << LPAIF_I2SCTL_MICEN_SHIFT) +#define LPAIF_I2SCTL_MICEN_DISABLE 0 +#define LPAIF_I2SCTL_MICEN_ENABLE 1 -#define LPAIF_I2SCTL_MICMODE_MASK GENMASK(7, 4) -#define LPAIF_I2SCTL_MICMODE_SHIFT 4 -#define LPAIF_I2SCTL_MICMODE(mode) ((mode) << LPAIF_I2SCTL_MICMODE_SHIFT) +#define LPAIF_I2SCTL_MICMODE(mode) mode -#define LPAIF_I2SCTL_MIMONO_MASK GENMASK(3, 3) -#define LPAIF_I2SCTL_MICMONO_SHIFT 3 -#define LPAIF_I2SCTL_MICMONO_STEREO (0 << LPAIF_I2SCTL_MICMONO_SHIFT) -#define LPAIF_I2SCTL_MICMONO_MONO (1 << LPAIF_I2SCTL_MICMONO_SHIFT) +#define LPAIF_I2SCTL_MICMONO_STEREO 0 +#define LPAIF_I2SCTL_MICMONO_MONO 1 -#define LPAIF_I2SCTL_WSSRC_MASK 0x0004 -#define LPAIF_I2SCTL_WSSRC_SHIFT 2 -#define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) -#define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT) +#define LPAIF_I2SCTL_WSSRC_INTERNAL 0 +#define LPAIF_I2SCTL_WSSRC_EXTERNAL 1 -#define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003 -#define LPAIF_I2SCTL_BITWIDTH_SHIFT 0 -#define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT) -#define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT) -#define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) +#define LPAIF_I2SCTL_BITWIDTH_16 0 +#define LPAIF_I2SCTL_BITWIDTH_24 1 +#define LPAIF_I2SCTL_BITWIDTH_32 2 /* LPAIF IRQ */ #define LPAIF_IRQ_REG_ADDR(v, addr, port) \ @@ -121,42 +115,59 @@ #define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER) #define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) -#define LPAIF_DMACTL_BURSTEN_MASK 0x800 -#define LPAIF_DMACTL_BURSTEN_SHIFT 11 -#define LPAIF_DMACTL_BURSTEN_SINGLE (0 << LPAIF_DMACTL_BURSTEN_SHIFT) -#define LPAIF_DMACTL_BURSTEN_INCR4 (1 << LPAIF_DMACTL_BURSTEN_SHIFT) - -#define LPAIF_DMACTL_WPSCNT_MASK 0x700 -#define LPAIF_DMACTL_WPSCNT_SHIFT 8 -#define LPAIF_DMACTL_WPSCNT_ONE (0 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_TWO (1 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_THREE (2 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_FOUR (3 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_SIX (5 << LPAIF_DMACTL_WPSCNT_SHIFT) -#define LPAIF_DMACTL_WPSCNT_EIGHT (7 << LPAIF_DMACTL_WPSCNT_SHIFT) - -#define LPAIF_DMACTL_AUDINTF_MASK 0x0F0 -#define LPAIF_DMACTL_AUDINTF_SHIFT 4 -#define LPAIF_DMACTL_AUDINTF(id) (id << LPAIF_DMACTL_AUDINTF_SHIFT) - -#define LPAIF_DMACTL_FIFOWM_MASK 0x00E -#define LPAIF_DMACTL_FIFOWM_SHIFT 1 -#define LPAIF_DMACTL_FIFOWM_1 (0 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_2 (1 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_3 (2 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_4 (3 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_5 (4 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_6 (5 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_7 (6 << LPAIF_DMACTL_FIFOWM_SHIFT) -#define LPAIF_DMACTL_FIFOWM_8 (7 << LPAIF_DMACTL_FIFOWM_SHIFT) - -#define LPAIF_DMACTL_ENABLE_MASK 0x1 -#define LPAIF_DMACTL_ENABLE_SHIFT 0 -#define LPAIF_DMACTL_ENABLE_OFF (0 << LPAIF_DMACTL_ENABLE_SHIFT) -#define LPAIF_DMACTL_ENABLE_ON (1 << LPAIF_DMACTL_ENABLE_SHIFT) - -#define LPAIF_DMACTL_DYNCLK_MASK BIT(12) -#define LPAIF_DMACTL_DYNCLK_SHIFT 12 -#define LPAIF_DMACTL_DYNCLK_OFF (0 << LPAIF_DMACTL_DYNCLK_SHIFT) -#define LPAIF_DMACTL_DYNCLK_ON (1 << LPAIF_DMACTL_DYNCLK_SHIFT) +#define LPAIF_DMACTL_BURSTEN_SINGLE 0 +#define LPAIF_DMACTL_BURSTEN_INCR4 1 + +#define LPAIF_DMACTL_WPSCNT_ONE 0 +#define LPAIF_DMACTL_WPSCNT_TWO 1 +#define LPAIF_DMACTL_WPSCNT_THREE 2 +#define LPAIF_DMACTL_WPSCNT_FOUR 3 +#define LPAIF_DMACTL_WPSCNT_SIX 5 +#define LPAIF_DMACTL_WPSCNT_EIGHT 7 +#define LPAIF_DMACTL_WPSCNT_TEN 9 +#define LPAIF_DMACTL_WPSCNT_TWELVE 11 +#define LPAIF_DMACTL_WPSCNT_FOURTEEN 13 +#define LPAIF_DMACTL_WPSCNT_SIXTEEN 15 + +#define LPAIF_DMACTL_AUDINTF(id) id + +#define LPAIF_DMACTL_FIFOWM_1 0 +#define LPAIF_DMACTL_FIFOWM_2 1 +#define LPAIF_DMACTL_FIFOWM_3 2 +#define LPAIF_DMACTL_FIFOWM_4 3 +#define LPAIF_DMACTL_FIFOWM_5 4 +#define LPAIF_DMACTL_FIFOWM_6 5 +#define LPAIF_DMACTL_FIFOWM_7 6 +#define LPAIF_DMACTL_FIFOWM_8 7 +#define LPAIF_DMACTL_FIFOWM_9 8 +#define LPAIF_DMACTL_FIFOWM_10 9 +#define LPAIF_DMACTL_FIFOWM_11 10 +#define LPAIF_DMACTL_FIFOWM_12 11 +#define LPAIF_DMACTL_FIFOWM_13 12 +#define LPAIF_DMACTL_FIFOWM_14 13 +#define LPAIF_DMACTL_FIFOWM_15 14 +#define LPAIF_DMACTL_FIFOWM_16 15 +#define LPAIF_DMACTL_FIFOWM_17 16 +#define LPAIF_DMACTL_FIFOWM_18 17 +#define LPAIF_DMACTL_FIFOWM_19 18 +#define LPAIF_DMACTL_FIFOWM_20 19 +#define LPAIF_DMACTL_FIFOWM_21 20 +#define LPAIF_DMACTL_FIFOWM_22 21 +#define LPAIF_DMACTL_FIFOWM_23 22 +#define LPAIF_DMACTL_FIFOWM_24 23 +#define LPAIF_DMACTL_FIFOWM_25 24 +#define LPAIF_DMACTL_FIFOWM_26 25 +#define LPAIF_DMACTL_FIFOWM_27 26 +#define LPAIF_DMACTL_FIFOWM_28 27 +#define LPAIF_DMACTL_FIFOWM_29 28 +#define LPAIF_DMACTL_FIFOWM_30 29 +#define LPAIF_DMACTL_FIFOWM_31 30 +#define LPAIF_DMACTL_FIFOWM_32 31 + +#define LPAIF_DMACTL_ENABLE_OFF 0 +#define LPAIF_DMACTL_ENABLE_ON 1 + +#define LPAIF_DMACTL_DYNCLK_OFF 0 +#define LPAIF_DMACTL_DYNCLK_ON 1 + #endif /* __LPASS_LPAIF_REG_H__ */ diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 315feda414d9..35aead1bfdf8 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -50,6 +50,53 @@ static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { .fifo_size = 0, }; +static int lpass_platform_alloc_dmactl_fields(struct device *dev, + struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + struct lpaif_dmactl *rd_dmactl, *wr_dmactl; + + drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), + GFP_KERNEL); + if (drvdata->rd_dmactl == NULL) + return -ENOMEM; + + drvdata->wr_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), + GFP_KERNEL); + if (drvdata->wr_dmactl == NULL) + return -ENOMEM; + + rd_dmactl = drvdata->rd_dmactl; + wr_dmactl = drvdata->wr_dmactl; + + rd_dmactl->bursten = devm_regmap_field_alloc(dev, map, v->rdma_bursten); + rd_dmactl->wpscnt = devm_regmap_field_alloc(dev, map, v->rdma_wpscnt); + rd_dmactl->fifowm = devm_regmap_field_alloc(dev, map, v->rdma_fifowm); + rd_dmactl->intf = devm_regmap_field_alloc(dev, map, v->rdma_intf); + rd_dmactl->enable = devm_regmap_field_alloc(dev, map, v->rdma_enable); + rd_dmactl->dyncclk = devm_regmap_field_alloc(dev, map, v->rdma_dyncclk); + + if (IS_ERR(rd_dmactl->bursten) || IS_ERR(rd_dmactl->wpscnt) || + IS_ERR(rd_dmactl->fifowm) || IS_ERR(rd_dmactl->intf) || + IS_ERR(rd_dmactl->enable) || IS_ERR(rd_dmactl->dyncclk)) + return -EINVAL; + + wr_dmactl->bursten = devm_regmap_field_alloc(dev, map, v->wrdma_bursten); + wr_dmactl->wpscnt = devm_regmap_field_alloc(dev, map, v->wrdma_wpscnt); + wr_dmactl->fifowm = devm_regmap_field_alloc(dev, map, v->wrdma_fifowm); + wr_dmactl->intf = devm_regmap_field_alloc(dev, map, v->wrdma_intf); + wr_dmactl->enable = devm_regmap_field_alloc(dev, map, v->wrdma_enable); + wr_dmactl->dyncclk = devm_regmap_field_alloc(dev, map, v->wrdma_dyncclk); + + if (IS_ERR(wr_dmactl->bursten) || IS_ERR(wr_dmactl->wpscnt) || + IS_ERR(wr_dmactl->fifowm) || IS_ERR(wr_dmactl->intf) || + IS_ERR(wr_dmactl->enable) || IS_ERR(wr_dmactl->dyncclk)) + return -EINVAL; + + return 0; +} + static int lpass_platform_pcmops_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -134,11 +181,18 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, snd_pcm_format_t format = params_format(params); unsigned int channels = params_channels(params); unsigned int regval; - int ch, dir = substream->stream; + struct lpaif_dmactl *dmactl; + int id, dir = substream->stream; int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; - ch = pcm_data->dma_ch; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + dmactl = drvdata->rd_dmactl; + id = pcm_data->dma_ch; + } else { + dmactl = drvdata->wr_dmactl; + id = pcm_data->dma_ch - v->wrdma_channel_start; + } bitwidth = snd_pcm_format_width(format); if (bitwidth < 0) { @@ -147,25 +201,39 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, return bitwidth; } - regval = LPAIF_DMACTL_BURSTEN_INCR4 | - LPAIF_DMACTL_AUDINTF(dma_port) | - LPAIF_DMACTL_FIFOWM_8; + ret = regmap_fields_write(dmactl->bursten, id, LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating bursten field: %d\n", ret); + return ret; + } + + regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8); + if (ret) { + dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret); + return ret; + } + + regmap_fields_write(dmactl->intf, id, LPAIF_DMACTL_AUDINTF(dma_port)); + if (ret) { + dev_err(soc_runtime->dev, "error updating audintf field: %d\n", ret); + return ret; + } switch (bitwidth) { case 16: switch (channels) { case 1: case 2: - regval |= LPAIF_DMACTL_WPSCNT_ONE; + regval = LPAIF_DMACTL_WPSCNT_ONE; break; case 4: - regval |= LPAIF_DMACTL_WPSCNT_TWO; + regval = LPAIF_DMACTL_WPSCNT_TWO; break; case 6: - regval |= LPAIF_DMACTL_WPSCNT_THREE; + regval = LPAIF_DMACTL_WPSCNT_THREE; break; case 8: - regval |= LPAIF_DMACTL_WPSCNT_FOUR; + regval = LPAIF_DMACTL_WPSCNT_FOUR; break; default: dev_err(soc_runtime->dev, @@ -178,19 +246,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, case 32: switch (channels) { case 1: - regval |= LPAIF_DMACTL_WPSCNT_ONE; + regval = LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval |= LPAIF_DMACTL_WPSCNT_TWO; + regval = LPAIF_DMACTL_WPSCNT_TWO; break; case 4: - regval |= LPAIF_DMACTL_WPSCNT_FOUR; + regval = LPAIF_DMACTL_WPSCNT_FOUR; break; case 6: - regval |= LPAIF_DMACTL_WPSCNT_SIX; + regval = LPAIF_DMACTL_WPSCNT_SIX; break; case 8: - regval |= LPAIF_DMACTL_WPSCNT_EIGHT; + regval = LPAIF_DMACTL_WPSCNT_EIGHT; break; default: dev_err(soc_runtime->dev, @@ -205,10 +273,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, return -EINVAL; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), regval); + ret = regmap_fields_write(dmactl->wpscnt, id, regval); if (ret) { - dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", + dev_err(soc_runtime->dev, "error writing to dmactl reg: %d\n", ret); return ret; } @@ -245,9 +312,17 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; - int ret, ch, dir = substream->stream; + struct lpaif_dmactl *dmactl; + int ret, id, ch, dir = substream->stream; ch = pcm_data->dma_ch; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + dmactl = drvdata->rd_dmactl; + id = pcm_data->dma_ch; + } else { + dmactl = drvdata->wr_dmactl; + id = pcm_data->dma_ch - v->wrdma_channel_start; + } ret = regmap_write(drvdata->lpaif_map, LPAIF_DMABASE_REG(v, ch, dir), @@ -276,9 +351,7 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, return ret; } - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), - LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON); + ret = regmap_fields_write(dmactl->enable, id, LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); @@ -297,9 +370,18 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; - int ret, ch, dir = substream->stream; + struct lpaif_dmactl *dmactl; + int ret, ch, id; + int dir = substream->stream; ch = pcm_data->dma_ch; + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + dmactl = drvdata->rd_dmactl; + id = pcm_data->dma_ch; + } else { + dmactl = drvdata->wr_dmactl; + id = pcm_data->dma_ch - v->wrdma_channel_start; + } switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -325,10 +407,8 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, return ret; } - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), - LPAIF_DMACTL_ENABLE_MASK, - LPAIF_DMACTL_ENABLE_ON); + ret = regmap_fields_write(dmactl->enable, id, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); @@ -338,10 +418,8 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, ch, dir), - LPAIF_DMACTL_ENABLE_MASK, - LPAIF_DMACTL_ENABLE_OFF); + ret = regmap_fields_write(dmactl->enable, id, + LPAIF_DMACTL_ENABLE_OFF); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); @@ -580,6 +658,13 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) return ret; } + ret = lpass_platform_alloc_dmactl_fields(&pdev->dev, + drvdata->lpaif_map); + if (ret) { + dev_err(&pdev->dev, + "error initializing dmactl fields: %d\n", ret); + return ret; + } return devm_snd_soc_register_component(&pdev->dev, &lpass_component_driver, NULL, 0); diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 450020e5f302..51c9991a0edf 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -17,6 +17,28 @@ #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) +struct lpaif_i2sctl { + struct regmap_field *loopback; + struct regmap_field *spken; + struct regmap_field *spkmode; + struct regmap_field *spkmono; + struct regmap_field *micen; + struct regmap_field *micmode; + struct regmap_field *micmono; + struct regmap_field *wssrc; + struct regmap_field *bitwidth; +}; + + +struct lpaif_dmactl { + struct regmap_field *bursten; + struct regmap_field *wpscnt; + struct regmap_field *intf; + struct regmap_field *fifowm; + struct regmap_field *enable; + struct regmap_field *dyncclk; +}; + /* Both the CPU DAI and platform drivers will access this data */ struct lpass_data { @@ -55,6 +77,10 @@ struct lpass_data { struct clk_bulk_data *clks; int num_clks; + /* Regmap fields of I2SCTL & DMACTL registers bitfields */ + struct lpaif_i2sctl *i2sctl; + struct lpaif_dmactl *rd_dmactl; + struct lpaif_dmactl *wr_dmactl; }; /* Vairant data per each SOC */ @@ -72,6 +98,33 @@ struct lpass_variant { u32 wrdma_reg_stride; u32 wrdma_channels; + /* I2SCTL Register fields */ + struct reg_field loopback; + struct reg_field spken; + struct reg_field spkmode; + struct reg_field spkmono; + struct reg_field micen; + struct reg_field micmode; + struct reg_field micmono; + struct reg_field wssrc; + struct reg_field bitwidth; + + /* RD_DMA Register fields */ + struct reg_field rdma_bursten; + struct reg_field rdma_wpscnt; + struct reg_field rdma_intf; + struct reg_field rdma_fifowm; + struct reg_field rdma_enable; + struct reg_field rdma_dyncclk; + + /* WR_DMA Register fields */ + struct reg_field wrdma_bursten; + struct reg_field wrdma_wpscnt; + struct reg_field wrdma_intf; + struct reg_field wrdma_fifowm; + struct reg_field wrdma_enable; + struct reg_field wrdma_dyncclk; + /** * on SOCs like APQ8016 the channel control bits start * at different offset to ipq806x -- cgit From 753a6e17942f6f425ca622e1610625998312ad89 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Aug 2020 16:23:02 +0530 Subject: ASoC: qcom: lpass-cpu: fix concurrency issue i2sctl register value is set to 0 during hw_free(). This impacts any ongoing concurrent session on the same i2s port. As trigger() stop already resets enable bit to 0, there is no need of explicit hw_free. Removing it to fix the issue. Fixes: 80beab8e1d86 ("ASoC: qcom: Add LPASS CPU DAI driver") Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-7-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 6b86f16b6ee5..5d84f63261a1 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -266,21 +266,6 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, return 0; } -static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, - struct snd_soc_dai *dai) -{ - struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); - int ret; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id), - 0); - if (ret) - dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); - - return ret; -} - static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -350,7 +335,6 @@ const struct snd_soc_dai_ops asoc_qcom_lpass_cpu_dai_ops = { .startup = lpass_cpu_daiops_startup, .shutdown = lpass_cpu_daiops_shutdown, .hw_params = lpass_cpu_daiops_hw_params, - .hw_free = lpass_cpu_daiops_hw_free, .prepare = lpass_cpu_daiops_prepare, .trigger = lpass_cpu_daiops_trigger, }; -- cgit From 24caf8d9eb108c52e144bcc7af94bb1edcb70700 Mon Sep 17 00:00:00 2001 From: Ajit Pandey Date: Fri, 14 Aug 2020 16:23:05 +0530 Subject: ASoC: qcom: lpass-sc7180: Add platform driver for lpass audio Add platform driver for configuring sc7180 lpass core I2S and DMA configuration to support playback & capture to external codecs connected over primary & secondary MI2S interfaces. Signed-off-by: Ajit Pandey Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-10-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/lpass-sc7180.c | 216 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 223 insertions(+) create mode 100644 sound/soc/qcom/lpass-sc7180.c (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 5d6b2466a2f2..b47d2bdd2a50 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -24,6 +24,11 @@ config SND_SOC_LPASS_APQ8016 select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM +config SND_SOC_LPASS_SC7180 + tristate + select SND_SOC_LPASS_CPU + select SND_SOC_LPASS_PLATFORM + config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" depends on SND_SOC_QCOM diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 41b2c7a23a4d..7972c9479ab0 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -4,11 +4,13 @@ snd-soc-lpass-cpu-objs := lpass-cpu.o snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o snd-soc-lpass-apq8016-objs := lpass-apq8016.o +snd-soc-lpass-sc7180-objs := lpass-sc7180.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o +obj-$(CONFIG_SND_SOC_LPASS_SC7180) += snd-soc-lpass-sc7180.o # Machine snd-soc-storm-objs := storm.o diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c new file mode 100644 index 000000000000..167bf2cbc556 --- /dev/null +++ b/sound/soc/qcom/lpass-sc7180.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * + * lpass-sc7180.c -- ALSA SoC platform-machine driver for QTi LPASS + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { + [MI2S_PRIMARY] = { + .id = MI2S_PRIMARY, + .name = "Primary MI2S", + .playback = { + .stream_name = "Primary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .stream_name = "Primary Capture", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, + + [MI2S_SECONDARY] = { + .id = MI2S_SECONDARY, + .name = "Secondary MI2S", + .playback = { + .stream_name = "Secondary Playback", + .formats = SNDRV_PCM_FMTBIT_S16, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .probe = &asoc_qcom_lpass_cpu_dai_probe, + .ops = &asoc_qcom_lpass_cpu_dai_ops, + }, +}; + +static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata, + int direction) +{ + struct lpass_variant *v = drvdata->variant; + int chan = 0; + + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, + v->rdma_channels); + + if (chan >= v->rdma_channels) + return -EBUSY; + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + v->wrdma_channel_start + + v->wrdma_channels, + v->wrdma_channel_start); + + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } + + set_bit(chan, &drvdata->dma_ch_bit_map); + + return chan; +} + +static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +{ + clear_bit(chan, &drvdata->dma_ch_bit_map); + + return 0; +} + +static int sc7180_lpass_init(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + struct lpass_variant *variant = drvdata->variant; + struct device *dev = &pdev->dev; + int ret, i; + + drvdata->clks = devm_kcalloc(dev, variant->num_clks, + sizeof(*drvdata->clks), GFP_KERNEL); + drvdata->num_clks = variant->num_clks; + + for (i = 0; i < drvdata->num_clks; i++) + drvdata->clks[i].id = variant->clk_name[i]; + + ret = devm_clk_bulk_get(dev, drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "Failed to get clocks %d\n", ret); + return ret; + } + + ret = clk_bulk_prepare_enable(drvdata->num_clks, drvdata->clks); + if (ret) { + dev_err(dev, "sc7180 clk_enable failed\n"); + return ret; + } + + return 0; +} + +static int sc7180_lpass_exit(struct platform_device *pdev) +{ + struct lpass_data *drvdata = platform_get_drvdata(pdev); + + clk_bulk_disable_unprepare(drvdata->num_clks, drvdata->clks); + + return 0; +} + +static struct lpass_variant sc7180_data = { + .i2sctrl_reg_base = 0x1000, + .i2sctrl_reg_stride = 0x1000, + .i2s_ports = 3, + .irq_reg_base = 0x9000, + .irq_reg_stride = 0x1000, + .irq_ports = 3, + .rdma_reg_base = 0xC000, + .rdma_reg_stride = 0x1000, + .rdma_channels = 5, + .dmactl_audif_start = 1, + .wrdma_reg_base = 0x18000, + .wrdma_reg_stride = 0x1000, + .wrdma_channel_start = 5, + .wrdma_channels = 4, + + .loopback = REG_FIELD_ID(0x1000, 17, 17, 3, 0x1000), + .spken = REG_FIELD_ID(0x1000, 16, 16, 3, 0x1000), + .spkmode = REG_FIELD_ID(0x1000, 11, 15, 3, 0x1000), + .spkmono = REG_FIELD_ID(0x1000, 10, 10, 3, 0x1000), + .micen = REG_FIELD_ID(0x1000, 9, 9, 3, 0x1000), + .micmode = REG_FIELD_ID(0x1000, 4, 8, 3, 0x1000), + .micmono = REG_FIELD_ID(0x1000, 3, 3, 3, 0x1000), + .wssrc = REG_FIELD_ID(0x1000, 2, 2, 3, 0x1000), + .bitwidth = REG_FIELD_ID(0x1000, 0, 0, 3, 0x1000), + + .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000), + .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000), + .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000), + .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000), + .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000), + .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000), + + .wrdma_dyncclk = REG_FIELD_ID(0x18000, 22, 22, 4, 0x1000), + .wrdma_bursten = REG_FIELD_ID(0x18000, 21, 21, 4, 0x1000), + .wrdma_wpscnt = REG_FIELD_ID(0x18000, 17, 20, 4, 0x1000), + .wrdma_intf = REG_FIELD_ID(0x18000, 12, 16, 4, 0x1000), + .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000), + .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000), + + .clk_name = (const char*[]) { + "pcnoc-sway-clk", + "audio-core", + "pcnoc-mport-clk", + }, + .num_clks = 3, + .dai_driver = sc7180_lpass_cpu_dai_driver, + .num_dai = ARRAY_SIZE(sc7180_lpass_cpu_dai_driver), + .dai_osr_clk_names = (const char *[]) { + "mclk0", + "null", + }, + .dai_bit_clk_names = (const char *[]) { + "mi2s-bit-clk0", + "mi2s-bit-clk1", + }, + .init = sc7180_lpass_init, + .exit = sc7180_lpass_exit, + .alloc_dma_channel = sc7180_lpass_alloc_dma_channel, + .free_dma_channel = sc7180_lpass_free_dma_channel, +}; + +static const struct of_device_id sc7180_lpass_cpu_device_id[] = { + {.compatible = "qcom,sc7180-lpass-cpu", .data = &sc7180_data}, + {} +}; + +static struct platform_driver sc7180_lpass_cpu_platform_driver = { + .driver = { + .name = "sc7180-lpass-cpu", + .of_match_table = of_match_ptr(sc7180_lpass_cpu_device_id), + }, + .probe = asoc_qcom_lpass_cpu_platform_probe, + .remove = asoc_qcom_lpass_cpu_platform_remove, +}; + +module_platform_driver(sc7180_lpass_cpu_platform_driver); + +MODULE_DESCRIPTION("SC7180 LPASS CPU DRIVER"); +MODULE_LICENSE("GPL v2"); -- cgit From 93dbbd657167a796583506834cc03950ce25bce1 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Aug 2020 16:23:06 +0530 Subject: ASoC: qcom: lpass-cpu: Use platform_get_resource platform_get_resource_byname() is used when there is list of reg entries. As lpass-cpu node has only one reg entry, use platform_get_resource() instead. Signed-off-by: Rohit kumar Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1597402388-14112-11-git-send-email-rohitkr@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 5d84f63261a1..1ee6d8b3f550 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -575,7 +575,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) of_lpass_cpu_parse_dai_data(dev, drvdata); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); drvdata->lpaif = devm_ioremap_resource(dev, res); if (IS_ERR((void const __force *)drvdata->lpaif)) { -- cgit From b05372c84d61ad2a905274db7e3d63a65c835463 Mon Sep 17 00:00:00 2001 From: Rohit kumar Date: Fri, 14 Aug 2020 16:23:07 +0530 Subject: ASoC: qcom: lpass-platform: Use platform_get_irq platform_get_irq_byname() is used when there is list of interrupts in the device node. As lpass-platform has only one interrupt entry, use platform_get_irq() instead. Signed-off-by: Rohit kumar Link: https://lore.kernel.org/r/1597402388-14112-12-git-send-email-rohitkr@codeaurora.org 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 35aead1bfdf8..df692ed95503 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -638,7 +638,7 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) struct lpass_variant *v = drvdata->variant; int ret; - drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); + drvdata->lpaif_irq = platform_get_irq(pdev, 0); if (drvdata->lpaif_irq < 0) return -ENODEV; -- cgit From 94741eba63c23b0f1527b0ae0125e6b553bde10e Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 5 Aug 2020 14:34:11 +0800 Subject: ASoC: fsl_sai: Refine enable/disable TE/RE sequence in trigger() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current code enables TCSR.TE and RCSR.RE together, and disable TCSR.TE and RCSR.RE together in trigger(), which only supports one operation mode: 1. Rx synchronous with Tx: TE is last enabled and first disabled Other operation mode need to be considered also: 2. Tx synchronous with Rx: RE is last enabled and first disabled. 3. Asynchronous mode: Tx and Rx are independent. So the enable TCSR.TE and RCSR.RE sequence and the disable sequence need to be refined accordingly for #2 and #3. There is slightly against what RM recommennds with this change. For example in Rx synchronous with Tx mode, case "aplay 1.wav; arecord 2.wav" enable TE before RE. But it should be safe to do so, judging by years of testing results. Signed-off-by: Shengjiu Wang Reviewed-by: Nicolin Chen Link: https://lore.kernel.org/r/20200805063413.4610-2-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 126 ++++++++++++++++++++++++++++++++---------------- 1 file changed, 85 insertions(+), 41 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index cdff739924e2..566c4747362a 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -37,6 +37,24 @@ static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = { .list = fsl_sai_rates, }; +/** + * fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream + * + * SAI supports synchronous mode using bit/frame clocks of either Transmitter's + * or Receiver's for both streams. This function is used to check if clocks of + * the stream's are synced by the opposite stream. + * + * @sai: SAI context + * @dir: stream direction + */ +static inline bool fsl_sai_dir_is_synced(struct fsl_sai *sai, int dir) +{ + int adir = (dir == TX) ? RX : TX; + + /* current dir in async mode while opposite dir in sync mode */ + return !sai->synchronous[dir] && sai->synchronous[adir]; +} + static irqreturn_t fsl_sai_isr(int irq, void *devid) { struct fsl_sai *sai = (struct fsl_sai *)devid; @@ -522,6 +540,38 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, return 0; } +static void fsl_sai_config_disable(struct fsl_sai *sai, int dir) +{ + unsigned int ofs = sai->soc_data->reg_offset; + bool tx = dir == TX; + u32 xcsr, count = 100; + + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), + FSL_SAI_CSR_TERE, 0); + + /* TERE will remain set till the end of current frame */ + do { + udelay(10); + regmap_read(sai->regmap, FSL_SAI_xCSR(tx, ofs), &xcsr); + } while (--count && xcsr & FSL_SAI_CSR_TERE); + + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), + FSL_SAI_CSR_FR, FSL_SAI_CSR_FR); + + /* + * For sai master mode, after several open/close sai, + * there will be no frame clock, and can't recover + * anymore. Add software reset to fix this issue. + * This is a hardware bug, and will be fix in the + * next sai version. + */ + if (!sai->is_slave_mode) { + /* Software Reset */ + regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR); + /* Clear SR bit to finish the reset */ + regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0); + } +} static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) @@ -530,7 +580,9 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, unsigned int ofs = sai->soc_data->reg_offset; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - u32 xcsr, count = 100; + int adir = tx ? RX : TX; + int dir = tx ? TX : RX; + u32 xcsr; /* * Asynchronous mode: Clear SYNC for both Tx and Rx. @@ -553,10 +605,22 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_FRDE, FSL_SAI_CSR_FRDE); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), - FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); - regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), + regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); + /* + * Enable the opposite direction for synchronous mode + * 1. Tx sync with Rx: only set RE for Rx; set TE & RE for Tx + * 2. Rx sync with Tx: only set TE for Tx; set RE & TE for Rx + * + * RM recommends to enable RE after TE for case 1 and to enable + * TE after RE for case 2, but we here may not always guarantee + * that happens: "arecord 1.wav; aplay 2.wav" in case 1 enables + * TE after RE, which is against what RM recommends but should + * be safe to do, judging by years of testing results. + */ + if (fsl_sai_dir_is_synced(sai, adir)) + regmap_update_bits(sai->regmap, FSL_SAI_xCSR((!tx), ofs), + FSL_SAI_CSR_TERE, FSL_SAI_CSR_TERE); regmap_update_bits(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_xIE_MASK, FSL_SAI_FLAGS); @@ -571,43 +635,23 @@ static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd, /* Check if the opposite FRDE is also disabled */ regmap_read(sai->regmap, FSL_SAI_xCSR(!tx, ofs), &xcsr); - if (!(xcsr & FSL_SAI_CSR_FRDE)) { - /* Disable both directions and reset their FIFOs */ - regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), - FSL_SAI_CSR_TERE, 0); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), - FSL_SAI_CSR_TERE, 0); - - /* TERE will remain set till the end of current frame */ - do { - udelay(10); - regmap_read(sai->regmap, - FSL_SAI_xCSR(tx, ofs), &xcsr); - } while (--count && xcsr & FSL_SAI_CSR_TERE); - - regmap_update_bits(sai->regmap, FSL_SAI_TCSR(ofs), - FSL_SAI_CSR_FR, FSL_SAI_CSR_FR); - regmap_update_bits(sai->regmap, FSL_SAI_RCSR(ofs), - FSL_SAI_CSR_FR, FSL_SAI_CSR_FR); - - /* - * For sai master mode, after several open/close sai, - * there will be no frame clock, and can't recover - * anymore. Add software reset to fix this issue. - * This is a hardware bug, and will be fix in the - * next sai version. - */ - if (!sai->is_slave_mode) { - /* Software Reset for both Tx and Rx */ - regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), - FSL_SAI_CSR_SR); - regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), - FSL_SAI_CSR_SR); - /* Clear SR bit to finish the reset */ - regmap_write(sai->regmap, FSL_SAI_TCSR(ofs), 0); - regmap_write(sai->regmap, FSL_SAI_RCSR(ofs), 0); - } - } + + /* + * If opposite stream provides clocks for synchronous mode and + * it is inactive, disable it before disabling the current one + */ + if (fsl_sai_dir_is_synced(sai, adir) && !(xcsr & FSL_SAI_CSR_FRDE)) + fsl_sai_config_disable(sai, adir); + + /* + * Disable current stream if either of: + * 1. current stream doesn't provide clocks for synchronous mode + * 2. current stream provides clocks for synchronous mode but no + * more stream is active. + */ + if (!fsl_sai_dir_is_synced(sai, dir) || !(xcsr & FSL_SAI_CSR_FRDE)) + fsl_sai_config_disable(sai, dir); + break; default: return -EINVAL; -- cgit From 7b3bee091ec375777ade2a37e4b0c9319f92de27 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 5 Aug 2020 14:34:12 +0800 Subject: ASoC: fsl_sai: Drop TMR/RMR settings for synchronous mode Tx synchronous with Rx: The RMR is the word mask register, it is used to mask any word in the frame, it is not relating to clock generation, So it is no need to be changed when Tx is going to be enabled. Rx synchronous with Tx: The TMR is the word mask register, it is used to mask any word in the frame, it is not relating to clock generation, So it is no need to be changed when Rx is going to be enabled. Signed-off-by: Shengjiu Wang Reviewed-by: Nicolin Chen Link: https://lore.kernel.org/r/20200805063413.4610-3-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 566c4747362a..334090d581ae 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -488,8 +488,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, /* * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), - * RCR5(TCR5) and RMR(TMR) for playback(capture), or there will be sync - * error. + * RCR5(TCR5) for playback(capture), or there will be sync error. */ if (!sai->is_slave_mode) { @@ -500,8 +499,6 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_TCR5(ofs), FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | FSL_SAI_CR5_FBT_MASK, val_cr5); - regmap_write(sai->regmap, FSL_SAI_TMR, - ~0UL - ((1 << channels) - 1)); } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) { regmap_update_bits(sai->regmap, FSL_SAI_RCR4(ofs), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, @@ -509,8 +506,6 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(sai->regmap, FSL_SAI_RCR5(ofs), FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | FSL_SAI_CR5_FBT_MASK, val_cr5); - regmap_write(sai->regmap, FSL_SAI_RMR, - ~0UL - ((1 << channels) - 1)); } } -- cgit From 9355a7b1896f6fadcbd63d199d1f343bf2e4fed8 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Wed, 5 Aug 2020 14:34:13 +0800 Subject: ASoC: fsl_sai: Replace synchronous check with fsl_sai_dir_is_synced As new function fsl_sai_dir_is_synced is included for checking if stream is synced by the opposite stream, then replace the existing synchronous checking with this new function. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/20200805063413.4610-4-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 334090d581ae..f6969a5d49e3 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -350,6 +350,8 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) unsigned int ofs = sai->soc_data->reg_offset; unsigned long clk_rate; u32 savediv = 0, ratio, savesub = freq; + int adir = tx ? RX : TX; + int dir = tx ? TX : RX; u32 id; int ret = 0; @@ -408,19 +410,17 @@ static int fsl_sai_set_bclk(struct snd_soc_dai *dai, bool tx, u32 freq) * 4) For Tx and Rx are both Synchronous with another SAI, we just * ignore it. */ - if ((sai->synchronous[TX] && !sai->synchronous[RX]) || - (!tx && !sai->synchronous[RX])) { - regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), + if (fsl_sai_dir_is_synced(sai, adir)) { + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - regmap_update_bits(sai->regmap, FSL_SAI_RCR2(ofs), + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(!tx, ofs), FSL_SAI_CR2_DIV_MASK, savediv - 1); - } else if ((sai->synchronous[RX] && !sai->synchronous[TX]) || - (tx && !sai->synchronous[TX])) { - regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), + } else if (!sai->synchronous[dir]) { + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), FSL_SAI_CR2_MSEL_MASK, FSL_SAI_CR2_MSEL(sai->mclk_id[tx])); - regmap_update_bits(sai->regmap, FSL_SAI_TCR2(ofs), + regmap_update_bits(sai->regmap, FSL_SAI_xCR2(tx, ofs), FSL_SAI_CR2_DIV_MASK, savediv - 1); } @@ -442,6 +442,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, u32 val_cr4 = 0, val_cr5 = 0; u32 slots = (channels == 1) ? 2 : channels; u32 slot_width = word_width; + int adir = tx ? RX : TX; int ret; if (sai->slots) @@ -491,22 +492,13 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, * RCR5(TCR5) for playback(capture), or there will be sync error. */ - if (!sai->is_slave_mode) { - if (!sai->synchronous[TX] && sai->synchronous[RX] && !tx) { - regmap_update_bits(sai->regmap, FSL_SAI_TCR4(ofs), - FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, - val_cr4); - regmap_update_bits(sai->regmap, FSL_SAI_TCR5(ofs), - FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | - FSL_SAI_CR5_FBT_MASK, val_cr5); - } else if (!sai->synchronous[RX] && sai->synchronous[TX] && tx) { - regmap_update_bits(sai->regmap, FSL_SAI_RCR4(ofs), - FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, - val_cr4); - regmap_update_bits(sai->regmap, FSL_SAI_RCR5(ofs), - FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | - FSL_SAI_CR5_FBT_MASK, val_cr5); - } + if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) { + regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs), + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + val_cr4); + regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs), + FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | + FSL_SAI_CR5_FBT_MASK, val_cr5); } regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), -- cgit From 90cac932976e93b17203b4216ba83bdcd68e0ed0 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 20:25:52 -0500 Subject: ASoC: sun8i-codec: Fix DAPM to match the hardware topology The A33/A64 digital codec has 4 physical inputs and 4 physical outputs: 3 AIFs/DAIs and one ADC/DAC pair. Internal routing is accomplished by a 4-channel mixer connected to each output. The analog and digital sides of the ADC/DAC are in separate ASoC components, so card-level DAPM routes (provided in the device tree) are necessary to connect them together. Currently, these routes are wrong. For AIF1 Playback, the correct topology is: ||<<============ sun8i-codec ===========>>|| || || CPU DAI -> AIF1 DA0 -> DAC Mixer -> DAC (digital) -> DAC (analog) || || but the driver and device trees currently describe: || || CPU DAI -> AIF1 DA0 -------------------------------> DAC (analog) || \--> DAC Mixer -> ??? [dead end] || For AIF1 Capture, there is an additional problem, because the Mixer route is backward. The topology should be: || || ADC (analog) -> ADC (digital) -> AIF1 AD0 Mixer -> AIF1 AD0 -> CPU DAI || || but the driver and device trees currently describe: || || ADC (analog) -> AIF1 AD0 ------------------------------------> CPU DAI || \--> ADC Mixer -> ??? [dead end] || The ADC/DAC are only powered because AIF1 AD0 (capture) has supply routes from the ADC, and AIF1 DA0 (playback) has supply routes from the DAC. However, neither set of supply routes matches the hardware topology. Audio can be routed among AIF1/2/3 without using the ADC or DAC at all; and audio can be routed from the ADC to the DAC without using any AIFs (via the "ADC Digital DAC Playback Switch"). Because the DAPM routes are wrong, both of these use cases are currently broken. This commit adds the necessary widgets and routes to represent the real hardware topology, with functionality equivalent to the current driver. For the existing "allwinner,sun8i-a33-codec" compatible, widgets with the old names are kept as wrappers around the new widgets, so existing device trees will continue to work. For "allwinner,sun50i-a64-codec", the old widgets can be omitted, because no device trees yet use that compatible. Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200726012557.38282-3-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 120 +++++++++++++++++++++++++++++++++--------- 1 file changed, 95 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index ca51af114419..ffeac150c086 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -85,10 +86,15 @@ #define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) +struct sun8i_codec_quirks { + bool legacy_widgets : 1; +}; + struct sun8i_codec { - struct regmap *regmap; - struct clk *clk_module; - struct clk *clk_bus; + struct regmap *regmap; + struct clk *clk_module; + struct clk *clk_bus; + const struct sun8i_codec_quirks *quirks; }; static int sun8i_codec_runtime_resume(struct device *dev) @@ -388,22 +394,30 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA, 0, NULL, 0), - /* Analog DAC AIF */ - SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left", "Playback", 0, + /* AIF "DAC" Inputs */ + SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), - SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right", "Playback", 0, + SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0, SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), - /* Analog ADC AIF */ - SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Left ADC", "Capture", 0, + /* AIF "ADC" Outputs */ + SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0, SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0), - SND_SOC_DAPM_AIF_IN("AIF1 Slot 0 Right ADC", "Capture", 0, + SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0, SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0), + /* ADC Inputs (connected to analog codec DAPM context) */ + SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), + + /* DAC Outputs (connected to analog codec DAPM context) */ + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + /* DAC and ADC Mixers */ SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), @@ -449,40 +463,86 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { /* Clock Routes */ { "AIF1", NULL, "SYSCLK AIF1" }, { "AIF1 PLL", NULL, "AIF1" }, - { "RST AIF1", NULL, "AIF1 PLL" }, + { "SYSCLK", NULL, "AIF1 PLL" }, + + { "RST AIF1", NULL, "SYSCLK" }, { "MODCLK AFI1", NULL, "RST AIF1" }, - { "DAC", NULL, "MODCLK AFI1" }, - { "ADC", NULL, "MODCLK AFI1" }, + { "AIF1 AD0L", NULL, "MODCLK AFI1" }, + { "AIF1 AD0R", NULL, "MODCLK AFI1" }, + { "AIF1 DA0L", NULL, "MODCLK AFI1" }, + { "AIF1 DA0R", NULL, "MODCLK AFI1" }, { "RST DAC", NULL, "SYSCLK" }, { "MODCLK DAC", NULL, "RST DAC" }, { "DAC", NULL, "MODCLK DAC" }, + { "DACL", NULL, "DAC" }, + { "DACR", NULL, "DAC" }, { "RST ADC", NULL, "SYSCLK" }, { "MODCLK ADC", NULL, "RST ADC" }, { "ADC", NULL, "MODCLK ADC" }, + { "ADCL", NULL, "ADC" }, + { "ADCR", NULL, "ADC" }, /* DAC Routes */ - { "AIF1 Slot 0 Right", NULL, "DAC" }, - { "AIF1 Slot 0 Left", NULL, "DAC" }, + { "DACL", NULL, "Left Digital DAC Mixer" }, + { "DACR", NULL, "Right Digital DAC Mixer" }, /* DAC Mixer Routes */ - { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", - "AIF1 Slot 0 Left"}, - { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", - "AIF1 Slot 0 Right"}, + { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, + { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, /* ADC Routes */ - { "AIF1 Slot 0 Right ADC", NULL, "ADC" }, - { "AIF1 Slot 0 Left ADC", NULL, "ADC" }, + { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" }, + { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" }, /* ADC Mixer Routes */ - { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", - "AIF1 Slot 0 Left ADC" }, - { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", - "AIF1 Slot 0 Right ADC" }, + { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, + { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, +}; + +static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = { + /* Legacy ADC Inputs (connected to analog codec DAPM context) */ + SND_SOC_DAPM_ADC("AIF1 Slot 0 Left ADC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("AIF1 Slot 0 Right ADC", NULL, SND_SOC_NOPM, 0, 0), + + /* Legacy DAC Outputs (connected to analog codec DAPM context) */ + SND_SOC_DAPM_DAC("AIF1 Slot 0 Left", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("AIF1 Slot 0 Right", NULL, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route sun8i_codec_legacy_routes[] = { + /* Legacy ADC Routes */ + { "ADCL", NULL, "AIF1 Slot 0 Left ADC" }, + { "ADCR", NULL, "AIF1 Slot 0 Right ADC" }, + + /* Legacy DAC Routes */ + { "AIF1 Slot 0 Left", NULL, "DACL" }, + { "AIF1 Slot 0 Right", NULL, "DACR" }, }; +static int sun8i_codec_component_probe(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + struct sun8i_codec *scodec = snd_soc_component_get_drvdata(component); + int ret; + + /* Add widgets for backward compatibility with old device trees. */ + if (scodec->quirks->legacy_widgets) { + ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_legacy_widgets, + ARRAY_SIZE(sun8i_codec_legacy_widgets)); + if (ret) + return ret; + + ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_legacy_routes, + ARRAY_SIZE(sun8i_codec_legacy_routes)); + if (ret) + return ret; + } + + return 0; +} + static const struct snd_soc_dai_ops sun8i_codec_dai_ops = { .hw_params = sun8i_codec_hw_params, .set_fmt = sun8i_set_fmt, @@ -566,6 +626,8 @@ static int sun8i_codec_probe(struct platform_device *pdev) return PTR_ERR(scodec->regmap); } + scodec->quirks = of_device_get_match_data(&pdev->dev); + platform_set_drvdata(pdev, scodec); pm_runtime_enable(&pdev->dev); @@ -603,8 +665,16 @@ static int sun8i_codec_remove(struct platform_device *pdev) return 0; } +static const struct sun8i_codec_quirks sun8i_a33_quirks = { + .legacy_widgets = true, +}; + +static const struct sun8i_codec_quirks sun50i_a64_quirks = { +}; + static const struct of_device_id sun8i_codec_of_match[] = { - { .compatible = "allwinner,sun8i-a33-codec" }, + { .compatible = "allwinner,sun8i-a33-codec", .data = &sun8i_a33_quirks }, + { .compatible = "allwinner,sun50i-a64-codec", .data = &sun50i_a64_quirks }, {} }; MODULE_DEVICE_TABLE(of, sun8i_codec_of_match); -- cgit From e47d2dcd88fc3e6837f8aa0060ce820ec9001e26 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 20:25:53 -0500 Subject: ASoC: sun8i-codec: Add missing mixer routes The sun8i-codec driver provides ALSA controls for enabling/disabling each of the inputs to the AIF1 Slot 0 and DAC mixers. For two of these inputs (ADC->DAC and AIF1 DA0->AIF1 AD0), the audio source is implemented, so the mixer inputs can be used. However, because the DAPM routes are missing, these mixer inputs only work when both the source and the mixer happen to be part of other active audio paths. Adding the appropriate routes makes these ALSA controls function all of the time. Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200726012557.38282-4-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index ffeac150c086..a75be9e82d22 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -490,14 +490,20 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { /* DAC Mixer Routes */ { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, + { "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" }, + { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, + { "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, /* ADC Routes */ { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" }, { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" }, /* ADC Mixer Routes */ + { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, + + { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, }; -- cgit From 7518805fb636308909a6a7953e9fdb194abb15f8 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 20:25:54 -0500 Subject: ASoC: sun8i-codec: Add a quirk for LRCK inversion On the A64, as tested using the PinePhone, the current code causes the left/right channels to be swapped during I2S playback from the CPU on AIF1, and breaks DSP_A communication with the modem on AIF2. Both of these are fixed when LRCK is no longer inverted. Trusting that the comment in the code is correct, the existing behavior is kept for the A33. Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200726012557.38282-5-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index a75be9e82d22..304683a71acd 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -88,6 +88,7 @@ struct sun8i_codec_quirks { bool legacy_widgets : 1; + bool lrck_inversion : 1; }; struct sun8i_codec { @@ -215,18 +216,19 @@ static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) value << SUN8I_AIF1CLK_CTRL_AIF1_BCLK_INV); /* - * It appears that the DAI and the codec don't share the same - * polarity for the LRCK signal when they mean 'normal' and - * 'inverted' in the datasheet. + * It appears that the DAI and the codec in the A33 SoC don't + * share the same polarity for the LRCK signal when they mean + * 'normal' and 'inverted' in the datasheet. * * Since the DAI here is our regular i2s driver that have been * tested with way more codecs than just this one, it means * that the codec probably gets it backward, and we have to * invert the value here. */ + value ^= scodec->quirks->lrck_inversion; regmap_update_bits(scodec->regmap, SUN8I_AIF1CLK_CTRL, BIT(SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV), - !value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); + value << SUN8I_AIF1CLK_CTRL_AIF1_LRCK_INV); /* DAI format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { @@ -673,6 +675,7 @@ static int sun8i_codec_remove(struct platform_device *pdev) static const struct sun8i_codec_quirks sun8i_a33_quirks = { .legacy_widgets = true, + .lrck_inversion = true, }; static const struct sun8i_codec_quirks sun50i_a64_quirks = { -- cgit From 11ec0edc6408a739dffca34ebbbe921817c3b10e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 12:58:35 -0500 Subject: ASOC: SOF: Intel: hda-codec: move unused label to correct position Cppcheck reports the following warning: sound/soc/sof/intel/hda-codec.c:191:1: style: Label 'error' is not used. [unusedLabel] This label is indeed only used conditionally, move it where it's actually used. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813175839.59422-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-codec.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 2c5c451fa19d..119aa9ffcc66 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -178,6 +178,11 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, } return ret; + +error: + snd_hdac_ext_bus_device_exit(hdev); + return -ENOENT; + #else hdev = devm_kzalloc(sdev->dev, sizeof(*hdev), GFP_KERNEL); if (!hdev) -- cgit From 2e3e0bc378f205370fc4c6dbd9374d66e803ce53 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 12:58:36 -0500 Subject: ASoC: SOF: Intel: hda-codec: move variable used conditionally Cppcheck reports the following warning: sound/soc/sof/intel/hda-codec.c:122:20: style: Unused variable: codec [unusedVariable] struct hda_codec *codec; ^ Move declaration inside a conditionally-compiled block. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813175839.59422-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-codec.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 119aa9ffcc66..55811b99e47a 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -116,10 +116,10 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) struct hdac_hda_priv *hda_priv; + struct hda_codec *codec; #endif struct hda_bus *hbus = sof_to_hbus(sdev); struct hdac_device *hdev; - struct hda_codec *codec; u32 hda_cmd = (address << 28) | (AC_NODE_ROOT << 20) | (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; u32 resp = -1; -- cgit From 1e6444271c667d56f3a793cfc295b72a1f8007da Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 12:58:37 -0500 Subject: ASoC: Intel: rename shadowed variable for all broadwell boards Fix cppcheck warnings: sound/soc/intel/boards/bdw-rt5650.c:91:23: style: Local variable 'channels' shadows outer variable [shadowVariable] sound/soc/intel/boards/bdw-rt5677.c:144:23: style: Local variable 'channels' shadows outer variable [shadowVariable] sound/soc/intel/boards/broadwell.c:91:23: style: Local variable 'channels' shadows outer variable [shadowVariable] This was fixed earlier in other machine drivers but keeps coming back with copy/paste. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813175839.59422-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5650.c | 10 +++++----- sound/soc/intel/boards/bdw-rt5677.c | 8 ++++---- sound/soc/intel/boards/broadwell.c | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index ce7320916b22..1412a9941ed4 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -87,14 +87,14 @@ static int broadwell_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); + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *chan = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); /* The ADSP will covert the FE rate to 48k, max 4-channels */ rate->min = rate->max = 48000; - channels->min = 2; - channels->max = 4; + chan->min = 2; + chan->max = 4; /* set SSP0 to 24 bit */ snd_mask_set_format(hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT), diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 86e427e3822f..297871bcaf5d 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -140,13 +140,13 @@ static int broadwell_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); + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *chan = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); /* The ADSP will covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; /* set SSP0 to 16 bit */ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index f6399077d291..56972af13b6f 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -87,13 +87,13 @@ static int broadwell_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); + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *chan = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); /* The ADSP will covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; - channels->min = channels->max = 2; + chan->min = chan->max = 2; /* set SSP0 to 16 bit */ params_set_format(params, SNDRV_PCM_FORMAT_S16_LE); -- cgit From 9c7deb0576d7fe4370a23f4e127b2a69325f7ce9 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 12:58:38 -0500 Subject: ASoC: Intel: bytcht_cx2072x: simplify return handling Fix cppcheck warning: sound/soc/intel/boards/bytcht_cx2072x.c:102:9: warning: Identical condition and return expression 'ret', return value is always 0 [identicalConditionAfterEarlyExit] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813175839.59422-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bytcht_cx2072x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bytcht_cx2072x.c b/sound/soc/intel/boards/bytcht_cx2072x.c index 9cb42ba40c07..0b50b3646d55 100644 --- a/sound/soc/intel/boards/bytcht_cx2072x.c +++ b/sound/soc/intel/boards/bytcht_cx2072x.c @@ -99,7 +99,7 @@ static int byt_cht_cx2072x_init(struct snd_soc_pcm_runtime *rtd) snd_soc_dai_set_bclk_ratio(asoc_rtd_to_codec(rtd, 0), 50); - return ret; + return 0; } static int byt_cht_cx2072x_fixup(struct snd_soc_pcm_runtime *rtd, -- cgit From ad5b7f69a09b6784f6fc263d7c0fffdda947a8ce Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:27 -0500 Subject: ASoC: sun50i-codec-analog: Fix duplicate use of ADC enable bits The same enable bits are currently used for both the "Left/Right ADC" and the "Left/Right ADC Mixer" widgets. This happens to work in practice because the widgets are always enabled/disabled at the same time, but each register bit should only be associated with a single widget. To keep symmetry with the DAC widgets, keep the bits on the ADC widgets, and remove them from the ADC Mixer widgets. Fixes: 42371f327df0 ("ASoC: sunxi: Add new driver for Allwinner A64 codec's analog path controls") Reported-by: Ondrej Jirman Signed-off-by: Samuel Holland Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-2-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index f5b7069bcca2..cbdb31c3b7bd 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -363,12 +363,10 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { SUN50I_ADDA_MIX_DAC_CTRL_RMIXEN, 0, sun50i_a64_codec_mixer_controls, ARRAY_SIZE(sun50i_a64_codec_mixer_controls)), - SND_SOC_DAPM_MIXER("Left ADC Mixer", SUN50I_ADDA_ADC_CTRL, - SUN50I_ADDA_ADC_CTRL_ADCLEN, 0, + SND_SOC_DAPM_MIXER("Left ADC Mixer", SND_SOC_NOPM, 0, 0, sun50i_codec_adc_mixer_controls, ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), - SND_SOC_DAPM_MIXER("Right ADC Mixer", SUN50I_ADDA_ADC_CTRL, - SUN50I_ADDA_ADC_CTRL_ADCREN, 0, + SND_SOC_DAPM_MIXER("Right ADC Mixer", SND_SOC_NOPM, 0, 0, sun50i_codec_adc_mixer_controls, ARRAY_SIZE(sun50i_codec_adc_mixer_controls)), }; -- cgit From 9b7612bb75e50acc55d2143cadb8057a6721d9c7 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:28 -0500 Subject: ASoC: sun50i-codec-analog: Gate the amplifier clock during suspend The clock must be running for the zero-crossing mute functionality. However, it must be gated for VDD-SYS to be turned off during system suspend. Disable it in the suspend callback, after everything has already been muted, to avoid pops when muting/unmuting outputs. Signed-off-by: Samuel Holland Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-3-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index cbdb31c3b7bd..4ad262c2e59b 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -438,6 +438,19 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "EARPIECE", NULL, "Earpiece Amp" }, }; +static int sun50i_a64_codec_suspend(struct snd_soc_component *component) +{ + return regmap_update_bits(component->regmap, SUN50I_ADDA_HP_CTRL, + BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE), + BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE)); +} + +static int sun50i_a64_codec_resume(struct snd_soc_component *component) +{ + return regmap_update_bits(component->regmap, SUN50I_ADDA_HP_CTRL, + BIT(SUN50I_ADDA_HP_CTRL_PA_CLK_GATE), 0); +} + static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = { .controls = sun50i_a64_codec_controls, .num_controls = ARRAY_SIZE(sun50i_a64_codec_controls), @@ -445,6 +458,8 @@ static const struct snd_soc_component_driver sun50i_codec_analog_cmpnt_drv = { .num_dapm_widgets = ARRAY_SIZE(sun50i_a64_codec_widgets), .dapm_routes = sun50i_a64_codec_routes, .num_dapm_routes = ARRAY_SIZE(sun50i_a64_codec_routes), + .suspend = sun50i_a64_codec_suspend, + .resume = sun50i_a64_codec_resume, }; static const struct of_device_id sun50i_codec_analog_of_match[] = { -- cgit From cababecb33c05b8229558df6248d5869a38ceec3 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:29 -0500 Subject: ASoC: sun50i-codec-analog: Group and sort mixer routes Sort the controls in the same order as the bits in the register. Then group the routes by sink, and sort them in the same order as the controls. This makes it much easier to verify that all mixer inputs are accounted for. Signed-off-by: Samuel Holland Acked-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-4-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 58 +++++++++++++++++------------------ 1 file changed, 28 insertions(+), 30 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 4ad262c2e59b..17165f1ddb63 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -121,50 +121,50 @@ /* mixer controls */ static const struct snd_kcontrol_new sun50i_a64_codec_mixer_controls[] = { - SOC_DAPM_DOUBLE_R("DAC Playback Switch", + SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", SUN50I_ADDA_OL_MIX_CTRL, SUN50I_ADDA_OR_MIX_CTRL, - SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0), - SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", + SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0), + SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", SUN50I_ADDA_OL_MIX_CTRL, SUN50I_ADDA_OR_MIX_CTRL, - SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0), + SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0), SOC_DAPM_DOUBLE_R("Line In Playback Switch", SUN50I_ADDA_OL_MIX_CTRL, SUN50I_ADDA_OR_MIX_CTRL, SUN50I_ADDA_OL_MIX_CTRL_LINEINL, 1, 0), - SOC_DAPM_DOUBLE_R("Mic1 Playback Switch", + SOC_DAPM_DOUBLE_R("DAC Playback Switch", SUN50I_ADDA_OL_MIX_CTRL, SUN50I_ADDA_OR_MIX_CTRL, - SUN50I_ADDA_OL_MIX_CTRL_MIC1, 1, 0), - SOC_DAPM_DOUBLE_R("Mic2 Playback Switch", + SUN50I_ADDA_OL_MIX_CTRL_DACL, 1, 0), + SOC_DAPM_DOUBLE_R("DAC Reversed Playback Switch", SUN50I_ADDA_OL_MIX_CTRL, SUN50I_ADDA_OR_MIX_CTRL, - SUN50I_ADDA_OL_MIX_CTRL_MIC2, 1, 0), + SUN50I_ADDA_OL_MIX_CTRL_DACR, 1, 0), }; /* ADC mixer controls */ static const struct snd_kcontrol_new sun50i_codec_adc_mixer_controls[] = { - SOC_DAPM_DOUBLE_R("Mixer Capture Switch", + SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", SUN50I_ADDA_L_ADCMIX_SRC, SUN50I_ADDA_R_ADCMIX_SRC, - SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0), - SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", + SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0), + SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", SUN50I_ADDA_L_ADCMIX_SRC, SUN50I_ADDA_R_ADCMIX_SRC, - SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0), + SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0), SOC_DAPM_DOUBLE_R("Line In Capture Switch", SUN50I_ADDA_L_ADCMIX_SRC, SUN50I_ADDA_R_ADCMIX_SRC, SUN50I_ADDA_L_ADCMIX_SRC_LINEINL, 1, 0), - SOC_DAPM_DOUBLE_R("Mic1 Capture Switch", + SOC_DAPM_DOUBLE_R("Mixer Capture Switch", SUN50I_ADDA_L_ADCMIX_SRC, SUN50I_ADDA_R_ADCMIX_SRC, - SUN50I_ADDA_L_ADCMIX_SRC_MIC1, 1, 0), - SOC_DAPM_DOUBLE_R("Mic2 Capture Switch", + SUN50I_ADDA_L_ADCMIX_SRC_OMIXRL, 1, 0), + SOC_DAPM_DOUBLE_R("Mixer Reversed Capture Switch", SUN50I_ADDA_L_ADCMIX_SRC, SUN50I_ADDA_R_ADCMIX_SRC, - SUN50I_ADDA_L_ADCMIX_SRC_MIC2, 1, 0), + SUN50I_ADDA_L_ADCMIX_SRC_OMIXRR, 1, 0), }; static const DECLARE_TLV_DB_SCALE(sun50i_codec_out_mixer_pregain_scale, @@ -373,24 +373,32 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { /* Left Mixer Routes */ + { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, + { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, + { "Left Mixer", "Line In Playback Switch", "LINEIN" }, { "Left Mixer", "DAC Playback Switch", "Left DAC" }, { "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" }, - { "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, /* Right Mixer Routes */ + { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, + { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, + { "Right Mixer", "Line In Playback Switch", "LINEIN" }, { "Right Mixer", "DAC Playback Switch", "Right DAC" }, { "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" }, - { "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" }, /* Left ADC Mixer Routes */ + { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, { "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" }, { "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" }, - { "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, /* Right ADC Mixer Routes */ + { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, + { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, + { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, { "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" }, { "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" }, - { "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" }, /* ADC Routes */ { "Left ADC", NULL, "Left ADC Mixer" }, @@ -410,16 +418,6 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { /* Microphone Routes */ { "Mic2 Amplifier", NULL, "MIC2"}, - { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, - { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" }, - { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, - { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" }, - - /* Line-in Routes */ - { "Left Mixer", "Line In Playback Switch", "LINEIN" }, - { "Right Mixer", "Line In Playback Switch", "LINEIN" }, - { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" }, - { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" }, /* Line-out Routes */ { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, -- cgit From 241a578a9ebf866351e12029fc77f5a48b742042 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:30 -0500 Subject: ASoC: sun50i-codec-analog: Make headphone routes stereo This matches the hardware more accurately, and is necessary for including the (stereo) headphone mute switch in the DAPM graph. Signed-off-by: Samuel Holland Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-5-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 17165f1ddb63..f98851067f97 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -311,9 +311,15 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { */ SND_SOC_DAPM_REGULATOR_SUPPLY("cpvdd", 0, 0), - SND_SOC_DAPM_MUX("Headphone Source Playback Route", + SND_SOC_DAPM_MUX("Left Headphone Source", SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), - SND_SOC_DAPM_OUT_DRV("Headphone Amp", SUN50I_ADDA_HP_CTRL, + SND_SOC_DAPM_MUX("Right Headphone Source", + SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), + SND_SOC_DAPM_OUT_DRV("Left Headphone Amp", + SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_OUT_DRV("Right Headphone Amp", + SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("Headphone Amp", SUN50I_ADDA_HP_CTRL, SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("HP"), @@ -405,13 +411,19 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Right ADC", NULL, "Right ADC Mixer" }, /* Headphone Routes */ - { "Headphone Source Playback Route", "DAC", "Left DAC" }, - { "Headphone Source Playback Route", "DAC", "Right DAC" }, - { "Headphone Source Playback Route", "Mixer", "Left Mixer" }, - { "Headphone Source Playback Route", "Mixer", "Right Mixer" }, - { "Headphone Amp", NULL, "Headphone Source Playback Route" }, + { "Left Headphone Source", "DAC", "Left DAC" }, + { "Left Headphone Source", "Mixer", "Left Mixer" }, + { "Left Headphone Amp", NULL, "Left Headphone Source" }, + { "Left Headphone Amp", NULL, "Headphone Amp" }, + { "HP", NULL, "Left Headphone Amp" }, + + { "Right Headphone Source", "DAC", "Right DAC" }, + { "Right Headphone Source", "Mixer", "Right Mixer" }, + { "Right Headphone Amp", NULL, "Right Headphone Source" }, + { "Right Headphone Amp", NULL, "Headphone Amp" }, + { "HP", NULL, "Right Headphone Amp" }, + { "Headphone Amp", NULL, "cpvdd" }, - { "HP", NULL, "Headphone Amp" }, /* Microphone Routes */ { "Mic1 Amplifier", NULL, "MIC1"}, -- cgit From 4b9f39e14cf606def16897d85da492fc54b94a43 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:31 -0500 Subject: ASoC: sun50i-codec-analog: Enable DAPM for headphone switch By including the headphone mute switch to the DAPM graph, both the headphone amplifier and the Mixer/DAC inputs can be powered off when the headphones are muted. The mute switch is between the source selection and the amplifier, as per the diagram in the SoC manual. Signed-off-by: Samuel Holland Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-6-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index f98851067f97..176d6658d099 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -193,11 +193,6 @@ static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = { SUN50I_ADDA_HP_CTRL_HPVOL, 0x3f, 0, sun50i_codec_hp_vol_scale), - SOC_DOUBLE("Headphone Playback Switch", - SUN50I_ADDA_MIX_DAC_CTRL, - SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE, - SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0), - /* Mixer pre-gain */ SOC_SINGLE_TLV("Mic1 Playback Volume", SUN50I_ADDA_MIC1_CTRL, SUN50I_ADDA_MIC1_CTRL_MIC1G, @@ -264,6 +259,12 @@ static const struct snd_kcontrol_new sun50i_codec_hp_src[] = { sun50i_codec_hp_src_enum), }; +static const struct snd_kcontrol_new sun50i_codec_hp_switch = + SOC_DAPM_DOUBLE("Headphone Playback Switch", + SUN50I_ADDA_MIX_DAC_CTRL, + SUN50I_ADDA_MIX_DAC_CTRL_LHPPAMUTE, + SUN50I_ADDA_MIX_DAC_CTRL_RHPPAMUTE, 1, 0); + static const char * const sun50i_codec_lineout_src_enum_text[] = { "Stereo", "Mono Differential", }; @@ -315,6 +316,10 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), SND_SOC_DAPM_MUX("Right Headphone Source", SND_SOC_NOPM, 0, 0, sun50i_codec_hp_src), + SND_SOC_DAPM_SWITCH("Left Headphone Switch", + SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch), + SND_SOC_DAPM_SWITCH("Right Headphone Switch", + SND_SOC_NOPM, 0, 0, &sun50i_codec_hp_switch), SND_SOC_DAPM_OUT_DRV("Left Headphone Amp", SND_SOC_NOPM, 0, 0, NULL, 0), SND_SOC_DAPM_OUT_DRV("Right Headphone Amp", @@ -413,13 +418,15 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { /* Headphone Routes */ { "Left Headphone Source", "DAC", "Left DAC" }, { "Left Headphone Source", "Mixer", "Left Mixer" }, - { "Left Headphone Amp", NULL, "Left Headphone Source" }, + { "Left Headphone Switch", "Headphone Playback Switch", "Left Headphone Source" }, + { "Left Headphone Amp", NULL, "Left Headphone Switch" }, { "Left Headphone Amp", NULL, "Headphone Amp" }, { "HP", NULL, "Left Headphone Amp" }, { "Right Headphone Source", "DAC", "Right DAC" }, { "Right Headphone Source", "Mixer", "Right Mixer" }, - { "Right Headphone Amp", NULL, "Right Headphone Source" }, + { "Right Headphone Switch", "Headphone Playback Switch", "Right Headphone Source" }, + { "Right Headphone Amp", NULL, "Right Headphone Switch" }, { "Right Headphone Amp", NULL, "Headphone Amp" }, { "HP", NULL, "Right Headphone Amp" }, -- cgit From dd8286a34963c47964ab3c73d56656c9719a36b4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:32 -0500 Subject: ASoC: sun50i-codec-analog: Make line out routes stereo This matches the hardware more accurately, and is necessary for including the (stereo) line out mute switch in the DAPM graph. Signed-off-by: Samuel Holland Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-7-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 176d6658d099..df39f6ffe25a 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -328,7 +328,9 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { SUN50I_ADDA_HP_CTRL_HPPA_EN, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("HP"), - SND_SOC_DAPM_MUX("Line Out Source Playback Route", + SND_SOC_DAPM_MUX("Left Line Out Source", + SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), + SND_SOC_DAPM_MUX("Right Line Out Source", SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), SND_SOC_DAPM_OUTPUT("LINEOUT"), @@ -439,12 +441,14 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Mic2 Amplifier", NULL, "MIC2"}, /* Line-out Routes */ - { "Line Out Source Playback Route", "Stereo", "Left Mixer" }, - { "Line Out Source Playback Route", "Stereo", "Right Mixer" }, - { "Line Out Source Playback Route", "Mono Differential", "Left Mixer" }, - { "Line Out Source Playback Route", "Mono Differential", - "Right Mixer" }, - { "LINEOUT", NULL, "Line Out Source Playback Route" }, + { "Left Line Out Source", "Stereo", "Left Mixer" }, + { "Left Line Out Source", "Mono Differential", "Left Mixer" }, + { "Left Line Out Source", "Mono Differential", "Right Mixer" }, + { "LINEOUT", NULL, "Left Line Out Source" }, + + { "Right Line Out Source", "Stereo", "Right Mixer" }, + { "Right Line Out Source", "Mono Differential", "Left Line Out Source" }, + { "LINEOUT", NULL, "Right Line Out Source" }, /* Earpiece Routes */ { "Earpiece Source Playback Route", "DACL", "Left DAC" }, -- cgit From 95d34762f201c0f7cf0ed920815f349cfe336fe1 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:33 -0500 Subject: ASoC: sun50i-codec-analog: Enable DAPM for line out switch By including the line out mute switch in the DAPM graph, the Mixer/DAC inputs can be powered off when the line output is muted. The line outputs have an unusual routing scheme. The left side mute switch is between the source selection and the amplifier, as usual. The right side source selection comes *after* its amplifier (and after the left side amplifier), and its mute switch controls whichever source is currently selected. This matches the diagram in the SoC manual. Signed-off-by: Samuel Holland Reviewed-by: Chen-Yu Tsai Link: https://lore.kernel.org/r/20200726025334.59931-8-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index df39f6ffe25a..84bb76cad74f 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -228,11 +228,6 @@ static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = { SUN50I_ADDA_LINEOUT_CTRL1_VOL, 0x1f, 0, sun50i_codec_lineout_vol_scale), - SOC_DOUBLE("Line Out Playback Switch", - SUN50I_ADDA_LINEOUT_CTRL0, - SUN50I_ADDA_LINEOUT_CTRL0_LEN, - SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0), - SOC_SINGLE_TLV("Earpiece Playback Volume", SUN50I_ADDA_EARPIECE_CTRL1, SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0, @@ -280,6 +275,12 @@ static const struct snd_kcontrol_new sun50i_codec_lineout_src[] = { sun50i_codec_lineout_src_enum), }; +static const struct snd_kcontrol_new sun50i_codec_lineout_switch = + SOC_DAPM_DOUBLE("Line Out Playback Switch", + SUN50I_ADDA_LINEOUT_CTRL0, + SUN50I_ADDA_LINEOUT_CTRL0_LEN, + SUN50I_ADDA_LINEOUT_CTRL0_REN, 1, 0); + static const char * const sun50i_codec_earpiece_src_enum_text[] = { "DACR", "DACL", "Right Mixer", "Left Mixer", }; @@ -332,6 +333,10 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), SND_SOC_DAPM_MUX("Right Line Out Source", SND_SOC_NOPM, 0, 0, sun50i_codec_lineout_src), + SND_SOC_DAPM_SWITCH("Left Line Out Switch", + SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch), + SND_SOC_DAPM_SWITCH("Right Line Out Switch", + SND_SOC_NOPM, 0, 0, &sun50i_codec_lineout_switch), SND_SOC_DAPM_OUTPUT("LINEOUT"), SND_SOC_DAPM_MUX("Earpiece Source Playback Route", @@ -444,10 +449,12 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Left Line Out Source", "Stereo", "Left Mixer" }, { "Left Line Out Source", "Mono Differential", "Left Mixer" }, { "Left Line Out Source", "Mono Differential", "Right Mixer" }, - { "LINEOUT", NULL, "Left Line Out Source" }, + { "Left Line Out Switch", "Line Out Playback Switch", "Left Line Out Source" }, + { "LINEOUT", NULL, "Left Line Out Switch" }, - { "Right Line Out Source", "Stereo", "Right Mixer" }, - { "Right Line Out Source", "Mono Differential", "Left Line Out Source" }, + { "Right Line Out Switch", "Line Out Playback Switch", "Right Mixer" }, + { "Right Line Out Source", "Stereo", "Right Line Out Switch" }, + { "Right Line Out Source", "Mono Differential", "Left Line Out Switch" }, { "LINEOUT", NULL, "Right Line Out Source" }, /* Earpiece Routes */ -- cgit From 7829e68d55692c9f7f5665ebec9fa1f33d5ad72f Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 25 Jul 2020 21:53:34 -0500 Subject: ASoC: sun50i-codec-analog: Enable DAPM for earpiece switch By including the earpiece mute switch in the DAPM graph, both the earpiece amplifier and the Mixer/DAC inputs can be powered off when the earpiece is muted. While the widget is really just a simple switch, it is represented as a "mixer with named controls" to avoid including the widget name in the kcontrol name. Otherwise, it is not possible to give the widget an accurate, descriptive name without changing the kcontrol name seen by userspace (which should be stable). The mute switch is between the source selection and the amplifier, as per the diagram in the SoC manual. Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200726025334.59931-9-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun50i-codec-analog.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun50i-codec-analog.c b/sound/soc/sunxi/sun50i-codec-analog.c index 84bb76cad74f..a41e25ad0aaf 100644 --- a/sound/soc/sunxi/sun50i-codec-analog.c +++ b/sound/soc/sunxi/sun50i-codec-analog.c @@ -232,11 +232,6 @@ static const struct snd_kcontrol_new sun50i_a64_codec_controls[] = { SUN50I_ADDA_EARPIECE_CTRL1, SUN50I_ADDA_EARPIECE_CTRL1_ESP_VOL, 0x1f, 0, sun50i_codec_earpiece_vol_scale), - - SOC_SINGLE("Earpiece Playback Switch", - SUN50I_ADDA_EARPIECE_CTRL1, - SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0), - }; static const char * const sun50i_codec_hp_src_enum_text[] = { @@ -295,6 +290,12 @@ static const struct snd_kcontrol_new sun50i_codec_earpiece_src[] = { sun50i_codec_earpiece_src_enum), }; +static const struct snd_kcontrol_new sun50i_codec_earpiece_switch[] = { + SOC_DAPM_SINGLE("Earpiece Playback Switch", + SUN50I_ADDA_EARPIECE_CTRL1, + SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_MUTE, 1, 0), +}; + static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { /* DAC */ SND_SOC_DAPM_DAC("Left DAC", NULL, SUN50I_ADDA_MIX_DAC_CTRL, @@ -341,6 +342,9 @@ static const struct snd_soc_dapm_widget sun50i_a64_codec_widgets[] = { SND_SOC_DAPM_MUX("Earpiece Source Playback Route", SND_SOC_NOPM, 0, 0, sun50i_codec_earpiece_src), + SOC_MIXER_NAMED_CTL_ARRAY("Earpiece Switch", + SND_SOC_NOPM, 0, 0, + sun50i_codec_earpiece_switch), SND_SOC_DAPM_OUT_DRV("Earpiece Amp", SUN50I_ADDA_EARPIECE_CTRL1, SUN50I_ADDA_EARPIECE_CTRL1_ESPPA_EN, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("EARPIECE"), @@ -462,7 +466,8 @@ static const struct snd_soc_dapm_route sun50i_a64_codec_routes[] = { { "Earpiece Source Playback Route", "DACR", "Right DAC" }, { "Earpiece Source Playback Route", "Left Mixer", "Left Mixer" }, { "Earpiece Source Playback Route", "Right Mixer", "Right Mixer" }, - { "Earpiece Amp", NULL, "Earpiece Source Playback Route" }, + { "Earpiece Switch", "Earpiece Playback Switch", "Earpiece Source Playback Route" }, + { "Earpiece Amp", NULL, "Earpiece Switch" }, { "EARPIECE", NULL, "Earpiece Amp" }, }; -- cgit From dcd79364bff36ce1341a59be8142ded5b7da7d19 Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Wed, 12 Aug 2020 17:46:29 +0800 Subject: ASoC: codec: tlv3204: Enable 24 bit audio support Enable 24 bit in 32 bit container audio support. Using the params_physical_width to differentiate 24 bit in 32 bit container and 24 bit in 24 bit container modes. Use the sample rate, bit depth and channel parameters to calculate the bit clock needed. Signed-off-by: Michael Sit Wei Hong Reviewed-by: Sia Jee Heng Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200812094631.4698-2-michael.wei.hong.sit@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 467802875c13..e3d12a7ea07a 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -665,7 +665,8 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, } static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned int sample_rate) + unsigned int sample_rate, unsigned int channel, + unsigned int bit_depth) { u8 aosr; u16 dosr; @@ -753,7 +754,8 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, dosr); clk_set_rate(clocks[5].clk, - sample_rate * 32); + sample_rate * channel * + bit_depth); return 0; } } @@ -775,9 +777,11 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, u8 iface1_reg = 0; u8 dacsetup_reg = 0; - aic32x4_setup_clocks(component, params_rate(params)); + aic32x4_setup_clocks(component, params_rate(params), + params_channels(params), + params_physical_width(params)); - switch (params_width(params)) { + switch (params_physical_width(params)) { case 16: iface1_reg |= (AIC32X4_WORD_LEN_16BITS << AIC32X4_IFACE1_DATALEN_SHIFT); @@ -862,7 +866,8 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component, #define AIC32X4_RATES SNDRV_PCM_RATE_8000_192000 #define AIC32X4_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \ - | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + | SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE \ + | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops aic32x4_ops = { .hw_params = aic32x4_hw_params, -- cgit From d1c859d314d87c6f4dc09c3d7cf6f3ec9f44bcc5 Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Wed, 12 Aug 2020 17:46:30 +0800 Subject: ASoC: codec: tlv3204: Increased maximum supported channels Increased maximum supported channel to 8 channels for audio capture running in TDM mode. Signed-off-by: Michael Sit Wei Hong Reviewed-by: Sia Jee Heng Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200812094631.4698-3-michael.wei.hong.sit@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index e3d12a7ea07a..6c2338ea5d8d 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -888,7 +888,7 @@ static struct snd_soc_dai_driver aic32x4_dai = { .capture = { .stream_name = "Capture", .channels_min = 1, - .channels_max = 2, + .channels_max = 8, .rates = AIC32X4_RATES, .formats = AIC32X4_FORMATS,}, .ops = &aic32x4_ops, -- cgit From 9d4befff5a959e5f2f94357b3554a6929f596e15 Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Wed, 12 Aug 2020 17:46:31 +0800 Subject: ASoC: codec: tlv3204: Moving GPIO reset and add ADC reset Moving GPIO reset to a later stage and before clock registration to ensure that the host system and codec clocks are in sync. If the host register clock values prior to gpio reset, the last configured codec clock is registered to the host. The codec then gets gpio resetted setting the codec clocks to their default value, causing a mismatch. Host system will skip clock setting thinking the codec clocks are already at the requested rate. ADC reset is added to ensure the next audio capture does not have undesired artifacts. It is probably related to the original code where the probe function resets the ADC prior to 1st record. Signed-off-by: Michael Sit Wei Hong Reviewed-by: Sia Jee Heng Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200812094631.4698-4-michael.wei.hong.sit@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 47 ++++++++++++++++++++++++++++++---------- 1 file changed, 35 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 6c2338ea5d8d..8dcea566b375 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -50,6 +50,28 @@ struct aic32x4_priv { struct device *dev; }; +static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + u32 adc_reg; + + /* + * Workaround: the datasheet does not mention a required programming + * sequence but experiments show the ADC needs to be reset after each + * capture to avoid audible artifacts. + */ + switch (event) { + case SND_SOC_DAPM_POST_PMD: + adc_reg = snd_soc_component_read(component, AIC32X4_ADCSETUP); + snd_soc_component_write(component, AIC32X4_ADCSETUP, adc_reg | + AIC32X4_LADC_EN | AIC32X4_RADC_EN); + snd_soc_component_write(component, AIC32X4_ADCSETUP, adc_reg); + break; + } + return 0; +}; + static int mic_bias_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -434,6 +456,7 @@ static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("Mic Bias", AIC32X4_MICBIAS, 6, 0, mic_bias_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST("ADC Reset", aic32x4_reset_adc), SND_SOC_DAPM_OUTPUT("HPL"), SND_SOC_DAPM_OUTPUT("HPR"), @@ -665,8 +688,8 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, } static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned int sample_rate, unsigned int channel, - unsigned int bit_depth) + unsigned int sample_rate, unsigned int channel, + unsigned int bit_depth) { u8 aosr; u16 dosr; @@ -958,12 +981,6 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (ret) return ret; - if (gpio_is_valid(aic32x4->rstn_gpio)) { - ndelay(10); - gpio_set_value(aic32x4->rstn_gpio, 1); - mdelay(1); - } - snd_soc_component_write(component, AIC32X4_RESET, 0x01); if (aic32x4->setup) @@ -1196,10 +1213,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) aic32x4->mclk_name = "mclk"; } - ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); - if (ret) - return ret; - if (gpio_is_valid(aic32x4->rstn_gpio)) { ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio, GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn"); @@ -1221,6 +1234,16 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) return ret; } + if (gpio_is_valid(aic32x4->rstn_gpio)) { + ndelay(10); + gpio_set_value_cansleep(aic32x4->rstn_gpio, 1); + mdelay(1); + } + + ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); + if (ret) + return ret; + return 0; } EXPORT_SYMBOL(aic32x4_probe); -- cgit From 9c3bab3c4f158bb79ebd7443ef83c32fa1a450a1 Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Tue, 11 Aug 2020 12:18:35 +0800 Subject: ASoC: Intel: KMB: Enable TDM audio capture Enable I2S TDM audio capture for Intel Keem Bay platform. The I2S TDM will support 4 channel and 8 channel audio capture only. 4 channel and 8 channel audio capture operates only in slave mode. Signed-off-by: Michael Sit Wei Hong Reviewed-by: Sia Jee Heng Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200811041836.999-2-michael.wei.hong.sit@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/keembay/kmb_platform.c | 145 ++++++++++++++++++++++++--------- 1 file changed, 108 insertions(+), 37 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index 16f9fc4c663d..ca25a6e40cc9 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include #include @@ -17,7 +19,7 @@ #define PERIODS_MAX 48 #define PERIOD_BYTES_MIN 4096 #define BUFFER_BYTES_MAX (PERIODS_MAX * PERIOD_BYTES_MIN) -#define TDM_OPERATION 1 +#define TDM_OPERATION 5 #define I2S_OPERATION 0 #define DATA_WIDTH_CONFIG_BIT 6 #define TDM_CHANNEL_CONFIG_BIT 3 @@ -82,19 +84,25 @@ static unsigned int kmb_pcm_rx_fn(struct kmb_i2s_info *kmb_i2s, { unsigned int period_pos = rx_ptr % runtime->period_size; void __iomem *i2s_base = kmb_i2s->i2s_base; + int chan = kmb_i2s->config.chan_nr; void *buf = runtime->dma_area; - int i; + int i, j; /* KMB i2s uses two separate L/R FIFO */ for (i = 0; i < kmb_i2s->fifo_th; i++) { - if (kmb_i2s->config.data_width == 16) { - ((u16(*)[2])buf)[rx_ptr][0] = readl(i2s_base + LRBR_LTHR(0)); - ((u16(*)[2])buf)[rx_ptr][1] = readl(i2s_base + RRBR_RTHR(0)); - } else { - ((u32(*)[2])buf)[rx_ptr][0] = readl(i2s_base + LRBR_LTHR(0)); - ((u32(*)[2])buf)[rx_ptr][1] = readl(i2s_base + RRBR_RTHR(0)); + for (j = 0; j < chan / 2; j++) { + if (kmb_i2s->config.data_width == 16) { + ((u16 *)buf)[rx_ptr * chan + (j * 2)] = + readl(i2s_base + LRBR_LTHR(j)); + ((u16 *)buf)[rx_ptr * chan + ((j * 2) + 1)] = + readl(i2s_base + RRBR_RTHR(j)); + } else { + ((u32 *)buf)[rx_ptr * chan + (j * 2)] = + readl(i2s_base + LRBR_LTHR(j)); + ((u32 *)buf)[rx_ptr * chan + ((j * 2) + 1)] = + readl(i2s_base + RRBR_RTHR(j)); + } } - period_pos++; if (++rx_ptr >= runtime->buffer_size) @@ -238,6 +246,7 @@ static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id) struct kmb_i2s_info *kmb_i2s = dev_id; struct i2s_clk_config_data *config = &kmb_i2s->config; irqreturn_t ret = IRQ_NONE; + u32 tx_enabled = 0; u32 isr[4]; int i; @@ -246,22 +255,45 @@ static irqreturn_t kmb_i2s_irq_handler(int irq, void *dev_id) kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_PLAYBACK); kmb_i2s_clear_irqs(kmb_i2s, SNDRV_PCM_STREAM_CAPTURE); + /* Only check TX interrupt if TX is active */ + tx_enabled = readl(kmb_i2s->i2s_base + ITER); + + /* + * Data available. Retrieve samples from FIFO + */ + + /* + * 8 channel audio will have isr[0..2] triggered, + * reading the specific isr based on the audio configuration, + * to avoid reading the buffers too early. + */ + switch (config->chan_nr) { + case 2: + if (isr[0] & ISR_RXDA) + kmb_pcm_operation(kmb_i2s, false); + ret = IRQ_HANDLED; + break; + case 4: + if (isr[1] & ISR_RXDA) + kmb_pcm_operation(kmb_i2s, false); + ret = IRQ_HANDLED; + break; + case 8: + if (isr[3] & ISR_RXDA) + kmb_pcm_operation(kmb_i2s, false); + ret = IRQ_HANDLED; + break; + } for (i = 0; i < config->chan_nr / 2; i++) { /* * Check if TX fifo is empty. If empty fill FIFO with samples */ - if ((isr[i] & ISR_TXFE)) { + if ((isr[i] & ISR_TXFE) && tx_enabled) { kmb_pcm_operation(kmb_i2s, true); ret = IRQ_HANDLED; } - /* - * Data available. Retrieve samples from FIFO - */ - if ((isr[i] & ISR_RXDA)) { - kmb_pcm_operation(kmb_i2s, false); - ret = IRQ_HANDLED; - } + /* Error Handling: TX */ if (isr[i] & ISR_TXFO) { dev_dbg(kmb_i2s->dev, "TX overrun (ch_id=%d)\n", i); @@ -445,7 +477,7 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream, { struct kmb_i2s_info *kmb_i2s = snd_soc_dai_get_drvdata(cpu_dai); struct i2s_clk_config_data *config = &kmb_i2s->config; - u32 register_val, write_val; + u32 write_val; int ret; switch (params_format(hw_params)) { @@ -472,16 +504,34 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream, config->chan_nr = params_channels(hw_params); switch (config->chan_nr) { - /* TODO: This switch case will handle up to TDM8 in the near future */ - case TWO_CHANNEL_SUPPORT: + case 8: + case 4: + /* + * Platform is not capable of providing clocks for + * multi channel audio + */ + if (kmb_i2s->master) + return -EINVAL; + write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | (config->data_width << DATA_WIDTH_CONFIG_BIT) | - MASTER_MODE | I2S_OPERATION; + !MASTER_MODE | TDM_OPERATION; writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); + break; + case 2: + /* + * Platform is only capable of providing clocks need for + * 2 channel master mode + */ + if (!(kmb_i2s->master)) + return -EINVAL; + + write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | + (config->data_width << DATA_WIDTH_CONFIG_BIT) | + MASTER_MODE | I2S_OPERATION; - register_val = readl(kmb_i2s->pss_base + I2S_GEN_CFG_0); - dev_dbg(kmb_i2s->dev, "pss register = 0x%X", register_val); + writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); break; default: dev_dbg(kmb_i2s->dev, "channel not supported\n"); @@ -529,9 +579,9 @@ static struct snd_soc_dai_ops kmb_dai_ops = { .set_fmt = kmb_set_dai_fmt, }; -static struct snd_soc_dai_driver intel_kmb_platform_dai[] = { +static struct snd_soc_dai_driver intel_kmb_i2s_dai[] = { { - .name = "kmb-plat-dai", + .name = "intel_kmb_i2s", .playback = { .channels_min = 2, .channels_max = 2, @@ -547,10 +597,6 @@ static struct snd_soc_dai_driver intel_kmb_platform_dai[] = { .capture = { .channels_min = 2, .channels_max = 2, - /* - * .channels_max will be overwritten - * if provided by Device Tree - */ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000, @@ -564,9 +610,35 @@ static struct snd_soc_dai_driver intel_kmb_platform_dai[] = { }, }; +static struct snd_soc_dai_driver intel_kmb_tdm_dai[] = { + { + .name = "intel_kmb_tdm", + .capture = { + .channels_min = 4, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_48000, + .rate_min = 8000, + .rate_max = 48000, + .formats = (SNDRV_PCM_FMTBIT_S32_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S16_LE), + }, + .ops = &kmb_dai_ops, + }, +}; + +static const struct of_device_id kmb_plat_of_match[] = { + { .compatible = "intel,keembay-i2s", .data = &intel_kmb_i2s_dai}, + { .compatible = "intel,keembay-tdm", .data = &intel_kmb_tdm_dai}, + {} +}; + static int kmb_plat_dai_probe(struct platform_device *pdev) { struct snd_soc_dai_driver *kmb_i2s_dai; + const struct of_device_id *match; struct device *dev = &pdev->dev; struct kmb_i2s_info *kmb_i2s; int ret, irq; @@ -580,7 +652,12 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) if (!kmb_i2s_dai) return -ENOMEM; - kmb_i2s_dai->ops = &kmb_dai_ops; + match = of_match_device(kmb_plat_of_match, &pdev->dev); + if (!match) { + dev_err(&pdev->dev, "Error: No device match found\n"); + return -ENODEV; + } + kmb_i2s_dai = (struct snd_soc_dai_driver *) match->data; /* Prepare the related clocks */ kmb_i2s->clk_apb = devm_clk_get(dev, "apb_clk"); @@ -630,8 +707,7 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) kmb_i2s->fifo_th = (1 << COMP1_FIFO_DEPTH(comp1_reg)) / 2; ret = devm_snd_soc_register_component(dev, &kmb_component, - intel_kmb_platform_dai, - ARRAY_SIZE(intel_kmb_platform_dai)); + kmb_i2s_dai, 1); if (ret) { dev_err(dev, "not able to register dai\n"); return ret; @@ -646,11 +722,6 @@ static int kmb_plat_dai_probe(struct platform_device *pdev) return ret; } -static const struct of_device_id kmb_plat_of_match[] = { - { .compatible = "intel,keembay-i2s", }, - {} -}; - static struct platform_driver kmb_plat_dai_driver = { .driver = { .name = "kmb-plat-dai", -- cgit From 99b7db5f6d4090e98eee79cefc8521fdf4f46ad8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:37:57 +0100 Subject: ASoC: q6asm: rename misleading session id variable Each q6asm session can have multiple streams, mixing usage of these names in variable are bit misleading to reader, so rename them accordingly. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 755062eadcc8..b543d77b2524 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -828,21 +828,21 @@ EXPORT_SYMBOL_GPL(q6asm_get_session_id); * @dev: Pointer to asm child device. * @cb: event callback. * @priv: private data associated with this client. - * @stream_id: stream id + * @session_id: session id * @perf_mode: performace mode for this client * * Return: Will be an error pointer on error or a valid audio client * on success. */ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, - void *priv, int stream_id, + void *priv, int session_id, int perf_mode) { struct q6asm *a = dev_get_drvdata(dev->parent); struct audio_client *ac; unsigned long flags; - ac = q6asm_get_audio_client(a, stream_id + 1); + ac = q6asm_get_audio_client(a, session_id + 1); if (ac) { dev_err(dev, "Audio Client already active\n"); return ac; @@ -853,9 +853,9 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, return ERR_PTR(-ENOMEM); spin_lock_irqsave(&a->slock, flags); - a->session[stream_id + 1] = ac; + a->session[session_id + 1] = ac; spin_unlock_irqrestore(&a->slock, flags); - ac->session = stream_id + 1; + ac->session = session_id + 1; ac->cb = cb; ac->dev = dev; ac->q6asm = a; -- cgit From 789e3b6c5238448a6dcfc9bc998dfe3ea9d87150 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:37:58 +0100 Subject: ASoC: q6asm: make commands specific to streams Each ASM session can have multiple streams attached to it, current design was to allow only one static stream id 1 per each session. However for use-case like gapless, we would need 2 streams to open per session. This patch converts all the q6asm apis to take stream id as argument to allow multiple streams to open on a single session, This is useful for gapless playback cases. Now the dai driver can specify which stream id for each command. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 79 ++++++++++++++++++++++------------ sound/soc/qcom/qdsp6/q6asm.c | 92 +++++++++++++++++++++++----------------- sound/soc/qcom/qdsp6/q6asm.h | 38 +++++++++++------ 3 files changed, 129 insertions(+), 80 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 9b7b218f2a20..0f157f118a3f 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -64,6 +64,7 @@ struct q6asm_dai_rtd { uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; + uint32_t stream_id; uint16_t session_id; enum stream_state state; }; @@ -181,7 +182,7 @@ static void event_handler(uint32_t opcode, uint32_t token, switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - q6asm_write_async(prtd->audio_client, + q6asm_write_async(prtd->audio_client, prtd->stream_id, prtd->pcm_count, 0, 0, NO_TIMESTAMP); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: @@ -191,7 +192,7 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_write_async(prtd->audio_client, + q6asm_write_async(prtd->audio_client, prtd->stream_id, prtd->pcm_count, 0, 0, NO_TIMESTAMP); break; @@ -200,7 +201,7 @@ static void event_handler(uint32_t opcode, uint32_t token, prtd->pcm_irq_pos += prtd->pcm_count; snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) - q6asm_read(prtd->audio_client); + q6asm_read(prtd->audio_client, prtd->stream_id); break; default: @@ -233,7 +234,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, /* rate and channels are sent to audio driver */ if (prtd->state) { /* clear the previous setup if any */ - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); q6routing_stream_close(soc_prtd->dai_link->id, @@ -252,11 +253,13 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - ret = q6asm_open_write(prtd->audio_client, FORMAT_LINEAR_PCM, + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, + FORMAT_LINEAR_PCM, 0, prtd->bits_per_sample); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - ret = q6asm_open_read(prtd->audio_client, FORMAT_LINEAR_PCM, - prtd->bits_per_sample); + ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, + FORMAT_LINEAR_PCM, + prtd->bits_per_sample); } if (ret < 0) { @@ -276,17 +279,19 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = q6asm_media_format_block_multi_ch_pcm( - prtd->audio_client, runtime->rate, - runtime->channels, NULL, + prtd->audio_client, prtd->stream_id, + runtime->rate, runtime->channels, NULL, prtd->bits_per_sample); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ret = q6asm_enc_cfg_blk_pcm_format_support(prtd->audio_client, - runtime->rate, runtime->channels, - prtd->bits_per_sample); + prtd->stream_id, + runtime->rate, + runtime->channels, + prtd->bits_per_sample); /* Queue the buffers */ for (i = 0; i < runtime->periods; i++) - q6asm_read(prtd->audio_client); + q6asm_read(prtd->audio_client, prtd->stream_id); } if (ret < 0) @@ -308,15 +313,18 @@ static int q6asm_dai_trigger(struct snd_soc_component *component, 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); + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, + 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: prtd->state = Q6ASM_STREAM_STOPPED; - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_EOS); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_PAUSE); break; default: ret = -EINVAL; @@ -361,6 +369,9 @@ static int q6asm_dai_open(struct snd_soc_component *component, return ret; } + /* DSP expects stream id from 1 */ + prtd->stream_id = 1; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) runtime->hw = q6asm_dai_hardware_playback; else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) @@ -427,7 +438,8 @@ static int q6asm_dai_close(struct snd_soc_component *component, if (prtd->audio_client) { if (prtd->state) - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, + CMD_CLOSE); q6asm_unmap_memory_regions(substream->stream, prtd->audio_client); @@ -499,8 +511,8 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, 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); + q6asm_write_async(prtd->audio_client, prtd->stream_id, + prtd->pcm_count, 0, 0, NO_TIMESTAMP); prtd->bytes_sent += prtd->pcm_count; } @@ -525,7 +537,7 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, avail = prtd->bytes_received - prtd->bytes_sent; if (avail >= prtd->pcm_count) { - q6asm_write_async(prtd->audio_client, + q6asm_write_async(prtd->audio_client, prtd->stream_id, prtd->pcm_count, 0, 0, NO_TIMESTAMP); prtd->bytes_sent += prtd->pcm_count; } @@ -560,6 +572,9 @@ static int q6asm_dai_compr_open(struct snd_soc_component *component, if (!prtd) return -ENOMEM; + /* DSP expects stream id from 1 */ + prtd->stream_id = 1; + prtd->cstream = stream; prtd->audio_client = q6asm_audio_client_alloc(dev, (q6asm_cb)compress_event_handler, @@ -607,7 +622,8 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, if (prtd->audio_client) { if (prtd->state) - q6asm_cmd(prtd->audio_client, CMD_CLOSE); + q6asm_cmd(prtd->audio_client, prtd->stream_id, + CMD_CLOSE); snd_dma_free_pages(&prtd->dma_buffer); q6asm_unmap_memory_regions(stream->direction, @@ -662,8 +678,9 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, 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, - params->codec.profile, prtd->bits_per_sample); + ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, + params->codec.id, params->codec.profile, + prtd->bits_per_sample); if (ret < 0) { dev_err(dev, "q6asm_open_write failed\n"); @@ -697,6 +714,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, flac_cfg.min_frame_size = flac->min_frame_size; ret = q6asm_stream_media_format_block_flac(prtd->audio_client, + prtd->stream_id, &flac_cfg); if (ret < 0) { dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); @@ -756,10 +774,12 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, if (wma_v9) ret = q6asm_stream_media_format_block_wma_v9( - prtd->audio_client, &wma_cfg); + prtd->audio_client, prtd->stream_id, + &wma_cfg); else ret = q6asm_stream_media_format_block_wma_v10( - prtd->audio_client, &wma_cfg); + prtd->audio_client, prtd->stream_id, + &wma_cfg); if (ret < 0) { dev_err(dev, "WMA9 CMD failed:%d\n", ret); return -EIO; @@ -792,6 +812,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } ret = q6asm_stream_media_format_block_alac(prtd->audio_client, + prtd->stream_id, &alac_cfg); if (ret < 0) { dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); @@ -816,6 +837,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, ape_cfg.seek_table_present = ape->seek_table_present; ret = q6asm_stream_media_format_block_ape(prtd->audio_client, + prtd->stream_id, &ape_cfg); if (ret < 0) { dev_err(dev, "APE CMD Format block failed:%d\n", ret); @@ -852,15 +874,18 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component, 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); + ret = q6asm_run_nowait(prtd->audio_client, prtd->stream_id, + 0, 0, 0); break; case SNDRV_PCM_TRIGGER_STOP: prtd->state = Q6ASM_STREAM_STOPPED; - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_EOS); break; case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE); + ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, + CMD_PAUSE); break; default: ret = -EINVAL; diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index b543d77b2524..97f448bc7972 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -270,7 +270,6 @@ struct audio_client { wait_queue_head_t cmd_wait; struct aprv2_ibasic_rsp_result_t result; int perf_mode; - int stream_id; struct q6asm *q6asm; struct device *dev; }; @@ -862,8 +861,6 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, ac->priv = priv; ac->io_mode = ASM_SYNC_IO_MODE; ac->perf_mode = perf_mode; - /* DSP expects stream id from 1 */ - ac->stream_id = 1; ac->adev = a->adev; kref_init(&ac->refcount); @@ -919,8 +916,9 @@ err: * * Return: Will be an negative value on error or zero on success */ -int q6asm_open_write(struct audio_client *ac, uint32_t format, - u32 codec_profile, uint16_t bits_per_sample) +int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, + uint32_t format, u32 codec_profile, + uint16_t bits_per_sample) { struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; @@ -935,7 +933,7 @@ int q6asm_open_write(struct audio_client *ac, uint32_t format, pkt = p; open = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; open->mode_flags = 0x00; @@ -998,8 +996,9 @@ err: } EXPORT_SYMBOL_GPL(q6asm_open_write); -static int __q6asm_run(struct audio_client *ac, uint32_t flags, - uint32_t msw_ts, uint32_t lsw_ts, bool wait) +static int __q6asm_run(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts, + bool wait) { struct asm_session_cmd_run_v2 *run; struct apr_pkt *pkt; @@ -1014,7 +1013,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags, pkt = p; run = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_SESSION_CMD_RUN_V2; run->flags = flags; @@ -1042,10 +1041,10 @@ static int __q6asm_run(struct audio_client *ac, uint32_t flags, * * Return: Will be an negative value on error or zero on success */ -int q6asm_run(struct audio_client *ac, uint32_t flags, +int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { - return __q6asm_run(ac, flags, msw_ts, lsw_ts, true); + return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, true); } EXPORT_SYMBOL_GPL(q6asm_run); @@ -1053,16 +1052,17 @@ EXPORT_SYMBOL_GPL(q6asm_run); * q6asm_run_nowait() - start the audio client withou blocking * * @ac: audio client pointer + * @stream_id: stream id * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw * * Return: Will be an negative value on error or zero on success */ -int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, - uint32_t msw_ts, uint32_t lsw_ts) +int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts) { - return __q6asm_run(ac, flags, msw_ts, lsw_ts, false); + return __q6asm_run(ac, stream_id, flags, msw_ts, lsw_ts, false); } EXPORT_SYMBOL_GPL(q6asm_run_nowait); @@ -1070,6 +1070,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait); * q6asm_media_format_block_multi_ch_pcm() - setup pcm configuration * * @ac: audio client pointer + * @stream_id: stream id * @rate: audio sample rate * @channels: number of audio channels. * @channel_map: channel map pointer @@ -1078,6 +1079,7 @@ EXPORT_SYMBOL_GPL(q6asm_run_nowait); * Return: Will be an negative value on error or zero on success */ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t stream_id, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample) @@ -1096,7 +1098,7 @@ int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1125,8 +1127,8 @@ err: } EXPORT_SYMBOL_GPL(q6asm_media_format_block_multi_ch_pcm); - int q6asm_stream_media_format_block_flac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_flac_cfg *cfg) { struct asm_flac_fmt_blk_v2 *fmt; @@ -1142,7 +1144,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1163,6 +1165,7 @@ int q6asm_stream_media_format_block_flac(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_flac); int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg) { struct asm_wmastdv9_fmt_blk_v2 *fmt; @@ -1178,7 +1181,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1200,6 +1203,7 @@ int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v9); int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg) { struct asm_wmaprov10_fmt_blk_v2 *fmt; @@ -1215,7 +1219,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1238,6 +1242,7 @@ int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_wma_v10); int q6asm_stream_media_format_block_alac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_alac_cfg *cfg) { struct asm_alac_fmt_blk_v2 *fmt; @@ -1253,7 +1258,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1279,6 +1284,7 @@ int q6asm_stream_media_format_block_alac(struct audio_client *ac, EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_alac); int q6asm_stream_media_format_block_ape(struct audio_client *ac, + uint32_t stream_id, struct q6asm_ape_cfg *cfg) { struct asm_ape_fmt_blk_v2 *fmt; @@ -1294,7 +1300,7 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, pkt = p; fmt = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2; fmt->fmt_blk.fmt_blk_size = sizeof(*fmt) - sizeof(fmt->fmt_blk); @@ -1321,6 +1327,7 @@ EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * * @ac: audio client pointer + * @stream_id: stream id * @rate: audio sample rate * @channels: number of audio channels. * @bits_per_sample: bits per sample @@ -1328,7 +1335,9 @@ EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); * Return: Will be an negative value on error or zero on success */ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, - uint32_t rate, uint32_t channels, uint16_t bits_per_sample) + uint32_t stream_id, uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample) { struct asm_multi_channel_pcm_enc_cfg_v2 *enc_cfg; struct apr_pkt *pkt; @@ -1344,7 +1353,7 @@ int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, pkt = p; enc_cfg = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM; enc_cfg->encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2; @@ -1376,10 +1385,11 @@ EXPORT_SYMBOL_GPL(q6asm_enc_cfg_blk_pcm_format_support); * q6asm_read() - read data of period size from audio client * * @ac: audio client pointer + * @stream_id: stream id * * Return: Will be an negative value on error or zero on success */ -int q6asm_read(struct audio_client *ac) +int q6asm_read(struct audio_client *ac, uint32_t stream_id) { struct asm_data_cmd_read_v2 *read; struct audio_port_data *port; @@ -1400,7 +1410,7 @@ int q6asm_read(struct audio_client *ac) spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_CAPTURE]; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; pkt->hdr.opcode = ASM_DATA_CMD_READ_V2; read->buf_addr_lsw = lower_32_bits(ab->phys); @@ -1428,7 +1438,7 @@ int q6asm_read(struct audio_client *ac) } EXPORT_SYMBOL_GPL(q6asm_read); -static int __q6asm_open_read(struct audio_client *ac, +static int __q6asm_open_read(struct audio_client *ac, uint32_t stream_id, uint32_t format, uint16_t bits_per_sample) { struct asm_stream_cmd_open_read_v3 *open; @@ -1444,7 +1454,7 @@ static int __q6asm_open_read(struct audio_client *ac, pkt = p; open = p + APR_HDR_SIZE; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3; /* Stream prio : High, provide meta info with encoded frames */ open->src_endpointype = ASM_END_POINT_DEVICE_MATRIX; @@ -1475,15 +1485,16 @@ static int __q6asm_open_read(struct audio_client *ac, * q6asm_open_read() - Open audio client for reading * * @ac: audio client pointer + * @stream_id: stream id * @format: audio sample format * @bits_per_sample: bits per sample * * Return: Will be an negative value on error or zero on success */ -int q6asm_open_read(struct audio_client *ac, uint32_t format, - uint16_t bits_per_sample) +int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, + uint32_t format, uint16_t bits_per_sample) { - return __q6asm_open_read(ac, format, bits_per_sample); + return __q6asm_open_read(ac, stream_id, format, bits_per_sample); } EXPORT_SYMBOL_GPL(q6asm_open_read); @@ -1491,6 +1502,7 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * q6asm_write_async() - non blocking write * * @ac: audio client pointer + * @stream_id: stream id * @len: length in bytes * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw @@ -1498,8 +1510,8 @@ EXPORT_SYMBOL_GPL(q6asm_open_read); * * Return: Will be an negative value on error or zero on success */ -int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, - uint32_t lsw_ts, uint32_t wflags) +int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t wflags) { struct asm_data_cmd_write_v2 *write; struct audio_port_data *port; @@ -1520,7 +1532,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, spin_lock_irqsave(&ac->lock, flags); port = &ac->port[SNDRV_PCM_STREAM_PLAYBACK]; - q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, ac->stream_id); + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; pkt->hdr.token = port->dsp_buf; @@ -1567,9 +1579,9 @@ static void q6asm_reset_buf_state(struct audio_client *ac) spin_unlock_irqrestore(&ac->lock, flags); } -static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) +static int __q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd, + bool wait) { - int stream_id = ac->stream_id; struct apr_pkt pkt; int rc; @@ -1616,13 +1628,14 @@ static int __q6asm_cmd(struct audio_client *ac, int cmd, bool wait) * q6asm_cmd() - run cmd on audio client * * @ac: audio client pointer + * @stream_id: stream id * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */ -int q6asm_cmd(struct audio_client *ac, int cmd) +int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd) { - return __q6asm_cmd(ac, cmd, true); + return __q6asm_cmd(ac, stream_id, cmd, true); } EXPORT_SYMBOL_GPL(q6asm_cmd); @@ -1630,13 +1643,14 @@ EXPORT_SYMBOL_GPL(q6asm_cmd); * q6asm_cmd_nowait() - non blocking, run cmd on audio client * * @ac: audio client pointer + * @stream_id: stream id * @cmd: command to run on audio client. * * Return: Will be an negative value on error or zero on success */ -int q6asm_cmd_nowait(struct audio_client *ac, int cmd) +int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd) { - return __q6asm_cmd(ac, cmd, false); + return __q6asm_cmd(ac, stream_id, cmd, false); } EXPORT_SYMBOL_GPL(q6asm_cmd_nowait); diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 38a207d6cd95..ceece124dd3d 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -93,37 +93,47 @@ struct audio_client *q6asm_audio_client_alloc(struct device *dev, q6asm_cb cb, void *priv, int session_id, int perf_mode); void q6asm_audio_client_free(struct audio_client *ac); -int q6asm_write_async(struct audio_client *ac, uint32_t len, uint32_t msw_ts, - uint32_t lsw_ts, uint32_t flags); -int q6asm_open_write(struct audio_client *ac, uint32_t format, - u32 codec_profile, uint16_t bits_per_sample); - -int q6asm_open_read(struct audio_client *ac, uint32_t format, +int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, + uint32_t msw_ts, uint32_t lsw_ts, uint32_t flags); +int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, + uint32_t format, u32 codec_profile, uint16_t bits_per_sample); + +int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, + uint32_t format, uint16_t bits_per_sample); int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac, - uint32_t rate, uint32_t channels, uint16_t bits_per_sample); -int q6asm_read(struct audio_client *ac); + uint32_t stream_id, uint32_t rate, + uint32_t channels, + uint16_t bits_per_sample); + +int q6asm_read(struct audio_client *ac, uint32_t stream_id); int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac, + uint32_t stream_id, uint32_t rate, uint32_t channels, u8 channel_map[PCM_MAX_NUM_CHANNEL], uint16_t bits_per_sample); int q6asm_stream_media_format_block_flac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_flac_cfg *cfg); int q6asm_stream_media_format_block_wma_v9(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg); int q6asm_stream_media_format_block_wma_v10(struct audio_client *ac, + uint32_t stream_id, struct q6asm_wma_cfg *cfg); int q6asm_stream_media_format_block_alac(struct audio_client *ac, + uint32_t stream_id, struct q6asm_alac_cfg *cfg); int q6asm_stream_media_format_block_ape(struct audio_client *ac, + uint32_t stream_id, struct q6asm_ape_cfg *cfg); -int q6asm_run(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, - uint32_t lsw_ts); -int q6asm_run_nowait(struct audio_client *ac, uint32_t flags, uint32_t msw_ts, - uint32_t lsw_ts); -int q6asm_cmd(struct audio_client *ac, int cmd); -int q6asm_cmd_nowait(struct audio_client *ac, int cmd); +int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, + uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, + uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd); +int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd); int q6asm_get_session_id(struct audio_client *ac); int q6asm_map_memory_regions(unsigned int dir, struct audio_client *ac, -- cgit From e0c078a8da333eb45745d1c5e4b72f0314b8b742 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:37:59 +0100 Subject: ASoC: q6asm: use flags directly from q6asm-dai use flags set by q6asm-dais directly! This will be useful gapless case where write needs a special flag to indicate that last buffer. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-4-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 8 ++++---- sound/soc/qcom/qdsp6/q6asm.c | 5 +---- sound/soc/qcom/qdsp6/q6asm.h | 1 - 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 0f157f118a3f..0df2fde9b2f3 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -183,7 +183,7 @@ static void event_handler(uint32_t opcode, uint32_t token, case ASM_CLIENT_EVENT_CMD_RUN_DONE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + prtd->pcm_count, 0, 0, 0); break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: prtd->state = Q6ASM_STREAM_STOPPED; @@ -193,7 +193,7 @@ static void event_handler(uint32_t opcode, uint32_t token, snd_pcm_period_elapsed(substream); if (prtd->state == Q6ASM_STREAM_RUNNING) q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + prtd->pcm_count, 0, 0, 0); break; } @@ -512,7 +512,7 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, spin_lock_irqsave(&prtd->lock, flags); if (!prtd->bytes_sent) { q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + prtd->pcm_count, 0, 0, 0); prtd->bytes_sent += prtd->pcm_count; } @@ -538,7 +538,7 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, if (avail >= prtd->pcm_count) { q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, NO_TIMESTAMP); + prtd->pcm_count, 0, 0, 0); prtd->bytes_sent += prtd->pcm_count; } diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 97f448bc7972..f7a94cd53c73 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -1546,10 +1546,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, write->mem_map_handle = ac->port[SNDRV_PCM_STREAM_PLAYBACK].mem_map_handle; - if (wflags == NO_TIMESTAMP) - write->flags = (wflags & 0x800000FF); - else - write->flags = (0x80000000 | wflags); + write->flags = wflags; port->dsp_buf++; diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index ceece124dd3d..9ac020c609c6 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -29,7 +29,6 @@ enum { }; #define MAX_SESSIONS 8 -#define NO_TIMESTAMP 0xFF00 #define FORMAT_LINEAR_PCM 0x0000 struct q6asm_flac_cfg { -- cgit From 50d4e2146cdd48fcc1cd748dfb06dd0fc486eb26 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:00 +0100 Subject: ASoC: q6asm: add length to write command token Add length to write command packet token so that we can track exactly how many bytes are consumed by DSP in the command reply. This is useful in some use-cases where the end of the file/stream is not aligned with period size. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-5-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 4 +++- sound/soc/qcom/qdsp6/q6asm.c | 7 ++++--- sound/soc/qcom/qdsp6/q6asm.h | 3 +++ 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 0df2fde9b2f3..2024917b6c52 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -506,6 +506,7 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, struct snd_compr_stream *substream = prtd->cstream; unsigned long flags; uint64_t avail; + uint32_t bytes_written; switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: @@ -526,7 +527,8 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, case ASM_CLIENT_EVENT_DATA_WRITE_DONE: spin_lock_irqsave(&prtd->lock, flags); - prtd->copied_total += prtd->pcm_count; + bytes_written = token >> ASM_WRITE_TOKEN_LEN_SHIFT; + prtd->copied_total += bytes_written; snd_compr_fragment_elapsed(substream); if (prtd->state != Q6ASM_STREAM_RUNNING) { diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index f7a94cd53c73..a2ac4e71b693 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -670,6 +670,7 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, if (ac->io_mode & ASM_SYNC_IO_MODE) { phys_addr_t phys; unsigned long flags; + int token = hdr->token & ASM_WRITE_TOKEN_MASK; spin_lock_irqsave(&ac->lock, flags); @@ -681,12 +682,12 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, goto done; } - phys = port->buf[hdr->token].phys; + phys = port->buf[token].phys; if (lower_32_bits(phys) != result->opcode || upper_32_bits(phys) != result->status) { dev_err(ac->dev, "Expected addr %pa\n", - &port->buf[hdr->token].phys); + &port->buf[token].phys); spin_unlock_irqrestore(&ac->lock, flags); ret = -EINVAL; goto done; @@ -1535,7 +1536,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, q6asm_add_hdr(ac, &pkt->hdr, pkt_size, false, stream_id); ab = &port->buf[port->dsp_buf]; - pkt->hdr.token = port->dsp_buf; + pkt->hdr.token = port->dsp_buf | (len << ASM_WRITE_TOKEN_LEN_SHIFT); pkt->hdr.opcode = ASM_DATA_CMD_WRITE_V2; write->buf_addr_lsw = lower_32_bits(ab->phys); write->buf_addr_msw = upper_32_bits(ab->phys); diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 9ac020c609c6..312a0452dcdf 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -20,6 +20,9 @@ #define ASM_CLIENT_EVENT_CMD_RUN_DONE 0x1008 #define ASM_CLIENT_EVENT_DATA_WRITE_DONE 0x1009 #define ASM_CLIENT_EVENT_DATA_READ_DONE 0x100a +#define ASM_WRITE_TOKEN_MASK GENMASK(15, 0) +#define ASM_WRITE_TOKEN_LEN_MASK GENMASK(31, 16) +#define ASM_WRITE_TOKEN_LEN_SHIFT 16 enum { LEGACY_PCM_MODE = 0, -- cgit From 39376129783168c51981bd2c195a1d10ae63104f Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:01 +0100 Subject: ASoC: q6asm: add support to remove intial and trailing silence This patch adds support to ASM_DATA_CMD_REMOVE_INITIAL_SILENCE and ASM_DATA_CMD_REMOVE_TRAILING_SILENCE q6asm command to support compressed metadata for gapless playback. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-6-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm.c | 53 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6asm.h | 6 +++++ 2 files changed, 59 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index a2ac4e71b693..8861b70fb0ce 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -51,6 +51,8 @@ #define ASM_STREAM_CMD_OPEN_READWRITE_V2 0x00010D8D #define ASM_MEDIA_FMT_ALAC 0x00012f31 #define ASM_MEDIA_FMT_APE 0x00012f32 +#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67 +#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68 #define ASM_LEGACY_STREAM_SESSION 0 @@ -639,6 +641,8 @@ static int32_t q6asm_stream_callback(struct apr_device *adev, case ASM_STREAM_CMD_OPEN_READWRITE_V2: case ASM_STREAM_CMD_SET_ENCDEC_PARAM: case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2: + case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE: + case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE: if (result->status != 0) { dev_err(ac->dev, "cmd = 0x%x returned error = 0x%x\n", @@ -1324,6 +1328,55 @@ int q6asm_stream_media_format_block_ape(struct audio_client *ac, } EXPORT_SYMBOL_GPL(q6asm_stream_media_format_block_ape); +static int q6asm_stream_remove_silence(struct audio_client *ac, uint32_t stream_id, + uint32_t cmd, + uint32_t num_samples) +{ + uint32_t *samples; + struct apr_pkt *pkt; + void *p; + int rc, pkt_size; + + pkt_size = APR_HDR_SIZE + sizeof(uint32_t); + p = kzalloc(pkt_size, GFP_ATOMIC); + if (!p) + return -ENOMEM; + + pkt = p; + samples = p + APR_HDR_SIZE; + + q6asm_add_hdr(ac, &pkt->hdr, pkt_size, true, stream_id); + + pkt->hdr.opcode = cmd; + *samples = num_samples; + rc = apr_send_pkt(ac->adev, pkt); + if (rc == pkt_size) + rc = 0; + + kfree(pkt); + + return rc; +} + +int q6asm_stream_remove_initial_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t initial_samples) +{ + return q6asm_stream_remove_silence(ac, stream_id, + ASM_DATA_CMD_REMOVE_INITIAL_SILENCE, + initial_samples); +} +EXPORT_SYMBOL_GPL(q6asm_stream_remove_initial_silence); + +int q6asm_stream_remove_trailing_silence(struct audio_client *ac, uint32_t stream_id, + uint32_t trailing_samples) +{ + return q6asm_stream_remove_silence(ac, stream_id, + ASM_DATA_CMD_REMOVE_TRAILING_SILENCE, + trailing_samples); +} +EXPORT_SYMBOL_GPL(q6asm_stream_remove_trailing_silence); + /** * q6asm_enc_cfg_blk_pcm_format_support() - setup pcm configuration for capture * diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 312a0452dcdf..2acfc2274069 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -134,6 +134,12 @@ int q6asm_run(struct audio_client *ac, uint32_t stream_id, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); int q6asm_run_nowait(struct audio_client *ac, uint32_t stream_id, uint32_t flags, uint32_t msw_ts, uint32_t lsw_ts); +int q6asm_stream_remove_initial_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t initial_samples); +int q6asm_stream_remove_trailing_silence(struct audio_client *ac, + uint32_t stream_id, + uint32_t trailing_samples); int q6asm_cmd(struct audio_client *ac, uint32_t stream_id, int cmd); int q6asm_cmd_nowait(struct audio_client *ac, uint32_t stream_id, int cmd); int q6asm_get_session_id(struct audio_client *ac); -- cgit From 983b8864f6033336e619a5936a02aea3026c5742 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:02 +0100 Subject: ASoC: q6asm: add support to gapless flag in q6asm open This patch adds support to gapless flag to q6asm_open_write(). Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-7-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 4 ++-- sound/soc/qcom/qdsp6/q6asm.c | 4 +++- sound/soc/qcom/qdsp6/q6asm.h | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 2024917b6c52..88e8452c1999 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -255,7 +255,7 @@ static int q6asm_dai_prepare(struct snd_soc_component *component, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, FORMAT_LINEAR_PCM, - 0, prtd->bits_per_sample); + 0, prtd->bits_per_sample, false); } else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { ret = q6asm_open_read(prtd->audio_client, prtd->stream_id, FORMAT_LINEAR_PCM, @@ -682,7 +682,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, if (dir == SND_COMPRESS_PLAYBACK) { ret = q6asm_open_write(prtd->audio_client, prtd->stream_id, params->codec.id, params->codec.profile, - prtd->bits_per_sample); + prtd->bits_per_sample, true); if (ret < 0) { dev_err(dev, "q6asm_open_write failed\n"); diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 8861b70fb0ce..d745a02fcd5f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -923,7 +923,7 @@ err: */ int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, uint32_t format, u32 codec_profile, - uint16_t bits_per_sample) + uint16_t bits_per_sample, bool is_gapless) { struct asm_stream_cmd_open_write_v3 *open; struct apr_pkt *pkt; @@ -943,6 +943,8 @@ int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, pkt->hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3; open->mode_flags = 0x00; open->mode_flags |= ASM_LEGACY_STREAM_SESSION; + if (is_gapless) + open->mode_flags |= BIT(ASM_SHIFT_GAPLESS_MODE_FLAG); /* source endpoint : matrix */ open->sink_endpointype = ASM_END_POINT_DEVICE_MATRIX; diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index 2acfc2274069..f20e1441988f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -99,7 +99,7 @@ int q6asm_write_async(struct audio_client *ac, uint32_t stream_id, uint32_t len, uint32_t msw_ts, uint32_t lsw_ts, uint32_t flags); int q6asm_open_write(struct audio_client *ac, uint32_t stream_id, uint32_t format, u32 codec_profile, - uint16_t bits_per_sample); + uint16_t bits_per_sample, bool is_gapless); int q6asm_open_read(struct audio_client *ac, uint32_t stream_id, uint32_t format, uint16_t bits_per_sample); -- cgit From 135bd5ea190f3e31d2289da98a53d28e1be5b6bf Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:03 +0100 Subject: ASoC: q6asm-dai: add next track metadata support This patch adds support to metadata required to do a gapless playback. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-8-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 88e8452c1999..4bd5f57bd1a7 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -67,6 +67,8 @@ struct q6asm_dai_rtd { uint32_t stream_id; uint16_t session_id; enum stream_state state; + uint32_t initial_samples_drop; + uint32_t trailing_samples_drop; }; struct q6asm_dai_data { @@ -865,6 +867,29 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return 0; } +static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_compr_metadata *metadata) +{ + struct snd_compr_runtime *runtime = stream->runtime; + struct q6asm_dai_rtd *prtd = runtime->private_data; + int ret = 0; + + switch (metadata->key) { + case SNDRV_COMPRESS_ENCODER_PADDING: + prtd->trailing_samples_drop = metadata->value[0]; + break; + case SNDRV_COMPRESS_ENCODER_DELAY: + prtd->initial_samples_drop = metadata->value[0]; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static int q6asm_dai_compr_trigger(struct snd_soc_component *component, struct snd_compr_stream *stream, int cmd) { @@ -981,6 +1006,7 @@ static struct snd_compress_ops q6asm_dai_compress_ops = { .open = q6asm_dai_compr_open, .free = q6asm_dai_compr_free, .set_params = q6asm_dai_compr_set_params, + .set_metadata = q6asm_dai_compr_set_metadata, .pointer = q6asm_dai_compr_pointer, .trigger = q6asm_dai_compr_trigger, .get_caps = q6asm_dai_compr_get_caps, -- cgit From 5b39363e54ccca8fee700e5cc6acf42a80ff1de3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:04 +0100 Subject: ASoC: q6asm-dai: prepare set params to accept profile change rearrange code so that it will be easy to change the codec profile at runtime. This means moving exiting set_params to an internal wrapper which can be called when codec profile changes. This is also preparing the code for easy to use in gapless cases. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-9-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 149 ++++++++++++++++++++++----------------- 1 file changed, 85 insertions(+), 64 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 4bd5f57bd1a7..e463d7ca3283 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -50,7 +50,7 @@ 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_codec codec; struct snd_dma_buffer dma_buffer; spinlock_t lock; phys_addr_t phys; @@ -641,15 +641,13 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, return 0; } -static int q6asm_dai_compr_set_params(struct snd_soc_component *component, - struct snd_compr_stream *stream, - struct snd_compr_params *params) +static int __q6asm_dai_compr_set_codec_params(struct snd_soc_component *component, + struct snd_compr_stream *stream, + struct snd_codec *codec, + int stream_id) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; - struct snd_soc_pcm_runtime *rtd = stream->private_data; - int dir = stream->direction; - struct q6asm_dai_data *pdata; struct q6asm_flac_cfg flac_cfg; struct q6asm_wma_cfg wma_cfg; struct q6asm_alac_cfg alac_cfg; @@ -663,53 +661,18 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, struct snd_dec_alac *alac; struct snd_dec_ape *ape; - codec_options = &(prtd->codec_param.codec.options); - - - memcpy(&prtd->codec_param, params, sizeof(*params)); - - pdata = snd_soc_component_get_drvdata(component); - 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, prtd->stream_id, - params->codec.id, params->codec.profile, - prtd->bits_per_sample, true); - - if (ret < 0) { - dev_err(dev, "q6asm_open_write failed\n"); - q6asm_audio_client_free(prtd->audio_client); - prtd->audio_client = NULL; - return ret; - } - } + codec_options = &(prtd->codec.options); - 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; - } + memcpy(&prtd->codec, codec, sizeof(*codec)); - switch (params->codec.id) { + switch (codec->id) { case SND_AUDIOCODEC_FLAC: memset(&flac_cfg, 0x0, sizeof(struct q6asm_flac_cfg)); flac = &codec_options->flac_d; - flac_cfg.ch_cfg = params->codec.ch_in; - flac_cfg.sample_rate = params->codec.sample_rate; + flac_cfg.ch_cfg = codec->ch_in; + flac_cfg.sample_rate = codec->sample_rate; flac_cfg.stream_info_present = 1; flac_cfg.sample_size = flac->sample_size; flac_cfg.min_blk_size = flac->min_blk_size; @@ -718,7 +681,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, flac_cfg.min_frame_size = flac->min_frame_size; ret = q6asm_stream_media_format_block_flac(prtd->audio_client, - prtd->stream_id, + stream_id, &flac_cfg); if (ret < 0) { dev_err(dev, "FLAC CMD Format block failed:%d\n", ret); @@ -731,10 +694,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&wma_cfg, 0x0, sizeof(struct q6asm_wma_cfg)); - wma_cfg.sample_rate = params->codec.sample_rate; - wma_cfg.num_channels = params->codec.ch_in; - wma_cfg.bytes_per_sec = params->codec.bit_rate / 8; - wma_cfg.block_align = params->codec.align; + wma_cfg.sample_rate = codec->sample_rate; + wma_cfg.num_channels = codec->ch_in; + wma_cfg.bytes_per_sec = codec->bit_rate / 8; + wma_cfg.block_align = codec->align; wma_cfg.bits_per_sample = prtd->bits_per_sample; wma_cfg.enc_options = wma->encoder_option; wma_cfg.adv_enc_options = wma->adv_encoder_option; @@ -748,7 +711,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, return -EINVAL; /* check the codec profile */ - switch (params->codec.profile) { + switch (codec->profile) { case SND_AUDIOPROFILE_WMA9: wma_cfg.fmtag = 0x161; wma_v9 = 1; @@ -772,17 +735,17 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, default: dev_err(dev, "Unknown WMA profile:%x\n", - params->codec.profile); + codec->profile); return -EIO; } if (wma_v9) ret = q6asm_stream_media_format_block_wma_v9( - prtd->audio_client, prtd->stream_id, + prtd->audio_client, stream_id, &wma_cfg); else ret = q6asm_stream_media_format_block_wma_v10( - prtd->audio_client, prtd->stream_id, + prtd->audio_client, stream_id, &wma_cfg); if (ret < 0) { dev_err(dev, "WMA9 CMD failed:%d\n", ret); @@ -794,10 +757,10 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&alac_cfg, 0x0, sizeof(alac_cfg)); alac = &codec_options->alac_d; - alac_cfg.sample_rate = params->codec.sample_rate; - alac_cfg.avg_bit_rate = params->codec.bit_rate; + alac_cfg.sample_rate = codec->sample_rate; + alac_cfg.avg_bit_rate = codec->bit_rate; alac_cfg.bit_depth = prtd->bits_per_sample; - alac_cfg.num_channels = params->codec.ch_in; + alac_cfg.num_channels = codec->ch_in; alac_cfg.frame_length = alac->frame_length; alac_cfg.pb = alac->pb; @@ -807,7 +770,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, alac_cfg.compatible_version = alac->compatible_version; alac_cfg.max_frame_bytes = alac->max_frame_bytes; - switch (params->codec.ch_in) { + switch (codec->ch_in) { case 1: alac_cfg.channel_layout_tag = ALAC_CH_LAYOUT_MONO; break; @@ -816,7 +779,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } ret = q6asm_stream_media_format_block_alac(prtd->audio_client, - prtd->stream_id, + stream_id, &alac_cfg); if (ret < 0) { dev_err(dev, "ALAC CMD Format block failed:%d\n", ret); @@ -828,8 +791,8 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, memset(&ape_cfg, 0x0, sizeof(ape_cfg)); ape = &codec_options->ape_d; - ape_cfg.sample_rate = params->codec.sample_rate; - ape_cfg.num_channels = params->codec.ch_in; + ape_cfg.sample_rate = codec->sample_rate; + ape_cfg.num_channels = codec->ch_in; ape_cfg.bits_per_sample = prtd->bits_per_sample; ape_cfg.compatible_version = ape->compatible_version; @@ -841,7 +804,7 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, ape_cfg.seek_table_present = ape->seek_table_present; ret = q6asm_stream_media_format_block_ape(prtd->audio_client, - prtd->stream_id, + stream_id, &ape_cfg); if (ret < 0) { dev_err(dev, "APE CMD Format block failed:%d\n", ret); @@ -853,6 +816,64 @@ static int q6asm_dai_compr_set_params(struct snd_soc_component *component, break; } + return 0; +} + +static int q6asm_dai_compr_set_params(struct snd_soc_component *component, + 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; + int dir = stream->direction; + struct q6asm_dai_data *pdata; + struct device *dev = component->dev; + int ret; + + pdata = snd_soc_component_get_drvdata(component); + 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, prtd->stream_id, params->codec.id, + params->codec.profile, prtd->bits_per_sample, + true); + + 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_dai_compr_set_codec_params(component, stream, + ¶ms->codec, + prtd->stream_id); + if (ret) { + dev_err(dev, "codec param setup 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); -- cgit From ee941a338ad67dfd852826eec381d8584edf29f2 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:05 +0100 Subject: ASoC: q6asm-dai: add gapless support Add support to gapless playback by implementing metadata, next_track, drain and partial drain support. Gapless on Q6ASM is implemented by opening 2 streams in a single q6asm stream and toggling them on next track. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-10-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 103 ++++++++++++++++++++++++++++++++++++--- sound/soc/qcom/qdsp6/q6asm.h | 1 + 2 files changed, 98 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index e463d7ca3283..68da769a25ec 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -64,11 +64,14 @@ struct q6asm_dai_rtd { uint16_t bits_per_sample; uint16_t source; /* Encoding source bit mask */ struct audio_client *audio_client; + uint32_t next_track_stream_id; + bool next_track; uint32_t stream_id; uint16_t session_id; enum stream_state state; uint32_t initial_samples_drop; uint32_t trailing_samples_drop; + bool notify_on_drain; }; struct q6asm_dai_data { @@ -507,13 +510,19 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, struct q6asm_dai_rtd *prtd = priv; struct snd_compr_stream *substream = prtd->cstream; unsigned long flags; + u32 wflags = 0; uint64_t avail; - uint32_t bytes_written; + uint32_t bytes_written, bytes_to_write; + bool is_last_buffer = false; switch (opcode) { case ASM_CLIENT_EVENT_CMD_RUN_DONE: spin_lock_irqsave(&prtd->lock, flags); if (!prtd->bytes_sent) { + q6asm_stream_remove_initial_silence(prtd->audio_client, + prtd->stream_id, + prtd->initial_samples_drop); + q6asm_write_async(prtd->audio_client, prtd->stream_id, prtd->pcm_count, 0, 0, 0); prtd->bytes_sent += prtd->pcm_count; @@ -523,7 +532,30 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, break; case ASM_CLIENT_EVENT_CMD_EOS_DONE: - prtd->state = Q6ASM_STREAM_STOPPED; + spin_lock_irqsave(&prtd->lock, flags); + if (prtd->notify_on_drain) { + if (substream->partial_drain) { + /* + * Close old stream and make it stale, switch + * the active stream now! + */ + q6asm_cmd_nowait(prtd->audio_client, + prtd->stream_id, + CMD_CLOSE); + /* + * vaild stream ids start from 1, So we are + * toggling this between 1 and 2. + */ + prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1); + } + + snd_compr_drain_notify(prtd->cstream); + prtd->notify_on_drain = false; + + } else { + prtd->state = Q6ASM_STREAM_STOPPED; + } + spin_unlock_irqrestore(&prtd->lock, flags); break; case ASM_CLIENT_EVENT_DATA_WRITE_DONE: @@ -539,13 +571,32 @@ static void compress_event_handler(uint32_t opcode, uint32_t token, } avail = prtd->bytes_received - prtd->bytes_sent; + if (avail > prtd->pcm_count) { + bytes_to_write = prtd->pcm_count; + } else { + if (substream->partial_drain || prtd->notify_on_drain) + is_last_buffer = true; + bytes_to_write = avail; + } + + if (bytes_to_write) { + if (substream->partial_drain && is_last_buffer) { + wflags |= ASM_LAST_BUFFER_FLAG; + q6asm_stream_remove_trailing_silence(prtd->audio_client, + prtd->stream_id, + prtd->trailing_samples_drop); + } - if (avail >= prtd->pcm_count) { q6asm_write_async(prtd->audio_client, prtd->stream_id, - prtd->pcm_count, 0, 0, 0); - prtd->bytes_sent += prtd->pcm_count; + bytes_to_write, 0, 0, wflags); + + prtd->bytes_sent += bytes_to_write; } + if (prtd->notify_on_drain && is_last_buffer) + q6asm_cmd_nowait(prtd->audio_client, + prtd->stream_id, CMD_EOS); + spin_unlock_irqrestore(&prtd->lock, flags); break; @@ -625,9 +676,15 @@ static int q6asm_dai_compr_free(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = stream->private_data; if (prtd->audio_client) { - if (prtd->state) + if (prtd->state) { q6asm_cmd(prtd->audio_client, prtd->stream_id, CMD_CLOSE); + if (prtd->next_track_stream_id) { + q6asm_cmd(prtd->audio_client, + prtd->next_track_stream_id, + CMD_CLOSE); + } + } snd_dma_free_pages(&prtd->dma_buffer); q6asm_unmap_memory_regions(stream->direction, @@ -902,6 +959,32 @@ static int q6asm_dai_compr_set_metadata(struct snd_soc_component *component, break; case SNDRV_COMPRESS_ENCODER_DELAY: prtd->initial_samples_drop = metadata->value[0]; + if (prtd->next_track_stream_id) { + ret = q6asm_open_write(prtd->audio_client, + prtd->next_track_stream_id, + prtd->codec.id, + prtd->codec.profile, + prtd->bits_per_sample, + true); + if (ret < 0) { + dev_err(component->dev, "q6asm_open_write failed\n"); + return ret; + } + ret = __q6asm_dai_compr_set_codec_params(component, stream, + &prtd->codec, + prtd->next_track_stream_id); + if (ret < 0) { + dev_err(component->dev, "q6asm_open_write failed\n"); + return ret; + } + + ret = q6asm_stream_remove_initial_silence(prtd->audio_client, + prtd->next_track_stream_id, + prtd->initial_samples_drop); + prtd->next_track_stream_id = 0; + + } + break; default: ret = -EINVAL; @@ -935,6 +1018,14 @@ static int q6asm_dai_compr_trigger(struct snd_soc_component *component, ret = q6asm_cmd_nowait(prtd->audio_client, prtd->stream_id, CMD_PAUSE); break; + case SND_COMPR_TRIGGER_NEXT_TRACK: + prtd->next_track = true; + prtd->next_track_stream_id = (prtd->stream_id == 1 ? 2 : 1); + break; + case SND_COMPR_TRIGGER_DRAIN: + case SND_COMPR_TRIGGER_PARTIAL_DRAIN: + prtd->notify_on_drain = true; + break; default: ret = -EINVAL; break; diff --git a/sound/soc/qcom/qdsp6/q6asm.h b/sound/soc/qcom/qdsp6/q6asm.h index f20e1441988f..82e584aa534f 100644 --- a/sound/soc/qcom/qdsp6/q6asm.h +++ b/sound/soc/qcom/qdsp6/q6asm.h @@ -33,6 +33,7 @@ enum { #define MAX_SESSIONS 8 #define FORMAT_LINEAR_PCM 0x0000 +#define ASM_LAST_BUFFER_FLAG BIT(30) struct q6asm_flac_cfg { u32 sample_rate; -- cgit From a08cd56a45245ea422d77e7ee5ce20d5f21f5b5d Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Mon, 27 Jul 2020 10:38:06 +0100 Subject: ASoC: q6asm-dai: add support to copy callback During gapless playback, its possible for previous track to end at unaligned boundary, starting next track on the same boundary can lead to unaligned address exception in dsp. So implement copy callback for finer control on the buffer offsets. Signed-off-by: Srinivas Kandagatla Tested-by: Vinod Koul Reviewed-by: Pierre-Louis Bossart Reviewed-by: Vinod Koul Link: https://lore.kernel.org/r/20200727093806.17089-11-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm-dai.c | 65 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index 68da769a25ec..a1dd31f306ce 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -1052,16 +1052,71 @@ static int q6asm_dai_compr_pointer(struct snd_soc_component *component, return 0; } -static int q6asm_dai_compr_ack(struct snd_soc_component *component, - struct snd_compr_stream *stream, - size_t count) +static int q6asm_compr_copy(struct snd_soc_component *component, + struct snd_compr_stream *stream, char __user *buf, + size_t count) { struct snd_compr_runtime *runtime = stream->runtime; struct q6asm_dai_rtd *prtd = runtime->private_data; unsigned long flags; + u32 wflags = 0; + int avail, bytes_in_flight = 0; + void *dstn; + size_t copy; + u32 app_pointer; + u32 bytes_received; + + bytes_received = prtd->bytes_received; + + /** + * Make sure that next track data pointer is aligned at 32 bit boundary + * This is a Mandatory requirement from DSP data buffers alignment + */ + if (prtd->next_track) + bytes_received = ALIGN(prtd->bytes_received, prtd->pcm_count); + + app_pointer = bytes_received/prtd->pcm_size; + app_pointer = bytes_received - (app_pointer * prtd->pcm_size); + dstn = prtd->dma_buffer.area + app_pointer; + + if (count < prtd->pcm_size - app_pointer) { + if (copy_from_user(dstn, buf, count)) + return -EFAULT; + } else { + copy = prtd->pcm_size - app_pointer; + if (copy_from_user(dstn, buf, copy)) + return -EFAULT; + if (copy_from_user(prtd->dma_buffer.area, buf + copy, + count - copy)) + return -EFAULT; + } spin_lock_irqsave(&prtd->lock, flags); - prtd->bytes_received += count; + + bytes_in_flight = prtd->bytes_received - prtd->copied_total; + + if (prtd->next_track) { + prtd->next_track = false; + prtd->copied_total = ALIGN(prtd->copied_total, prtd->pcm_count); + prtd->bytes_sent = ALIGN(prtd->bytes_sent, prtd->pcm_count); + } + + prtd->bytes_received = bytes_received + count; + + /* Kick off the data to dsp if its starving!! */ + if (prtd->state == Q6ASM_STREAM_RUNNING && (bytes_in_flight == 0)) { + uint32_t bytes_to_write = prtd->pcm_count; + + avail = prtd->bytes_received - prtd->bytes_sent; + + if (avail < prtd->pcm_count) + bytes_to_write = avail; + + q6asm_write_async(prtd->audio_client, prtd->stream_id, + bytes_to_write, 0, 0, wflags); + prtd->bytes_sent += bytes_to_write; + } + spin_unlock_irqrestore(&prtd->lock, flags); return count; @@ -1124,7 +1179,7 @@ static struct snd_compress_ops q6asm_dai_compress_ops = { .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, + .copy = q6asm_compr_copy, }; static int q6asm_dai_pcm_new(struct snd_soc_component *component, -- cgit From 21f279f34c212e82f0330697394840898908f7a6 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 11 Aug 2020 17:57:23 +0100 Subject: ASoC: da7219: Move required devm_* allocations to device level code In preparation for cleanup of device level and codec level probe funcitonality, all necessary devm_* allocations and fw retrieval functions are moved to the I2C probe level code. Signed-off-by: Adam Thomson Link: https://lore.kernel.org/r/7a9a2ead6e37820a6025c0a62dc45952d5032ab7.1597164865.git.Adam.Thomson.Opensource@diasemi.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219-aad.c | 85 +++++++++++++++++++++++++------------------ sound/soc/codecs/da7219-aad.h | 3 ++ sound/soc/codecs/da7219.c | 30 ++++++++------- 3 files changed, 70 insertions(+), 48 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index b1dfd91609f7..48081d71c22c 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -460,7 +460,7 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data) */ static enum da7219_aad_micbias_pulse_lvl - da7219_aad_fw_micbias_pulse_lvl(struct snd_soc_component *component, u32 val) + da7219_aad_fw_micbias_pulse_lvl(struct device *dev, u32 val) { switch (val) { case 2800: @@ -468,13 +468,13 @@ static enum da7219_aad_micbias_pulse_lvl case 2900: return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V; default: - dev_warn(component->dev, "Invalid micbias pulse level"); + dev_warn(dev, "Invalid micbias pulse level"); return DA7219_AAD_MICBIAS_PULSE_LVL_OFF; } } static enum da7219_aad_btn_cfg - da7219_aad_fw_btn_cfg(struct snd_soc_component *component, u32 val) + da7219_aad_fw_btn_cfg(struct device *dev, u32 val) { switch (val) { case 2: @@ -492,13 +492,13 @@ static enum da7219_aad_btn_cfg case 500: return DA7219_AAD_BTN_CFG_500MS; default: - dev_warn(component->dev, "Invalid button config"); + dev_warn(dev, "Invalid button config"); return DA7219_AAD_BTN_CFG_10MS; } } static enum da7219_aad_mic_det_thr - da7219_aad_fw_mic_det_thr(struct snd_soc_component *component, u32 val) + da7219_aad_fw_mic_det_thr(struct device *dev, u32 val) { switch (val) { case 200: @@ -510,13 +510,13 @@ static enum da7219_aad_mic_det_thr case 1000: return DA7219_AAD_MIC_DET_THR_1000_OHMS; default: - dev_warn(component->dev, "Invalid mic detect threshold"); + dev_warn(dev, "Invalid mic detect threshold"); return DA7219_AAD_MIC_DET_THR_500_OHMS; } } static enum da7219_aad_jack_ins_deb - da7219_aad_fw_jack_ins_deb(struct snd_soc_component *component, u32 val) + da7219_aad_fw_jack_ins_deb(struct device *dev, u32 val) { switch (val) { case 5: @@ -536,13 +536,13 @@ static enum da7219_aad_jack_ins_deb case 1000: return DA7219_AAD_JACK_INS_DEB_1S; default: - dev_warn(component->dev, "Invalid jack insert debounce"); + dev_warn(dev, "Invalid jack insert debounce"); return DA7219_AAD_JACK_INS_DEB_20MS; } } static enum da7219_aad_jack_det_rate - da7219_aad_fw_jack_det_rate(struct snd_soc_component *component, const char *str) + da7219_aad_fw_jack_det_rate(struct device *dev, const char *str) { if (!strcmp(str, "32ms_64ms")) { return DA7219_AAD_JACK_DET_RATE_32_64MS; @@ -553,13 +553,13 @@ static enum da7219_aad_jack_det_rate } else if (!strcmp(str, "256ms_512ms")) { return DA7219_AAD_JACK_DET_RATE_256_512MS; } else { - dev_warn(component->dev, "Invalid jack detect rate"); + dev_warn(dev, "Invalid jack detect rate"); return DA7219_AAD_JACK_DET_RATE_256_512MS; } } static enum da7219_aad_jack_rem_deb - da7219_aad_fw_jack_rem_deb(struct snd_soc_component *component, u32 val) + da7219_aad_fw_jack_rem_deb(struct device *dev, u32 val) { switch (val) { case 1: @@ -571,13 +571,13 @@ static enum da7219_aad_jack_rem_deb case 20: return DA7219_AAD_JACK_REM_DEB_20MS; default: - dev_warn(component->dev, "Invalid jack removal debounce"); + dev_warn(dev, "Invalid jack removal debounce"); return DA7219_AAD_JACK_REM_DEB_1MS; } } static enum da7219_aad_btn_avg - da7219_aad_fw_btn_avg(struct snd_soc_component *component, u32 val) + da7219_aad_fw_btn_avg(struct device *dev, u32 val) { switch (val) { case 1: @@ -589,13 +589,13 @@ static enum da7219_aad_btn_avg case 8: return DA7219_AAD_BTN_AVG_8; default: - dev_warn(component->dev, "Invalid button average value"); + dev_warn(dev, "Invalid button average value"); return DA7219_AAD_BTN_AVG_2; } } static enum da7219_aad_adc_1bit_rpt - da7219_aad_fw_adc_1bit_rpt(struct snd_soc_component *component, u32 val) + da7219_aad_fw_adc_1bit_rpt(struct device *dev, u32 val) { switch (val) { case 1: @@ -607,14 +607,13 @@ static enum da7219_aad_adc_1bit_rpt case 8: return DA7219_AAD_ADC_1BIT_RPT_8; default: - dev_warn(component->dev, "Invalid ADC 1-bit repeat value"); + dev_warn(dev, "Invalid ADC 1-bit repeat value"); return DA7219_AAD_ADC_1BIT_RPT_1; } } -static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component *component) +static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) { - struct device *dev = component->dev; struct i2c_client *i2c = to_i2c_client(dev); struct fwnode_handle *aad_np; struct da7219_aad_pdata *aad_pdata; @@ -634,7 +633,7 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component if (fwnode_property_read_u32(aad_np, "dlg,micbias-pulse-lvl", &fw_val32) >= 0) aad_pdata->micbias_pulse_lvl = - da7219_aad_fw_micbias_pulse_lvl(component, fw_val32); + da7219_aad_fw_micbias_pulse_lvl(dev, fw_val32); else aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF; @@ -643,31 +642,31 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component aad_pdata->micbias_pulse_time = fw_val32; if (fwnode_property_read_u32(aad_np, "dlg,btn-cfg", &fw_val32) >= 0) - aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(component, fw_val32); + aad_pdata->btn_cfg = da7219_aad_fw_btn_cfg(dev, fw_val32); else aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS; if (fwnode_property_read_u32(aad_np, "dlg,mic-det-thr", &fw_val32) >= 0) aad_pdata->mic_det_thr = - da7219_aad_fw_mic_det_thr(component, fw_val32); + da7219_aad_fw_mic_det_thr(dev, fw_val32); else aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS; if (fwnode_property_read_u32(aad_np, "dlg,jack-ins-deb", &fw_val32) >= 0) aad_pdata->jack_ins_deb = - da7219_aad_fw_jack_ins_deb(component, fw_val32); + da7219_aad_fw_jack_ins_deb(dev, fw_val32); else aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str)) aad_pdata->jack_det_rate = - da7219_aad_fw_jack_det_rate(component, fw_str); + da7219_aad_fw_jack_det_rate(dev, fw_str); else aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS; if (fwnode_property_read_u32(aad_np, "dlg,jack-rem-deb", &fw_val32) >= 0) aad_pdata->jack_rem_deb = - da7219_aad_fw_jack_rem_deb(component, fw_val32); + da7219_aad_fw_jack_rem_deb(dev, fw_val32); else aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS; @@ -692,13 +691,13 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct snd_soc_component aad_pdata->c_mic_btn_thr = 0x3E; if (fwnode_property_read_u32(aad_np, "dlg,btn-avg", &fw_val32) >= 0) - aad_pdata->btn_avg = da7219_aad_fw_btn_avg(component, fw_val32); + aad_pdata->btn_avg = da7219_aad_fw_btn_avg(dev, fw_val32); else aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2; if (fwnode_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &fw_val32) >= 0) aad_pdata->adc_1bit_rpt = - da7219_aad_fw_adc_1bit_rpt(component, fw_val32); + da7219_aad_fw_adc_1bit_rpt(dev, fw_val32); else aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1; @@ -887,21 +886,13 @@ void da7219_aad_resume(struct snd_soc_component *component) int da7219_aad_init(struct snd_soc_component *component) { struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - struct da7219_aad_priv *da7219_aad; + struct da7219_aad_priv *da7219_aad = da7219->aad; u8 mask[DA7219_AAD_IRQ_REG_MAX]; int ret; - da7219_aad = devm_kzalloc(component->dev, sizeof(*da7219_aad), GFP_KERNEL); - if (!da7219_aad) - return -ENOMEM; - - da7219->aad = da7219_aad; da7219_aad->component = component; /* Handle any DT/ACPI/platform data */ - if (da7219->pdata && !da7219->pdata->aad_pdata) - da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(component); - da7219_aad_handle_pdata(component); /* Disable button detection */ @@ -947,6 +938,30 @@ void da7219_aad_exit(struct snd_soc_component *component) } EXPORT_SYMBOL_GPL(da7219_aad_exit); +/* + * AAD related I2C probe handling + */ + +int da7219_aad_probe(struct i2c_client *i2c) +{ + struct da7219_priv *da7219 = i2c_get_clientdata(i2c); + struct device *dev = &i2c->dev; + struct da7219_aad_priv *da7219_aad; + + da7219_aad = devm_kzalloc(dev, sizeof(*da7219_aad), GFP_KERNEL); + if (!da7219_aad) + return -ENOMEM; + + da7219->aad = da7219_aad; + + /* Retrieve any DT/ACPI/platform data */ + if (da7219->pdata && !da7219->pdata->aad_pdata) + da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(da7219_aad_probe); + MODULE_DESCRIPTION("ASoC DA7219 AAD Driver"); MODULE_AUTHOR("Adam Thomson "); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h index cfa46fba2390..f48a12012ef3 100644 --- a/sound/soc/codecs/da7219-aad.h +++ b/sound/soc/codecs/da7219-aad.h @@ -212,4 +212,7 @@ void da7219_aad_resume(struct snd_soc_component *component); int da7219_aad_init(struct snd_soc_component *component); void da7219_aad_exit(struct snd_soc_component *component); +/* I2C Probe */ +int da7219_aad_probe(struct i2c_client *i2c); + #endif /* __DA7219_AAD_H */ diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 153ea30b5a8f..577b5a614c64 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1753,9 +1753,8 @@ static enum da7219_mic_amp_in_sel } } -static struct da7219_pdata *da7219_fw_to_pdata(struct snd_soc_component *component) +static struct da7219_pdata *da7219_fw_to_pdata(struct device *dev) { - struct device *dev = component->dev; struct da7219_pdata *pdata; const char *of_str; u32 of_val32; @@ -2291,10 +2290,6 @@ static int da7219_probe(struct snd_soc_component *component) } /* Handle DT/ACPI/Platform data */ - da7219->pdata = dev_get_platdata(component->dev); - if (!da7219->pdata) - da7219->pdata = da7219_fw_to_pdata(component); - da7219_handle_pdata(component); /* Check if MCLK provided */ @@ -2571,11 +2566,12 @@ static const struct regmap_config da7219_regmap_config = { static int da7219_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { + struct device *dev = &i2c->dev; struct da7219_priv *da7219; unsigned int system_active, system_status; int i, ret; - da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv), + da7219 = devm_kzalloc(dev, sizeof(struct da7219_priv), GFP_KERNEL); if (!da7219) return -ENOMEM; @@ -2585,7 +2581,7 @@ static int da7219_i2c_probe(struct i2c_client *i2c, da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config); if (IS_ERR(da7219->regmap)) { ret = PTR_ERR(da7219->regmap); - dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret); + dev_err(dev, "regmap_init() failed: %d\n", ret); return ret; } @@ -2620,12 +2616,20 @@ static int da7219_i2c_probe(struct i2c_client *i2c, regcache_cache_bypass(da7219->regmap, false); - ret = devm_snd_soc_register_component(&i2c->dev, - &soc_component_dev_da7219, - &da7219_dai, 1); + /* Retrieve DT/ACPI/Platform data */ + da7219->pdata = dev_get_platdata(dev); + if (!da7219->pdata) + da7219->pdata = da7219_fw_to_pdata(dev); + + /* AAD */ + ret = da7219_aad_probe(i2c); + if (ret) + return ret; + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_da7219, + &da7219_dai, 1); if (ret < 0) { - dev_err(&i2c->dev, "Failed to register da7219 component: %d\n", - ret); + dev_err(dev, "Failed to register da7219 component: %d\n", ret); } return ret; } -- cgit From aa5b18d1c29023b315073661b74c67f91bf2f27c Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 11 Aug 2020 17:57:24 +0100 Subject: ASoC: da7219: Move soft reset handling to codec level probe As part of the reorganisation of the device level and codec level probe functionlity, the soft reset handling should really reside at the codec level and after the instantiation of supplies. This commit makes the relevant changes to support this change of scope including the remove of devm_* functions being called for regulator instantiation at the codec level. Signed-off-by: Adam Thomson Link: https://lore.kernel.org/r/f7603a4855647429b754ce76f887ec441622015c.1597164865.git.Adam.Thomson.Opensource@diasemi.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 371 +++++++++++++++++++++++----------------------- 1 file changed, 188 insertions(+), 183 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 577b5a614c64..3bdfd8c00602 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1846,19 +1846,19 @@ static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = { [DA7219_SUPPLY_VDDIO] = "VDDIO", }; -static int da7219_handle_supplies(struct snd_soc_component *component) +static int da7219_handle_supplies(struct snd_soc_component *component, + u8 *io_voltage_lvl) { struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); struct regulator *vddio; - u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; int i, ret; /* Get required supplies */ for (i = 0; i < DA7219_NUM_SUPPLIES; ++i) da7219->supplies[i].supply = da7219_supply_names[i]; - ret = devm_regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES, - da7219->supplies); + ret = regulator_bulk_get(component->dev, DA7219_NUM_SUPPLIES, + da7219->supplies); if (ret) { dev_err(component->dev, "Failed to get supplies"); return ret; @@ -1870,21 +1870,18 @@ static int da7219_handle_supplies(struct snd_soc_component *component) if (ret < 1200000) dev_warn(component->dev, "Invalid VDDIO voltage\n"); else if (ret < 2800000) - io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; + *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; + else + *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; /* Enable main supplies */ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); if (ret) { dev_err(component->dev, "Failed to enable supplies"); + regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); return ret; } - /* Ensure device in active mode */ - snd_soc_component_write(component, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK); - - /* Update IO voltage level range */ - snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl); - return 0; } @@ -2250,6 +2247,142 @@ static void da7219_handle_pdata(struct snd_soc_component *component) } } + +/* + * Regmap configs + */ + +static struct reg_default da7219_reg_defaults[] = { + { DA7219_MIC_1_SELECT, 0x00 }, + { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, + { DA7219_SR_24_48, 0x00 }, + { DA7219_SR, 0x0A }, + { DA7219_CIF_I2C_ADDR_CFG, 0x02 }, + { DA7219_PLL_CTRL, 0x10 }, + { DA7219_PLL_FRAC_TOP, 0x00 }, + { DA7219_PLL_FRAC_BOT, 0x00 }, + { DA7219_PLL_INTEGER, 0x20 }, + { DA7219_DIG_ROUTING_DAI, 0x10 }, + { DA7219_DAI_CLK_MODE, 0x01 }, + { DA7219_DAI_CTRL, 0x28 }, + { DA7219_DAI_TDM_CTRL, 0x40 }, + { DA7219_DIG_ROUTING_DAC, 0x32 }, + { DA7219_DAI_OFFSET_LOWER, 0x00 }, + { DA7219_DAI_OFFSET_UPPER, 0x00 }, + { DA7219_REFERENCES, 0x08 }, + { DA7219_MIXIN_L_SELECT, 0x00 }, + { DA7219_MIXIN_L_GAIN, 0x03 }, + { DA7219_ADC_L_GAIN, 0x6F }, + { DA7219_ADC_FILTERS1, 0x80 }, + { DA7219_MIC_1_GAIN, 0x01 }, + { DA7219_SIDETONE_CTRL, 0x40 }, + { DA7219_SIDETONE_GAIN, 0x0E }, + { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 }, + { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 }, + { DA7219_DAC_FILTERS5, 0x00 }, + { DA7219_DAC_FILTERS2, 0x88 }, + { DA7219_DAC_FILTERS3, 0x88 }, + { DA7219_DAC_FILTERS4, 0x08 }, + { DA7219_DAC_FILTERS1, 0x80 }, + { DA7219_DAC_L_GAIN, 0x6F }, + { DA7219_DAC_R_GAIN, 0x6F }, + { DA7219_CP_CTRL, 0x20 }, + { DA7219_HP_L_GAIN, 0x39 }, + { DA7219_HP_R_GAIN, 0x39 }, + { DA7219_MIXOUT_L_SELECT, 0x00 }, + { DA7219_MIXOUT_R_SELECT, 0x00 }, + { DA7219_MICBIAS_CTRL, 0x03 }, + { DA7219_MIC_1_CTRL, 0x40 }, + { DA7219_MIXIN_L_CTRL, 0x40 }, + { DA7219_ADC_L_CTRL, 0x40 }, + { DA7219_DAC_L_CTRL, 0x40 }, + { DA7219_DAC_R_CTRL, 0x40 }, + { DA7219_HP_L_CTRL, 0x40 }, + { DA7219_HP_R_CTRL, 0x40 }, + { DA7219_MIXOUT_L_CTRL, 0x10 }, + { DA7219_MIXOUT_R_CTRL, 0x10 }, + { DA7219_CHIP_ID1, 0x23 }, + { DA7219_CHIP_ID2, 0x93 }, + { DA7219_IO_CTRL, 0x00 }, + { DA7219_GAIN_RAMP_CTRL, 0x00 }, + { DA7219_PC_COUNT, 0x02 }, + { DA7219_CP_VOL_THRESHOLD1, 0x0E }, + { DA7219_DIG_CTRL, 0x00 }, + { DA7219_ALC_CTRL2, 0x00 }, + { DA7219_ALC_CTRL3, 0x00 }, + { DA7219_ALC_NOISE, 0x3F }, + { DA7219_ALC_TARGET_MIN, 0x3F }, + { DA7219_ALC_TARGET_MAX, 0x00 }, + { DA7219_ALC_GAIN_LIMITS, 0xFF }, + { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 }, + { DA7219_ALC_ANTICLIP_CTRL, 0x00 }, + { DA7219_ALC_ANTICLIP_LEVEL, 0x00 }, + { DA7219_DAC_NG_SETUP_TIME, 0x00 }, + { DA7219_DAC_NG_OFF_THRESH, 0x00 }, + { DA7219_DAC_NG_ON_THRESH, 0x00 }, + { DA7219_DAC_NG_CTRL, 0x00 }, + { DA7219_TONE_GEN_CFG1, 0x00 }, + { DA7219_TONE_GEN_CFG2, 0x00 }, + { DA7219_TONE_GEN_CYCLES, 0x00 }, + { DA7219_TONE_GEN_FREQ1_L, 0x55 }, + { DA7219_TONE_GEN_FREQ1_U, 0x15 }, + { DA7219_TONE_GEN_FREQ2_L, 0x00 }, + { DA7219_TONE_GEN_FREQ2_U, 0x40 }, + { DA7219_TONE_GEN_ON_PER, 0x02 }, + { DA7219_TONE_GEN_OFF_PER, 0x01 }, + { DA7219_ACCDET_IRQ_MASK_A, 0x00 }, + { DA7219_ACCDET_IRQ_MASK_B, 0x00 }, + { DA7219_ACCDET_CONFIG_1, 0xD6 }, + { DA7219_ACCDET_CONFIG_2, 0x34 }, + { DA7219_ACCDET_CONFIG_3, 0x0A }, + { DA7219_ACCDET_CONFIG_4, 0x16 }, + { DA7219_ACCDET_CONFIG_5, 0x21 }, + { DA7219_ACCDET_CONFIG_6, 0x3E }, + { DA7219_ACCDET_CONFIG_7, 0x01 }, + { DA7219_SYSTEM_ACTIVE, 0x00 }, +}; + +static bool da7219_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case DA7219_MIC_1_GAIN_STATUS: + case DA7219_MIXIN_L_GAIN_STATUS: + case DA7219_ADC_L_GAIN_STATUS: + case DA7219_DAC_L_GAIN_STATUS: + case DA7219_DAC_R_GAIN_STATUS: + case DA7219_HP_L_GAIN_STATUS: + case DA7219_HP_R_GAIN_STATUS: + case DA7219_CIF_CTRL: + case DA7219_PLL_SRM_STS: + case DA7219_ALC_CTRL1: + case DA7219_SYSTEM_MODES_INPUT: + case DA7219_SYSTEM_MODES_OUTPUT: + case DA7219_ALC_OFFSET_AUTO_M_L: + case DA7219_ALC_OFFSET_AUTO_U_L: + case DA7219_TONE_GEN_CFG1: + case DA7219_ACCDET_STATUS_A: + case DA7219_ACCDET_STATUS_B: + case DA7219_ACCDET_IRQ_EVENT_A: + case DA7219_ACCDET_IRQ_EVENT_B: + case DA7219_ACCDET_CONFIG_8: + case DA7219_SYSTEM_STATUS: + return true; + default: + return false; + } +} + +static const struct regmap_config da7219_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = DA7219_SYSTEM_ACTIVE, + .reg_defaults = da7219_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults), + .volatile_reg = da7219_volatile_register, + .cache_type = REGCACHE_RBTREE, +}; + static struct reg_sequence da7219_rev_aa_patch[] = { { DA7219_REFERENCES, 0x08 }, }; @@ -2257,18 +2390,56 @@ static struct reg_sequence da7219_rev_aa_patch[] = { static int da7219_probe(struct snd_soc_component *component) { struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - unsigned int rev; - int ret; + unsigned int system_active, system_status, rev; + u8 io_voltage_lvl; + int i, ret; da7219->component = component; mutex_init(&da7219->ctrl_lock); mutex_init(&da7219->pll_lock); /* Regulator configuration */ - ret = da7219_handle_supplies(component); + ret = da7219_handle_supplies(component, &io_voltage_lvl); if (ret) return ret; + regcache_cache_bypass(da7219->regmap, true); + + /* Disable audio paths if still active from previous start */ + regmap_read(da7219->regmap, DA7219_SYSTEM_ACTIVE, &system_active); + if (system_active) { + regmap_write(da7219->regmap, DA7219_GAIN_RAMP_CTRL, + DA7219_GAIN_RAMP_RATE_NOMINAL); + regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_INPUT, 0x00); + regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_OUTPUT, 0x01); + + for (i = 0; i < DA7219_SYS_STAT_CHECK_RETRIES; ++i) { + regmap_read(da7219->regmap, DA7219_SYSTEM_STATUS, + &system_status); + if (!system_status) + break; + + msleep(DA7219_SYS_STAT_CHECK_DELAY); + } + } + + /* Soft reset component */ + regmap_write_bits(da7219->regmap, DA7219_ACCDET_CONFIG_1, + DA7219_ACCDET_EN_MASK, 0); + regmap_write_bits(da7219->regmap, DA7219_CIF_CTRL, + DA7219_CIF_REG_SOFT_RESET_MASK, + DA7219_CIF_REG_SOFT_RESET_MASK); + regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK, 0); + regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE, + DA7219_SYSTEM_ACTIVE_MASK, 1); + + regcache_cache_bypass(da7219->regmap, false); + regmap_reinit_cache(da7219->regmap, &da7219_regmap_config); + + /* Update IO voltage level range based on supply level */ + snd_soc_component_write(component, DA7219_IO_CTRL, io_voltage_lvl); + ret = regmap_read(da7219->regmap, DA7219_CHIP_REVISION, &rev); if (ret) { dev_err(component->dev, "Failed to read chip revision: %d\n", ret); @@ -2349,6 +2520,7 @@ static int da7219_probe(struct snd_soc_component *component) err_disable_reg: regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); + regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); return ret; } @@ -2371,6 +2543,7 @@ static void da7219_remove(struct snd_soc_component *component) /* Supplies */ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); + regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); } #ifdef CONFIG_PM @@ -2423,142 +2596,6 @@ static const struct snd_soc_component_driver soc_component_dev_da7219 = { }; -/* - * Regmap configs - */ - -static struct reg_default da7219_reg_defaults[] = { - { DA7219_MIC_1_SELECT, 0x00 }, - { DA7219_CIF_TIMEOUT_CTRL, 0x01 }, - { DA7219_SR_24_48, 0x00 }, - { DA7219_SR, 0x0A }, - { DA7219_CIF_I2C_ADDR_CFG, 0x02 }, - { DA7219_PLL_CTRL, 0x10 }, - { DA7219_PLL_FRAC_TOP, 0x00 }, - { DA7219_PLL_FRAC_BOT, 0x00 }, - { DA7219_PLL_INTEGER, 0x20 }, - { DA7219_DIG_ROUTING_DAI, 0x10 }, - { DA7219_DAI_CLK_MODE, 0x01 }, - { DA7219_DAI_CTRL, 0x28 }, - { DA7219_DAI_TDM_CTRL, 0x40 }, - { DA7219_DIG_ROUTING_DAC, 0x32 }, - { DA7219_DAI_OFFSET_LOWER, 0x00 }, - { DA7219_DAI_OFFSET_UPPER, 0x00 }, - { DA7219_REFERENCES, 0x08 }, - { DA7219_MIXIN_L_SELECT, 0x00 }, - { DA7219_MIXIN_L_GAIN, 0x03 }, - { DA7219_ADC_L_GAIN, 0x6F }, - { DA7219_ADC_FILTERS1, 0x80 }, - { DA7219_MIC_1_GAIN, 0x01 }, - { DA7219_SIDETONE_CTRL, 0x40 }, - { DA7219_SIDETONE_GAIN, 0x0E }, - { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 }, - { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 }, - { DA7219_DAC_FILTERS5, 0x00 }, - { DA7219_DAC_FILTERS2, 0x88 }, - { DA7219_DAC_FILTERS3, 0x88 }, - { DA7219_DAC_FILTERS4, 0x08 }, - { DA7219_DAC_FILTERS1, 0x80 }, - { DA7219_DAC_L_GAIN, 0x6F }, - { DA7219_DAC_R_GAIN, 0x6F }, - { DA7219_CP_CTRL, 0x20 }, - { DA7219_HP_L_GAIN, 0x39 }, - { DA7219_HP_R_GAIN, 0x39 }, - { DA7219_MIXOUT_L_SELECT, 0x00 }, - { DA7219_MIXOUT_R_SELECT, 0x00 }, - { DA7219_MICBIAS_CTRL, 0x03 }, - { DA7219_MIC_1_CTRL, 0x40 }, - { DA7219_MIXIN_L_CTRL, 0x40 }, - { DA7219_ADC_L_CTRL, 0x40 }, - { DA7219_DAC_L_CTRL, 0x40 }, - { DA7219_DAC_R_CTRL, 0x40 }, - { DA7219_HP_L_CTRL, 0x40 }, - { DA7219_HP_R_CTRL, 0x40 }, - { DA7219_MIXOUT_L_CTRL, 0x10 }, - { DA7219_MIXOUT_R_CTRL, 0x10 }, - { DA7219_CHIP_ID1, 0x23 }, - { DA7219_CHIP_ID2, 0x93 }, - { DA7219_IO_CTRL, 0x00 }, - { DA7219_GAIN_RAMP_CTRL, 0x00 }, - { DA7219_PC_COUNT, 0x02 }, - { DA7219_CP_VOL_THRESHOLD1, 0x0E }, - { DA7219_DIG_CTRL, 0x00 }, - { DA7219_ALC_CTRL2, 0x00 }, - { DA7219_ALC_CTRL3, 0x00 }, - { DA7219_ALC_NOISE, 0x3F }, - { DA7219_ALC_TARGET_MIN, 0x3F }, - { DA7219_ALC_TARGET_MAX, 0x00 }, - { DA7219_ALC_GAIN_LIMITS, 0xFF }, - { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 }, - { DA7219_ALC_ANTICLIP_CTRL, 0x00 }, - { DA7219_ALC_ANTICLIP_LEVEL, 0x00 }, - { DA7219_DAC_NG_SETUP_TIME, 0x00 }, - { DA7219_DAC_NG_OFF_THRESH, 0x00 }, - { DA7219_DAC_NG_ON_THRESH, 0x00 }, - { DA7219_DAC_NG_CTRL, 0x00 }, - { DA7219_TONE_GEN_CFG1, 0x00 }, - { DA7219_TONE_GEN_CFG2, 0x00 }, - { DA7219_TONE_GEN_CYCLES, 0x00 }, - { DA7219_TONE_GEN_FREQ1_L, 0x55 }, - { DA7219_TONE_GEN_FREQ1_U, 0x15 }, - { DA7219_TONE_GEN_FREQ2_L, 0x00 }, - { DA7219_TONE_GEN_FREQ2_U, 0x40 }, - { DA7219_TONE_GEN_ON_PER, 0x02 }, - { DA7219_TONE_GEN_OFF_PER, 0x01 }, - { DA7219_ACCDET_IRQ_MASK_A, 0x00 }, - { DA7219_ACCDET_IRQ_MASK_B, 0x00 }, - { DA7219_ACCDET_CONFIG_1, 0xD6 }, - { DA7219_ACCDET_CONFIG_2, 0x34 }, - { DA7219_ACCDET_CONFIG_3, 0x0A }, - { DA7219_ACCDET_CONFIG_4, 0x16 }, - { DA7219_ACCDET_CONFIG_5, 0x21 }, - { DA7219_ACCDET_CONFIG_6, 0x3E }, - { DA7219_ACCDET_CONFIG_7, 0x01 }, - { DA7219_SYSTEM_ACTIVE, 0x00 }, -}; - -static bool da7219_volatile_register(struct device *dev, unsigned int reg) -{ - switch (reg) { - case DA7219_MIC_1_GAIN_STATUS: - case DA7219_MIXIN_L_GAIN_STATUS: - case DA7219_ADC_L_GAIN_STATUS: - case DA7219_DAC_L_GAIN_STATUS: - case DA7219_DAC_R_GAIN_STATUS: - case DA7219_HP_L_GAIN_STATUS: - case DA7219_HP_R_GAIN_STATUS: - case DA7219_CIF_CTRL: - case DA7219_PLL_SRM_STS: - case DA7219_ALC_CTRL1: - case DA7219_SYSTEM_MODES_INPUT: - case DA7219_SYSTEM_MODES_OUTPUT: - case DA7219_ALC_OFFSET_AUTO_M_L: - case DA7219_ALC_OFFSET_AUTO_U_L: - case DA7219_TONE_GEN_CFG1: - case DA7219_ACCDET_STATUS_A: - case DA7219_ACCDET_STATUS_B: - case DA7219_ACCDET_IRQ_EVENT_A: - case DA7219_ACCDET_IRQ_EVENT_B: - case DA7219_ACCDET_CONFIG_8: - case DA7219_SYSTEM_STATUS: - return true; - default: - return false; - } -} - -static const struct regmap_config da7219_regmap_config = { - .reg_bits = 8, - .val_bits = 8, - - .max_register = DA7219_SYSTEM_ACTIVE, - .reg_defaults = da7219_reg_defaults, - .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults), - .volatile_reg = da7219_volatile_register, - .cache_type = REGCACHE_RBTREE, -}; - - /* * I2C layer */ @@ -2568,8 +2605,7 @@ static int da7219_i2c_probe(struct i2c_client *i2c, { struct device *dev = &i2c->dev; struct da7219_priv *da7219; - unsigned int system_active, system_status; - int i, ret; + int ret; da7219 = devm_kzalloc(dev, sizeof(struct da7219_priv), GFP_KERNEL); @@ -2585,37 +2621,6 @@ static int da7219_i2c_probe(struct i2c_client *i2c, return ret; } - regcache_cache_bypass(da7219->regmap, true); - - /* Disable audio paths if still active from previous start */ - regmap_read(da7219->regmap, DA7219_SYSTEM_ACTIVE, &system_active); - if (system_active) { - regmap_write(da7219->regmap, DA7219_GAIN_RAMP_CTRL, - DA7219_GAIN_RAMP_RATE_NOMINAL); - regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_INPUT, 0x00); - regmap_write(da7219->regmap, DA7219_SYSTEM_MODES_OUTPUT, 0x01); - - for (i = 0; i < DA7219_SYS_STAT_CHECK_RETRIES; ++i) { - regmap_read(da7219->regmap, DA7219_SYSTEM_STATUS, - &system_status); - if (!system_status) - break; - - msleep(DA7219_SYS_STAT_CHECK_DELAY); - } - } - - /* Soft reset component */ - regmap_write_bits(da7219->regmap, DA7219_ACCDET_CONFIG_1, - DA7219_ACCDET_EN_MASK, 0); - regmap_write_bits(da7219->regmap, DA7219_CIF_CTRL, - DA7219_CIF_REG_SOFT_RESET_MASK, - DA7219_CIF_REG_SOFT_RESET_MASK); - regmap_write_bits(da7219->regmap, DA7219_SYSTEM_ACTIVE, - DA7219_SYSTEM_ACTIVE_MASK, 0); - - regcache_cache_bypass(da7219->regmap, false); - /* Retrieve DT/ACPI/Platform data */ da7219->pdata = dev_get_platdata(dev); if (!da7219->pdata) -- cgit From 78013a1cf2971684775f6956d5666237ac53a1aa Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Tue, 11 Aug 2020 17:57:25 +0100 Subject: ASoC: da7219: Fix clock handling around codec level probe Previously the driver would use devm_* related functions at the codec level probe() to allocate clock resources for MCLK and the DAI clocks exposed by the device. This caused issues when registering clocks on a re-probe (no device level remove/prove involved) as the devm_* resources were never freed up so the clocks were still registered from the previous codec level probe(). This commit updates the clock handling for MCLK usage and DAI clock provision to fix this discrepancy and allow the codec level probe/remove functionality to operate as intended. Signed-off-by: Adam Thomson Link: https://lore.kernel.org/r/b92c461baeed27a6cd92e59e36a55c2547218683.1597164865.git.Adam.Thomson.Opensource@diasemi.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 96 +++++++++++++++++++++++++++++++++++------------ sound/soc/codecs/da7219.h | 1 + 2 files changed, 72 insertions(+), 25 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 3bdfd8c00602..301d8be2c5f7 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2117,14 +2117,26 @@ static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = { static int da7219_register_dai_clks(struct snd_soc_component *component) { struct device *dev = component->dev; + struct device_node *np = dev->of_node; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); struct da7219_pdata *pdata = da7219->pdata; const char *parent_name; + struct clk_hw_onecell_data *clk_data; int i, ret; + /* For DT platforms allocate onecell data for clock registration */ + if (np) { + clk_data = kzalloc(struct_size(clk_data, hws, DA7219_DAI_NUM_CLKS), + GFP_KERNEL); + if (!clk_data) + return -ENOMEM; + + clk_data->num = DA7219_DAI_NUM_CLKS; + da7219->clk_hw_data = clk_data; + } + for (i = 0; i < DA7219_DAI_NUM_CLKS; ++i) { struct clk_init_data init = {}; - struct clk *dai_clk; struct clk_lookup *dai_clk_lookup; struct clk_hw *dai_clk_hw = &da7219->dai_clks_hw[i]; @@ -2160,22 +2172,20 @@ static int da7219_register_dai_clks(struct snd_soc_component *component) init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE; dai_clk_hw->init = &init; - dai_clk = devm_clk_register(dev, dai_clk_hw); - if (IS_ERR(dai_clk)) { - dev_warn(dev, "Failed to register %s: %ld\n", - init.name, PTR_ERR(dai_clk)); - ret = PTR_ERR(dai_clk); + ret = clk_hw_register(dev, dai_clk_hw); + if (ret) { + dev_warn(dev, "Failed to register %s: %d\n", init.name, + ret); goto err; } - da7219->dai_clks[i] = dai_clk; + da7219->dai_clks[i] = dai_clk_hw->clk; - /* If we're using DT, then register as provider accordingly */ - if (dev->of_node) { - devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, - dai_clk_hw); + /* For DT setup onecell data, otherwise create lookup */ + if (np) { + da7219->clk_hw_data->hws[i] = dai_clk_hw; } else { - dai_clk_lookup = clkdev_create(dai_clk, init.name, - "%s", dev_name(dev)); + dai_clk_lookup = clkdev_hw_create(dai_clk_hw, init.name, + "%s", dev_name(dev)); if (!dai_clk_lookup) { ret = -ENOMEM; goto err; @@ -2185,21 +2195,58 @@ static int da7219_register_dai_clks(struct snd_soc_component *component) } } + /* If we're using DT, then register as provider accordingly */ + if (np) { + ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get, + da7219->clk_hw_data); + if (ret) { + dev_err(dev, "Failed to register clock provider\n"); + goto err; + } + } + return 0; err: do { if (da7219->dai_clks_lookup[i]) clkdev_drop(da7219->dai_clks_lookup[i]); + + clk_hw_unregister(&da7219->dai_clks_hw[i]); } while (i-- > 0); + if (np) + kfree(da7219->clk_hw_data); + return ret; } + +static void da7219_free_dai_clks(struct snd_soc_component *component) +{ + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + struct device_node *np = component->dev->of_node; + int i; + + if (np) + of_clk_del_provider(np); + + for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) { + if (da7219->dai_clks_lookup[i]) + clkdev_drop(da7219->dai_clks_lookup[i]); + + clk_hw_unregister(&da7219->dai_clks_hw[i]); + } + + if (np) + kfree(da7219->clk_hw_data); +} #else static inline int da7219_register_dai_clks(struct snd_soc_component *component) { return 0; } + +static void da7219_free_dai_clks(struct snd_soc_component *component) {} #endif /* CONFIG_COMMON_CLK */ static void da7219_handle_pdata(struct snd_soc_component *component) @@ -2464,7 +2511,7 @@ static int da7219_probe(struct snd_soc_component *component) da7219_handle_pdata(component); /* Check if MCLK provided */ - da7219->mclk = devm_clk_get(component->dev, "mclk"); + da7219->mclk = clk_get(component->dev, "mclk"); if (IS_ERR(da7219->mclk)) { if (PTR_ERR(da7219->mclk) != -ENOENT) { ret = PTR_ERR(da7219->mclk); @@ -2477,7 +2524,7 @@ static int da7219_probe(struct snd_soc_component *component) /* Register CCF DAI clock control */ ret = da7219_register_dai_clks(component); if (ret) - return ret; + goto err_put_clk; /* Default PC counter to free-running */ snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, @@ -2514,10 +2561,16 @@ static int da7219_probe(struct snd_soc_component *component) /* Initialise AAD block */ ret = da7219_aad_init(component); if (ret) - goto err_disable_reg; + goto err_free_dai_clks; return 0; +err_free_dai_clks: + da7219_free_dai_clks(component); + +err_put_clk: + clk_put(da7219->mclk); + err_disable_reg: regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); regulator_bulk_free(DA7219_NUM_SUPPLIES, da7219->supplies); @@ -2528,18 +2581,11 @@ err_disable_reg: static void da7219_remove(struct snd_soc_component *component) { struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); -#ifdef CONFIG_COMMON_CLK - int i; -#endif da7219_aad_exit(component); -#ifdef CONFIG_COMMON_CLK - for (i = DA7219_DAI_NUM_CLKS - 1; i >= 0; --i) { - if (da7219->dai_clks_lookup[i]) - clkdev_drop(da7219->dai_clks_lookup[i]); - } -#endif + da7219_free_dai_clks(component); + clk_put(da7219->mclk); /* Supplies */ regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h index 88b67fedd01b..94af88f52589 100644 --- a/sound/soc/codecs/da7219.h +++ b/sound/soc/codecs/da7219.h @@ -817,6 +817,7 @@ struct da7219_priv { #ifdef CONFIG_COMMON_CLK struct clk_hw dai_clks_hw[DA7219_DAI_NUM_CLKS]; + struct clk_hw_onecell_data *clk_hw_data; #endif struct clk_lookup *dai_clks_lookup[DA7219_DAI_NUM_CLKS]; struct clk *dai_clks[DA7219_DAI_NUM_CLKS]; -- cgit From c6a6586126d3f8010a181c01d4dd6aa90a953b66 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:27 -0500 Subject: ASoC: Intel: Atom: sst-atom-controls: remove redundant assignments cppcheck complains of a possible NULL pointer dereference but setting a pointer before using list_for_each_entry() is not useful. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-atom-controls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c index ff42f629b035..6b5a34a15acb 100644 --- a/sound/soc/intel/atom/sst-atom-controls.c +++ b/sound/soc/intel/atom/sst-atom-controls.c @@ -299,7 +299,7 @@ static int sst_find_and_send_pipe_algo(struct sst_data *drv, { int ret = 0; struct sst_algo_control *bc; - struct sst_module *algo = NULL; + struct sst_module *algo; dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe); @@ -602,7 +602,7 @@ static int sst_set_pipe_gain(struct sst_ids *ids, int ret = 0; struct sst_gain_mixer_control *mc; struct sst_gain_value *gv; - struct sst_module *gain = NULL; + struct sst_module *gain; list_for_each_entry(gain, &ids->gain_list, node) { struct snd_kcontrol *kctl = gain->kctl; -- cgit From b0a2a93dc3acb4c2868c60ef0d3278289a532816 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:28 -0500 Subject: ASoC: Intel: Atom: compress: remove redundant assignment Fix cppcheck warning: sound/soc/intel/atom/sst-mfld-platform-compress.c:46:14: style: Variable 'ret_val' is assigned a value that is never used. [unreadVariable] int ret_val = 0; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-compress.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c index 1595e01a7e12..89c9c5ad6b21 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-compress.c +++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c @@ -42,8 +42,7 @@ static void sst_drain_notify(void *arg) static int sst_platform_compr_open(struct snd_soc_component *component, struct snd_compr_stream *cstream) { - - int ret_val = 0; + int ret_val; struct snd_compr_runtime *runtime = cstream->runtime; struct sst_runtime_stream *stream; -- cgit From b0754c55adf9a2aeb3c3732eb159615e91c70397 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:29 -0500 Subject: ASoC: Intel: Atom: platform-pcm: remove redundant assignment fix cppcheck warning: sound/soc/intel/atom/sst-mfld-platform-pcm.c:387:9: warning: Identical condition and return expression 'ret_val', return value is always 0 [identicalConditionAfterEarlyExit] return ret_val; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index b1cac7abdc0a..eb1aa58d94b9 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -366,7 +366,7 @@ static int sst_media_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct sst_runtime_stream *stream; - int ret_val = 0, str_id; + int ret_val, str_id; stream = substream->runtime->private_data; str_id = stream->stream_info.str_id; -- cgit From 4e3aab3217cdd1506ff89642e554995cbfa0806a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:30 -0500 Subject: ASoC: Intel: Atom: sst: remove useless NULL assignment Fix cppcheck warnings: sound/soc/intel/atom/sst/sst.c:373:2: warning: Assignment of function parameter has no effect outside the function. Did you forget dereferencing it? [uselessAssignmentPtrArg] ctx = NULL; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index d6563985e008..1d1c58e48f42 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -370,7 +370,6 @@ void sst_context_cleanup(struct intel_sst_drv *ctx) kfree(ctx->fw_in_mem); ctx->fw_in_mem = NULL; sst_memcpy_free_resources(ctx); - ctx = NULL; } EXPORT_SYMBOL_GPL(sst_context_cleanup); -- cgit From 30701e0f3b1a134cbd000ca607a26342a10f6383 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:31 -0500 Subject: ASoC: Intel: Atom: remove redundant initialization Fix cppcheck warnings: sound/soc/intel/atom/sst/sst.c:427:13: style: Variable 'ret' is assigned a value that is never used. [unreadVariable] int i, ret = 0; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 1d1c58e48f42..1900a37d03cc 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -423,7 +423,7 @@ static int intel_sst_suspend(struct device *dev) { struct intel_sst_drv *ctx = dev_get_drvdata(dev); struct sst_fw_save *fw_save; - int i, ret = 0; + int i, ret; /* check first if we are already in SW reset */ if (ctx->sst_state == SST_RESET) -- cgit From f3352e6b7c27078d9342c9faa8332b8113719f4b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:32 -0500 Subject: ASoC: Intel: Atom: sst_pvt: remove redundant initialization Fix cppcheck warning: sound/soc/intel/atom/sst/sst_pvt.c:201:9: warning: Identical condition and return expression 'retval', return value is always 0 [identicalConditionAfterEarlyExit] return retval; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_pvt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 053c27707147..3bd1bd3e43d6 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -188,7 +188,7 @@ int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, struct intel_sst_drv *sst_drv_ctx, struct sst_block **block, u32 msg_id, u32 drv_id) { - int retval = 0; + int retval; retval = sst_create_ipc_msg(arg, large); if (retval) -- cgit From 5ab56a224398a05ade1762d591321f238c284b19 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:33 -0500 Subject: ASoC: Intel: Atom: platform-pcm: fix redundant return Fix cppcheck warning return ret_val; ^ sound/soc/intel/atom/sst-mfld-platform-pcm.c:384:6: note: If condition 'ret_val' is true, the function will return/exit if (ret_val) ^ sound/soc/intel/atom/sst-mfld-platform-pcm.c:387:9: note: Returning identical expression 'ret_val' return ret_val; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c index eb1aa58d94b9..6add70500ed8 100644 --- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c +++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -385,7 +385,7 @@ static int sst_media_prepare(struct snd_pcm_substream *substream, if (ret_val) return ret_val; substream->runtime->hw.info = SNDRV_PCM_INFO_BLOCK_TRANSFER; - return ret_val; + return 0; } static int sst_enable_ssp(struct snd_pcm_substream *substream, -- cgit From eeb460f21250d46e5fa8e8429c38de47fe912481 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:34 -0500 Subject: ASoC: Intel: Atom: remove useless assignment Fix cppcheck warning: sound/soc/intel/atom/sst/sst.c:52:20: style: Variable 'size' is assigned a value that is never used. [unreadVariable] unsigned int size = 0; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index 1900a37d03cc..d450b9848028 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -49,7 +49,7 @@ static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context) union ipc_header_mrfld header; union sst_imr_reg_mrfld imr; struct ipc_post *msg = NULL; - unsigned int size = 0; + unsigned int size; struct intel_sst_drv *drv = (struct intel_sst_drv *) context; irqreturn_t retval = IRQ_HANDLED; -- cgit From b66a056e1539d8470013fff00f476377a616baa3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:35 -0500 Subject: ASoC: Intel: Atom: sst_loader: remove always-true condition Fix cppcheck warning: sound/soc/intel/atom/sst/sst_loader.c:401:43: style: Redundant condition: If 'EXPR == 4', the comparison 'EXPR != 3' is always true. [redundantCondition] if (sst_drv_ctx->sst_state != SST_RESET || ^ In this case, if sst_state == SST_SHUTDOWN then the first test is already true. 2014 bug, yay. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_loader.c | 3 +-- 1 file changed, 1 insertion(+), 2 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 fc91a304256b..5c42cce90ce2 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -398,8 +398,7 @@ int sst_load_fw(struct intel_sst_drv *sst_drv_ctx) dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n"); - if (sst_drv_ctx->sst_state != SST_RESET || - sst_drv_ctx->sst_state == SST_SHUTDOWN) + if (sst_drv_ctx->sst_state != SST_RESET) return -EAGAIN; if (!sst_drv_ctx->fw_in_mem) { -- cgit From 7b99434c2c35e6045cc65992e3aaf231012e93ef Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:36 -0500 Subject: ASoC: Intel: Atom: sst_pvt: simplify return handling Fix cppcheck warning: sound/soc/intel/atom/sst/sst_pvt.c:201:9: warning: Identical condition and return expression 'retval', return value is always 0 [identicalConditionAfterEarlyExit] return retval; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-11-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst_pvt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index 3bd1bd3e43d6..c099f7df6168 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -198,7 +198,7 @@ int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large, kfree(*arg); return -ENOMEM; } - return retval; + return 0; } /* -- cgit From cfe8cc9419918bc56f528fa370aa8435d99b8353 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:37 -0500 Subject: ASoC: Intel: Atom: (cosmetic) align parameters Fix cppcheck warnings and use same function parameters in headers and C code. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-12-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst-mfld-platform.h | 4 ++-- sound/soc/intel/atom/sst/sst.h | 34 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst-mfld-platform.h b/sound/soc/intel/atom/sst-mfld-platform.h index 10c9ecfa7038..8b5777d3229a 100644 --- a/sound/soc/intel/atom/sst-mfld-platform.h +++ b/sound/soc/intel/atom/sst-mfld-platform.h @@ -173,6 +173,6 @@ struct sst_data { struct snd_soc_card *soc_card; struct sst_cmd_sba_hw_set_ssp ssp_cmd; }; -int sst_register_dsp(struct sst_device *sst); -int sst_unregister_dsp(struct sst_device *sst); +int sst_register_dsp(struct sst_device *dev); +int sst_unregister_dsp(struct sst_device *dev); #endif diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 50441cf6f77d..2bee646e81b8 100644 --- a/sound/soc/intel/atom/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -428,34 +428,34 @@ struct intel_sst_ops { }; int sst_realloc_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); -int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id); -int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id); -int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id); -int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id); +int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); +int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); +int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); +int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id); -int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx, - struct snd_sst_bytes_v2 *sbytes); +int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, + struct snd_sst_bytes_v2 *bytes); int sst_set_stream_param(int str_id, struct snd_sst_params *str_param); int sst_set_metadata(int str_id, char *params); -int sst_get_stream(struct intel_sst_drv *sst_drv_ctx, +int sst_get_stream(struct intel_sst_drv *ctx, struct snd_sst_params *str_param); int sst_get_stream_allocated(struct intel_sst_drv *ctx, struct snd_sst_params *str_param, struct snd_sst_lib_download **lib_dnld); int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx, int str_id, bool partial_drain); -int sst_post_message_mrfld(struct intel_sst_drv *ctx, - struct ipc_post *msg, bool sync); -void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg); -int sst_start_mrfld(struct intel_sst_drv *ctx); -int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx); -void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx); - -int sst_load_fw(struct intel_sst_drv *ctx); +int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx, + struct ipc_post *ipc_msg, bool sync); +void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx, struct ipc_post *msg); +int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx); +int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx); +void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx); + +int sst_load_fw(struct intel_sst_drv *sst_drv_ctx); int sst_load_library(struct snd_sst_lib_download *lib, u8 ops); void sst_post_download_mrfld(struct intel_sst_drv *ctx); int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx); -void sst_memcpy_free_resources(struct intel_sst_drv *ctx); +void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx); int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block); @@ -490,7 +490,7 @@ int sst_prepare_and_post_msg(struct intel_sst_drv *sst, bool large, bool fill_dsp, bool sync, bool response); void sst_process_pending_msg(struct work_struct *work); -int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx); +int sst_assign_pvt_id(struct intel_sst_drv *drv); int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id); struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx, int str_id); -- cgit From 0af1fcea054497ba784fc2d9b0125c4c2f6609e3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:38 -0500 Subject: ASoC: Intel: Baytrail: (cosmetic) align function parameters Fix cppcheck warning: sound/soc/intel/baytrail/sst-baytrail-ipc.c:339:8: style:inconclusive: Function 'sst_byt_stream_new' argument 3 names different: declaration 'get_write_position' definition 'notify_position'. [funcArgNamesDifferent] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-13-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/baytrail/sst-baytrail-ipc.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.h b/sound/soc/intel/baytrail/sst-baytrail-ipc.h index 755098509327..cc960b79e9e1 100644 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.h +++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.h @@ -26,8 +26,7 @@ extern struct sst_ops sst_byt_ops; /* stream API */ struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, - uint32_t (*get_write_position)(struct sst_byt_stream *stream, - void *data), + uint32_t (*notify_position)(struct sst_byt_stream *stream, void *data), void *data); /* stream configuration */ -- cgit From 2b84a26f99762774b98fdb1e66be7400c0d22199 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:39 -0500 Subject: ASoC: Intel: common: (cosmetic) align function parameters Fix cppcheck style warnings, align headers and code and remove useless prototypes. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-14-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 2 +- sound/soc/intel/common/sst-dsp.h | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 3d8765ce3e0d..9a760123b46f 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -344,7 +344,7 @@ void sst_fw_unload(struct sst_fw *sst_fw); /* Create/Free firmware modules */ struct sst_module *sst_module_new(struct sst_fw *sst_fw, struct sst_module_template *template, void *private); -void sst_module_free(struct sst_module *module); +void sst_module_free(struct sst_module *sst_module); struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id); int sst_module_alloc_blocks(struct sst_module *module); int sst_module_free_blocks(struct sst_module *module); diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 604a80c5859b..d55014587415 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -268,15 +268,14 @@ void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); /* Mailbox management */ -int sst_dsp_mailbox_init(struct sst_dsp *dsp, u32 inbox_offset, +int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size, u32 outbox_offset, size_t outbox_size); -void sst_dsp_inbox_write(struct sst_dsp *dsp, void *message, size_t bytes); -void sst_dsp_inbox_read(struct sst_dsp *dsp, void *message, size_t bytes); -void sst_dsp_outbox_write(struct sst_dsp *dsp, void *message, size_t bytes); -void sst_dsp_outbox_read(struct sst_dsp *dsp, void *message, size_t bytes); -void sst_dsp_mailbox_dump(struct sst_dsp *dsp, size_t bytes); -int sst_dsp_register_poll(struct sst_dsp *dsp, u32 offset, u32 mask, - u32 expected_value, u32 timeout, char *operation); +void sst_dsp_inbox_write(struct sst_dsp *sst, void *message, size_t bytes); +void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes); +void sst_dsp_outbox_write(struct sst_dsp *sst, void *message, size_t bytes); +void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes); +int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, + u32 target, u32 time, char *operation); /* Debug */ void sst_dsp_dump(struct sst_dsp *sst); -- cgit From c9b1f82d53930762094af0381c5dc7a8a2b6a86d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:40 -0500 Subject: ASoC: Intel: haswell: (cosmetic) align function parameters Fix cppcheck warning: sound/soc/intel/haswell/sst-haswell-ipc.c:963:8: style:inconclusive: Function 'sst_hsw_stream_new' argument 3 names different: declaration 'get_write_position' definition 'notify_position'. [funcArgNamesDifferent] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-15-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/haswell/sst-haswell-ipc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.h b/sound/soc/intel/haswell/sst-haswell-ipc.h index fdc70c77e688..646dbbba327b 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.h +++ b/sound/soc/intel/haswell/sst-haswell-ipc.h @@ -414,7 +414,7 @@ int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, /* Stream API */ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, - u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data), + u32 (*notify_position)(struct sst_hsw_stream *stream, void *data), void *data); int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream); -- cgit From 523615b716005b189f5a6cd9f7a255a902e45bce Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:41 -0500 Subject: ASoC: Intel: haswell-ipc: remove redundant assignments Fix cppcheck warnings: sound/soc/intel/haswell/sst-haswell-ipc.c:430:8: style: Variable 'i' is assigned a value that is never used. [unreadVariable] sound/soc/intel/haswell/sst-haswell-ipc.c:1792:8: style: Variable 'id' is assigned a value that is never used. [unreadVariable] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-16-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/haswell/sst-haswell-ipc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index 0ff89ea96ccf..345fd7c1b1d7 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -427,7 +427,7 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) u32 offset; u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)]; char *tmp[5], *pinfo; - int i = 0; + int i; offset = (header & 0x1FFFFFFF) << 3; @@ -1789,7 +1789,7 @@ int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf) int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf) { - u8 id = 0; + u8 id; /* read the first matching line from param buffer */ while (hsw->param_idx_r < WAVES_PARAM_LINES) { -- cgit From 8be54edba9096056ff5d0fb2a29d0b3b962fa60c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:42 -0500 Subject: ASoC: Intel: Skylake: skl-nhlt: remove redundant initialization Fix cppcheck warning: sound/soc/intel/skylake/skl-nhlt.c:203:21: style: Variable 'rate' is assigned a value that is never used. [unreadVariable] unsigned long rate = 0; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-17-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-nhlt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c index d9c8f5cb389e..87c891c46291 100644 --- a/sound/soc/intel/skylake/skl-nhlt.c +++ b/sound/soc/intel/skylake/skl-nhlt.c @@ -200,7 +200,7 @@ static void skl_get_ssp_clks(struct skl_dev *skl, struct skl_ssp_clk *ssp_clks, struct skl_ssp_clk *sclk, *sclkfs; struct nhlt_fmt_cfg *fmt_cfg; struct wav_fmt_ext *wav_fmt; - unsigned long rate = 0; + unsigned long rate; bool present = false; int rate_index = 0; u16 channels, bps; -- cgit From 3b4d60f0f7dd13594916bcd6644ea6208597d993 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:43 -0500 Subject: ASoC: Intel: Skylake: cldma: remove redundant initialization Fix cppcheck warning: sound/soc/intel/skylake/skl-sst-cldma.c:248:10: style: Variable 'ret' is assigned a value that is never used. [unreadVariable] int ret = 0; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-18-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-cldma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index 36f697c61074..b91f7a652a2b 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -245,7 +245,7 @@ static int skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size, bool wait) { - int ret = 0; + int ret; bool start = true; unsigned int excess_bytes; u32 size; -- cgit From c6193988e945b658bffd09174edae1b43b8ab0b6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:44 -0500 Subject: ASoC: Intel: Skylake: sst-utils: remove redundant assignment Fix cppcheck warning: sound/soc/intel/skylake/skl-sst-utils.c:240:10: style: Variable 'ret' is assigned a value that is never used. [unreadVariable] int ret = 0; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-19-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-sst-utils.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c index b233f89517c1..57ea815d3f04 100644 --- a/sound/soc/intel/skylake/skl-sst-utils.c +++ b/sound/soc/intel/skylake/skl-sst-utils.c @@ -237,7 +237,7 @@ int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw, struct uuid_module *module; struct firmware stripped_fw; unsigned int safe_file; - int ret = 0; + int ret; /* Get the FW pointer to derive ADSP header */ stripped_fw.data = fw->data; -- cgit From 25722cf606f6c58b1f2f709434aeb418c4b0b1ca Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:45 -0500 Subject: ASoC: Intel: Skylake: skl-topology: remove redundant assignments Cppcheck complains about possible NULL pointer dereferences but the assignments are actually not needed before walking through lists. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-20-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index b7d2d97d12a7..3928a1645820 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -550,8 +550,8 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl, struct skl_pipe *pipe) { int ret = 0; - struct skl_pipe_module *w_module = NULL; - struct skl_module_cfg *mconfig = NULL; + struct skl_pipe_module *w_module; + struct skl_module_cfg *mconfig; list_for_each_entry(w_module, &pipe->w_list, node) { guid_t *uuid_mod; @@ -1893,7 +1893,7 @@ static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w, struct skl_pipe_params *params) { - struct snd_soc_dapm_path *p = NULL; + struct snd_soc_dapm_path *p; int ret = -EIO; snd_soc_dapm_widget_for_each_sink_path(w, p) { -- cgit From 11a790f94b340c3deb893c6229c3948233a91a4a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:46 -0500 Subject: ASoC: Intel: Skylake: skl-topology: remove redundant assignment Fix cppcheck warning: sound/soc/intel/skylake/skl-topology.c:2879:29: style: Variable 'block_size' is assigned a value that is never used. [unreadVariable] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-21-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/skl-topology.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index 3928a1645820..40bee10b0c65 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -2876,7 +2876,7 @@ static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w, struct skl_module_cfg *mconfig) { struct snd_soc_tplg_vendor_array *array; - int num_blocks, block_size = 0, block_type, off = 0; + int num_blocks, block_size, block_type, off = 0; char *data; int ret; -- cgit From 8f0ccd59bcc9305241507cb48c6aaa08a9f375c8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 15:01:47 -0500 Subject: ASoC: Intel: Skylake: (cosmetic) align function parameters Fix cppcheck warnings and align headers with code. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200813200147.61990-22-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/cnl-sst-dsp.h | 4 ++-- sound/soc/intel/skylake/skl-sst-ipc.h | 16 ++++++++-------- sound/soc/intel/skylake/skl-topology.h | 8 ++++---- sound/soc/intel/skylake/skl.h | 2 +- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/cnl-sst-dsp.h b/sound/soc/intel/skylake/cnl-sst-dsp.h index 7bd4d2a8fdfa..d3cf4bd1a070 100644 --- a/sound/soc/intel/skylake/cnl-sst-dsp.h +++ b/sound/soc/intel/skylake/cnl-sst-dsp.h @@ -82,8 +82,8 @@ struct sst_generic_ipc; #define CNL_ADSPCS_CPA_SHIFT 24 #define CNL_ADSPCS_CPA(x) (x << CNL_ADSPCS_CPA_SHIFT) -int cnl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core); -int cnl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core); +int cnl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask); +int cnl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask); irqreturn_t cnl_dsp_sst_interrupt(int irq, void *dev_id); void cnl_dsp_free(struct sst_dsp *dsp); diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h index 08ac31778325..aaaab3b3ec42 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.h +++ b/sound/soc/intel/skylake/skl-sst-ipc.h @@ -107,12 +107,12 @@ struct skl_ipc_d0ix_msg { irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context); -int skl_ipc_create_pipeline(struct sst_generic_ipc *sst_ipc, +int skl_ipc_create_pipeline(struct sst_generic_ipc *ipc, u16 ppl_mem_size, u8 ppl_type, u8 instance_id, u8 lp_mode); -int skl_ipc_delete_pipeline(struct sst_generic_ipc *sst_ipc, u8 instance_id); +int skl_ipc_delete_pipeline(struct sst_generic_ipc *ipc, u8 instance_id); -int skl_ipc_set_pipeline_state(struct sst_generic_ipc *sst_ipc, +int skl_ipc_set_pipeline_state(struct sst_generic_ipc *ipc, u8 instance_id, enum skl_ipc_pipeline_state state); int skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, @@ -120,10 +120,10 @@ int skl_ipc_save_pipeline(struct sst_generic_ipc *ipc, int skl_ipc_restore_pipeline(struct sst_generic_ipc *ipc, u8 instance_id); -int skl_ipc_init_instance(struct sst_generic_ipc *sst_ipc, +int skl_ipc_init_instance(struct sst_generic_ipc *ipc, struct skl_ipc_init_instance_msg *msg, void *param_data); -int skl_ipc_bind_unbind(struct sst_generic_ipc *sst_ipc, +int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc, struct skl_ipc_bind_unbind_msg *msg); int skl_ipc_load_modules(struct sst_generic_ipc *ipc, @@ -150,12 +150,12 @@ int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc, int skl_ipc_check_D0i0(struct sst_dsp *dsp, bool state); -void skl_ipc_int_enable(struct sst_dsp *dsp); +void skl_ipc_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_enable(struct sst_dsp *ctx); void skl_ipc_op_int_disable(struct sst_dsp *ctx); -void skl_ipc_int_disable(struct sst_dsp *dsp); +void skl_ipc_int_disable(struct sst_dsp *ctx); -bool skl_ipc_int_status(struct sst_dsp *dsp); +bool skl_ipc_int_status(struct sst_dsp *ctx); void skl_ipc_free(struct sst_generic_ipc *ipc); int skl_ipc_init(struct device *dev, struct skl_dev *skl); void skl_clear_module_cnt(struct sst_dsp *ctx); diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 5e93ad85e06d..fb011862fb24 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -453,7 +453,7 @@ int skl_dsp_set_dma_control(struct skl_dev *skl, u32 *caps, void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai, struct skl_pipe_params *params, int stream); int skl_tplg_init(struct snd_soc_component *component, - struct hdac_bus *ebus); + struct hdac_bus *bus); void skl_tplg_exit(struct snd_soc_component *component, struct hdac_bus *bus); struct skl_module_cfg *skl_tplg_fe_get_cpr_module( @@ -476,13 +476,13 @@ int skl_stop_pipe(struct skl_dev *skl, struct skl_pipe *pipe); int skl_reset_pipe(struct skl_dev *skl, struct skl_pipe *pipe); -int skl_init_module(struct skl_dev *skl, struct skl_module_cfg *module_config); +int skl_init_module(struct skl_dev *skl, struct skl_module_cfg *mconfig); int skl_bind_modules(struct skl_dev *skl, struct skl_module_cfg - *src_module, struct skl_module_cfg *dst_module); + *src_mcfg, struct skl_module_cfg *dst_mcfg); int skl_unbind_modules(struct skl_dev *skl, struct skl_module_cfg - *src_module, struct skl_module_cfg *dst_module); + *src_mcfg, struct skl_module_cfg *dst_mcfg); int skl_set_module_params(struct skl_dev *skl, u32 *params, int size, u32 param_id, struct skl_module_cfg *mcfg); diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h index 26057f38a014..857ea17e3c9f 100644 --- a/sound/soc/intel/skylake/skl.h +++ b/sound/soc/intel/skylake/skl.h @@ -166,7 +166,7 @@ int skl_platform_unregister(struct device *dev); int skl_platform_register(struct device *dev); struct nhlt_specific_cfg *skl_get_ep_blob(struct skl_dev *skl, u32 instance, - u8 link_type, u8 s_fmt, u8 no_ch, + u8 link_type, u8 s_fmt, u8 num_ch, u32 s_rate, u8 dirn, u8 dev_type); int skl_nhlt_update_topology_bin(struct skl_dev *skl); -- cgit From 0d8aa2ccb2f21c79bc9d4dceab0c6f99ff20bae1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 7 Aug 2020 18:22:09 -0700 Subject: ASoC: various vendors: delete repeated words in comments Drop the repeated words {related, we, is, the} in comments. Signed-off-by: Randy Dunlap Cc: Liam Girdwood Cc: Mark Brown Cc: alsa-devel@alsa-project.org Link: https://lore.kernel.org/r/20200808012209.10880-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_dma.c | 2 +- sound/soc/intel/skylake/skl-sst.c | 2 +- sound/soc/meson/axg-tdm-formatter.c | 2 +- sound/soc/sprd/sprd-pcm-compress.c | 2 +- sound/soc/sunxi/sun4i-codec.c | 2 +- sound/soc/ti/davinci-mcasp.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_dma.c b/sound/soc/fsl/fsl_dma.c index be021250d6e9..e0c39c5f4854 100644 --- a/sound/soc/fsl/fsl_dma.c +++ b/sound/soc/fsl/fsl_dma.c @@ -154,7 +154,7 @@ static void fsl_dma_abort_stream(struct snd_pcm_substream *substream) /** * fsl_dma_update_pointers - update LD pointers to point to the next period * - * As each period is completed, this function changes the the link + * As each period is completed, this function changes the link * descriptor pointers for that period to point to the next period. */ static void fsl_dma_update_pointers(struct fsl_dma_private *dma_private) diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 61a8e4756a2b..00a97cea58b4 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -354,7 +354,7 @@ static int skl_transfer_module(struct sst_dsp *ctx, const void *data, /* * if bytes_left > 0 then wait for BDL complete interrupt and * copy the next chunk till bytes_left is 0. if bytes_left is - * is zero, then wait for load module IPC reply + * zero, then wait for load module IPC reply */ while (bytes_left > 0) { curr_pos = size - bytes_left; diff --git a/sound/soc/meson/axg-tdm-formatter.c b/sound/soc/meson/axg-tdm-formatter.c index f7e8e9da68a0..cab7fa2851aa 100644 --- a/sound/soc/meson/axg-tdm-formatter.c +++ b/sound/soc/meson/axg-tdm-formatter.c @@ -398,7 +398,7 @@ void axg_tdm_stream_free(struct axg_tdm_stream *ts) /* * If the list is not empty, it would mean that one of the formatter * widget is still powered and attached to the interface while we - * we are removing the TDM DAI. It should not be possible + * are removing the TDM DAI. It should not be possible */ WARN_ON(!list_empty(&ts->formatter_list)); mutex_destroy(&ts->lock); diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c index 749dcb7b993b..6507c03cc80e 100644 --- a/sound/soc/sprd/sprd-pcm-compress.c +++ b/sound/soc/sprd/sprd-pcm-compress.c @@ -559,7 +559,7 @@ static int sprd_platform_compr_copy(struct snd_soc_component *component, } else { /* * If the data count is larger than the available spaces - * of the the stage 0 IRAM buffer, we should copy one + * of the stage 0 IRAM buffer, we should copy one * partial data to the stage 0 IRAM buffer, and copy * the left to the stage 1 DDR buffer. */ diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c index 2af6404dbd62..6c13cc84b3fb 100644 --- a/sound/soc/sunxi/sun4i-codec.c +++ b/sound/soc/sunxi/sun4i-codec.c @@ -335,7 +335,7 @@ static int sun4i_codec_prepare_capture(struct snd_pcm_substream *substream, /* * FIXME: Undocumented in the datasheet, but - * Allwinner's code mentions that it is related + * Allwinner's code mentions that it is * related to microphone gain */ if (of_device_is_compatible(scodec->dev->of_node, diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 617440767c45..3ffdd0f6292a 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -633,7 +633,7 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id, * 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 + * tdm_slot width by dividing the ratio by the * number of configured tdm slots. */ mcasp->slot_width = div / mcasp->tdm_slots; -- cgit From 85131d9489a8b745d4cfa2b549371e97758008f1 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 7 Aug 2020 18:21:43 -0700 Subject: ASoC: codecs: delete repeated words in comments Drop the repeated words {start, it, the} in comments. Signed-off-by: Randy Dunlap Cc: Liam Girdwood Cc: Mark Brown Cc: alsa-devel@alsa-project.org Link: https://lore.kernel.org/r/20200808012143.10777-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/codecs/nau8825.c | 2 +- sound/soc/codecs/tas5086.c | 2 +- sound/soc/codecs/wm9713.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index 9f5aee7de686..f0cba7b5758b 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -251,7 +251,7 @@ static const unsigned short logtable[256] = { * * Acquires the semaphore without jiffies. Try to acquire the semaphore * atomically. Returns 0 if the semaphore has been acquired successfully - * or 1 if it it cannot be acquired. + * or 1 if it cannot be acquired. */ static int nau8825_sema_acquire(struct nau8825 *nau8825, long timeout) { diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 0250b94c8f65..7831c96d0d83 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -487,7 +487,7 @@ static int tas5086_init(struct device *dev, struct tas5086_private *priv) /* * If any of the channels is configured to start in Mid-Z mode, * configure 'part 1' of the PWM starts to use Mid-Z, and tell - * all configured mid-z channels to start start under 'part 1'. + * all configured mid-z channels to start under 'part 1'. */ if (priv->pwm_start_mid_z) regmap_write(priv->regmap, TAS5086_PWM_START, diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 7072ffacbdfd..f333e2ff4a16 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -755,7 +755,7 @@ static void pll_factors(struct snd_soc_component *component, u64 Kpart; unsigned int K, Ndiv, Nmod, target; - /* The the PLL output is always 98.304MHz. */ + /* The PLL output is always 98.304MHz. */ target = 98304000; /* If the input frequency is over 14.4MHz then scale it down. */ -- cgit From ade5376dff579e759cd40cf7831306173bb875cf Mon Sep 17 00:00:00 2001 From: Youling Tang Date: Thu, 13 Aug 2020 14:12:37 +0800 Subject: sound/soc/intel: Fix spelling mistake "cant" --> "can't" There is some spelling mistakes in a dev_err message. Fix it. Signed-off-by: Youling Tang Link: https://lore.kernel.org/r/1597299157-32221-1-git-send-email-tangyouling@loongson.cn Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-firmware.c | 4 ++-- sound/soc/intel/haswell/sst-haswell-ipc.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c index 0594f89ea7f2..1189ec37134e 100644 --- a/sound/soc/intel/common/sst-firmware.c +++ b/sound/soc/intel/common/sst-firmware.c @@ -130,7 +130,7 @@ static void block_list_remove(struct sst_dsp *dsp, err = block->ops->disable(block); if (err < 0) dev_err(dsp->dev, - "error: cant disable block %d:%d\n", + "error: can't disable block %d:%d\n", block->type, block->index); } } @@ -158,7 +158,7 @@ static int block_list_prepare(struct sst_dsp *dsp, ret = block->ops->enable(block); if (ret < 0) { dev_err(dsp->dev, - "error: cant disable block %d:%d\n", + "error: can't disable block %d:%d\n", block->type, block->index); goto err; } diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c index 0ff89ea96ccf..773688b8eb3f 100644 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ b/sound/soc/intel/haswell/sst-haswell-ipc.c @@ -1507,7 +1507,7 @@ static int sst_hsw_dx_state_dump(struct sst_hsw *hsw) ret = sst_dsp_dma_get_channel(sst, 0); if (ret < 0) { - dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret); + dev_err(hsw->dev, "error: can't allocate dma channel %d\n", ret); return ret; } @@ -1587,7 +1587,7 @@ int sst_hsw_dsp_load(struct sst_hsw *hsw) ret = sst_dsp_dma_get_channel(dsp, 0); if (ret < 0) { - dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret); + dev_err(hsw->dev, "error: can't allocate dma channel %d\n", ret); return ret; } @@ -1616,7 +1616,7 @@ static int sst_hsw_dsp_restore(struct sst_hsw *hsw) ret = sst_dsp_dma_get_channel(dsp, 0); if (ret < 0) { - dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret); + dev_err(hsw->dev, "error: can't allocate dma channel %d\n", ret); return ret; } -- cgit From 1f53bcb3fc952b55a0ad0e7f6eabfc93a170659d Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Tue, 18 Aug 2020 08:44:13 +0800 Subject: ASoC: hdac_hdmi: support 'ELD' mixer Add an binary mixer 'ELD' to each HDMI PCM device so user space could read the ELD data of external HDMI display. Signed-off-by: Brent Lu Link: https://lore.kernel.org/r/20200818004413.12852-1-brent.lu@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 138 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index f26b77faed59..869d1547ae5d 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -9,6 +9,7 @@ * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + #include #include #include @@ -107,6 +108,7 @@ struct hdac_hdmi_pcm { unsigned char chmap[8]; /* ALSA API channel-map */ struct mutex lock; int jack_event; + struct snd_kcontrol *eld_ctl; }; struct hdac_hdmi_dai_port_map { @@ -1248,6 +1250,7 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, struct hdac_hdmi_pcm *pcm; int size = 0; int port_id = -1; + bool eld_valid, eld_changed; if (!hdmi) return; @@ -1273,6 +1276,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, size = -EINVAL; } + eld_valid = port->eld.eld_valid; + if (size > 0) { port->eld.eld_valid = true; port->eld.eld_size = size; @@ -1281,6 +1286,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, port->eld.eld_size = 0; } + eld_changed = (eld_valid != port->eld.eld_valid); + pcm = hdac_hdmi_get_pcm(hdev, port); if (!port->eld.monitor_present || !port->eld.eld_valid) { @@ -1313,6 +1320,12 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, } mutex_unlock(&hdmi->pin_mutex); + + if (eld_changed && pcm) + snd_ctl_notify(hdmi->card, + SNDRV_CTL_EVENT_MASK_VALUE | + SNDRV_CTL_EVENT_MASK_INFO, + &pcm->eld_ctl->id); } static int hdac_hdmi_add_ports(struct hdac_device *hdev, @@ -1411,6 +1424,122 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev) } +static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + struct hdac_hdmi_eld *eld; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; + uinfo->count = 0; + + pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); + if (!pcm) { + dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, + kcontrol->id.device); + return 0; + } + + if (list_empty(&pcm->port_list)) { + dev_dbg(component->dev, "%s: empty port list, device %d\n", + __func__, kcontrol->id.device); + return 0; + } + + mutex_lock(&hdmi->pin_mutex); + + list_for_each_entry(port, &pcm->port_list, head) { + eld = &port->eld; + + if (eld->eld_valid) { + uinfo->count = eld->eld_size; + break; + } + } + + mutex_unlock(&hdmi->pin_mutex); + + return 0; +} + +static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component); + struct hdac_hdmi_pcm *pcm; + struct hdac_hdmi_port *port; + struct hdac_hdmi_eld *eld; + + memset(ucontrol->value.bytes.data, 0, ARRAY_SIZE(ucontrol->value.bytes.data)); + + pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); + if (!pcm) { + dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__, + kcontrol->id.device); + return 0; + } + + if (list_empty(&pcm->port_list)) { + dev_dbg(component->dev, "%s: empty port list, device %d\n", + __func__, kcontrol->id.device); + return 0; + } + + mutex_lock(&hdmi->pin_mutex); + + list_for_each_entry(port, &pcm->port_list, head) { + eld = &port->eld; + + if (!eld->eld_valid) + continue; + + if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) || + eld->eld_size > ELD_MAX_SIZE) { + mutex_unlock(&hdmi->pin_mutex); + + dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n", + __func__, kcontrol->id.device, eld->eld_size); + snd_BUG(); + return -EINVAL; + } + + memcpy(ucontrol->value.bytes.data, eld->eld_buffer, + eld->eld_size); + break; + } + + mutex_unlock(&hdmi->pin_mutex); + + return 0; +} + +static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm) +{ + struct snd_kcontrol *kctl; + struct snd_kcontrol_new hdmi_eld_ctl = { + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "ELD", + .info = hdac_hdmi_eld_ctl_info, + .get = hdac_hdmi_eld_ctl_get, + .device = pcm->pcm_id, + }; + + /* add ELD ctl with the device number corresponding to the PCM stream */ + kctl = snd_ctl_new1(&hdmi_eld_ctl, component); + if (!kctl) + return -ENOMEM; + + pcm->eld_ctl = kctl; + + return snd_ctl_add(component->card->snd_card, kctl); +} + static const struct snd_soc_dai_ops hdmi_dai_ops = { .startup = hdac_hdmi_pcm_open, .shutdown = hdac_hdmi_pcm_close, @@ -1784,6 +1913,15 @@ int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device, } } + /* add control for ELD Bytes */ + err = hdac_hdmi_create_eld_ctl(component, pcm); + if (err < 0) { + dev_err(&hdev->dev, + "eld control add failed with err: %d for pcm: %d\n", + err, device); + return err; + } + list_add_tail(&pcm->head, &hdmi->pcm_list); return 0; -- cgit From 7e3096e8f823682c20e033113ec32dd590364774 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 14 Aug 2020 17:32:41 +0800 Subject: ASoC: ak4458: Add regulator support "AVDD" is for analog power supply, "DVDD" is for digital power supply, they can improve the power management. As the regulator is enabled in pm runtime resume, which is behind the component driver probe, so accessing registers in component driver probe will fail. Fix this issue by enabling regcache_cache_only after pm_runtime_enable. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1597397561-2426-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4458.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index cbe3c782e0ca..763e6839428f 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,12 @@ #include "ak4458.h" +#define AK4458_NUM_SUPPLIES 2 +static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = { + "DVDD", + "AVDD", +}; + struct ak4458_drvdata { struct snd_soc_dai_driver *dai_drv; const struct snd_soc_component_driver *comp_drv; @@ -28,6 +35,7 @@ struct ak4458_drvdata { /* AK4458 Codec Private Data */ struct ak4458_priv { + struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES]; struct device *dev; struct regmap *regmap; struct gpio_desc *reset_gpiod; @@ -587,12 +595,22 @@ static int __maybe_unused ak4458_runtime_suspend(struct device *dev) if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 0); + regulator_bulk_disable(ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); return 0; } static int __maybe_unused ak4458_runtime_resume(struct device *dev) { struct ak4458_priv *ak4458 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + if (ret != 0) { + dev_err(ak4458->dev, "Failed to enable supplies: %d\n", ret); + return ret; + } if (ak4458->mute_gpiod) gpiod_set_value_cansleep(ak4458->mute_gpiod, 1); @@ -667,7 +685,7 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) { struct ak4458_priv *ak4458; const struct ak4458_drvdata *drvdata; - int ret; + int ret, i; ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); if (!ak4458) @@ -692,6 +710,16 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak4458->mute_gpiod)) return PTR_ERR(ak4458->mute_gpiod); + for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++) + ak4458->supplies[i].supply = ak4458_supply_names[i]; + + ret = devm_regulator_bulk_get(ak4458->dev, ARRAY_SIZE(ak4458->supplies), + ak4458->supplies); + if (ret != 0) { + dev_err(ak4458->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv, drvdata->dai_drv, 1); if (ret < 0) { @@ -700,6 +728,7 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) } pm_runtime_enable(&i2c->dev); + regcache_cache_only(ak4458->regmap, true); return 0; } -- cgit From 1ae0965dc21698ba41638c95b5478779f3c4a9e4 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Sat, 1 Aug 2020 12:02:55 +0200 Subject: ASoC: core: Add common helper to parse aux devs from device tree simple-card.c and meson-card-utils.c use pretty much the same helper function to parse auxiliary devices from the device tree. Make it easier for other drivers to parse these from the device tree as well by adding a shared helper function to soc-core.c. snd_soc_of_parse_aux_devs() is pretty much a copy of meson_card_add_aux_devices() from meson-card-utils.c with two minor changes: - Make property name configurable as parameter - Change dev_err() message slightly for consistency with other error messages in soc-core.c Signed-off-by: Stephan Gerhold Reviewed-by: Jerome Brunet Link: https://lore.kernel.org/r/20200801100257.22658-1-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 2fe1b2ec7c8f..bf46f410c8c6 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -2827,6 +2827,37 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card, } EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_routing); +int snd_soc_of_parse_aux_devs(struct snd_soc_card *card, const char *propname) +{ + struct device_node *node = card->dev->of_node; + struct snd_soc_aux_dev *aux; + int num, i; + + num = of_count_phandle_with_args(node, propname, NULL); + if (num == -ENOENT) { + return 0; + } else if (num < 0) { + dev_err(card->dev, "ASOC: Property '%s' could not be read: %d\n", + propname, num); + return num; + } + + aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); + if (!aux) + return -ENOMEM; + card->aux_dev = aux; + card->num_aux_devs = num; + + for_each_card_pre_auxs(card, i, aux) { + aux->dlc.of_node = of_parse_phandle(node, propname, i); + if (!aux->dlc.of_node) + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_of_parse_aux_devs); + unsigned int snd_soc_of_parse_daifmt(struct device_node *np, const char *prefix, struct device_node **bitclkmaster, -- cgit From d9ffff696c5b468ba5a4ddb2cbc05c9e4dd4b2d1 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Sat, 1 Aug 2020 12:02:56 +0200 Subject: ASoC: simple-card: Use snd_soc_of_parse_aux_devs() Use the new common snd_soc_of_parse_aux_devs() helper function to parse auxiliary devices from the device tree. The code is slightly different but the binding that is parsed is exactly the same. Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20200801100257.22658-2-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/generic/simple-card.c | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) (limited to 'sound') diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c index 04d4d28ed511..75365c7bb393 100644 --- a/sound/soc/generic/simple-card.c +++ b/sound/soc/generic/simple-card.c @@ -424,37 +424,6 @@ static int simple_for_each_link(struct asoc_simple_priv *priv, return ret; } -static int simple_parse_aux_devs(struct device_node *node, - struct asoc_simple_priv *priv) -{ - struct device *dev = simple_priv_to_dev(priv); - struct device_node *aux_node; - struct snd_soc_card *card = simple_priv_to_card(priv); - int i, n, len; - - if (!of_find_property(node, PREFIX "aux-devs", &len)) - return 0; /* Ok to have no aux-devs */ - - n = len / sizeof(__be32); - if (n <= 0) - return -EINVAL; - - card->aux_dev = devm_kcalloc(dev, - n, sizeof(*card->aux_dev), GFP_KERNEL); - if (!card->aux_dev) - return -ENOMEM; - - for (i = 0; i < n; i++) { - aux_node = of_parse_phandle(node, PREFIX "aux-devs", i); - if (!aux_node) - return -EINVAL; - card->aux_dev[i].dlc.of_node = aux_node; - } - - card->num_aux_devs = n; - return 0; -} - static int simple_parse_of(struct asoc_simple_priv *priv) { struct device *dev = simple_priv_to_dev(priv); @@ -504,7 +473,7 @@ static int simple_parse_of(struct asoc_simple_priv *priv) if (ret < 0) return ret; - ret = simple_parse_aux_devs(top, priv); + ret = snd_soc_of_parse_aux_devs(card, PREFIX "aux-devs"); return ret; } -- cgit From 6bc37d32f630363b68059e3fa854383e6abf171e Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Sat, 1 Aug 2020 12:02:57 +0200 Subject: ASoC: meson: Use snd_soc_of_parse_aux_devs() Use the new common snd_soc_of_parse_aux_devs() helper function to parse auxiliary devices from the device tree. The new helper is just a copy of meson_card_add_aux_devices() so there is no functional change. Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20200801100257.22658-3-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/meson/meson-card-utils.c | 33 +-------------------------------- 1 file changed, 1 insertion(+), 32 deletions(-) (limited to 'sound') diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c index 6a64ac01b5ca..300ac8be46ef 100644 --- a/sound/soc/meson/meson-card-utils.c +++ b/sound/soc/meson/meson-card-utils.c @@ -254,37 +254,6 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card, return func(card, propname); } -static int meson_card_add_aux_devices(struct snd_soc_card *card) -{ - struct device_node *node = card->dev->of_node; - struct snd_soc_aux_dev *aux; - int num, i; - - num = of_count_phandle_with_args(node, "audio-aux-devs", NULL); - if (num == -ENOENT) { - return 0; - } else if (num < 0) { - dev_err(card->dev, "error getting auxiliary devices: %d\n", - num); - return num; - } - - aux = devm_kcalloc(card->dev, num, sizeof(*aux), GFP_KERNEL); - if (!aux) - return -ENOMEM; - card->aux_dev = aux; - card->num_aux_devs = num; - - for_each_card_pre_auxs(card, i, aux) { - aux->dlc.of_node = - of_parse_phandle(node, "audio-aux-devs", i); - if (!aux->dlc.of_node) - return -EINVAL; - } - - return 0; -} - static void meson_card_clean_references(struct meson_card *priv) { struct snd_soc_card *card = &priv->card; @@ -357,7 +326,7 @@ int meson_card_probe(struct platform_device *pdev) if (ret) goto out_err; - ret = meson_card_add_aux_devices(&priv->card); + ret = snd_soc_of_parse_aux_devs(&priv->card, "audio-aux-devs"); if (ret) goto out_err; -- cgit From 093513b8ed12d3f074c846cfae5ca4e34f3b37f4 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 13 Aug 2020 12:54:42 -0500 Subject: ASoC: codecs: wm0010: use DECLARE_COMPLETION_ONSTACK() macro Follow recommendation in Documentation/scheduler/completion.rst and use macro to declare local 'struct completion' Signed-off-by: Pierre-Louis Bossart Acked-by: Charles Keepax Link: https://lore.kernel.org/r/20200813175442.59067-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm0010.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 2f2b2f5d55e4..28b4656c4e14 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -346,7 +346,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp struct list_head xfer_list; struct wm0010_boot_xfer *xfer; int ret; - struct completion done; + DECLARE_COMPLETION_ONSTACK(done); const struct firmware *fw; const struct dfw_binrec *rec; const struct dfw_inforec *inforec; @@ -370,7 +370,6 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_component *comp wm0010->boot_failed = false; if (WARN_ON(!list_empty(&xfer_list))) return -EINVAL; - init_completion(&done); /* First record should be INFO */ if (rec->command != DFW_CMD_INFO) { -- cgit From 8932f0cb20e815478a5aaa6ff2850ff25f10a2fe Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 7 Aug 2020 18:21:56 -0700 Subject: ASoC: SOF: delete repeated words in comments Drop the repeated words {that, the} in comments. Signed-off-by: Randy Dunlap Acked-by: Pierre-Louis Bossart Cc: Liam Girdwood Cc: Mark Brown Cc: alsa-devel@alsa-project.org Link: https://lore.kernel.org/r/20200808012156.10827-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/sof/intel/cnl.c | 2 +- sound/soc/sof/sof-audio.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 16db0f50d139..95234ae59e42 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -202,7 +202,7 @@ static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, * IPCs are sent at a high-rate. mod_delayed_work() * modifies the timer if the work is pending. * Also, a new delayed work should not be queued after the - * the CTX_SAVE IPC, which is sent before the DSP enters D3. + * CTX_SAVE IPC, which is sent before the DSP enters D3. */ if (hdr->cmd != (SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CTX_SAVE)) mod_delayed_work(system_wq, &hdev->d0i3_work, diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 1c7698f8edd6..33d84405cf9c 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -29,7 +29,7 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev) continue; /* - * substream->runtime being not NULL indicates that + * substream->runtime being not NULL indicates * that the stream is open. No need to check the * stream state. */ -- cgit From 466a806a7d2beffa6a79d61dbabac8a48685c3e2 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 17 Aug 2020 23:47:06 +0100 Subject: sound: remove duplicate "the the" phrase in Kconfig text There are a couple of occurrences of "the the" in the Kconfig text. Fix these. Signed-off-by: Colin Ian King Acked-by: Michal Simek Link: https://lore.kernel.org/r/20200817224706.6139-1-colin.king@canonical.com Signed-off-by: Mark Brown --- sound/soc/xilinx/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig index 1d3586b68db7..5bd2730aab76 100644 --- a/sound/soc/xilinx/Kconfig +++ b/sound/soc/xilinx/Kconfig @@ -9,14 +9,14 @@ config SND_SOC_XILINX_I2S encapsulates PCM in AES format and sends AES data. config SND_SOC_XILINX_AUDIO_FORMATTER - tristate "Audio support for the the Xilinx audio formatter" + tristate "Audio support for the Xilinx audio formatter" help Select this option to enable Xilinx audio formatter support. This provides DMA platform device support for audio functionality. config SND_SOC_XILINX_SPDIF - tristate "Audio support for the the Xilinx SPDIF" + tristate "Audio support for the Xilinx SPDIF" help Select this option to enable Xilinx SPDIF Audio. This provides playback and capture of SPDIF audio in -- cgit From 89383a2707e54b39e01407fb611662d131551bb2 Mon Sep 17 00:00:00 2001 From: Sebastian Krzyszkowiak Date: Sun, 16 Aug 2020 03:23:34 +0200 Subject: ASoC: wm8962: Export DAC/ADC monomix switches This allows solutions like ALSA UCM to utilize hardware mono downmix for cases where mono output to a single speaker is desired only in specific situations (like on a mobile phone). Signed-off-by: Sebastian Krzyszkowiak Acked-by: Charles Keepax Link: https://lore.kernel.org/r/3662154.EqNIRYjrc8@pliszka Signed-off-by: Mark Brown --- sound/soc/codecs/wm8962.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 0623a2251084..0bd3bbc2aacf 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -1702,6 +1702,8 @@ SOC_DOUBLE_R_TLV("Digital Playback Volume", WM8962_LEFT_DAC_VOLUME, SOC_SINGLE("DAC High Performance Switch", WM8962_ADC_DAC_CONTROL_2, 0, 1, 0), SOC_SINGLE("DAC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 5, 1, 0), SOC_SINGLE("ADC L/R Swap Switch", WM8962_AUDIO_INTERFACE_0, 8, 1, 0), +SOC_SINGLE("DAC Monomix Switch", WM8962_DAC_DSP_MIXING_1, WM8962_DAC_MONOMIX_SHIFT, 1, 0), +SOC_SINGLE("ADC Monomix Switch", WM8962_THREED1, WM8962_ADC_MONOMIX_SHIFT, 1, 0), SOC_SINGLE("ADC High Performance Switch", WM8962_ADDITIONAL_CONTROL_1, 5, 1, 0), -- cgit From db24fa5756e944a711a66692af7e25a2189bfe52 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 11 Aug 2020 11:58:18 +0100 Subject: ASoC: qcom: add a dedicated menuconfig Currently list of Qualcomm drivers is growing, so put them in to a proper menu so that it does not mix up with other ASOC configs in menuconfig. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200811105818.7890-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 5d6b2466a2f2..be6b8d0e2f70 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -1,11 +1,13 @@ # SPDX-License-Identifier: GPL-2.0-only -config SND_SOC_QCOM +menuconfig SND_SOC_QCOM tristate "ASoC support for QCOM platforms" depends on ARCH_QCOM || COMPILE_TEST help Say Y or M if you want to add support to use audio devices in Qualcomm Technologies SOC-based platforms. +if SND_SOC_QCOM + config SND_SOC_LPASS_CPU tristate select REGMAP_MMIO @@ -26,7 +28,6 @@ config SND_SOC_LPASS_APQ8016 config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" - depends on SND_SOC_QCOM select SND_SOC_LPASS_IPQ806X select SND_SOC_MAX98357A help @@ -35,7 +36,6 @@ config SND_SOC_STORM config SND_SOC_APQ8016_SBC tristate "SoC Audio support for APQ8016 SBC platforms" - depends on SND_SOC_QCOM select SND_SOC_LPASS_APQ8016 select SND_SOC_QCOM_COMMON help @@ -110,3 +110,5 @@ config SND_SOC_SDM845 To add support for audio on Qualcomm Technologies Inc. SDM845 SoC-based systems. Say Y if you want to use audio device on this SoCs. + +endif #SND_SOC_QCOM -- cgit From a8fd5ca8016948a611bff62b0990ade9e1f8e83c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 10 Aug 2020 16:11:43 +0800 Subject: ASoC: fsl-asoc-card: Get "extal" clock rate by clk_get_rate On some platform(.e.g. i.MX8QM MEK), the "extal" clock is different with the mclk of codec, then the clock rate is also different. So it is better to get clock rate of "extal" rate by clk_get_rate, don't reuse the clock rate of mclk. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1597047103-6863-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl-asoc-card.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 52adedc03245..32f8f756e6bb 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -696,6 +696,17 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) goto asrc_fail; } } else if (of_node_name_eq(cpu_np, "esai")) { + struct clk *esai_clk = clk_get(&cpu_pdev->dev, "extal"); + + if (!IS_ERR(esai_clk)) { + priv->cpu_priv.sysclk_freq[TX] = clk_get_rate(esai_clk); + priv->cpu_priv.sysclk_freq[RX] = clk_get_rate(esai_clk); + clk_put(esai_clk); + } else if (PTR_ERR(esai_clk) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto asrc_fail; + } + priv->cpu_priv.sysclk_id[1] = ESAI_HCKT_EXTAL; priv->cpu_priv.sysclk_id[0] = ESAI_HCKR_EXTAL; } else if (of_node_name_eq(cpu_np, "sai")) { -- cgit From a4d328efed242b9912f5fe72948ed4587aba5197 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 18 Aug 2020 17:01:26 +0100 Subject: ASoC: wm_adsp: Update naming in error handling It seems the datasheet has never used the word slave for this error status bit and has always used the term address error. So update the driver to match the datasheets and also in the process align a bit better with avoiding the use of such words where possible. Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20200818160126.4852-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 410cca57da52..4ff0c6cfa32d 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -199,7 +199,7 @@ #define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C #define ADSP2_REGION_LOCK_ERR_MASK 0x8000 -#define ADSP2_SLAVE_ERR_MASK 0x4000 +#define ADSP2_ADDR_ERR_MASK 0x4000 #define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000 #define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002 #define ADSP2_CTRL_ERR_EINT 0x0001 @@ -4364,9 +4364,9 @@ irqreturn_t wm_adsp2_bus_error(int irq, void *data) wm_adsp_fatal_error(dsp); } - if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { - if (val & ADSP2_SLAVE_ERR_MASK) - adsp_err(dsp, "bus error: slave error\n"); + if (val & (ADSP2_ADDR_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) { + if (val & ADSP2_ADDR_ERR_MASK) + adsp_err(dsp, "bus error: address error\n"); else adsp_err(dsp, "bus error: region lock error\n"); -- cgit From 9e4730586e0b3ca423e7147104790908a1adb1ba Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 18 Aug 2020 22:14:35 +0800 Subject: ASoC: codecs: rt*-sdw: use SDW_SLAVE_ENTRY_EXT Add version and class information explicitly to prepare for support for new devices. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200818141435.29205-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1308-sdw.c | 2 +- sound/soc/codecs/rt5682-sdw.c | 2 +- sound/soc/codecs/rt700-sdw.c | 2 +- sound/soc/codecs/rt711-sdw.c | 2 +- sound/soc/codecs/rt715-sdw.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index b0ba0d2acbdd..6a2318e04bb7 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -693,7 +693,7 @@ static int rt1308_sdw_probe(struct sdw_slave *slave, } static const struct sdw_device_id rt1308_id[] = { - SDW_SLAVE_ENTRY(0x025d, 0x1308, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x1308, 0x2, 0, 0), {}, }; MODULE_DEVICE_TABLE(sdw, rt1308_id); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 94bf6bee78e6..b0386f592290 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -717,7 +717,7 @@ static int rt5682_sdw_remove(struct sdw_slave *slave) } static const struct sdw_device_id rt5682_id[] = { - SDW_SLAVE_ENTRY(0x025d, 0x5682, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x5682, 0x2, 0, 0), {}, }; MODULE_DEVICE_TABLE(sdw, rt5682_id); diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 4d14048d1197..5430941cb2da 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -478,7 +478,7 @@ static int rt700_sdw_remove(struct sdw_slave *slave) } static const struct sdw_device_id rt700_id[] = { - SDW_SLAVE_ENTRY(0x025d, 0x700, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x700, 0x1, 0, 0), {}, }; MODULE_DEVICE_TABLE(sdw, rt700_id); diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 45b928954b58..dff098ddde01 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -479,7 +479,7 @@ static int rt711_sdw_remove(struct sdw_slave *slave) } static const struct sdw_device_id rt711_id[] = { - SDW_SLAVE_ENTRY(0x025d, 0x711, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x2, 0, 0), {}, }; MODULE_DEVICE_TABLE(sdw, rt711_id); diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index d11b23d6b240..00e441560f00 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -541,7 +541,7 @@ static int rt715_sdw_probe(struct sdw_slave *slave, } static const struct sdw_device_id rt715_id[] = { - SDW_SLAVE_ENTRY(0x025d, 0x715, 0), + SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0), {}, }; MODULE_DEVICE_TABLE(sdw, rt715_id); -- cgit From 9666e27f90b995456f84f8b95eb4eacdb38a3c3b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2020 20:20:41 +0200 Subject: ASoC: samsung: h1940: turn into platform driver Avoid machine specific headers by using a gpio lookup table combined with a platform_driver for this board. Signed-off-by: Arnd Bergmann Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200806182059.2431-24-krzk@kernel.org Signed-off-by: Mark Brown --- sound/soc/samsung/h1940_uda1380.c | 71 +++++++++++++-------------------------- 1 file changed, 23 insertions(+), 48 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/h1940_uda1380.c b/sound/soc/samsung/h1940_uda1380.c index b8f0057a0510..8aa78ff640f5 100644 --- a/sound/soc/samsung/h1940_uda1380.c +++ b/sound/soc/samsung/h1940_uda1380.c @@ -15,9 +15,6 @@ #include #include "regs-iis.h" -#include - -#include #include "s3c24xx-i2s.h" static const unsigned int rates[] = { @@ -31,6 +28,8 @@ static const struct snd_pcm_hw_constraint_list hw_rates = { .list = rates, }; +static struct gpio_desc *gpiod_speaker_power; + static struct snd_soc_jack hp_jack; static struct snd_soc_jack_pin hp_jack_pins[] = { @@ -47,7 +46,6 @@ static struct snd_soc_jack_pin hp_jack_pins[] = { static struct snd_soc_jack_gpio hp_jack_gpios[] = { { - .gpio = S3C2410_GPG(4), .name = "hp-gpio", .report = SND_JACK_HEADPHONE, .invert = 1, @@ -123,9 +121,9 @@ static int h1940_spk_power(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { if (SND_SOC_DAPM_EVENT_ON(event)) - gpio_set_value(S3C_GPIO_END + 9, 1); + gpiod_set_value(gpiod_speaker_power, 1); else - gpio_set_value(S3C_GPIO_END + 9, 0); + gpiod_set_value(gpiod_speaker_power, 0); return 0; } @@ -151,8 +149,6 @@ static const struct snd_soc_dapm_route audio_map[] = { {"VINM", NULL, "Mic Jack"}, }; -static struct platform_device *s3c24xx_snd_device; - static int h1940_uda1380_init(struct snd_soc_pcm_runtime *rtd) { snd_soc_card_jack_new(rtd->card, "Headphone Jack", SND_JACK_HEADPHONE, @@ -194,55 +190,34 @@ static struct snd_soc_card h1940_asoc = { .num_dapm_routes = ARRAY_SIZE(audio_map), }; -static int __init h1940_init(void) +static int h1940_probe(struct platform_device *pdev) { - int ret; + struct device *dev = &pdev->dev; - if (!machine_is_h1940()) - return -ENODEV; + h1940_asoc.dev = dev; + hp_jack_gpios[0].gpiod_dev = dev; + gpiod_speaker_power = devm_gpiod_get(&pdev->dev, "speaker-power", + GPIOD_OUT_LOW); - /* configure some gpios */ - ret = gpio_request(S3C_GPIO_END + 9, "speaker-power"); - if (ret) - goto err_out; - - ret = gpio_direction_output(S3C_GPIO_END + 9, 0); - if (ret) - goto err_gpio; - - s3c24xx_snd_device = platform_device_alloc("soc-audio", -1); - if (!s3c24xx_snd_device) { - ret = -ENOMEM; - goto err_gpio; + if (IS_ERR(gpiod_speaker_power)) { + dev_err(dev, "Could not get gpio\n"); + return PTR_ERR(gpiod_speaker_power); } - platform_set_drvdata(s3c24xx_snd_device, &h1940_asoc); - ret = platform_device_add(s3c24xx_snd_device); - - if (ret) - goto err_plat; - - return 0; - -err_plat: - platform_device_put(s3c24xx_snd_device); -err_gpio: - gpio_free(S3C_GPIO_END + 9); - -err_out: - return ret; -} - -static void __exit h1940_exit(void) -{ - platform_device_unregister(s3c24xx_snd_device); - gpio_free(S3C_GPIO_END + 9); + return devm_snd_soc_register_card(dev, &h1940_asoc); } -module_init(h1940_init); -module_exit(h1940_exit); +static struct platform_driver h1940_audio_driver = { + .driver = { + .name = "h1940-audio", + .pm = &snd_soc_pm_ops, + }, + .probe = h1940_probe, +}; +module_platform_driver(h1940_audio_driver); /* Module information */ MODULE_AUTHOR("Arnaud Patard, Vasily Khoruzhick"); MODULE_DESCRIPTION("ALSA SoC H1940"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:h1940-audio"); -- cgit From e26a2abcc2465ee8e8fd9a19ac1a418d08973b94 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2020 20:20:42 +0200 Subject: ASoC: samsung: neo1973: turn into platform driver Avoid machine specific headers by using a gpio lookup table combined with a platform_driver for this board. Signed-off-by: Arnd Bergmann Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200806182059.2431-25-krzk@kernel.org Signed-off-by: Mark Brown --- sound/soc/samsung/neo1973_wm8753.c | 85 ++++++++++++-------------------------- 1 file changed, 26 insertions(+), 59 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/neo1973_wm8753.c b/sound/soc/samsung/neo1973_wm8753.c index 54317e0f68f8..9266070e0181 100644 --- a/sound/soc/samsung/neo1973_wm8753.c +++ b/sound/soc/samsung/neo1973_wm8753.c @@ -11,14 +11,11 @@ #include #include -#include +#include #include -#include -#include #include "regs-iis.h" - #include "../codecs/wm8753.h" #include "s3c24xx-i2s.h" @@ -166,6 +163,7 @@ static struct snd_soc_ops neo1973_voice_ops = { .hw_free = neo1973_voice_hw_free, }; +static struct gpio_desc *gpiod_hp_in, *gpiod_amp_shut; static int gta02_speaker_enabled; static int lm4853_set_spk(struct snd_kcontrol *kcontrol, @@ -173,7 +171,7 @@ static int lm4853_set_spk(struct snd_kcontrol *kcontrol, { gta02_speaker_enabled = ucontrol->value.integer.value[0]; - gpio_set_value(S3C2410_GPJ(2), !gta02_speaker_enabled); + gpiod_set_value(gpiod_hp_in, !gta02_speaker_enabled); return 0; } @@ -188,7 +186,7 @@ static int lm4853_get_spk(struct snd_kcontrol *kcontrol, static int lm4853_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *k, int event) { - gpio_set_value(S3C2410_GPJ(1), SND_SOC_DAPM_EVENT_OFF(event)); + gpiod_set_value(gpiod_amp_shut, SND_SOC_DAPM_EVENT_OFF(event)); return 0; } @@ -308,13 +306,8 @@ static struct snd_soc_codec_conf neo1973_codec_conf[] = { }, }; -static const struct gpio neo1973_gta02_gpios[] = { - { S3C2410_GPJ(2), GPIOF_OUT_INIT_HIGH, "GTA02_HP_IN" }, - { S3C2410_GPJ(1), GPIOF_OUT_INIT_HIGH, "GTA02_AMP_SHUT" }, -}; - static struct snd_soc_card neo1973 = { - .name = "neo1973", + .name = "neo1973gta02", .owner = THIS_MODULE, .dai_link = neo1973_dai, .num_links = ARRAY_SIZE(neo1973_dai), @@ -332,62 +325,36 @@ static struct snd_soc_card neo1973 = { .fully_routed = true, }; -static struct platform_device *neo1973_snd_device; - -static int __init neo1973_init(void) +static int neo1973_probe(struct platform_device *pdev) { - int ret; - - if (!machine_is_neo1973_gta02()) - return -ENODEV; + struct device *dev = &pdev->dev; - if (machine_is_neo1973_gta02()) { - neo1973.name = "neo1973gta02"; - neo1973.num_aux_devs = 1; - - ret = gpio_request_array(neo1973_gta02_gpios, - ARRAY_SIZE(neo1973_gta02_gpios)); - if (ret) - return ret; + gpiod_hp_in = devm_gpiod_get(dev, "hp", GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_hp_in)) { + dev_err(dev, "missing gpio %s\n", "hp"); + return PTR_ERR(gpiod_hp_in); } - - neo1973_snd_device = platform_device_alloc("soc-audio", -1); - if (!neo1973_snd_device) { - ret = -ENOMEM; - goto err_gpio_free; + gpiod_amp_shut = devm_gpiod_get(dev, "amp-shut", GPIOD_OUT_HIGH); + if (IS_ERR(gpiod_amp_shut)) { + dev_err(dev, "missing gpio %s\n", "amp-shut"); + return PTR_ERR(gpiod_amp_shut); } - platform_set_drvdata(neo1973_snd_device, &neo1973); - ret = platform_device_add(neo1973_snd_device); - - if (ret) - goto err_put_device; - - return 0; - -err_put_device: - platform_device_put(neo1973_snd_device); -err_gpio_free: - if (machine_is_neo1973_gta02()) { - gpio_free_array(neo1973_gta02_gpios, - ARRAY_SIZE(neo1973_gta02_gpios)); - } - return ret; + neo1973.dev = dev; + return devm_snd_soc_register_card(dev, &neo1973); } -module_init(neo1973_init); - -static void __exit neo1973_exit(void) -{ - platform_device_unregister(neo1973_snd_device); - if (machine_is_neo1973_gta02()) { - gpio_free_array(neo1973_gta02_gpios, - ARRAY_SIZE(neo1973_gta02_gpios)); - } -} -module_exit(neo1973_exit); +struct platform_driver neo1973_audio = { + .driver = { + .name = "neo1973-audio", + .pm = &snd_soc_pm_ops, + }, + .probe = neo1973_probe, +}; +module_platform_driver(neo1973_audio); /* Module information */ MODULE_AUTHOR("Graeme Gregory, graeme@openmoko.org, www.openmoko.org"); MODULE_DESCRIPTION("ALSA SoC WM8753 Neo1973 and Frerunner"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:neo1973-audio"); -- cgit From 83d74e354200eb27ba88e81e9bd10ff26cbeeb39 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2020 20:20:43 +0200 Subject: ASoC: samsung: rx1950: turn into platform driver Avoid machine specific headers by using a gpio lookup table combined with a platform_driver for this board. Signed-off-by: Arnd Bergmann Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20200806182059.2431-26-krzk@kernel.org Signed-off-by: Mark Brown --- sound/soc/samsung/rx1950_uda1380.c | 72 ++++++++++++-------------------------- 1 file changed, 23 insertions(+), 49 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/rx1950_uda1380.c b/sound/soc/samsung/rx1950_uda1380.c index 08f7c82aedb6..400a7f77c711 100644 --- a/sound/soc/samsung/rx1950_uda1380.c +++ b/sound/soc/samsung/rx1950_uda1380.c @@ -12,16 +12,13 @@ // Vasily Khoruzhick #include -#include +#include #include #include #include -#include #include "regs-iis.h" -#include - #include "s3c24xx-i2s.h" static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd); @@ -58,7 +55,6 @@ static struct snd_soc_jack_pin hp_jack_pins[] = { static struct snd_soc_jack_gpio hp_jack_gpios[] = { [0] = { - .gpio = S3C2410_GPG(12), .name = "hp-gpio", .report = SND_JACK_HEADPHONE, .invert = 1, @@ -123,8 +119,6 @@ static struct snd_soc_card rx1950_asoc = { .num_dapm_routes = ARRAY_SIZE(audio_map), }; -static struct platform_device *s3c24xx_snd_device; - static int rx1950_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; @@ -134,13 +128,15 @@ static int rx1950_startup(struct snd_pcm_substream *substream) &hw_rates); } +struct gpio_desc *gpiod_speaker_power; + static int rx1950_spk_power(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { if (SND_SOC_DAPM_EVENT_ON(event)) - gpio_set_value(S3C2410_GPA(1), 1); + gpiod_set_value(gpiod_speaker_power, 1); else - gpio_set_value(S3C2410_GPA(1), 0); + gpiod_set_value(gpiod_speaker_power, 0); return 0; } @@ -214,57 +210,35 @@ static int rx1950_uda1380_init(struct snd_soc_pcm_runtime *rtd) return 0; } -static int __init rx1950_init(void) +static int rx1950_probe(struct platform_device *pdev) { - int ret; - - if (!machine_is_rx1950()) - return -ENODEV; + struct device *dev = &pdev->dev; /* configure some gpios */ - ret = gpio_request(S3C2410_GPA(1), "speaker-power"); - if (ret) - goto err_gpio; - - ret = gpio_direction_output(S3C2410_GPA(1), 0); - if (ret) - goto err_gpio_conf; - - s3c24xx_snd_device = platform_device_alloc("soc-audio", -1); - if (!s3c24xx_snd_device) { - ret = -ENOMEM; - goto err_plat_alloc; - } - - platform_set_drvdata(s3c24xx_snd_device, &rx1950_asoc); - ret = platform_device_add(s3c24xx_snd_device); - - if (ret) { - platform_device_put(s3c24xx_snd_device); - goto err_plat_add; + gpiod_speaker_power = devm_gpiod_get(dev, "speaker-power", GPIOD_OUT_LOW); + if (IS_ERR(gpiod_speaker_power)) { + dev_err(dev, "cannot get gpio\n"); + return PTR_ERR(gpiod_speaker_power); } - return 0; - -err_plat_add: -err_plat_alloc: -err_gpio_conf: - gpio_free(S3C2410_GPA(1)); + hp_jack_gpios[0].gpiod_dev = dev; + rx1950_asoc.dev = dev; -err_gpio: - return ret; + return devm_snd_soc_register_card(dev, &rx1950_asoc); } -static void __exit rx1950_exit(void) -{ - platform_device_unregister(s3c24xx_snd_device); - gpio_free(S3C2410_GPA(1)); -} +struct platform_driver rx1950_audio = { + .driver = { + .name = "rx1950-audio", + .pm = &snd_soc_pm_ops, + }, + .probe = rx1950_probe, +}; -module_init(rx1950_init); -module_exit(rx1950_exit); +module_platform_driver(rx1950_audio); /* Module information */ MODULE_AUTHOR("Vasily Khoruzhick"); MODULE_DESCRIPTION("ALSA SoC RX1950"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rx1950-audio"); -- cgit From dcacbc0f9bb89ac48d5b602d27a8630e641294cc Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 6 Aug 2020 20:20:44 +0200 Subject: ASoC: samsung: s3c2412-i2s: avoid hardcoded S3C2410_PA_IIS The constant requires indirectly including a machine header file, but it's not actually used any more since commit 87b132bc0315 ("ASoC: samsung: s3c24{xx,12}-i2s: port to use generic dmaengine API"), so remove it completely. Signed-off-by: Arnd Bergmann Signed-off-by: Krzysztof Kozlowski Acked-by: Mark Brown Link: https://lore.kernel.org/r/20200806182059.2431-27-krzk@kernel.org Signed-off-by: Mark Brown --- sound/soc/samsung/s3c-i2s-v2.c | 3 +-- sound/soc/samsung/s3c-i2s-v2.h | 3 +-- sound/soc/samsung/s3c2412-i2s.c | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c index ed21786104a1..e9481187a08c 100644 --- a/sound/soc/samsung/s3c-i2s-v2.c +++ b/sound/soc/samsung/s3c-i2s-v2.c @@ -616,8 +616,7 @@ int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, EXPORT_SYMBOL_GPL(s3c_i2sv2_iis_calc_rate); int s3c_i2sv2_probe(struct snd_soc_dai *dai, - struct s3c_i2sv2_info *i2s, - unsigned long base) + struct s3c_i2sv2_info *i2s) { struct device *dev = dai->dev; unsigned int iismod; diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h index fe42b77999fd..8c6fc0d3d77e 100644 --- a/sound/soc/samsung/s3c-i2s-v2.h +++ b/sound/soc/samsung/s3c-i2s-v2.h @@ -83,8 +83,7 @@ extern int s3c_i2sv2_iis_calc_rate(struct s3c_i2sv2_rate_calc *info, * @base: The base address for the registers. */ extern int s3c_i2sv2_probe(struct snd_soc_dai *dai, - struct s3c_i2sv2_info *i2s, - unsigned long base); + struct s3c_i2sv2_info *i2s); /** * s3c_i2sv2_cleanup - cleanup resources allocated in s3c_i2sv2_probe diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c index b35d828c1cfe..edfa9d11d2e5 100644 --- a/sound/soc/samsung/s3c2412-i2s.c +++ b/sound/soc/samsung/s3c2412-i2s.c @@ -49,7 +49,7 @@ static int s3c2412_i2s_probe(struct snd_soc_dai *dai) snd_soc_dai_init_dma_data(dai, &s3c2412_i2s_pcm_stereo_out, &s3c2412_i2s_pcm_stereo_in); - ret = s3c_i2sv2_probe(dai, &s3c2412_i2s, S3C2410_PA_IIS); + ret = s3c_i2sv2_probe(dai, &s3c2412_i2s); if (ret) return ret; -- cgit From a2f6d303e24d8d694863d3beba668c4b3ea4fe02 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Tue, 18 Aug 2020 22:40:38 -0500 Subject: ASoC: sun8i-codec: Hook up component probe function Due to a mistake made while reordering patches, commit 90cac932976e ("ASoC: sun8i-codec: Fix DAPM to match the hardware topology") added the sun8i_codec_component_probe function without referencing it from the component definition. Add the reference so the probe function gets called as expected. Fixes: 90cac932976e ("ASoC: sun8i-codec: Fix DAPM to match the hardware topology") Reported-by: Stephen Rothwell Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200819034038.46418-1-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 304683a71acd..e3a1347d7ecd 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -584,6 +584,7 @@ static const struct snd_soc_component_driver sun8i_soc_component = { .num_dapm_widgets = ARRAY_SIZE(sun8i_codec_dapm_widgets), .dapm_routes = sun8i_codec_dapm_routes, .num_dapm_routes = ARRAY_SIZE(sun8i_codec_dapm_routes), + .probe = sun8i_codec_component_probe, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, -- cgit From 5436f59bc5bcc27ca0244ebdb02b4228bf8b1aae Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Aug 2020 10:21:38 +0200 Subject: ALSA: usb-audio: Move device rename and profile quirks to an internal table So far we've added the devices that need vendor/product string renames or the profile setup into the standard quirk table in quirks-table.h. This table is imported into the primary USB audio device entry, hence it's all exported for the probing so that udev and co can take a look at it. OTOH, for renaming or profile setup, we don't need to expose those explicit entries because the probe itself follows the standard way. That said, we're exposing unnecessarily too many entries. This patch moves such internal quirk entries into the own table, and reduces the exported device table size. Along with the moving items, re-arrange the entries in the proper order. Link: https://lore.kernel.org/r/20200817082140.20232-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/card.c | 133 +++++++++++++++++++++++++++++++++++++++++++---- sound/usb/quirks-table.h | 120 ------------------------------------------ sound/usb/usbaudio.h | 1 - 3 files changed, 124 insertions(+), 130 deletions(-) (limited to 'sound') diff --git a/sound/usb/card.c b/sound/usb/card.c index 696e788c5d31..fa764b61fe9c 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -332,6 +332,106 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif) return 0; } +/* + * Profile name preset table + */ +struct usb_audio_device_name { + u32 id; + const char *vendor_name; + const char *product_name; + const char *profile_name; /* override card->longname */ +}; + +#define PROFILE_NAME(vid, pid, vendor, product, profile) \ + { .id = USB_ID(vid, pid), .vendor_name = (vendor), \ + .product_name = (product), .profile_name = (profile) } +#define DEVICE_NAME(vid, pid, vendor, product) \ + PROFILE_NAME(vid, pid, vendor, product, NULL) + +/* vendor/product and profile name presets, sorted in device id order */ +static const struct usb_audio_device_name usb_audio_names[] = { + /* HP Thunderbolt Dock Audio Headset */ + PROFILE_NAME(0x03f0, 0x0269, "HP", "Thunderbolt Dock Audio Headset", + "HP-Thunderbolt-Dock-Audio-Headset"), + /* HP Thunderbolt Dock Audio Module */ + PROFILE_NAME(0x03f0, 0x0567, "HP", "Thunderbolt Dock Audio Module", + "HP-Thunderbolt-Dock-Audio-Module"), + + /* Two entries for Gigabyte TRX40 Aorus Master: + * TRX40 Aorus Master has two USB-audio devices, one for the front + * headphone with ESS SABRE9218 DAC chip, while another for the rest + * I/O (the rear panel and the front mic) with Realtek ALC1220-VB. + * Here we provide two distinct names for making UCM profiles easier. + */ + PROFILE_NAME(0x0414, 0xa000, "Gigabyte", "Aorus Master Front Headphone", + "Gigabyte-Aorus-Master-Front-Headphone"), + PROFILE_NAME(0x0414, 0xa001, "Gigabyte", "Aorus Master Main Audio", + "Gigabyte-Aorus-Master-Main-Audio"), + + /* Gigabyte TRX40 Aorus Pro WiFi */ + PROFILE_NAME(0x0414, 0xa002, + "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), + + /* Creative/E-Mu devices */ + DEVICE_NAME(0x041e, 0x3010, "Creative Labs", "Sound Blaster MP3+"), + /* Creative/Toshiba Multimedia Center SB-0500 */ + DEVICE_NAME(0x041e, 0x3048, "Toshiba", "SB-0500"), + + DEVICE_NAME(0x046d, 0x0990, "Logitech, Inc.", "QuickCam Pro 9000"), + + /* Dell WD15 Dock */ + PROFILE_NAME(0x0bda, 0x4014, "Dell", "WD15 Dock", "Dell-WD15-Dock"), + /* Dell WD19 Dock */ + PROFILE_NAME(0x0bda, 0x402e, "Dell", "WD19 Dock", "Dell-WD15-Dock"), + + DEVICE_NAME(0x0ccd, 0x0028, "TerraTec", "Aureon5.1MkII"), + + /* + * The original product_name is "USB Sound Device", however this name + * is also used by the CM106 based cards, so make it unique. + */ + DEVICE_NAME(0x0d8c, 0x0102, NULL, "ICUSBAUDIO7D"), + DEVICE_NAME(0x0d8c, 0x0103, NULL, "Audio Advantage MicroII"), + + /* MSI TRX40 Creator */ + PROFILE_NAME(0x0db0, 0x0d64, + "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), + /* MSI TRX40 */ + PROFILE_NAME(0x0db0, 0x543d, + "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), + + /* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ + DEVICE_NAME(0x103d, 0x0100, "Stanton", "ScratchAmp"), + DEVICE_NAME(0x103d, 0x0101, "Stanton", "ScratchAmp"), + + /* aka. Serato Scratch Live DJ Box */ + DEVICE_NAME(0x13e5, 0x0001, "Rane", "SL-1"), + + /* Lenovo ThinkStation P620 Rear Line-in, Line-out and Microphone */ + PROFILE_NAME(0x17aa, 0x1046, "Lenovo", "ThinkStation P620 Rear", + "Lenovo-ThinkStation-P620-Rear"), + /* Lenovo ThinkStation P620 Internal Speaker + Front Headset */ + PROFILE_NAME(0x17aa, 0x104d, "Lenovo", "ThinkStation P620 Main", + "Lenovo-ThinkStation-P620-Main"), + + /* Asrock TRX40 Creator */ + PROFILE_NAME(0x26ce, 0x0a01, + "Realtek", "ALC1220-VB-DT", "Realtek-ALC1220-VB-Desktop"), + + { } /* terminator */ +}; + +static const struct usb_audio_device_name * +lookup_device_name(u32 id) +{ + static const struct usb_audio_device_name *p; + + for (p = usb_audio_names; p->id; p++) + if (p->id == id) + return p; + return NULL; +} + /* * free the chip instance * @@ -357,10 +457,16 @@ static void usb_audio_make_shortname(struct usb_device *dev, const struct snd_usb_audio_quirk *quirk) { struct snd_card *card = chip->card; - - if (quirk && quirk->product_name && *quirk->product_name) { - strlcpy(card->shortname, quirk->product_name, - sizeof(card->shortname)); + const struct usb_audio_device_name *preset; + const char *s = NULL; + + preset = lookup_device_name(chip->usb_id); + if (preset && preset->product_name) + s = preset->product_name; + else if (quirk && quirk->product_name) + s = quirk->product_name; + if (s && *s) { + strlcpy(card->shortname, s, sizeof(card->shortname)); return; } @@ -382,17 +488,26 @@ static void usb_audio_make_longname(struct usb_device *dev, const struct snd_usb_audio_quirk *quirk) { struct snd_card *card = chip->card; + const struct usb_audio_device_name *preset; + const char *s = NULL; int len; + preset = lookup_device_name(chip->usb_id); + /* shortcut - if any pre-defined string is given, use it */ - if (quirk && quirk->profile_name && *quirk->profile_name) { - strlcpy(card->longname, quirk->profile_name, - sizeof(card->longname)); + if (preset && preset->profile_name) + s = preset->profile_name; + if (s && *s) { + strlcpy(card->longname, s, sizeof(card->longname)); return; } - if (quirk && quirk->vendor_name && *quirk->vendor_name) { - len = strlcpy(card->longname, quirk->vendor_name, sizeof(card->longname)); + if (preset && preset->vendor_name) + s = preset->vendor_name; + else if (quirk && quirk->vendor_name) + s = quirk->vendor_name; + if (s && *s) { + len = strlcpy(card->longname, s, sizeof(card->longname)); } else { /* retrieve the vendor and device strings as longname */ if (dev->descriptor.iManufacturer) diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index f4fb002e3ef4..978f7113bd81 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -25,33 +25,6 @@ .idProduct = prod, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC -#define QUIRK_RENAME_DEVICE(_vendor, _device) \ - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ - .vendor_name = _vendor, \ - .product_name = _device, \ - .ifnum = QUIRK_NO_INTERFACE \ - } - -#define QUIRK_DEVICE_PROFILE(_vendor, _device, _profile) \ - .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { \ - .vendor_name = _vendor, \ - .product_name = _device, \ - .profile_name = _profile, \ - .ifnum = QUIRK_NO_INTERFACE \ - } - -/* HP Thunderbolt Dock Audio Headset */ -{ - USB_DEVICE(0x03f0, 0x0269), - QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Headset", - "HP-Thunderbolt-Dock-Audio-Headset"), -}, -/* HP Thunderbolt Dock Audio Module */ -{ - USB_DEVICE(0x03f0, 0x0567), - QUIRK_DEVICE_PROFILE("HP", "Thunderbolt Dock Audio Module", - "HP-Thunderbolt-Dock-Audio-Module"), -}, /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -85,16 +58,6 @@ } }, -/* Creative/E-Mu devices */ -{ - USB_DEVICE(0x041e, 0x3010), - QUIRK_RENAME_DEVICE("Creative Labs", "Sound Blaster MP3+") -}, -/* Creative/Toshiba Multimedia Center SB-0500 */ -{ - USB_DEVICE(0x041e, 0x3048), - QUIRK_RENAME_DEVICE("Toshiba", "SB-0500") -}, { /* E-Mu 0202 USB */ .match_flags = USB_DEVICE_ID_MATCH_DEVICE, @@ -226,7 +189,6 @@ .idProduct = 0x0990, .bInterfaceClass = USB_CLASS_AUDIO, .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, - QUIRK_RENAME_DEVICE("Logitech, Inc.", "QuickCam Pro 9000") }, /* @@ -2609,10 +2571,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), .type = QUIRK_MIDI_STANDARD_INTERFACE } }, -{ - USB_DEVICE(0x0ccd, 0x0028), - QUIRK_RENAME_DEVICE("TerraTec", "Aureon5.1MkII") -}, { USB_DEVICE(0x0ccd, 0x0035), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -2623,16 +2581,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, -/* Stanton/N2IT Final Scratch v1 device ('Scratchamp') */ -{ - USB_DEVICE(0x103d, 0x0100), - QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp") -}, -{ - USB_DEVICE(0x103d, 0x0101), - QUIRK_RENAME_DEVICE("Stanton", "ScratchAmp") -}, - /* Novation EMS devices */ { USB_DEVICE_VENDOR_SPEC(0x1235, 0x0001), @@ -2817,26 +2765,6 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, -/* */ -{ - /* aka. Serato Scratch Live DJ Box */ - USB_DEVICE(0x13e5, 0x0001), - QUIRK_RENAME_DEVICE("Rane", "SL-1") -}, - -/* Lenovo ThinkStation P620 Rear Line-in, Line-out and Microphone */ -{ - USB_DEVICE(0x17aa, 0x1046), - QUIRK_DEVICE_PROFILE("Lenovo", "ThinkStation P620 Rear", - "Lenovo-ThinkStation-P620-Rear"), -}, -/* Lenovo ThinkStation P620 Internal Speaker + Front Headset */ -{ - USB_DEVICE(0x17aa, 0x104d), - QUIRK_DEVICE_PROFILE("Lenovo", "ThinkStation P620 Main", - "Lenovo-ThinkStation-P620-Main"), -}, - /* Native Instruments MK2 series */ { /* Komplete Audio 6 */ @@ -3295,19 +3223,6 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } }, -/* - * The original product_name is "USB Sound Device", however this name - * is also used by the CM106 based cards, so make it unique. - */ -{ - USB_DEVICE(0x0d8c, 0x0102), - QUIRK_RENAME_DEVICE(NULL, "ICUSBAUDIO7D") -}, -{ - USB_DEVICE(0x0d8c, 0x0103), - QUIRK_RENAME_DEVICE(NULL, "Audio Advantage MicroII") -}, - /* disabled due to regression for other devices; * see https://bugzilla.kernel.org/show_bug.cgi?id=199905 */ @@ -3408,18 +3323,10 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } } }, -/* Dell WD15 Dock */ -{ - USB_DEVICE(0x0bda, 0x4014), - QUIRK_DEVICE_PROFILE("Dell", "WD15 Dock", "Dell-WD15-Dock") -}, /* Dell WD19 Dock */ { USB_DEVICE(0x0bda, 0x402e), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { - .vendor_name = "Dell", - .product_name = "WD19 Dock", - .profile_name = "Dell-WD15-Dock", .ifnum = QUIRK_ANY_INTERFACE, .type = QUIRK_SETUP_FMT_AFTER_RESUME } @@ -3645,33 +3552,6 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), } }, -#define ALC1220_VB_DESKTOP(vend, prod) { \ - USB_DEVICE(vend, prod), \ - QUIRK_DEVICE_PROFILE("Realtek", "ALC1220-VB-DT", \ - "Realtek-ALC1220-VB-Desktop") \ -} -ALC1220_VB_DESKTOP(0x0414, 0xa002), /* Gigabyte TRX40 Aorus Pro WiFi */ -ALC1220_VB_DESKTOP(0x0db0, 0x0d64), /* MSI TRX40 Creator */ -ALC1220_VB_DESKTOP(0x0db0, 0x543d), /* MSI TRX40 */ -ALC1220_VB_DESKTOP(0x26ce, 0x0a01), /* Asrock TRX40 Creator */ -#undef ALC1220_VB_DESKTOP - -/* Two entries for Gigabyte TRX40 Aorus Master: - * TRX40 Aorus Master has two USB-audio devices, one for the front headphone - * with ESS SABRE9218 DAC chip, while another for the rest I/O (the rear - * panel and the front mic) with Realtek ALC1220-VB. - * Here we provide two distinct names for making UCM profiles easier. - */ -{ - USB_DEVICE(0x0414, 0xa000), - QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Front Headphone", - "Gigabyte-Aorus-Master-Front-Headphone") -}, -{ - USB_DEVICE(0x0414, 0xa001), - QUIRK_DEVICE_PROFILE("Gigabyte", "Aorus Master Main Audio", - "Gigabyte-Aorus-Master-Main-Audio") -}, { /* * Pioneer DJ DJM-900NXS2 diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index b91c4c0807ec..2275295fe71f 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -109,7 +109,6 @@ enum quirk_type { struct snd_usb_audio_quirk { const char *vendor_name; const char *product_name; - const char *profile_name; /* override the card->longname */ int16_t ifnum; uint16_t type; bool shares_media_device; -- cgit From fa10635fca359f047df6a18b3befd2f1e7304e1a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Aug 2020 10:21:39 +0200 Subject: ALSA: usb-audio: Simplify quirk entries with a macro Introduce a new macro USB_AUDIO_DEVICE() for the entries matching with the pid/vid pair and the class/subclass, and remove the open-code. Link: https://lore.kernel.org/r/20200817082140.20232-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 105 ++++++++++------------------------------------- 1 file changed, 21 insertions(+), 84 deletions(-) (limited to 'sound') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 978f7113bd81..988bb9d00192 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -25,6 +25,16 @@ .idProduct = prod, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC +/* A standard entry matching with vid/pid and the audio class/subclass */ +#define USB_AUDIO_DEVICE(vend, prod) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS | \ + USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ + .idVendor = vend, \ + .idProduct = prod, \ + .bInterfaceClass = USB_CLASS_AUDIO, \ + .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL + /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -127,69 +137,13 @@ * Logitech QuickCam: bDeviceClass is vendor-specific, so generic interface * class matches do not take effect without an explicit ID match. */ -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x0850, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08ae, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08c6, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08f0, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08f5, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x08f6, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -}, -{ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x046d, - .idProduct = 0x0990, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, -}, +{ USB_AUDIO_DEVICE(0x046d, 0x0850) }, +{ USB_AUDIO_DEVICE(0x046d, 0x08ae) }, +{ USB_AUDIO_DEVICE(0x046d, 0x08c6) }, +{ USB_AUDIO_DEVICE(0x046d, 0x08f0) }, +{ USB_AUDIO_DEVICE(0x046d, 0x08f5) }, +{ USB_AUDIO_DEVICE(0x046d, 0x08f6) }, +{ USB_AUDIO_DEVICE(0x046d, 0x0990) }, /* * Yamaha devices @@ -2831,13 +2785,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), */ #define AU0828_DEVICE(vid, pid, vname, pname) { \ - .idVendor = vid, \ - .idProduct = pid, \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS | \ - USB_DEVICE_ID_MATCH_INT_SUBCLASS, \ - .bInterfaceClass = USB_CLASS_AUDIO, \ - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, \ + USB_AUDIO_DEVICE(vid, pid), \ .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { \ .vendor_name = vname, \ .product_name = pname, \ @@ -2867,13 +2815,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), /* Syntek STK1160 */ { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x05e1, - .idProduct = 0x0408, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + USB_AUDIO_DEVICE(0x05e1, 0x0408), .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .vendor_name = "Syntek", .product_name = "STK1160", @@ -3628,13 +3570,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), * channels to be swapped and out of phase, which is dealt with in quirks.c. */ { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | - USB_DEVICE_ID_MATCH_INT_CLASS | - USB_DEVICE_ID_MATCH_INT_SUBCLASS, - .idVendor = 0x534d, - .idProduct = 0x2109, - .bInterfaceClass = USB_CLASS_AUDIO, - .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, + USB_AUDIO_DEVICE(0x534d, 0x2109), .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .vendor_name = "MacroSilicon", .product_name = "MS2109", @@ -3675,3 +3611,4 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), }, #undef USB_DEVICE_VENDOR_SPEC +#undef USB_AUDIO_DEVICE -- cgit From 51ab5d77dcb8a50283c0eded08c67a6d275cb910 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 17 Aug 2020 10:21:40 +0200 Subject: ALSA: usb-audio: Properly match with audio interface class There are a few entries in the quirk table that set the device ID with USB_DEVICE() macro while having an extra bInterfaceClass field. But bInterfaceClass field is never checked unless the proper match_flags is set, so those may match incorrectly with all interfaces. Introduce another macro to match with the vid/pid pair and the audio class interface, and apply it to such entries, so that they can match properly. Link: https://lore.kernel.org/r/20200817082140.20232-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 55 +++++++++++++++++------------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) (limited to 'sound') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 988bb9d00192..7a80ef31bbe4 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -35,6 +35,14 @@ .bInterfaceClass = USB_CLASS_AUDIO, \ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL +/* Another standard entry matching with vid/pid and the audio class */ +#define USB_AUDIO_CLASS(vend, prod) \ + .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ + USB_DEVICE_ID_MATCH_INT_CLASS, \ + .idVendor = vend, \ + .idProduct = prod, \ + .bInterfaceClass = USB_CLASS_AUDIO + /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -68,34 +76,14 @@ } }, -{ - /* E-Mu 0202 USB */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f02, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - /* E-Mu 0404 USB */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f04, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - /* E-Mu Tracker Pre */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f0a, - .bInterfaceClass = USB_CLASS_AUDIO, -}, -{ - /* E-Mu 0204 USB */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x041e, - .idProduct = 0x3f19, - .bInterfaceClass = USB_CLASS_AUDIO, -}, +/* E-Mu 0202 USB */ +{ USB_AUDIO_CLASS(0x041e, 0x3f02) }, +/* E-Mu 0404 USB */ +{ USB_AUDIO_CLASS(0x041e, 0x3f04) }, +/* E-Mu Tracker Pre */ +{ USB_AUDIO_CLASS(0x041e, 0x3f0a) }, +/* E-Mu 0204 USB */ +{ USB_AUDIO_CLASS(0x041e, 0x3f19) }, /* * HP Wireless Audio @@ -2751,10 +2739,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, /* KeithMcMillen Stringport */ -{ - USB_DEVICE(0x1f38, 0x0001), - .bInterfaceClass = USB_CLASS_AUDIO, -}, +{ USB_AUDIO_CLASS(0x1f38, 0x0001) }, /* Miditech devices */ { @@ -2977,10 +2962,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), }, { /* Tascam US122 MKII - playback-only support */ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE, - .idVendor = 0x0644, - .idProduct = 0x8021, - .bInterfaceClass = USB_CLASS_AUDIO, + USB_AUDIO_CLASS(0x0644, 0x8021), .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .vendor_name = "TASCAM", .product_name = "US122 MKII", @@ -3612,3 +3594,4 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), #undef USB_DEVICE_VENDOR_SPEC #undef USB_AUDIO_DEVICE +#undef USB_AUDIO_CLASS -- cgit From 8061734ab65498f4802578564fc0948ec9aaf933 Mon Sep 17 00:00:00 2001 From: Jiaxin Yu Date: Thu, 20 Aug 2020 16:51:32 +0800 Subject: ASoC: mediatek: mt6359: add codec driver Add the mt6359 codec driver. Signed-off-by: Jiaxin Yu Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/1597913493-10747-2-git-send-email-jiaxin.yu@mediatek.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 8 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/mt6359.c | 2753 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/mt6359.h | 2640 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 5403 insertions(+) create mode 100644 sound/soc/codecs/mt6359.c create mode 100644 sound/soc/codecs/mt6359.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 946a70210f49..e6c71ca0cd9b 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -127,6 +127,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ML26124 imply SND_SOC_MT6351 imply SND_SOC_MT6358 + imply SND_SOC_MT6359 imply SND_SOC_MT6660 imply SND_SOC_NAU8540 imply SND_SOC_NAU8810 @@ -1724,6 +1725,13 @@ config SND_SOC_MT6358 Enable support for the platform which uses MT6358 as external codec device. +config SND_SOC_MT6359 + tristate "MediaTek MT6359 Codec" + depends on MTK_PMIC_WRAP + help + Enable support for the platform which uses MT6359 as + external codec device. + config SND_SOC_MT6660 tristate "Mediatek MT6660 Speaker Amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0140c60db695..9a3f58c1069a 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -126,6 +126,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o snd-soc-mt6351-objs := mt6351.o snd-soc-mt6358-objs := mt6358.o +snd-soc-mt6359-objs := mt6359.o snd-soc-mt6660-objs := mt6660.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o @@ -431,6 +432,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o +obj-$(CONFIG_SND_SOC_MT6359) += snd-soc-mt6359.o obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c new file mode 100644 index 000000000000..72f05335b420 --- /dev/null +++ b/sound/soc/codecs/mt6359.c @@ -0,0 +1,2753 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// mt6359.c -- mt6359 ALSA SoC audio codec driver +// +// Copyright (c) 2020 MediaTek Inc. +// Author: KaiChieh Chuang + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt6359.h" + +static void mt6359_set_playback_gpio(struct mt6359_priv *priv) +{ + /* set gpio mosi mode, clk / data mosi */ + regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ffe); + regmap_write(priv->regmap, MT6359_GPIO_MODE2_SET, 0x0249); + + /* sync mosi */ + regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x6); + regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x1); +} + +static void mt6359_reset_playback_gpio(struct mt6359_priv *priv) +{ + /* set pad_aud_*_mosi to GPIO mode and dir input + * reason: + * pad_aud_dat_mosi*, because the pin is used as boot strap + * don't clean clk/sync, for mtkaif protocol 2 + */ + regmap_write(priv->regmap, MT6359_GPIO_MODE2_CLR, 0x0ff8); + regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0, 0x7 << 9, 0x0); +} + +static void mt6359_set_capture_gpio(struct mt6359_priv *priv) +{ + /* set gpio miso mode */ + regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00); + regmap_write(priv->regmap, MT6359_GPIO_MODE3_SET, 0x0200); + + regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f); + regmap_write(priv->regmap, MT6359_GPIO_MODE4_SET, 0x0009); +} + +static void mt6359_reset_capture_gpio(struct mt6359_priv *priv) +{ + /* set pad_aud_*_miso to GPIO mode and dir input + * reason: + * pad_aud_clk_miso, because when playback only the miso_clk + * will also have 26m, so will have power leak + * pad_aud_dat_miso*, because the pin is used as boot strap + */ + regmap_write(priv->regmap, MT6359_GPIO_MODE3_CLR, 0x0e00); + + regmap_write(priv->regmap, MT6359_GPIO_MODE4_CLR, 0x003f); + + regmap_update_bits(priv->regmap, MT6359_GPIO_DIR0, + 0x7 << 13, 0x0); + regmap_update_bits(priv->regmap, MT6359_GPIO_DIR1, + 0x3 << 0, 0x0); +} + +static void mt6359_set_decoder_clk(struct mt6359_priv *priv, bool enable) +{ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13, + RG_RSTB_DECODER_VA32_MASK_SFT, + (enable ? 1 : 0) << RG_RSTB_DECODER_VA32_SFT); +} + +static void mt6359_mtkaif_tx_enable(struct mt6359_priv *priv) +{ + switch (priv->mtkaif_protocol) { + case MT6359_MTKAIF_PROTOCOL_2_CLK_P2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6359_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0210); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3800); + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3900); + break; + case MT6359_MTKAIF_PROTOCOL_2: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6359_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0210); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + case MT6359_MTKAIF_PROTOCOL_1: + default: + /* MTKAIF TX format setting */ + regmap_update_bits(priv->regmap, + MT6359_AFE_ADDA_MTKAIF_CFG0, + 0xffff, 0x0000); + /* enable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, + MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3100); + break; + } +} + +static void mt6359_mtkaif_tx_disable(struct mt6359_priv *priv) +{ + /* disable aud_pad TX fifos */ + regmap_update_bits(priv->regmap, MT6359_AFE_AUD_PAD_TOP, + 0xff00, 0x3000); +} + +static void zcd_disable(struct mt6359_priv *priv) +{ + regmap_write(priv->regmap, MT6359_ZCD_CON0, 0x0000); +} + +static void hp_main_output_ramp(struct mt6359_priv *priv, bool up) +{ + int i = 0, stage = 0; + int target = 7; + + /* Enable/Reduce HPL/R main output stage step by step */ + for (i = 0; i <= target; i++) { + stage = up ? i : target - i; + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT, + stage << RG_HPLOUTSTGCTRL_VAUDP32_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT, + stage << RG_HPROUTSTGCTRL_VAUDP32_SFT); + usleep_range(600, 650); + } +} + +static void hp_aux_feedback_loop_gain_ramp(struct mt6359_priv *priv, bool up) +{ + int i = 0, stage = 0; + int target = 0xf; + + /* Enable/Reduce HP aux feedback loop gain step by step */ + for (i = 0; i <= target; i++) { + stage = up ? i : target - i; + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9, + 0xf << 12, stage << 12); + usleep_range(600, 650); + } +} + +static void hp_in_pair_current(struct mt6359_priv *priv, bool increase) +{ + int i = 0, stage = 0; + int target = 0x3; + + /* Set input diff pair bias select (Hi-Fi mode) */ + if (priv->hp_hifi_mode) { + /* Reduce HP aux feedback loop gain step by step */ + for (i = 0; i <= target; i++) { + stage = increase ? i : target - i; + regmap_update_bits(priv->regmap, + MT6359_AUDDEC_ANA_CON10, + 0x3 << 3, stage << 3); + usleep_range(100, 150); + } + } +} + +static void hp_pull_down(struct mt6359_priv *priv, bool enable) +{ + int i; + + if (enable) { + for (i = 0x0; i <= 0x7; i++) { + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_HPPSHORT2VCM_VAUDP32_MASK_SFT, + i << RG_HPPSHORT2VCM_VAUDP32_SFT); + usleep_range(100, 150); + } + } else { + for (i = 0x7; i >= 0x0; i--) { + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_HPPSHORT2VCM_VAUDP32_MASK_SFT, + i << RG_HPPSHORT2VCM_VAUDP32_SFT); + usleep_range(100, 150); + } + } +} + +static bool is_valid_hp_pga_idx(int reg_idx) +{ + return (reg_idx >= DL_GAIN_8DB && reg_idx <= DL_GAIN_N_22DB) || + reg_idx == DL_GAIN_N_40DB; +} + +static void headset_volume_ramp(struct mt6359_priv *priv, + int from, int to) +{ + int offset = 0, count = 1, reg_idx; + + if (!is_valid_hp_pga_idx(from) || !is_valid_hp_pga_idx(to)) { + dev_warn(priv->dev, "%s(), volume index is not valid, from %d, to %d\n", + __func__, from, to); + return; + } + + dev_dbg(priv->dev, "%s(), from %d, to %d\n", __func__, from, to); + + if (to > from) + offset = to - from; + else + offset = from - to; + + while (offset > 0) { + if (to > from) + reg_idx = from + count; + else + reg_idx = from - count; + + if (is_valid_hp_pga_idx(reg_idx)) { + regmap_update_bits(priv->regmap, + MT6359_ZCD_CON2, + DL_GAIN_REG_MASK, + (reg_idx << 7) | reg_idx); + usleep_range(600, 650); + } + offset--; + count++; + } +} + +static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg; + int index = ucontrol->value.integer.value[0]; + int ret; + + ret = snd_soc_put_volsw(kcontrol, ucontrol); + if (ret < 0) + return ret; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + regmap_read(priv->regmap, MT6359_ZCD_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL] = + (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = + (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + break; + case MT6359_ZCD_CON1: + regmap_read(priv->regmap, MT6359_ZCD_CON1, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL] = + (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = + (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + break; + case MT6359_ZCD_CON3: + regmap_read(priv->regmap, MT6359_ZCD_CON3, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] = + (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + break; + case MT6359_AUDENC_ANA_CON0: + regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] = + (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK; + break; + case MT6359_AUDENC_ANA_CON1: + regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] = + (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK; + break; + case MT6359_AUDENC_ANA_CON2: + regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, ®); + priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] = + (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK; + break; + } + + dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n", + __func__, kcontrol->id.name, mc->reg, reg, index); + + return ret; +} + +/* MUX */ + +/* LOL MUX */ +static const char * const lo_in_mux_map[] = { + "Open", "Playback_L_DAC", "Playback", "Test Mode" +}; + +static SOC_ENUM_SINGLE_DECL(lo_in_mux_map_enum, SND_SOC_NOPM, 0, lo_in_mux_map); + +static const struct snd_kcontrol_new lo_in_mux_control = + SOC_DAPM_ENUM("LO Select", lo_in_mux_map_enum); + +/*HP MUX */ +static const char * const hp_in_mux_map[] = { + "Open", + "LoudSPK Playback", + "Audio Playback", + "Test Mode", + "HP Impedance", +}; + +static SOC_ENUM_SINGLE_DECL(hp_in_mux_map_enum, + SND_SOC_NOPM, + 0, + hp_in_mux_map); + +static const struct snd_kcontrol_new hp_in_mux_control = + SOC_DAPM_ENUM("HP Select", hp_in_mux_map_enum); + +/* RCV MUX */ +static const char * const rcv_in_mux_map[] = { + "Open", "Mute", "Voice Playback", "Test Mode" +}; + +static SOC_ENUM_SINGLE_DECL(rcv_in_mux_map_enum, + SND_SOC_NOPM, + 0, + rcv_in_mux_map); + +static const struct snd_kcontrol_new rcv_in_mux_control = + SOC_DAPM_ENUM("RCV Select", rcv_in_mux_map_enum); + +/* DAC In MUX */ +static const char * const dac_in_mux_map[] = { + "Normal Path", "Sgen" +}; + +static int dac_in_mux_map_value[] = { + 0x0, 0x1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dac_in_mux_map_enum, + MT6359_AFE_TOP_CON0, + DL_SINE_ON_SFT, + DL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new dac_in_mux_control = + SOC_DAPM_ENUM("DAC Select", dac_in_mux_map_enum); + +/* AIF Out MUX */ +static SOC_VALUE_ENUM_SINGLE_DECL(aif_out_mux_map_enum, + MT6359_AFE_TOP_CON0, + UL_SINE_ON_SFT, + UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif_out_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(aif2_out_mux_map_enum, + MT6359_AFE_TOP_CON0, + ADDA6_UL_SINE_ON_SFT, + ADDA6_UL_SINE_ON_MASK, + dac_in_mux_map, + dac_in_mux_map_value); + +static const struct snd_kcontrol_new aif2_out_mux_control = + SOC_DAPM_ENUM("AIF Out Select", aif2_out_mux_map_enum); + +static const char * const ul_src_mux_map[] = { + "AMIC", + "DMIC", +}; + +static int ul_src_mux_map_value[] = { + UL_SRC_MUX_AMIC, + UL_SRC_MUX_DMIC, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(ul_src_mux_map_enum, + MT6359_AFE_UL_SRC_CON0_L, + UL_SDM_3_LEVEL_CTL_SFT, + UL_SDM_3_LEVEL_CTL_MASK, + ul_src_mux_map, + ul_src_mux_map_value); + +static const struct snd_kcontrol_new ul_src_mux_control = + SOC_DAPM_ENUM("UL_SRC_MUX Select", ul_src_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(ul2_src_mux_map_enum, + MT6359_AFE_ADDA6_UL_SRC_CON0_L, + ADDA6_UL_SDM_3_LEVEL_CTL_SFT, + ADDA6_UL_SDM_3_LEVEL_CTL_MASK, + ul_src_mux_map, + ul_src_mux_map_value); + +static const struct snd_kcontrol_new ul2_src_mux_control = + SOC_DAPM_ENUM("UL_SRC_MUX Select", ul2_src_mux_map_enum); + +static const char * const miso_mux_map[] = { + "UL1_CH1", + "UL1_CH2", + "UL2_CH1", + "UL2_CH2", +}; + +static int miso_mux_map_value[] = { + MISO_MUX_UL1_CH1, + MISO_MUX_UL1_CH2, + MISO_MUX_UL2_CH1, + MISO_MUX_UL2_CH2, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(miso0_mux_map_enum, + MT6359_AFE_MTKAIF_MUX_CFG, + RG_ADDA_CH1_SEL_SFT, + RG_ADDA_CH1_SEL_MASK, + miso_mux_map, + miso_mux_map_value); + +static const struct snd_kcontrol_new miso0_mux_control = + SOC_DAPM_ENUM("MISO_MUX Select", miso0_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(miso1_mux_map_enum, + MT6359_AFE_MTKAIF_MUX_CFG, + RG_ADDA_CH2_SEL_SFT, + RG_ADDA_CH2_SEL_MASK, + miso_mux_map, + miso_mux_map_value); + +static const struct snd_kcontrol_new miso1_mux_control = + SOC_DAPM_ENUM("MISO_MUX Select", miso1_mux_map_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(miso2_mux_map_enum, + MT6359_AFE_MTKAIF_MUX_CFG, + RG_ADDA6_CH1_SEL_SFT, + RG_ADDA6_CH1_SEL_MASK, + miso_mux_map, + miso_mux_map_value); + +static const struct snd_kcontrol_new miso2_mux_control = + SOC_DAPM_ENUM("MISO_MUX Select", miso2_mux_map_enum); + +static const char * const dmic_mux_map[] = { + "DMIC_DATA0", + "DMIC_DATA1_L", + "DMIC_DATA1_L_1", + "DMIC_DATA1_R", +}; + +static int dmic_mux_map_value[] = { + DMIC_MUX_DMIC_DATA0, + DMIC_MUX_DMIC_DATA1_L, + DMIC_MUX_DMIC_DATA1_L_1, + DMIC_MUX_DMIC_DATA1_R, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(dmic0_mux_map_enum, + MT6359_AFE_MIC_ARRAY_CFG, + RG_DMIC_ADC1_SOURCE_SEL_SFT, + RG_DMIC_ADC1_SOURCE_SEL_MASK, + dmic_mux_map, + dmic_mux_map_value); + +static const struct snd_kcontrol_new dmic0_mux_control = + SOC_DAPM_ENUM("DMIC_MUX Select", dmic0_mux_map_enum); + +/* ul1 ch2 use RG_DMIC_ADC3_SOURCE_SEL */ +static SOC_VALUE_ENUM_SINGLE_DECL(dmic1_mux_map_enum, + MT6359_AFE_MIC_ARRAY_CFG, + RG_DMIC_ADC3_SOURCE_SEL_SFT, + RG_DMIC_ADC3_SOURCE_SEL_MASK, + dmic_mux_map, + dmic_mux_map_value); + +static const struct snd_kcontrol_new dmic1_mux_control = + SOC_DAPM_ENUM("DMIC_MUX Select", dmic1_mux_map_enum); + +/* ul2 ch1 use RG_DMIC_ADC2_SOURCE_SEL */ +static SOC_VALUE_ENUM_SINGLE_DECL(dmic2_mux_map_enum, + MT6359_AFE_MIC_ARRAY_CFG, + RG_DMIC_ADC2_SOURCE_SEL_SFT, + RG_DMIC_ADC2_SOURCE_SEL_MASK, + dmic_mux_map, + dmic_mux_map_value); + +static const struct snd_kcontrol_new dmic2_mux_control = + SOC_DAPM_ENUM("DMIC_MUX Select", dmic2_mux_map_enum); + +/* ADC L MUX */ +static const char * const adc_left_mux_map[] = { + "Idle", "AIN0", "Left Preamplifier", "Idle_1" +}; + +static int adc_mux_map_value[] = { + ADC_MUX_IDLE, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_left_mux_map_enum, + MT6359_AUDENC_ANA_CON0, + RG_AUDADCLINPUTSEL_SFT, + RG_AUDADCLINPUTSEL_MASK, + adc_left_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_left_mux_control = + SOC_DAPM_ENUM("ADC L Select", adc_left_mux_map_enum); + +/* ADC R MUX */ +static const char * const adc_right_mux_map[] = { + "Idle", "AIN0", "Right Preamplifier", "Idle_1" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_right_mux_map_enum, + MT6359_AUDENC_ANA_CON1, + RG_AUDADCRINPUTSEL_SFT, + RG_AUDADCRINPUTSEL_MASK, + adc_right_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_right_mux_control = + SOC_DAPM_ENUM("ADC R Select", adc_right_mux_map_enum); + +/* ADC 3 MUX */ +static const char * const adc_3_mux_map[] = { + "Idle", "AIN0", "Preamplifier", "Idle_1" +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(adc_3_mux_map_enum, + MT6359_AUDENC_ANA_CON2, + RG_AUDADC3INPUTSEL_SFT, + RG_AUDADC3INPUTSEL_MASK, + adc_3_mux_map, + adc_mux_map_value); + +static const struct snd_kcontrol_new adc_3_mux_control = + SOC_DAPM_ENUM("ADC 3 Select", adc_3_mux_map_enum); + +static const char * const pga_l_mux_map[] = { + "None", "AIN0", "AIN1" +}; + +static int pga_l_mux_map_value[] = { + PGA_L_MUX_NONE, + PGA_L_MUX_AIN0, + PGA_L_MUX_AIN1 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_left_mux_map_enum, + MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLINPUTSEL_SFT, + RG_AUDPREAMPLINPUTSEL_MASK, + pga_l_mux_map, + pga_l_mux_map_value); + +static const struct snd_kcontrol_new pga_left_mux_control = + SOC_DAPM_ENUM("PGA L Select", pga_left_mux_map_enum); + +static const char * const pga_r_mux_map[] = { + "None", "AIN2", "AIN3", "AIN0" +}; + +static int pga_r_mux_map_value[] = { + PGA_R_MUX_NONE, + PGA_R_MUX_AIN2, + PGA_R_MUX_AIN3, + PGA_R_MUX_AIN0 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_right_mux_map_enum, + MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRINPUTSEL_SFT, + RG_AUDPREAMPRINPUTSEL_MASK, + pga_r_mux_map, + pga_r_mux_map_value); + +static const struct snd_kcontrol_new pga_right_mux_control = + SOC_DAPM_ENUM("PGA R Select", pga_right_mux_map_enum); + +static const char * const pga_3_mux_map[] = { + "None", "AIN3", "AIN2" +}; + +static int pga_3_mux_map_value[] = { + PGA_3_MUX_NONE, + PGA_3_MUX_AIN3, + PGA_3_MUX_AIN2 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(pga_3_mux_map_enum, + MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3INPUTSEL_SFT, + RG_AUDPREAMP3INPUTSEL_MASK, + pga_3_mux_map, + pga_3_mux_map_value); + +static const struct snd_kcontrol_new pga_3_mux_control = + SOC_DAPM_ENUM("PGA 3 Select", pga_3_mux_map_enum); + +static int mt_sgen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1); + /* sdm power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x000b); + + regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG0, + 0xff3f, + 0x0000); + regmap_update_bits(priv->regmap, MT6359_AFE_SGEN_CFG1, + 0xffff, + 0x0001); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON2, 0x0000); + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static void mtk_hp_enable(struct mt6359_priv *priv) +{ + if (priv->hp_hifi_mode) { + /* Set HP DR bias current optimization, 010: 6uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_HP_MASK_SFT, + DRBIAS_6UA << DRBIAS_HP_SFT); + /* Set HP & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_4UA << IBIAS_ZCD_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_HP_MASK_SFT, + IBIAS_5UA << IBIAS_HP_SFT); + } else { + /* Set HP DR bias current optimization, 001: 5uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_HP_MASK_SFT, + DRBIAS_5UA << DRBIAS_HP_SFT); + /* Set HP & ZCD bias current optimization */ + /* 00: ZCD: 3uA, HP/HS/LO: 4uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_3UA << IBIAS_ZCD_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_HP_MASK_SFT, + IBIAS_4UA << IBIAS_HP_SFT); + } + + /* HP damp circuit enable */ + /* Enable HPRN/HPLN output 4K to VCM */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0087); + + /* HP Feedback Cap select 2'b00: 15pF */ + /* for >= 96KHz sampling rate: 2'b01: 10.5pF */ + if (priv->dl_rate[MT6359_AIF_1] >= 96000) + regmap_update_bits(priv->regmap, + MT6359_AUDDEC_ANA_CON4, + RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT); + else + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON4, 0x0000); + + /* Set HPP/N STB enhance circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON2, 0xf133); + + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x000c); + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x003c); + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c00); + /* Enable HP driver bias circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30c0); + /* Enable HP driver core circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30f0); + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00fc); + + /* Increase HP input pair current to HPM step by step */ + hp_in_pair_current(priv, true); + + /* Enable HP main CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e00); + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0200); + + /* Enable HP main output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x00ff); + /* Enable HPR/L main output stage step by step */ + hp_main_output_ramp(priv, true); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, true); + /* Disable HP aux feedback loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf); + + /* apply volume setting */ + headset_volume_ramp(priv, + DL_GAIN_N_22DB, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]); + + /* Disable HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3); + /* Unshort HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x7703); + usleep_range(100, 120); + + /* Enable AUD_CLK */ + mt6359_set_decoder_clk(priv, true); + + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x30ff); + if (priv->hp_hifi_mode) { + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf201); + } else { + /* Disable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200); + } + usleep_range(100, 120); + + /* Switch HPL MUX to audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x32ff); + /* Switch HPR MUX to audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3aff); + + /* Disable Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, false); +} + +static void mtk_hp_disable(struct mt6359_priv *priv) +{ + /* Pull-down HPL/R to AVSS28_AUD */ + hp_pull_down(priv, true); + + /* HPR/HPL mux to open */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x0f00, 0x0000); + + /* Disable low-noise mode of DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON9, + 0x0001, 0x0000); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + mt6359_set_decoder_clk(priv, false); + + /* Short HP main output to HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77c3); + /* Enable HP aux output stage */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77cf); + + /* decrease HPL/R gain to normal gain step by step */ + headset_volume_ramp(priv, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL], + DL_GAIN_N_22DB); + + /* Enable HP aux feedback loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x77ff); + + /* Reduce HP aux feedback loop gain */ + hp_aux_feedback_loop_gain_ramp(priv, false); + + /* decrease HPR/L main output stage step by step */ + hp_main_output_ramp(priv, false); + + /* Disable HP main output stage */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, 0x3, 0x0); + + /* Enable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0e01); + + /* Disable HP main CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0c01); + + /* Decrease HP input pair current to 2'b00 step by step */ + hp_in_pair_current(priv, false); + + /* Unshort HP main output to HP aux output stage */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + 0x3 << 6, 0x0); + + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + + /* Disable HP aux CMFB loop */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x201); + + /* Disable HP aux feedback loop */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + 0x3 << 4, 0x0); + + /* Disable HP aux output stage */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON1, + 0x3 << 2, 0x0); +} + +static int mt_hp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + int device = DEVICE_HP; + + dev_dbg(priv->dev, "%s(), event 0x%x, dev_counter[DEV_HP] %d, mux %u\n", + __func__, event, priv->dev_counter[device], mux); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + priv->dev_counter[device]++; + if (mux == HP_MUX_HP) + mtk_hp_enable(priv); + break; + case SND_SOC_DAPM_PRE_PMD: + priv->dev_counter[device]--; + if (mux == HP_MUX_HP) + mtk_hp_disable(priv); + break; + default: + break; + } + + return 0; +} + +static int mt_rcv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, event, dapm_kcontrol_get_value(w->kcontrols[0])); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable handset short-circuit protection */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0010); + + /* Set RCV DR bias current optimization, 010: 6uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_HS_MASK_SFT, + DRBIAS_6UA << DRBIAS_HS_SFT); + /* Set RCV & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_4UA << IBIAS_ZCD_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_HS_MASK_SFT, + IBIAS_5UA << IBIAS_HS_SFT); + + /* Set HS STB enhance circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0090); + + /* Set HS output stage (3'b111 = 8x) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x7000); + + /* Enable HS driver bias circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0092); + /* Enable HS driver core circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x0093); + + /* Set HS gain to normal gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON3, + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]); + + /* Enable AUD_CLK */ + mt6359_set_decoder_clk(priv, true); + + /* Enable Audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x0009); + /* Enable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch HS MUX to audio DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON6, 0x009b); + break; + case SND_SOC_DAPM_PRE_PMD: + /* HS mux to open */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT, + RCV_MUX_OPEN); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + mt6359_set_decoder_clk(priv, false); + + /* decrease HS gain to minimum gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON3, DL_GAIN_N_40DB); + + /* Disable HS driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSPWRUP_VAUDP32_MASK_SFT, 0x0); + + /* Disable HS driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_lo_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", + __func__, event, dapm_kcontrol_get_value(w->kcontrols[0])); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Disable handset short-circuit protection */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0010); + + /* Set LO DR bias current optimization, 010: 6uA */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON11, + DRBIAS_LO_MASK_SFT, + DRBIAS_6UA << DRBIAS_LO_SFT); + /* Set LO & ZCD bias current optimization */ + /* 01: ZCD: 4uA, HP/HS/LO: 5uA */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_update_bits(priv->regmap, + MT6359_AUDDEC_ANA_CON12, + IBIAS_ZCD_MASK_SFT, + IBIAS_ZCD_4UA << IBIAS_ZCD_SFT); + + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON12, + IBIAS_LO_MASK_SFT, + IBIAS_5UA << IBIAS_LO_SFT); + + /* Set LO STB enhance circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0110); + + /* Enable LO driver bias circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0112); + /* Enable LO driver core circuits */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0113); + + /* Set LO gain to normal gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON1, + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]); + + /* Enable AUD_CLK */ + mt6359_set_decoder_clk(priv, true); + + /* Enable Audio DAC (3rd DAC) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); + /* Enable low-noise mode of DAC */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_write(priv->regmap, + MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch LOL MUX to audio 3rd DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + break; + case SND_SOC_DAPM_PRE_PMD: + /* Switch LOL MUX to open */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT, + LO_MUX_OPEN); + + /* Disable Audio DAC */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x000f, 0x0000); + + /* Disable AUD_CLK */ + mt6359_set_decoder_clk(priv, false); + + /* decrease LO gain to minimum gain step by step */ + regmap_write(priv->regmap, MT6359_ZCD_CON1, DL_GAIN_N_40DB); + + /* Disable LO driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLPWRUP_VAUDP32_MASK_SFT, 0x0); + + /* Disable LO driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_clk_gen_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + /* ADC CLK from CLKGEN (6.5MHz) */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKRSTB_MASK_SFT, + 0x1 << RG_AUDADCCLKRSTB_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSOURCE_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSEL_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKGENMODE_MASK_SFT, + 0x1 << RG_AUDADCCLKGENMODE_SFT); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSOURCE_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKSEL_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKGENMODE_MASK_SFT, 0x0); + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON5, + RG_AUDADCCLKRSTB_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_dcc_clk_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* DCC 50k CLK (from 26M) */ + /* MT6359_AFE_DCCLK_CFG0, bit 3 for dm ck swap */ + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2062); + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2060); + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2061); + + regmap_write(priv->regmap, MT6359_AFE_DCCLK_CFG1, 0x0100); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2060); + regmap_update_bits(priv->regmap, MT6359_AFE_DCCLK_CFG0, + 0xfff7, 0x2062); + break; + default: + break; + } + + return 0; +} + +static int mt_mic_bias_0_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_0]; + + dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n", + __func__, event, mic_type); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mic_type) { + case MIC_TYPE_MUX_DCC_ECM_DIFF: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON15, + 0xff00, 0x7700); + break; + case MIC_TYPE_MUX_DCC_ECM_SINGLE: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON15, + 0xff00, 0x1100); + break; + default: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON15, + 0xff00, 0x0000); + break; + } + + /* DMIC enable */ + regmap_write(priv->regmap, + MT6359_AUDENC_ANA_CON14, 0x0004); + /* MISBIAS0 = 1P9V */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15, + RG_AUDMICBIAS0VREF_MASK_SFT, + MIC_BIAS_1P9 << RG_AUDMICBIAS0VREF_SFT); + /* normal power select */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON15, + RG_AUDMICBIAS0LOWPEN_MASK_SFT, + 0 << RG_AUDMICBIAS0LOWPEN_SFT); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable MICBIAS0, MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON15, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_mic_bias_1_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_1]; + + dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n", + __func__, event, mic_type); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* MISBIAS1 = 2P6V */ + if (mic_type == MIC_TYPE_MUX_DCC_ECM_SINGLE) + regmap_write(priv->regmap, + MT6359_AUDENC_ANA_CON16, 0x0160); + else + regmap_write(priv->regmap, + MT6359_AUDENC_ANA_CON16, 0x0060); + + /* normal power select */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON16, + RG_AUDMICBIAS1LOWPEN_MASK_SFT, + 0 << RG_AUDMICBIAS1LOWPEN_SFT); + break; + default: + break; + } + + return 0; +} + +static int mt_mic_bias_2_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mic_type = priv->mux_select[MUX_MIC_TYPE_2]; + + dev_dbg(priv->dev, "%s(), event 0x%x, mic_type %d\n", + __func__, event, mic_type); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + switch (mic_type) { + case MIC_TYPE_MUX_DCC_ECM_DIFF: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON17, + 0xff00, 0x7700); + break; + case MIC_TYPE_MUX_DCC_ECM_SINGLE: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON17, + 0xff00, 0x1100); + break; + default: + regmap_update_bits(priv->regmap, + MT6359_AUDENC_ANA_CON17, + 0xff00, 0x0000); + break; + } + + /* MISBIAS2 = 1P9V */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17, + RG_AUDMICBIAS2VREF_MASK_SFT, + MIC_BIAS_1P9 << RG_AUDMICBIAS2VREF_SFT); + /* normal power select */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON17, + RG_AUDMICBIAS2LOWPEN_MASK_SFT, + 0 << RG_AUDMICBIAS2LOWPEN_SFT); + break; + case SND_SOC_DAPM_POST_PMD: + /* Disable MICBIAS2, MISBIAS0 = 1P7V */ + regmap_write(priv->regmap, MT6359_AUDENC_ANA_CON17, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_mtkaif_tx_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + mt6359_mtkaif_tx_enable(priv); + break; + case SND_SOC_DAPM_POST_PMD: + mt6359_mtkaif_tx_disable(priv); + break; + default: + break; + } + + return 0; +} + +static int mt_ul_src_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* UL dmic setting */ + if (priv->dmic_one_wire_mode) + regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H, + 0x0400); + else + regmap_write(priv->regmap, MT6359_AFE_UL_SRC_CON0_H, + 0x0080); + /* default one wire, 3.25M */ + regmap_update_bits(priv->regmap, MT6359_AFE_UL_SRC_CON0_L, + 0xfffc, 0x0000); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_write(priv->regmap, + MT6359_AFE_UL_SRC_CON0_H, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_ul_src_34_dmic_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* default two wire, 3.25M */ + regmap_write(priv->regmap, + MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0080); + regmap_update_bits(priv->regmap, MT6359_AFE_ADDA6_UL_SRC_CON0_L, + 0xfffc, 0x0000); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_write(priv->regmap, + MT6359_AFE_ADDA6_L_SRC_CON0_H, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_l_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio L preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_r_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio R preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_adc_3_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s(), event = 0x%x\n", __func__, event); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + usleep_range(100, 120); + /* Audio R preamplifier DCC precharge off */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCPRECHARGE_MASK_SFT, + 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_pga_l_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux); + priv->mux_select[MUX_PGA_L] = mux >> RG_AUDPREAMPLINPUTSEL_SFT; + return 0; +} + +static int mt_pga_r_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux); + priv->mux_select[MUX_PGA_R] = mux >> RG_AUDPREAMPRINPUTSEL_SFT; + return 0; +} + +static int mt_pga_3_mux_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); + + dev_dbg(priv->dev, "%s(), mux %d\n", __func__, mux); + priv->mux_select[MUX_PGA_3] = mux >> RG_AUDPREAMP3INPUTSEL_SFT; + return 0; +} + +static int mt_pga_l_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int mic_gain_l = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + unsigned int mux_pga = priv->mux_select[MUX_PGA_L]; + unsigned int mic_type; + + switch (mux_pga) { + case PGA_L_MUX_AIN0: + mic_type = priv->mux_select[MUX_MIC_TYPE_0]; + break; + case PGA_L_MUX_AIN1: + mic_type = priv->mux_select[MUX_MIC_TYPE_1]; + break; + default: + dev_err(priv->dev, "%s(), invalid pga mux %d\n", + __func__, mux_pga); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (IS_DCC_BASE(mic_type)) { + /* Audio L preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCPRECHARGE_MASK_SFT, + 0x1 << RG_AUDPREAMPLDCPRECHARGE_SFT); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* set mic pga gain */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLGAIN_MASK_SFT, + mic_gain_l << RG_AUDPREAMPLGAIN_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPLDCCEN_SFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* L preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLDCCEN_MASK_SFT, + 0x0 << RG_AUDPREAMPLDCCEN_SFT); + break; + default: + break; + } + + return 0; +} + +static int mt_pga_r_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int mic_gain_r = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + unsigned int mux_pga = priv->mux_select[MUX_PGA_R]; + unsigned int mic_type; + + switch (mux_pga) { + case PGA_R_MUX_AIN0: + mic_type = priv->mux_select[MUX_MIC_TYPE_0]; + break; + case PGA_R_MUX_AIN2: + case PGA_R_MUX_AIN3: + mic_type = priv->mux_select[MUX_MIC_TYPE_2]; + break; + default: + dev_err(priv->dev, "%s(), invalid pga mux %d\n", + __func__, mux_pga); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (IS_DCC_BASE(mic_type)) { + /* Audio R preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCPRECHARGE_MASK_SFT, + 0x1 << RG_AUDPREAMPRDCPRECHARGE_SFT); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* set mic pga gain */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRGAIN_MASK_SFT, + mic_gain_r << RG_AUDPREAMPRGAIN_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMPRDCCEN_SFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* R preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRDCCEN_MASK_SFT, + 0x0 << RG_AUDPREAMPRDCCEN_SFT); + break; + default: + break; + } + + return 0; +} + +static int mt_pga_3_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + int mic_gain_3 = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + unsigned int mux_pga = priv->mux_select[MUX_PGA_3]; + unsigned int mic_type; + + switch (mux_pga) { + case PGA_3_MUX_AIN2: + case PGA_3_MUX_AIN3: + mic_type = priv->mux_select[MUX_MIC_TYPE_2]; + break; + default: + dev_err(priv->dev, "%s(), invalid pga mux %d\n", + __func__, mux_pga); + return -EINVAL; + } + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + if (IS_DCC_BASE(mic_type)) { + /* Audio 3 preamplifier DCC precharge */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCPRECHARGE_MASK_SFT, + 0x1 << RG_AUDPREAMP3DCPRECHARGE_SFT); + } + break; + case SND_SOC_DAPM_POST_PMU: + /* set mic pga gain */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3GAIN_MASK_SFT, + mic_gain_3 << RG_AUDPREAMP3GAIN_SFT); + + if (IS_DCC_BASE(mic_type)) { + /* 3 preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCCEN_MASK_SFT, + 0x1 << RG_AUDPREAMP3DCCEN_SFT); + } + break; + case SND_SOC_DAPM_POST_PMD: + /* 3 preamplifier DCCEN */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3DCCEN_MASK_SFT, + 0x0 << RG_AUDPREAMP3DCCEN_SFT); + break; + default: + break; + } + + return 0; +} + +/* It is based on hw's control sequenece to add some delay when PMU/PMD */ +static int mt_delay_250_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMD: + usleep_range(250, 270); + break; + default: + break; + } + + return 0; +} + +static int mt_delay_100_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + switch (event) { + case SND_SOC_DAPM_POST_PMU: + case SND_SOC_DAPM_PRE_PMD: + usleep_range(100, 120); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_pull_down_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + hp_pull_down(priv, true); + break; + case SND_SOC_DAPM_POST_PMD: + hp_pull_down(priv, false); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_mute_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Set HPR/HPL gain to -22dB */ + regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_22DB_REG); + break; + case SND_SOC_DAPM_POST_PMD: + /* Set HPL/HPR gain to mute */ + regmap_write(priv->regmap, MT6359_ZCD_CON2, DL_GAIN_N_40DB_REG); + break; + default: + break; + } + + return 0; +} + +static int mt_hp_damp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_POST_PMD: + /* Disable HP damping circuit & HPN 4K load */ + /* reset CMFB PW level */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON10, 0x0000); + break; + default: + break; + } + + return 0; +} + +static int mt_esd_resist_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* Reduce ESD resistance of AU_REFN */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT, + 0x1 << RG_AUDREFN_DERES_EN_VAUDP32_SFT); + usleep_range(250, 270); + break; + case SND_SOC_DAPM_POST_PMD: + /* Increase ESD resistance of AU_REFN */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON2, + RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT, 0x0); + break; + default: + break; + } + + return 0; +} + +static int mt_sdm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba1); + /* sdm power on */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x0003); + /* sdm fifo enable */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x000B); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_update_bits(priv->regmap, MT6359_AFUNC_AUD_CON2, + 0xfffd, 0x0000); + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON0, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static int mt_sdm_3rd_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + /* sdm audio fifo clock power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0006); + /* scrambler clock on enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba1); + /* sdm power on */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0003); + /* sdm fifo enable */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x000b); + break; + case SND_SOC_DAPM_POST_PMD: + /* DL scrambler disabling sequence */ + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON11, 0x0000); + regmap_write(priv->regmap, MT6359_AFUNC_AUD_CON9, 0xcba0); + break; + default: + break; + } + + return 0; +} + +static int mt_ncp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, + int event) +{ + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + regmap_write(priv->regmap, MT6359_AFE_NCP_CFG0, 0xc800); + break; + default: + break; + } + + return 0; +} + +/* DAPM Widgets */ +static const struct snd_soc_dapm_widget mt6359_dapm_widgets[] = { + /* Global Supply*/ + SND_SOC_DAPM_SUPPLY_S("CLK_BUF", SUPPLY_SEQ_CLK_BUF, + MT6359_DCXO_CW12, + RG_XO_AUDIO_EN_M_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO_VAUD18", SUPPLY_SEQ_LDO_VAUD18, + MT6359_LDO_VAUD18_CON0, + RG_LDO_VAUD18_EN_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDGLB", SUPPLY_SEQ_AUD_GLB, + MT6359_AUDDEC_ANA_CON13, + RG_AUDGLB_PWRDN_VA32_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("CLKSQ Audio", SUPPLY_SEQ_CLKSQ, + MT6359_AUDENC_ANA_CON23, + RG_CLKSQ_EN_SFT, 0, NULL, SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("AUDNCP_CK", SUPPLY_SEQ_TOP_CK, + MT6359_AUD_TOP_CKPDN_CON0, + RG_AUDNCP_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("ZCD13M_CK", SUPPLY_SEQ_TOP_CK, + MT6359_AUD_TOP_CKPDN_CON0, + RG_ZCD13M_CK_PDN_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUD_CK", SUPPLY_SEQ_TOP_CK_LAST, + MT6359_AUD_TOP_CKPDN_CON0, + RG_AUD_CK_PDN_SFT, 1, mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIF_CK", SUPPLY_SEQ_TOP_CK, + MT6359_AUD_TOP_CKPDN_CON0, + RG_AUDIF_CK_PDN_SFT, 1, NULL, 0), + /* Digital Clock */ + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_AFE_CTL", SUPPLY_SEQ_AUD_TOP_LAST, + MT6359_AUDIO_TOP_CON0, + PDN_AFE_CTL_SFT, 1, + mt_delay_250_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_DAC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_DAC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_ADC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_ADDA6_ADC_CTL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_ADDA6_ADC_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_I2S_DL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_I2S_DL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PWR_CLK", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PWR_CLK_DIS_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_AFE_TESTMODEL", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_AFE_TESTMODEL_CTL_SFT, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("AUDIO_TOP_PDN_RESERVED", SUPPLY_SEQ_AUD_TOP, + MT6359_AUDIO_TOP_CON0, + PDN_RESERVED_SFT, 1, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("SDM", SUPPLY_SEQ_DL_SDM, + SND_SOC_NOPM, 0, 0, + mt_sdm_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("SDM_3RD", SUPPLY_SEQ_DL_SDM, + SND_SOC_NOPM, 0, 0, + mt_sdm_3rd_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* ch123 share SDM FIFO CLK */ + SND_SOC_DAPM_SUPPLY_S("SDM_FIFO_CLK", SUPPLY_SEQ_DL_SDM_FIFO_CLK, + MT6359_AFUNC_AUD_CON2, + CCI_AFIFO_CLK_PWDB_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("NCP", SUPPLY_SEQ_DL_NCP, + MT6359_AFE_NCP_CFG0, + RG_NCP_ON_SFT, 0, + mt_ncp_event, + SND_SOC_DAPM_PRE_PMU), + + SND_SOC_DAPM_SUPPLY("DL Digital Clock", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_1_2", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DL Digital Clock CH_3", SND_SOC_NOPM, + 0, 0, NULL, 0), + + /* AFE ON */ + SND_SOC_DAPM_SUPPLY_S("AFE_ON", SUPPLY_SEQ_AFE, + MT6359_AFE_UL_DL_CON0, AFE_ON_SFT, 0, + NULL, 0), + + /* AIF Rx*/ + SND_SOC_DAPM_AIF_IN("AIF_RX", "AIF1 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("AIF2_RX", "AIF2 Playback", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY_S("AFE_DL_SRC", SUPPLY_SEQ_DL_SRC, + MT6359_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, + NULL, 0), + + /* DL Supply */ + SND_SOC_DAPM_SUPPLY("DL Power Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("ESD_RESIST", SUPPLY_SEQ_DL_ESD_RESIST, + SND_SOC_NOPM, + 0, 0, + mt_esd_resist_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("LDO", SUPPLY_SEQ_DL_LDO, + MT6359_AUDDEC_ANA_CON14, + RG_LCLDO_DEC_EN_VA32_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("LDO_REMOTE", SUPPLY_SEQ_DL_LDO_REMOTE_SENSE, + MT6359_AUDDEC_ANA_CON14, + RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("NV_REGULATOR", SUPPLY_SEQ_DL_NV, + MT6359_AUDDEC_ANA_CON14, + RG_NVREG_EN_VAUDP32_SFT, 0, + mt_delay_100_event, SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("IBIST", SUPPLY_SEQ_DL_IBIST, + MT6359_AUDDEC_ANA_CON12, + RG_AUDIBIASPWRDN_VAUDP32_SFT, 1, + NULL, 0), + + /* DAC */ + SND_SOC_DAPM_MUX("DAC In Mux", SND_SOC_NOPM, 0, 0, &dac_in_mux_control), + + SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC_3RD", NULL, SND_SOC_NOPM, 0, 0), + + /* Headphone */ + SND_SOC_DAPM_MUX_E("HP Mux", SND_SOC_NOPM, 0, 0, + &hp_in_mux_control, + mt_hp_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY("HP_Supply", SND_SOC_NOPM, + 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY_S("HP_PULL_DOWN", SUPPLY_SEQ_HP_PULL_DOWN, + SND_SOC_NOPM, + 0, 0, + mt_hp_pull_down_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("HP_MUTE", SUPPLY_SEQ_HP_MUTE, + SND_SOC_NOPM, + 0, 0, + mt_hp_mute_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("HP_DAMP", SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB, + SND_SOC_NOPM, + 0, 0, + mt_hp_damp_event, + SND_SOC_DAPM_POST_PMD), + + /* Receiver */ + SND_SOC_DAPM_MUX_E("RCV Mux", SND_SOC_NOPM, 0, 0, + &rcv_in_mux_control, + mt_rcv_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + /* LOL */ + SND_SOC_DAPM_MUX_E("LOL Mux", SND_SOC_NOPM, 0, 0, + &lo_in_mux_control, + mt_lo_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + /* Outputs */ + SND_SOC_DAPM_OUTPUT("Receiver"), + SND_SOC_DAPM_OUTPUT("Headphone L"), + SND_SOC_DAPM_OUTPUT("Headphone R"), + SND_SOC_DAPM_OUTPUT("Headphone L Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("Headphone R Ext Spk Amp"), + SND_SOC_DAPM_OUTPUT("LINEOUT L"), + + /* SGEN */ + SND_SOC_DAPM_SUPPLY("SGEN DL Enable", MT6359_AFE_SGEN_CFG0, + SGEN_DAC_EN_CTL_SFT, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SGEN MUTE", MT6359_AFE_SGEN_CFG0, + SGEN_MUTE_SW_CTL_SFT, 1, + mt_sgen_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY("SGEN DL SRC", MT6359_AFE_DL_SRC2_CON0_L, + DL_2_SRC_ON_TMP_CTL_PRE_SFT, 0, NULL, 0), + + SND_SOC_DAPM_INPUT("SGEN DL"), + + /* Uplinks */ + SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, + SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY_S("ADC_CLKGEN", SUPPLY_SEQ_ADC_CLKGEN, + SND_SOC_NOPM, 0, 0, + mt_adc_clk_gen_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_SUPPLY_S("DCC_CLK", SUPPLY_SEQ_DCC_CLK, + SND_SOC_NOPM, 0, 0, + mt_dcc_clk_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* Uplinks MUX */ + SND_SOC_DAPM_MUX("AIF Out Mux", SND_SOC_NOPM, 0, 0, + &aif_out_mux_control), + + SND_SOC_DAPM_MUX("AIF2 Out Mux", SND_SOC_NOPM, 0, 0, + &aif2_out_mux_control), + + SND_SOC_DAPM_SUPPLY("AIFTX_Supply", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("MTKAIF_TX", SUPPLY_SEQ_UL_MTKAIF, + SND_SOC_NOPM, 0, 0, + mt_mtkaif_tx_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC", SUPPLY_SEQ_UL_SRC, + MT6359_AFE_UL_SRC_CON0_L, + UL_SRC_ON_TMP_CTL_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC_DMIC", SUPPLY_SEQ_UL_SRC_DMIC, + SND_SOC_NOPM, 0, 0, + mt_ul_src_dmic_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC_34", SUPPLY_SEQ_UL_SRC, + MT6359_AFE_ADDA6_UL_SRC_CON0_L, + ADDA6_UL_SRC_ON_TMP_CTL_SFT, 0, + NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("UL_SRC_34_DMIC", SUPPLY_SEQ_UL_SRC_DMIC, + SND_SOC_NOPM, 0, 0, + mt_ul_src_34_dmic_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + SND_SOC_DAPM_MUX("MISO0_MUX", SND_SOC_NOPM, 0, 0, &miso0_mux_control), + SND_SOC_DAPM_MUX("MISO1_MUX", SND_SOC_NOPM, 0, 0, &miso1_mux_control), + SND_SOC_DAPM_MUX("MISO2_MUX", SND_SOC_NOPM, 0, 0, &miso2_mux_control), + + SND_SOC_DAPM_MUX("UL_SRC_MUX", SND_SOC_NOPM, 0, 0, + &ul_src_mux_control), + SND_SOC_DAPM_MUX("UL2_SRC_MUX", SND_SOC_NOPM, 0, 0, + &ul2_src_mux_control), + + SND_SOC_DAPM_MUX("DMIC0_MUX", SND_SOC_NOPM, 0, 0, &dmic0_mux_control), + SND_SOC_DAPM_MUX("DMIC1_MUX", SND_SOC_NOPM, 0, 0, &dmic1_mux_control), + SND_SOC_DAPM_MUX("DMIC2_MUX", SND_SOC_NOPM, 0, 0, &dmic2_mux_control), + + SND_SOC_DAPM_MUX_E("ADC_L_Mux", SND_SOC_NOPM, 0, 0, + &adc_left_mux_control, NULL, 0), + SND_SOC_DAPM_MUX_E("ADC_R_Mux", SND_SOC_NOPM, 0, 0, + &adc_right_mux_control, NULL, 0), + SND_SOC_DAPM_MUX_E("ADC_3_Mux", SND_SOC_NOPM, 0, 0, + &adc_3_mux_control, NULL, 0), + + SND_SOC_DAPM_ADC("ADC_L", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC_R", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("ADC_3", NULL, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SUPPLY_S("ADC_L_EN", SUPPLY_SEQ_UL_ADC, + MT6359_AUDENC_ANA_CON0, + RG_AUDADCLPWRUP_SFT, 0, + mt_adc_l_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("ADC_R_EN", SUPPLY_SEQ_UL_ADC, + MT6359_AUDENC_ANA_CON1, + RG_AUDADCRPWRUP_SFT, 0, + mt_adc_r_event, + SND_SOC_DAPM_POST_PMU), + SND_SOC_DAPM_SUPPLY_S("ADC_3_EN", SUPPLY_SEQ_UL_ADC, + MT6359_AUDENC_ANA_CON2, + RG_AUDADC3PWRUP_SFT, 0, + mt_adc_3_event, + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_MUX_E("PGA_L_Mux", SND_SOC_NOPM, 0, 0, + &pga_left_mux_control, + mt_pga_l_mux_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("PGA_R_Mux", SND_SOC_NOPM, 0, 0, + &pga_right_mux_control, + mt_pga_r_mux_event, + SND_SOC_DAPM_WILL_PMU), + SND_SOC_DAPM_MUX_E("PGA_3_Mux", SND_SOC_NOPM, 0, 0, + &pga_3_mux_control, + mt_pga_3_mux_event, + SND_SOC_DAPM_WILL_PMU), + + SND_SOC_DAPM_PGA("PGA_L", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA_R", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("PGA_3", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY_S("PGA_L_EN", SUPPLY_SEQ_UL_PGA, + MT6359_AUDENC_ANA_CON0, + RG_AUDPREAMPLON_SFT, 0, + mt_pga_l_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("PGA_R_EN", SUPPLY_SEQ_UL_PGA, + MT6359_AUDENC_ANA_CON1, + RG_AUDPREAMPRON_SFT, 0, + mt_pga_r_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("PGA_3_EN", SUPPLY_SEQ_UL_PGA, + MT6359_AUDENC_ANA_CON2, + RG_AUDPREAMP3ON_SFT, 0, + mt_pga_3_event, + SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMU | + SND_SOC_DAPM_POST_PMD), + + /* UL input */ + SND_SOC_DAPM_INPUT("AIN0"), + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + + SND_SOC_DAPM_INPUT("AIN0_DMIC"), + SND_SOC_DAPM_INPUT("AIN2_DMIC"), + SND_SOC_DAPM_INPUT("AIN3_DMIC"), + + /* mic bias */ + SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_0", SUPPLY_SEQ_MIC_BIAS, + MT6359_AUDENC_ANA_CON15, + RG_AUDPWDBMICBIAS0_SFT, 0, + mt_mic_bias_0_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_1", SUPPLY_SEQ_MIC_BIAS, + MT6359_AUDENC_ANA_CON16, + RG_AUDPWDBMICBIAS1_SFT, 0, + mt_mic_bias_1_event, + SND_SOC_DAPM_PRE_PMU), + SND_SOC_DAPM_SUPPLY_S("MIC_BIAS_2", SUPPLY_SEQ_MIC_BIAS, + MT6359_AUDENC_ANA_CON17, + RG_AUDPWDBMICBIAS2_SFT, 0, + mt_mic_bias_2_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + + /* dmic */ + SND_SOC_DAPM_SUPPLY_S("DMIC_0", SUPPLY_SEQ_DMIC, + MT6359_AUDENC_ANA_CON13, + RG_AUDDIGMICEN_SFT, 0, + NULL, 0), + SND_SOC_DAPM_SUPPLY_S("DMIC_1", SUPPLY_SEQ_DMIC, + MT6359_AUDENC_ANA_CON14, + RG_AUDDIGMIC1EN_SFT, 0, + NULL, 0), +}; + +static int mt_dcc_clk_connect(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_dapm_widget *w = sink; + struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + if (IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_0]) || + IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_1]) || + IS_DCC_BASE(priv->mux_select[MUX_MIC_TYPE_2])) + return 1; + else + return 0; +} + +static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { + /* Capture */ + {"AIFTX_Supply", NULL, "CLK_BUF"}, + {"AIFTX_Supply", NULL, "LDO_VAUD18"}, + {"AIFTX_Supply", NULL, "AUDGLB"}, + {"AIFTX_Supply", NULL, "CLKSQ Audio"}, + {"AIFTX_Supply", NULL, "AUD_CK"}, + {"AIFTX_Supply", NULL, "AUDIF_CK"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_AFE_CTL"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_PWR_CLK"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_I2S_DL"}, + /* + * *_ADC_CTL should enable only if UL_SRC in use, + * but dm ck may be needed even UL_SRC_x not in use + */ + {"AIFTX_Supply", NULL, "AUDIO_TOP_ADC_CTL"}, + {"AIFTX_Supply", NULL, "AUDIO_TOP_ADDA6_ADC_CTL"}, + {"AIFTX_Supply", NULL, "AFE_ON"}, + + /* ul ch 12 */ + {"AIF1TX", NULL, "AIF Out Mux"}, + {"AIF1TX", NULL, "AIFTX_Supply"}, + {"AIF1TX", NULL, "MTKAIF_TX"}, + + {"AIF2TX", NULL, "AIF2 Out Mux"}, + {"AIF2TX", NULL, "AIFTX_Supply"}, + {"AIF2TX", NULL, "MTKAIF_TX"}, + + {"AIF Out Mux", "Normal Path", "MISO0_MUX"}, + {"AIF Out Mux", "Normal Path", "MISO1_MUX"}, + {"AIF2 Out Mux", "Normal Path", "MISO2_MUX"}, + + {"MISO0_MUX", "UL1_CH1", "UL_SRC_MUX"}, + {"MISO0_MUX", "UL1_CH2", "UL_SRC_MUX"}, + {"MISO0_MUX", "UL2_CH1", "UL2_SRC_MUX"}, + {"MISO0_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + + {"MISO1_MUX", "UL1_CH1", "UL_SRC_MUX"}, + {"MISO1_MUX", "UL1_CH2", "UL_SRC_MUX"}, + {"MISO1_MUX", "UL2_CH1", "UL2_SRC_MUX"}, + {"MISO1_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + + {"MISO2_MUX", "UL1_CH1", "UL_SRC_MUX"}, + {"MISO2_MUX", "UL1_CH2", "UL_SRC_MUX"}, + {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"}, + {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + + {"UL_SRC_MUX", "AMIC", "ADC_L"}, + {"UL_SRC_MUX", "AMIC", "ADC_R"}, + {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"}, + {"UL_SRC_MUX", "DMIC", "DMIC1_MUX"}, + {"UL_SRC_MUX", NULL, "UL_SRC"}, + + {"UL2_SRC_MUX", "AMIC", "ADC_3"}, + {"UL2_SRC_MUX", "DMIC", "DMIC2_MUX"}, + {"UL2_SRC_MUX", NULL, "UL_SRC_34"}, + + {"DMIC0_MUX", "DMIC_DATA0", "AIN0_DMIC"}, + {"DMIC0_MUX", "DMIC_DATA1_L", "AIN2_DMIC"}, + {"DMIC0_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"}, + {"DMIC0_MUX", "DMIC_DATA1_R", "AIN3_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA0", "AIN0_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA1_L", "AIN2_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"}, + {"DMIC1_MUX", "DMIC_DATA1_R", "AIN3_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA0", "AIN0_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA1_L", "AIN2_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA1_L_1", "AIN2_DMIC"}, + {"DMIC2_MUX", "DMIC_DATA1_R", "AIN3_DMIC"}, + + {"DMIC0_MUX", NULL, "UL_SRC_DMIC"}, + {"DMIC1_MUX", NULL, "UL_SRC_DMIC"}, + {"DMIC2_MUX", NULL, "UL_SRC_34_DMIC"}, + + {"AIN0_DMIC", NULL, "DMIC_0"}, + {"AIN2_DMIC", NULL, "DMIC_1"}, + {"AIN3_DMIC", NULL, "DMIC_1"}, + {"AIN0_DMIC", NULL, "MIC_BIAS_0"}, + {"AIN2_DMIC", NULL, "MIC_BIAS_2"}, + {"AIN3_DMIC", NULL, "MIC_BIAS_2"}, + + /* adc */ + {"ADC_L", NULL, "ADC_L_Mux"}, + {"ADC_L", NULL, "ADC_CLKGEN"}, + {"ADC_L", NULL, "ADC_L_EN"}, + {"ADC_R", NULL, "ADC_R_Mux"}, + {"ADC_R", NULL, "ADC_CLKGEN"}, + {"ADC_R", NULL, "ADC_R_EN"}, + /* + * amic fifo ch1/2 clk from ADC_L, + * enable ADC_L even use ADC_R only + */ + {"ADC_R", NULL, "ADC_L_EN"}, + {"ADC_3", NULL, "ADC_3_Mux"}, + {"ADC_3", NULL, "ADC_CLKGEN"}, + {"ADC_3", NULL, "ADC_3_EN"}, + + {"ADC_L_Mux", "Left Preamplifier", "PGA_L"}, + {"ADC_R_Mux", "Right Preamplifier", "PGA_R"}, + {"ADC_3_Mux", "Preamplifier", "PGA_3"}, + + {"PGA_L", NULL, "PGA_L_Mux"}, + {"PGA_L", NULL, "PGA_L_EN"}, + {"PGA_R", NULL, "PGA_R_Mux"}, + {"PGA_R", NULL, "PGA_R_EN"}, + {"PGA_3", NULL, "PGA_3_Mux"}, + {"PGA_3", NULL, "PGA_3_EN"}, + + {"PGA_L", NULL, "DCC_CLK", mt_dcc_clk_connect}, + {"PGA_R", NULL, "DCC_CLK", mt_dcc_clk_connect}, + {"PGA_3", NULL, "DCC_CLK", mt_dcc_clk_connect}, + + {"PGA_L_Mux", "AIN0", "AIN0"}, + {"PGA_L_Mux", "AIN1", "AIN1"}, + + {"PGA_R_Mux", "AIN0", "AIN0"}, + {"PGA_R_Mux", "AIN2", "AIN2"}, + {"PGA_R_Mux", "AIN3", "AIN3"}, + + {"PGA_3_Mux", "AIN2", "AIN2"}, + {"PGA_3_Mux", "AIN3", "AIN3"}, + + {"AIN0", NULL, "MIC_BIAS_0"}, + {"AIN1", NULL, "MIC_BIAS_1"}, + {"AIN2", NULL, "MIC_BIAS_0"}, + {"AIN2", NULL, "MIC_BIAS_2"}, + {"AIN3", NULL, "MIC_BIAS_2"}, + + /* DL Supply */ + {"DL Power Supply", NULL, "CLK_BUF"}, + {"DL Power Supply", NULL, "LDO_VAUD18"}, + {"DL Power Supply", NULL, "AUDGLB"}, + {"DL Power Supply", NULL, "CLKSQ Audio"}, + {"DL Power Supply", NULL, "AUDNCP_CK"}, + {"DL Power Supply", NULL, "ZCD13M_CK"}, + {"DL Power Supply", NULL, "AUD_CK"}, + {"DL Power Supply", NULL, "AUDIF_CK"}, + {"DL Power Supply", NULL, "ESD_RESIST"}, + {"DL Power Supply", NULL, "LDO"}, + {"DL Power Supply", NULL, "LDO_REMOTE"}, + {"DL Power Supply", NULL, "NV_REGULATOR"}, + {"DL Power Supply", NULL, "IBIST"}, + + /* DL Digital Supply */ + {"DL Digital Clock", NULL, "AUDIO_TOP_AFE_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_DAC_CTL"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PWR_CLK"}, + {"DL Digital Clock", NULL, "AUDIO_TOP_PDN_RESERVED"}, + {"DL Digital Clock", NULL, "SDM_FIFO_CLK"}, + {"DL Digital Clock", NULL, "NCP"}, + {"DL Digital Clock", NULL, "AFE_ON"}, + {"DL Digital Clock", NULL, "AFE_DL_SRC"}, + + {"DL Digital Clock CH_1_2", NULL, "DL Digital Clock"}, + {"DL Digital Clock CH_1_2", NULL, "SDM"}, + + {"DL Digital Clock CH_3", NULL, "DL Digital Clock"}, + {"DL Digital Clock CH_3", NULL, "SDM_3RD"}, + + {"AIF_RX", NULL, "DL Digital Clock CH_1_2"}, + + {"AIF2_RX", NULL, "DL Digital Clock CH_3"}, + + /* DL Path */ + {"DAC In Mux", "Normal Path", "AIF_RX"}, + {"DAC In Mux", "Sgen", "SGEN DL"}, + {"SGEN DL", NULL, "SGEN DL SRC"}, + {"SGEN DL", NULL, "SGEN MUTE"}, + {"SGEN DL", NULL, "SGEN DL Enable"}, + {"SGEN DL", NULL, "DL Digital Clock CH_1_2"}, + {"SGEN DL", NULL, "DL Digital Clock CH_3"}, + {"SGEN DL", NULL, "AUDIO_TOP_PDN_AFE_TESTMODEL"}, + + {"DACL", NULL, "DAC In Mux"}, + {"DACL", NULL, "DL Power Supply"}, + + {"DACR", NULL, "DAC In Mux"}, + {"DACR", NULL, "DL Power Supply"}, + + /* DAC 3RD */ + {"DAC In Mux", "Normal Path", "AIF2_RX"}, + {"DAC_3RD", NULL, "DAC In Mux"}, + {"DAC_3RD", NULL, "DL Power Supply"}, + + /* Lineout Path */ + {"LOL Mux", "Playback", "DAC_3RD"}, + {"LINEOUT L", NULL, "LOL Mux"}, + + /* Headphone Path */ + {"HP_Supply", NULL, "HP_PULL_DOWN"}, + {"HP_Supply", NULL, "HP_MUTE"}, + {"HP_Supply", NULL, "HP_DAMP"}, + {"HP Mux", NULL, "HP_Supply"}, + + {"HP Mux", "Audio Playback", "DACL"}, + {"HP Mux", "Audio Playback", "DACR"}, + {"HP Mux", "HP Impedance", "DACL"}, + {"HP Mux", "HP Impedance", "DACR"}, + {"HP Mux", "LoudSPK Playback", "DACL"}, + {"HP Mux", "LoudSPK Playback", "DACR"}, + + {"Headphone L", NULL, "HP Mux"}, + {"Headphone R", NULL, "HP Mux"}, + {"Headphone L Ext Spk Amp", NULL, "HP Mux"}, + {"Headphone R Ext Spk Amp", NULL, "HP Mux"}, + + /* Receiver Path */ + {"RCV Mux", "Voice Playback", "DACL"}, + {"Receiver", NULL, "RCV Mux"}, +}; + +static int mt6359_codec_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int rate = params_rate(params); + int id = dai->id; + + dev_dbg(priv->dev, "%s(), id %d, substream->stream %d, rate %d, number %d\n", + __func__, id, substream->stream, rate, substream->number); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + priv->dl_rate[id] = rate; + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + priv->ul_rate[id] = rate; + + return 0; +} + +static int mt6359_codec_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mt6359_set_playback_gpio(priv); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + mt6359_set_capture_gpio(priv); + + return 0; +} + +static void mt6359_codec_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *cmpnt = dai->component; + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + dev_dbg(priv->dev, "%s stream %d\n", __func__, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + mt6359_reset_playback_gpio(priv); + else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + mt6359_reset_capture_gpio(priv); +} + +static const struct snd_soc_dai_ops mt6359_codec_dai_ops = { + .hw_params = mt6359_codec_dai_hw_params, + .startup = mt6359_codec_dai_startup, + .shutdown = mt6359_codec_dai_shutdown, +}; + +#define MT6359_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\ + SNDRV_PCM_FMTBIT_U24_LE | SNDRV_PCM_FMTBIT_U24_BE |\ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |\ + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE) + +static struct snd_soc_dai_driver mt6359_dai_driver[] = { + { + .id = MT6359_AIF_1, + .name = "mt6359-snd-codec-aif1", + .playback = { + .stream_name = "AIF1 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6359_FORMATS, + }, + .capture = { + .stream_name = "AIF1 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6359_FORMATS, + }, + .ops = &mt6359_codec_dai_ops, + }, + { + .id = MT6359_AIF_2, + .name = "mt6359-snd-codec-aif2", + .playback = { + .stream_name = "AIF2 Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_48000 | + SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_192000, + .formats = MT6359_FORMATS, + }, + .capture = { + .stream_name = "AIF2 Capture", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000 | + SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_48000, + .formats = MT6359_FORMATS, + }, + .ops = &mt6359_codec_dai_ops, + }, +}; + +static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + /* enable clk buf */ + regmap_update_bits(priv->regmap, MT6359_DCXO_CW12, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + 0x1 << RG_XO_AUDIO_EN_M_SFT); + + /* set those not controlled by dapm widget */ + + /* audio clk source from internal dcxo */ + regmap_update_bits(priv->regmap, MT6359_AUDENC_ANA_CON23, + RG_CLKSQ_IN_SEL_TEST_MASK_SFT, + 0x0); + + /* Disable HeadphoneL/HeadphoneR short circuit protection */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHPLSCDISABLE_VAUDP32_SFT); + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHPRSCDISABLE_VAUDP32_SFT); + /* Disable voice short circuit protection */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON6, + RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDHSSCDISABLE_VAUDP32_SFT); + /* disable LO buffer left short circuit protection */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON7, + RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT, + 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT); + + /* set gpio */ + mt6359_reset_playback_gpio(priv); + mt6359_reset_capture_gpio(priv); + + /* hp hifi mode, default normal mode */ + priv->hp_hifi_mode = 0; + + /* Disable AUD_ZCD */ + zcd_disable(priv); + + /* disable clk buf */ + regmap_update_bits(priv->regmap, MT6359_DCXO_CW12, + 0x1 << RG_XO_AUDIO_EN_M_SFT, + 0x0 << RG_XO_AUDIO_EN_M_SFT); + + return 0; +} + +static int mt6359_codec_probe(struct snd_soc_component *cmpnt) +{ + struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + + snd_soc_component_init_regmap(cmpnt, priv->regmap); + + return mt6359_codec_init_reg(cmpnt); +} + +static void mt6359_codec_remove(struct snd_soc_component *cmpnt) +{ + snd_soc_component_exit_regmap(cmpnt); +} + +static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0); +static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); +static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0); + +static const struct snd_kcontrol_new mt6359_snd_controls[] = { + /* dl pga gain */ + SOC_DOUBLE_EXT_TLV("Headset Volume", + MT6359_ZCD_CON2, 0, 7, 0x1E, 0, + snd_soc_get_volsw, mt6359_put_volsw, + hp_playback_tlv), + SOC_DOUBLE_EXT_TLV("Lineout Volume", + MT6359_ZCD_CON1, 0, 7, 0x12, 0, + snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + SOC_SINGLE_EXT_TLV("Handset Volume", + MT6359_ZCD_CON3, 0, 0x12, 0, + snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + + /* ul pga gain */ + SOC_SINGLE_EXT_TLV("PGA1 Volume", + MT6359_AUDENC_ANA_CON0, RG_AUDPREAMPLGAIN_SFT, 4, 0, + snd_soc_get_volsw, mt6359_put_volsw, capture_tlv), + SOC_SINGLE_EXT_TLV("PGA2 Volume", + MT6359_AUDENC_ANA_CON1, RG_AUDPREAMPRGAIN_SFT, 4, 0, + snd_soc_get_volsw, mt6359_put_volsw, capture_tlv), + SOC_SINGLE_EXT_TLV("PGA3 Volume", + MT6359_AUDENC_ANA_CON2, RG_AUDPREAMP3GAIN_SFT, 4, 0, + snd_soc_get_volsw, mt6359_put_volsw, capture_tlv), +}; + +static const struct snd_soc_component_driver mt6359_soc_component_driver = { + .name = CODEC_MT6359_NAME, + .probe = mt6359_codec_probe, + .remove = mt6359_codec_remove, + .controls = mt6359_snd_controls, + .num_controls = ARRAY_SIZE(mt6359_snd_controls), + .dapm_widgets = mt6359_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt6359_dapm_widgets), + .dapm_routes = mt6359_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt6359_dapm_routes), +}; + +static int mt6359_parse_dt(struct mt6359_priv *priv) +{ + int ret; + struct device *dev = priv->dev; + + ret = of_property_read_u32(dev->of_node, "mediatek,dmic-mode", + &priv->dmic_one_wire_mode); + if (ret) { + dev_warn(priv->dev, "%s() failed to read dmic-mode\n", + __func__); + priv->dmic_one_wire_mode = 0; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,mic-type-0", + &priv->mux_select[MUX_MIC_TYPE_0]); + if (ret) { + dev_warn(priv->dev, "%s() failed to read mic-type-0\n", + __func__); + priv->mux_select[MUX_MIC_TYPE_0] = MIC_TYPE_MUX_IDLE; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,mic-type-1", + &priv->mux_select[MUX_MIC_TYPE_1]); + if (ret) { + dev_warn(priv->dev, "%s() failed to read mic-type-1\n", + __func__); + priv->mux_select[MUX_MIC_TYPE_1] = MIC_TYPE_MUX_IDLE; + } + + ret = of_property_read_u32(dev->of_node, "mediatek,mic-type-2", + &priv->mux_select[MUX_MIC_TYPE_2]); + if (ret) { + dev_warn(priv->dev, "%s() failed to read mic-type-2\n", + __func__); + priv->mux_select[MUX_MIC_TYPE_2] = MIC_TYPE_MUX_IDLE; + } + + return 0; +} + +static int mt6359_platform_driver_probe(struct platform_device *pdev) +{ + struct mt6359_priv *priv; + int ret; + struct mt6397_chip *mt6397 = dev_get_drvdata(pdev->dev.parent); + + dev_dbg(&pdev->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->regmap = mt6397->regmap; + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + dev_set_drvdata(&pdev->dev, priv); + priv->dev = &pdev->dev; + + priv->avdd_reg = devm_regulator_get(&pdev->dev, "vaud18"); + if (IS_ERR(priv->avdd_reg)) { + dev_err(&pdev->dev, "%s(), have no vaud18 supply: %ld", + __func__, PTR_ERR(priv->avdd_reg)); + return PTR_ERR(priv->avdd_reg); + } + + ret = regulator_enable(priv->avdd_reg); + if (ret) { + dev_err(&pdev->dev, "%s(), failed to enable regulator!\n", + __func__); + return ret; + } + + ret = mt6359_parse_dt(priv); + if (ret) { + dev_warn(&pdev->dev, "%s() failed to parse dts\n", __func__); + return ret; + } + + return devm_snd_soc_register_component(&pdev->dev, + &mt6359_soc_component_driver, + mt6359_dai_driver, + ARRAY_SIZE(mt6359_dai_driver)); +} + +static int mt6359_platform_driver_remove(struct platform_device *pdev) +{ + struct mt6359_priv *priv = dev_get_drvdata(&pdev->dev); + int ret; + + dev_dbg(&pdev->dev, "%s(), dev name %s\n", + __func__, dev_name(&pdev->dev)); + + ret = regulator_disable(priv->avdd_reg); + if (ret) { + dev_err(&pdev->dev, "%s(), failed to disable regulator!\n", + __func__); + return ret; + } + + return 0; +} + +static struct platform_driver mt6359_platform_driver = { + .driver = { + .name = "mt6359-sound", + }, + .probe = mt6359_platform_driver_probe, + .remove = mt6359_platform_driver_remove, +}; + +module_platform_driver(mt6359_platform_driver) + +/* Module information */ +MODULE_DESCRIPTION("MT6359 ALSA SoC codec driver"); +MODULE_AUTHOR("KaiChieh Chuang "); +MODULE_AUTHOR("Eason Yen "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/mt6359.h b/sound/soc/codecs/mt6359.h new file mode 100644 index 000000000000..3792e534a91b --- /dev/null +++ b/sound/soc/codecs/mt6359.h @@ -0,0 +1,2640 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2020 MediaTek Inc. + * Author: Argus Lin + */ + +#ifndef _MT6359_H_ +#define _MT6359_H_ + +/*************Register Bit Define*************/ +#define PMIC_ACCDET_IRQ_SHIFT 0 +#define PMIC_ACCDET_EINT0_IRQ_SHIFT 2 +#define PMIC_ACCDET_EINT1_IRQ_SHIFT 3 +#define PMIC_ACCDET_IRQ_CLR_SHIFT 8 +#define PMIC_ACCDET_EINT0_IRQ_CLR_SHIFT 10 +#define PMIC_ACCDET_EINT1_IRQ_CLR_SHIFT 11 +#define PMIC_RG_INT_STATUS_ACCDET_SHIFT 5 +#define PMIC_RG_INT_STATUS_ACCDET_EINT0_SHIFT 6 +#define PMIC_RG_INT_STATUS_ACCDET_EINT1_SHIFT 7 +#define PMIC_RG_EINT0CONFIGACCDET_SHIFT 11 +#define PMIC_RG_EINT1CONFIGACCDET_SHIFT 0 +#define PMIC_ACCDET_EINT0_INVERTER_SW_EN_SHIFT 6 +#define PMIC_ACCDET_EINT1_INVERTER_SW_EN_SHIFT 8 +#define PMIC_RG_MTEST_EN_SHIFT 8 +#define PMIC_RG_MTEST_SEL_SHIFT 9 +#define PMIC_ACCDET_EINT0_M_SW_EN_SHIFT 10 +#define PMIC_ACCDET_EINT1_M_SW_EN_SHIFT 11 +#define PMIC_ACCDET_EINT0_CEN_STABLE_SHIFT 5 +#define PMIC_ACCDET_EINT1_CEN_STABLE_SHIFT 10 +#define PMIC_ACCDET_DA_STABLE_SHIFT 0 +#define PMIC_ACCDET_EINT0_EN_STABLE_SHIFT 1 +#define PMIC_ACCDET_EINT0_CMPEN_STABLE_SHIFT 2 +#define PMIC_ACCDET_EINT1_EN_STABLE_SHIFT 6 +#define PMIC_ACCDET_EINT1_CMPEN_STABLE_SHIFT 7 +#define PMIC_ACCDET_EINT_CTURBO_SEL_SHIFT 7 +#define PMIC_ACCDET_EINT0_CTURBO_SW_SHIFT 7 +#define PMIC_RG_EINTCOMPVTH_SHIFT 4 +#define PMIC_RG_EINT0HIRENB_SHIFT 12 +#define PMIC_RG_EINT0NOHYS_SHIFT 10 +#define PMIC_ACCDET_SW_EN_SHIFT 0 +#define PMIC_ACCDET_EINT0_MEM_IN_SHIFT 6 +#define PMIC_ACCDET_MEM_IN_SHIFT 6 +#define PMIC_ACCDET_EINT_DEBOUNCE0_SHIFT 0 +#define PMIC_ACCDET_EINT_DEBOUNCE1_SHIFT 4 +#define PMIC_ACCDET_EINT_DEBOUNCE2_SHIFT 8 +#define PMIC_ACCDET_EINT_DEBOUNCE3_SHIFT 12 +#define PMIC_RG_ACCDET2AUXSWEN_SHIFT 14 +#define PMIC_AUDACCDETAUXADCSWCTRL_SEL_SHIFT 9 +#define PMIC_AUDACCDETAUXADCSWCTRL_SW_SHIFT 10 +#define PMIC_RG_EINT0CTURBO_SHIFT 5 +#define PMIC_RG_EINT1CTURBO_SHIFT 13 +#define PMIC_ACCDET_EINT_M_PLUG_IN_NUM_SHIFT 12 +#define PMIC_ACCDET_EINT_M_DETECT_EN_SHIFT 12 +#define PMIC_ACCDET_EINT0_SW_EN_SHIFT 2 +#define PMIC_ACCDET_EINT1_SW_EN_SHIFT 4 +#define PMIC_ACCDET_EINT_CMPMOUT_SEL_SHIFT 12 +#define PMIC_ACCDET_EINT_CMPMEN_SEL_SHIFT 6 +#define PMIC_RG_HPLOUTPUTSTBENH_VAUDP32_SHIFT 0 +#define PMIC_RG_HPROUTPUTSTBENH_VAUDP32_SHIFT 4 +#define PMIC_RG_EINT0EN_SHIFT 2 +#define PMIC_RG_EINT1EN_SHIFT 10 +#define PMIC_RG_NCP_PDDIS_EN_SHIFT 0 +#define PMIC_RG_ACCDETSPARE_SHIFT 0 +#define PMIC_RG_ACCDET_RST_SHIFT 1 +#define PMIC_RG_AUDMICBIAS1HVEN_SHIFT 12 +#define PMIC_RG_AUDMICBIAS1VREF_SHIFT 4 +#define PMIC_RG_ANALOGFDEN_SHIFT 12 +#define PMIC_RG_AUDMICBIAS1DCSW1PEN_SHIFT 8 +#define PMIC_RG_AUDMICBIAS1LOWPEN_SHIFT 2 +#define PMIC_ACCDET_SEQ_INIT_SHIFT 1 +#define PMIC_RG_EINTCOMPVTH_MASK 0xf +#define PMIC_ACCDET_EINT0_MEM_IN_MASK 0x3 +#define PMIC_ACCDET_EINT_DEBOUNCE0_MASK 0xf +#define PMIC_ACCDET_EINT_DEBOUNCE1_MASK 0xf +#define PMIC_ACCDET_EINT_DEBOUNCE2_MASK 0xf +#define PMIC_ACCDET_EINT_DEBOUNCE3_MASK 0xf +#define PMIC_ACCDET_EINT0_IRQ_SHIFT 2 +#define PMIC_ACCDET_EINT1_IRQ_SHIFT 3 + +/* AUDENC_ANA_CON16: */ +#define RG_AUD_MICBIAS1_LOWP_EN BIT(PMIC_RG_AUDMICBIAS1LOWPEN_SHIFT) + +/* AUDENC_ANA_CON18: */ +#define RG_ACCDET_MODE_ANA11_MODE1 (0x000f) +#define RG_ACCDET_MODE_ANA11_MODE2 (0x008f) +#define RG_ACCDET_MODE_ANA11_MODE6 (0x008f) + +/* AUXADC_ADC5: Auxadc CH5 read data */ +#define AUXADC_DATA_RDY_CH5 BIT(15) +#define AUXADC_DATA_PROCEED_CH5 BIT(15) +#define AUXADC_DATA_MASK (0x0fff) + +/* AUXADC_RQST0_SET: Auxadc CH5 request, relevant 0x07EC */ +#define AUXADC_RQST_CH5_SET BIT(5) +/* AUXADC_RQST0_CLR: Auxadc CH5 request, relevant 0x07EC */ +#define AUXADC_RQST_CH5_CLR BIT(5) + +#define ACCDET_CALI_MASK0 (0xff) +#define ACCDET_CALI_MASK1 (0xff << 8) +#define ACCDET_CALI_MASK2 (0xff) +#define ACCDET_CALI_MASK3 (0xff << 8) +#define ACCDET_CALI_MASK4 (0xff) + +#define ACCDET_EINT1_IRQ_CLR_B11 BIT(PMIC_ACCDET_EINT1_IRQ_CLR_SHIFT) +#define ACCDET_EINT0_IRQ_CLR_B10 BIT(PMIC_ACCDET_EINT0_IRQ_CLR_SHIFT) +#define ACCDET_EINT_IRQ_CLR_B10_11 (0x03 << \ + PMIC_ACCDET_EINT0_IRQ_CLR_SHIFT) +#define ACCDET_IRQ_CLR_B8 BIT(PMIC_ACCDET_IRQ_CLR_SHIFT) + +#define ACCDET_EINT1_IRQ_B3 BIT(PMIC_ACCDET_EINT1_IRQ_SHIFT) +#define ACCDET_EINT0_IRQ_B2 BIT(PMIC_ACCDET_EINT0_IRQ_SHIFT) +#define ACCDET_EINT_IRQ_B2_B3 (0x03 << PMIC_ACCDET_EINT0_IRQ_SHIFT) +#define ACCDET_IRQ_B0 BIT(PMIC_ACCDET_IRQ_SHIFT) + +/* ACCDET_CON25: RO, accdet FSM state,etc.*/ +#define ACCDET_STATE_MEM_IN_OFFSET (PMIC_ACCDET_MEM_IN_SHIFT) +#define ACCDET_STATE_AB_MASK (0x03) +#define ACCDET_STATE_AB_00 (0x00) +#define ACCDET_STATE_AB_01 (0x01) +#define ACCDET_STATE_AB_10 (0x02) +#define ACCDET_STATE_AB_11 (0x03) + +/* ACCDET_CON19 */ +#define ACCDET_EINT0_STABLE_VAL ((1 << PMIC_ACCDET_DA_STABLE_SHIFT) | \ + (1 << PMIC_ACCDET_EINT0_EN_STABLE_SHIFT) | \ + (1 << PMIC_ACCDET_EINT0_CMPEN_STABLE_SHIFT) | \ + (1 << PMIC_ACCDET_EINT0_CEN_STABLE_SHIFT)) + +#define ACCDET_EINT1_STABLE_VAL ((1 << PMIC_ACCDET_DA_STABLE_SHIFT) | \ + (1 << PMIC_ACCDET_EINT1_EN_STABLE_SHIFT) | \ + (1 << PMIC_ACCDET_EINT1_CMPEN_STABLE_SHIFT) | \ + (1 << PMIC_ACCDET_EINT1_CEN_STABLE_SHIFT)) + +/* The following are used for mt6359.c */ +/* MT6359_DCXO_CW12 */ +#define RG_XO_AUDIO_EN_M_SFT 13 + +/* LDO_VAUD18_CON0 */ +#define RG_LDO_VAUD18_EN_SFT 0 +#define RG_LDO_VAUD18_EN_MASK 0x1 +#define RG_LDO_VAUD18_EN_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CKPDN_CON0 */ +#define RG_VOW13M_CK_PDN_SFT 13 +#define RG_VOW13M_CK_PDN_MASK 0x1 +#define RG_VOW13M_CK_PDN_MASK_SFT (0x1 << 13) +#define RG_VOW32K_CK_PDN_SFT 12 +#define RG_VOW32K_CK_PDN_MASK 0x1 +#define RG_VOW32K_CK_PDN_MASK_SFT (0x1 << 12) +#define RG_AUD_INTRP_CK_PDN_SFT 8 +#define RG_AUD_INTRP_CK_PDN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_MASK_SFT (0x1 << 8) +#define RG_PAD_AUD_CLK_MISO_CK_PDN_SFT 7 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK 0x1 +#define RG_PAD_AUD_CLK_MISO_CK_PDN_MASK_SFT (0x1 << 7) +#define RG_AUDNCP_CK_PDN_SFT 6 +#define RG_AUDNCP_CK_PDN_MASK 0x1 +#define RG_AUDNCP_CK_PDN_MASK_SFT (0x1 << 6) +#define RG_ZCD13M_CK_PDN_SFT 5 +#define RG_ZCD13M_CK_PDN_MASK 0x1 +#define RG_ZCD13M_CK_PDN_MASK_SFT (0x1 << 5) +#define RG_AUDIF_CK_PDN_SFT 2 +#define RG_AUDIF_CK_PDN_MASK 0x1 +#define RG_AUDIF_CK_PDN_MASK_SFT (0x1 << 2) +#define RG_AUD_CK_PDN_SFT 1 +#define RG_AUD_CK_PDN_MASK 0x1 +#define RG_AUD_CK_PDN_MASK_SFT (0x1 << 1) +#define RG_ACCDET_CK_PDN_SFT 0 +#define RG_ACCDET_CK_PDN_MASK 0x1 +#define RG_ACCDET_CK_PDN_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CKPDN_CON0_SET */ +#define RG_AUD_TOP_CKPDN_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_SET_MASK_SFT (0x3fff << 0) + +/* AUD_TOP_CKPDN_CON0_CLR */ +#define RG_AUD_TOP_CKPDN_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK 0x3fff +#define RG_AUD_TOP_CKPDN_CON0_CLR_MASK_SFT (0x3fff << 0) + +/* AUD_TOP_CKSEL_CON0 */ +#define RG_AUDIF_CK_CKSEL_SFT 3 +#define RG_AUDIF_CK_CKSEL_MASK 0x1 +#define RG_AUDIF_CK_CKSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_CKSEL_SFT 2 +#define RG_AUD_CK_CKSEL_MASK 0x1 +#define RG_AUD_CK_CKSEL_MASK_SFT (0x1 << 2) + +/* AUD_TOP_CKSEL_CON0_SET */ +#define RG_AUD_TOP_CKSEL_CON0_SET_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_SET_MASK_SFT (0xf << 0) + +/* AUD_TOP_CKSEL_CON0_CLR */ +#define RG_AUD_TOP_CKSEL_CON0_CLR_SFT 0 +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_CKSEL_CON0_CLR_MASK_SFT (0xf << 0) + +/* AUD_TOP_CKTST_CON0 */ +#define RG_VOW13M_CK_TSTSEL_SFT 9 +#define RG_VOW13M_CK_TSTSEL_MASK 0x1 +#define RG_VOW13M_CK_TSTSEL_MASK_SFT (0x1 << 9) +#define RG_VOW13M_CK_TST_DIS_SFT 8 +#define RG_VOW13M_CK_TST_DIS_MASK 0x1 +#define RG_VOW13M_CK_TST_DIS_MASK_SFT (0x1 << 8) +#define RG_AUD26M_CK_TSTSEL_SFT 4 +#define RG_AUD26M_CK_TSTSEL_MASK 0x1 +#define RG_AUD26M_CK_TSTSEL_MASK_SFT (0x1 << 4) +#define RG_AUDIF_CK_TSTSEL_SFT 3 +#define RG_AUDIF_CK_TSTSEL_MASK 0x1 +#define RG_AUDIF_CK_TSTSEL_MASK_SFT (0x1 << 3) +#define RG_AUD_CK_TSTSEL_SFT 2 +#define RG_AUD_CK_TSTSEL_MASK 0x1 +#define RG_AUD_CK_TSTSEL_MASK_SFT (0x1 << 2) +#define RG_AUD26M_CK_TST_DIS_SFT 0 +#define RG_AUD26M_CK_TST_DIS_MASK 0x1 +#define RG_AUD26M_CK_TST_DIS_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CLK_HWEN_CON0 */ +#define RG_AUD_INTRP_CK_PDN_HWEN_SFT 0 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK 0x1 +#define RG_AUD_INTRP_CK_PDN_HWEN_MASK_SFT (0x1 << 0) + +/* AUD_TOP_CLK_HWEN_CON0_SET */ +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_SFT 0 +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK 0xffff +#define RG_AUD_INTRP_CK_PND_HWEN_CON0_SET_MASK_SFT (0xffff << 0) + +/* AUD_TOP_CLK_HWEN_CON0_CLR */ +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_SFT 0 +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK 0xffff +#define RG_AUD_INTRP_CLK_PDN_HWEN_CON0_CLR_MASK_SFT (0xffff << 0) + +/* AUD_TOP_RST_CON0 */ +#define RG_AUDNCP_RST_SFT 3 +#define RG_AUDNCP_RST_MASK 0x1 +#define RG_AUDNCP_RST_MASK_SFT (0x1 << 3) +#define RG_ZCD_RST_SFT 2 +#define RG_ZCD_RST_MASK 0x1 +#define RG_ZCD_RST_MASK_SFT (0x1 << 2) +#define RG_ACCDET_RST_SFT 1 +#define RG_ACCDET_RST_MASK 0x1 +#define RG_ACCDET_RST_MASK_SFT (0x1 << 1) +#define RG_AUDIO_RST_SFT 0 +#define RG_AUDIO_RST_MASK 0x1 +#define RG_AUDIO_RST_MASK_SFT (0x1 << 0) + +/* AUD_TOP_RST_CON0_SET */ +#define RG_AUD_TOP_RST_CON0_SET_SFT 0 +#define RG_AUD_TOP_RST_CON0_SET_MASK 0xf +#define RG_AUD_TOP_RST_CON0_SET_MASK_SFT (0xf << 0) + +/* AUD_TOP_RST_CON0_CLR */ +#define RG_AUD_TOP_RST_CON0_CLR_SFT 0 +#define RG_AUD_TOP_RST_CON0_CLR_MASK 0xf +#define RG_AUD_TOP_RST_CON0_CLR_MASK_SFT (0xf << 0) + +/* AUD_TOP_RST_BANK_CON0 */ +#define BANK_AUDZCD_SWRST_SFT 2 +#define BANK_AUDZCD_SWRST_MASK 0x1 +#define BANK_AUDZCD_SWRST_MASK_SFT (0x1 << 2) +#define BANK_AUDIO_SWRST_SFT 1 +#define BANK_AUDIO_SWRST_MASK 0x1 +#define BANK_AUDIO_SWRST_MASK_SFT (0x1 << 1) +#define BANK_ACCDET_SWRST_SFT 0 +#define BANK_ACCDET_SWRST_MASK 0x1 +#define BANK_ACCDET_SWRST_MASK_SFT (0x1 << 0) + +/* AFE_UL_DL_CON0 */ +#define AFE_UL_LR_SWAP_SFT 15 +#define AFE_UL_LR_SWAP_MASK 0x1 +#define AFE_UL_LR_SWAP_MASK_SFT (0x1 << 15) +#define AFE_DL_LR_SWAP_SFT 14 +#define AFE_DL_LR_SWAP_MASK 0x1 +#define AFE_DL_LR_SWAP_MASK_SFT (0x1 << 14) +#define AFE_ON_SFT 0 +#define AFE_ON_MASK 0x1 +#define AFE_ON_MASK_SFT (0x1 << 0) + +/* AFE_DL_SRC2_CON0_L */ +#define DL_2_SRC_ON_TMP_CTL_PRE_SFT 0 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK 0x1 +#define DL_2_SRC_ON_TMP_CTL_PRE_MASK_SFT (0x1 << 0) + +/* AFE_UL_SRC_CON0_H */ +#define C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11) +#define C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8) +#define C_TWO_DIGITAL_MIC_CTL_SFT 7 +#define C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7) + +/* AFE_UL_SRC_CON0_L */ +#define DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define DIGMIC_4P33M_SEL_CTL_SFT 6 +#define DIGMIC_4P33M_SEL_CTL_MASK 0x1 +#define DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6) +#define DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define UL_LOOP_BACK_MODE_CTL_SFT 2 +#define UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define UL_SDM_3_LEVEL_CTL_SFT 1 +#define UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define UL_SRC_ON_TMP_CTL_SFT 0 +#define UL_SRC_ON_TMP_CTL_MASK 0x1 +#define UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* AFE_ADDA6_L_SRC_CON0_H */ +#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_SFT 11 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK 0x7 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH1_CTL_MASK_SFT (0x7 << 11) +#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_SFT 8 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK 0x7 +#define ADDA6_C_DIGMIC_PHASE_SEL_CH2_CTL_MASK_SFT (0x7 << 8) +#define ADDA6_C_TWO_DIGITAL_MIC_CTL_SFT 7 +#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK 0x1 +#define ADDA6_C_TWO_DIGITAL_MIC_CTL_MASK_SFT (0x1 << 7) + +/* AFE_ADDA6_UL_SRC_CON0_L */ +#define ADDA6_DMIC_LOW_POWER_MODE_CTL_SFT 14 +#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK 0x3 +#define ADDA6_DMIC_LOW_POWER_MODE_CTL_MASK_SFT (0x3 << 14) +#define ADDA6_DIGMIC_4P33M_SEL_CTL_SFT 6 +#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK 0x1 +#define ADDA6_DIGMIC_4P33M_SEL_CTL_MASK_SFT (0x1 << 6) +#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_SFT 5 +#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK 0x1 +#define ADDA6_DIGMIC_3P25M_1P625M_SEL_CTL_MASK_SFT (0x1 << 5) +#define ADDA6_UL_LOOP_BACK_MODE_CTL_SFT 2 +#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK 0x1 +#define ADDA6_UL_LOOP_BACK_MODE_CTL_MASK_SFT (0x1 << 2) +#define ADDA6_UL_SDM_3_LEVEL_CTL_SFT 1 +#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK 0x1 +#define ADDA6_UL_SDM_3_LEVEL_CTL_MASK_SFT (0x1 << 1) +#define ADDA6_UL_SRC_ON_TMP_CTL_SFT 0 +#define ADDA6_UL_SRC_ON_TMP_CTL_MASK 0x1 +#define ADDA6_UL_SRC_ON_TMP_CTL_MASK_SFT (0x1 << 0) + +/* AFE_TOP_CON0 */ +#define ADDA6_MTKAIF_SINE_ON_SFT 4 +#define ADDA6_MTKAIF_SINE_ON_MASK 0x1 +#define ADDA6_MTKAIF_SINE_ON_MASK_SFT (0x1 << 4) +#define ADDA6_UL_SINE_ON_SFT 3 +#define ADDA6_UL_SINE_ON_MASK 0x1 +#define ADDA6_UL_SINE_ON_MASK_SFT (0x1 << 3) +#define MTKAIF_SINE_ON_SFT 2 +#define MTKAIF_SINE_ON_MASK 0x1 +#define MTKAIF_SINE_ON_MASK_SFT (0x1 << 2) +#define UL_SINE_ON_SFT 1 +#define UL_SINE_ON_MASK 0x1 +#define UL_SINE_ON_MASK_SFT (0x1 << 1) +#define DL_SINE_ON_SFT 0 +#define DL_SINE_ON_MASK 0x1 +#define DL_SINE_ON_MASK_SFT (0x1 << 0) + +/* AUDIO_TOP_CON0 */ +#define PDN_AFE_CTL_SFT 7 +#define PDN_AFE_CTL_MASK 0x1 +#define PDN_AFE_CTL_MASK_SFT (0x1 << 7) +#define PDN_DAC_CTL_SFT 6 +#define PDN_DAC_CTL_MASK 0x1 +#define PDN_DAC_CTL_MASK_SFT (0x1 << 6) +#define PDN_ADC_CTL_SFT 5 +#define PDN_ADC_CTL_MASK 0x1 +#define PDN_ADC_CTL_MASK_SFT (0x1 << 5) +#define PDN_ADDA6_ADC_CTL_SFT 4 +#define PDN_ADDA6_ADC_CTL_MASK 0x1 +#define PDN_ADDA6_ADC_CTL_MASK_SFT (0x1 << 4) +#define PDN_I2S_DL_CTL_SFT 3 +#define PDN_I2S_DL_CTL_MASK 0x1 +#define PDN_I2S_DL_CTL_MASK_SFT (0x1 << 3) +#define PWR_CLK_DIS_CTL_SFT 2 +#define PWR_CLK_DIS_CTL_MASK 0x1 +#define PWR_CLK_DIS_CTL_MASK_SFT (0x1 << 2) +#define PDN_AFE_TESTMODEL_CTL_SFT 1 +#define PDN_AFE_TESTMODEL_CTL_MASK 0x1 +#define PDN_AFE_TESTMODEL_CTL_MASK_SFT (0x1 << 1) +#define PDN_RESERVED_SFT 0 +#define PDN_RESERVED_MASK 0x1 +#define PDN_RESERVED_MASK_SFT (0x1 << 0) + +/* AFE_MON_DEBUG0 */ +#define AUDIO_SYS_TOP_MON_SWAP_SFT 14 +#define AUDIO_SYS_TOP_MON_SWAP_MASK 0x3 +#define AUDIO_SYS_TOP_MON_SWAP_MASK_SFT (0x3 << 14) +#define AUDIO_SYS_TOP_MON_SEL_SFT 8 +#define AUDIO_SYS_TOP_MON_SEL_MASK 0x1f +#define AUDIO_SYS_TOP_MON_SEL_MASK_SFT (0x1f << 8) +#define AFE_MON_SEL_SFT 0 +#define AFE_MON_SEL_MASK 0xff +#define AFE_MON_SEL_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON0 */ +#define CCI_AUD_ANACK_SEL_SFT 15 +#define CCI_AUD_ANACK_SEL_MASK 0x1 +#define CCI_AUD_ANACK_SEL_MASK_SFT (0x1 << 15) +#define CCI_AUDIO_FIFO_WPTR_SFT 12 +#define CCI_AUDIO_FIFO_WPTR_MASK 0x7 +#define CCI_AUDIO_FIFO_WPTR_MASK_SFT (0x7 << 12) +#define CCI_SCRAMBLER_CG_EN_SFT 11 +#define CCI_SCRAMBLER_CG_EN_MASK 0x1 +#define CCI_SCRAMBLER_CG_EN_MASK_SFT (0x1 << 11) +#define CCI_LCH_INV_SFT 10 +#define CCI_LCH_INV_MASK 0x1 +#define CCI_LCH_INV_MASK_SFT (0x1 << 10) +#define CCI_RAND_EN_SFT 9 +#define CCI_RAND_EN_MASK 0x1 +#define CCI_RAND_EN_MASK_SFT (0x1 << 9) +#define CCI_SPLT_SCRMB_CLK_ON_SFT 8 +#define CCI_SPLT_SCRMB_CLK_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_CLK_ON_MASK_SFT (0x1 << 8) +#define CCI_SPLT_SCRMB_ON_SFT 7 +#define CCI_SPLT_SCRMB_ON_MASK 0x1 +#define CCI_SPLT_SCRMB_ON_MASK_SFT (0x1 << 7) +#define CCI_AUD_IDAC_TEST_EN_SFT 6 +#define CCI_AUD_IDAC_TEST_EN_MASK 0x1 +#define CCI_AUD_IDAC_TEST_EN_MASK_SFT (0x1 << 6) +#define CCI_ZERO_PAD_DISABLE_SFT 5 +#define CCI_ZERO_PAD_DISABLE_MASK 0x1 +#define CCI_ZERO_PAD_DISABLE_MASK_SFT (0x1 << 5) +#define CCI_AUD_SPLIT_TEST_EN_SFT 4 +#define CCI_AUD_SPLIT_TEST_EN_MASK 0x1 +#define CCI_AUD_SPLIT_TEST_EN_MASK_SFT (0x1 << 4) +#define CCI_AUD_SDM_MUTEL_SFT 3 +#define CCI_AUD_SDM_MUTEL_MASK 0x1 +#define CCI_AUD_SDM_MUTEL_MASK_SFT (0x1 << 3) +#define CCI_AUD_SDM_MUTER_SFT 2 +#define CCI_AUD_SDM_MUTER_MASK 0x1 +#define CCI_AUD_SDM_MUTER_MASK_SFT (0x1 << 2) +#define CCI_AUD_SDM_7BIT_SEL_SFT 1 +#define CCI_AUD_SDM_7BIT_SEL_MASK 0x1 +#define CCI_AUD_SDM_7BIT_SEL_MASK_SFT (0x1 << 1) +#define CCI_SCRAMBLER_EN_SFT 0 +#define CCI_SCRAMBLER_EN_MASK 0x1 +#define CCI_SCRAMBLER_EN_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON1 */ +#define AUD_SDM_TEST_L_SFT 8 +#define AUD_SDM_TEST_L_MASK 0xff +#define AUD_SDM_TEST_L_MASK_SFT (0xff << 8) +#define AUD_SDM_TEST_R_SFT 0 +#define AUD_SDM_TEST_R_MASK 0xff +#define AUD_SDM_TEST_R_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON2 */ +#define CCI_AUD_DAC_ANA_MUTE_SFT 7 +#define CCI_AUD_DAC_ANA_MUTE_MASK 0x1 +#define CCI_AUD_DAC_ANA_MUTE_MASK_SFT (0x1 << 7) +#define CCI_AUD_DAC_ANA_RSTB_SEL_SFT 6 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK 0x1 +#define CCI_AUD_DAC_ANA_RSTB_SEL_MASK_SFT (0x1 << 6) +#define CCI_AUDIO_FIFO_CLKIN_INV_SFT 4 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK 0x1 +#define CCI_AUDIO_FIFO_CLKIN_INV_MASK_SFT (0x1 << 4) +#define CCI_AUDIO_FIFO_ENABLE_SFT 3 +#define CCI_AUDIO_FIFO_ENABLE_MASK 0x1 +#define CCI_AUDIO_FIFO_ENABLE_MASK_SFT (0x1 << 3) +#define CCI_ACD_MODE_SFT 2 +#define CCI_ACD_MODE_MASK 0x1 +#define CCI_ACD_MODE_MASK_SFT (0x1 << 2) +#define CCI_AFIFO_CLK_PWDB_SFT 1 +#define CCI_AFIFO_CLK_PWDB_MASK 0x1 +#define CCI_AFIFO_CLK_PWDB_MASK_SFT (0x1 << 1) +#define CCI_ACD_FUNC_RSTB_SFT 0 +#define CCI_ACD_FUNC_RSTB_MASK 0x1 +#define CCI_ACD_FUNC_RSTB_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON3 */ +#define SDM_ANA13M_TESTCK_SEL_SFT 15 +#define SDM_ANA13M_TESTCK_SEL_MASK 0x1 +#define SDM_ANA13M_TESTCK_SEL_MASK_SFT (0x1 << 15) +#define SDM_ANA13M_TESTCK_SRC_SEL_SFT 12 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_ANA13M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 12) +#define SDM_TESTCK_SRC_SEL_SFT 8 +#define SDM_TESTCK_SRC_SEL_MASK 0x7 +#define SDM_TESTCK_SRC_SEL_MASK_SFT (0x7 << 8) +#define DIGMIC_TESTCK_SRC_SEL_SFT 4 +#define DIGMIC_TESTCK_SRC_SEL_MASK 0x7 +#define DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 4) +#define DIGMIC_TESTCK_SEL_SFT 0 +#define DIGMIC_TESTCK_SEL_MASK 0x1 +#define DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON4 */ +#define UL_FIFO_WCLK_INV_SFT 8 +#define UL_FIFO_WCLK_INV_MASK 0x1 +#define UL_FIFO_WCLK_INV_MASK_SFT (0x1 << 8) +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6) +#define UL_FIFO_WDATA_TESTEN_SFT 5 +#define UL_FIFO_WDATA_TESTEN_MASK 0x1 +#define UL_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5) +#define UL_FIFO_WDATA_TESTSRC_SEL_SFT 4 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4) +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1 +#define UL_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3) +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7 +#define UL_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0) + +/* AFUNC_AUD_CON5 */ +#define R_AUD_DAC_POS_LARGE_MONO_SFT 8 +#define R_AUD_DAC_POS_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_POS_LARGE_MONO_MASK_SFT (0xff << 8) +#define R_AUD_DAC_NEG_LARGE_MONO_SFT 0 +#define R_AUD_DAC_NEG_LARGE_MONO_MASK 0xff +#define R_AUD_DAC_NEG_LARGE_MONO_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON6 */ +#define R_AUD_DAC_POS_SMALL_MONO_SFT 12 +#define R_AUD_DAC_POS_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_POS_SMALL_MONO_MASK_SFT (0xf << 12) +#define R_AUD_DAC_NEG_SMALL_MONO_SFT 8 +#define R_AUD_DAC_NEG_SMALL_MONO_MASK 0xf +#define R_AUD_DAC_NEG_SMALL_MONO_MASK_SFT (0xf << 8) +#define R_AUD_DAC_POS_TINY_MONO_SFT 6 +#define R_AUD_DAC_POS_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_POS_TINY_MONO_MASK_SFT (0x3 << 6) +#define R_AUD_DAC_NEG_TINY_MONO_SFT 4 +#define R_AUD_DAC_NEG_TINY_MONO_MASK 0x3 +#define R_AUD_DAC_NEG_TINY_MONO_MASK_SFT (0x3 << 4) +#define R_AUD_DAC_MONO_SEL_SFT 3 +#define R_AUD_DAC_MONO_SEL_MASK 0x1 +#define R_AUD_DAC_MONO_SEL_MASK_SFT (0x1 << 3) +#define R_AUD_DAC_3TH_SEL_SFT 1 +#define R_AUD_DAC_3TH_SEL_MASK 0x1 +#define R_AUD_DAC_3TH_SEL_MASK_SFT (0x1 << 1) +#define R_AUD_DAC_SW_RSTB_SFT 0 +#define R_AUD_DAC_SW_RSTB_MASK 0x1 +#define R_AUD_DAC_SW_RSTB_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON7 */ +#define UL2_DIGMIC_TESTCK_SRC_SEL_SFT 10 +#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK 0x7 +#define UL2_DIGMIC_TESTCK_SRC_SEL_MASK_SFT (0x7 << 10) +#define UL2_DIGMIC_TESTCK_SEL_SFT 9 +#define UL2_DIGMIC_TESTCK_SEL_MASK 0x1 +#define UL2_DIGMIC_TESTCK_SEL_MASK_SFT (0x1 << 9) +#define UL2_FIFO_WCLK_INV_SFT 8 +#define UL2_FIFO_WCLK_INV_MASK 0x1 +#define UL2_FIFO_WCLK_INV_MASK_SFT (0x1 << 8) +#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_SFT 6 +#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL2_FIFO_DIGMIC_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 6) +#define UL2_FIFO_WDATA_TESTEN_SFT 5 +#define UL2_FIFO_WDATA_TESTEN_MASK 0x1 +#define UL2_FIFO_WDATA_TESTEN_MASK_SFT (0x1 << 5) +#define UL2_FIFO_WDATA_TESTSRC_SEL_SFT 4 +#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK 0x1 +#define UL2_FIFO_WDATA_TESTSRC_SEL_MASK_SFT (0x1 << 4) +#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_SFT 3 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK 0x1 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SEL_MASK_SFT (0x1 << 3) +#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_SFT 0 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK 0x7 +#define UL2_FIFO_WCLK_6P5M_TESTCK_SRC_SEL_MASK_SFT (0x7 << 0) + +/* AFUNC_AUD_CON8 */ +#define SPLITTER2_DITHER_EN_SFT 9 +#define SPLITTER2_DITHER_EN_MASK 0x1 +#define SPLITTER2_DITHER_EN_MASK_SFT (0x1 << 9) +#define SPLITTER1_DITHER_EN_SFT 8 +#define SPLITTER1_DITHER_EN_MASK 0x1 +#define SPLITTER1_DITHER_EN_MASK_SFT (0x1 << 8) +#define SPLITTER2_DITHER_GAIN_SFT 4 +#define SPLITTER2_DITHER_GAIN_MASK 0xf +#define SPLITTER2_DITHER_GAIN_MASK_SFT (0xf << 4) +#define SPLITTER1_DITHER_GAIN_SFT 0 +#define SPLITTER1_DITHER_GAIN_MASK 0xf +#define SPLITTER1_DITHER_GAIN_MASK_SFT (0xf << 0) + +/* AFUNC_AUD_CON9 */ +#define CCI_AUD_ANACK_SEL_2ND_SFT 15 +#define CCI_AUD_ANACK_SEL_2ND_MASK 0x1 +#define CCI_AUD_ANACK_SEL_2ND_MASK_SFT (0x1 << 15) +#define CCI_AUDIO_FIFO_WPTR_2ND_SFT 12 +#define CCI_AUDIO_FIFO_WPTR_2ND_MASK 0x7 +#define CCI_AUDIO_FIFO_WPTR_2ND_MASK_SFT (0x7 << 12) +#define CCI_SCRAMBLER_CG_EN_2ND_SFT 11 +#define CCI_SCRAMBLER_CG_EN_2ND_MASK 0x1 +#define CCI_SCRAMBLER_CG_EN_2ND_MASK_SFT (0x1 << 11) +#define CCI_LCH_INV_2ND_SFT 10 +#define CCI_LCH_INV_2ND_MASK 0x1 +#define CCI_LCH_INV_2ND_MASK_SFT (0x1 << 10) +#define CCI_RAND_EN_2ND_SFT 9 +#define CCI_RAND_EN_2ND_MASK 0x1 +#define CCI_RAND_EN_2ND_MASK_SFT (0x1 << 9) +#define CCI_SPLT_SCRMB_CLK_ON_2ND_SFT 8 +#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK 0x1 +#define CCI_SPLT_SCRMB_CLK_ON_2ND_MASK_SFT (0x1 << 8) +#define CCI_SPLT_SCRMB_ON_2ND_SFT 7 +#define CCI_SPLT_SCRMB_ON_2ND_MASK 0x1 +#define CCI_SPLT_SCRMB_ON_2ND_MASK_SFT (0x1 << 7) +#define CCI_AUD_IDAC_TEST_EN_2ND_SFT 6 +#define CCI_AUD_IDAC_TEST_EN_2ND_MASK 0x1 +#define CCI_AUD_IDAC_TEST_EN_2ND_MASK_SFT (0x1 << 6) +#define CCI_ZERO_PAD_DISABLE_2ND_SFT 5 +#define CCI_ZERO_PAD_DISABLE_2ND_MASK 0x1 +#define CCI_ZERO_PAD_DISABLE_2ND_MASK_SFT (0x1 << 5) +#define CCI_AUD_SPLIT_TEST_EN_2ND_SFT 4 +#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK 0x1 +#define CCI_AUD_SPLIT_TEST_EN_2ND_MASK_SFT (0x1 << 4) +#define CCI_AUD_SDM_MUTEL_2ND_SFT 3 +#define CCI_AUD_SDM_MUTEL_2ND_MASK 0x1 +#define CCI_AUD_SDM_MUTEL_2ND_MASK_SFT (0x1 << 3) +#define CCI_AUD_SDM_MUTER_2ND_SFT 2 +#define CCI_AUD_SDM_MUTER_2ND_MASK 0x1 +#define CCI_AUD_SDM_MUTER_2ND_MASK_SFT (0x1 << 2) +#define CCI_AUD_SDM_7BIT_SEL_2ND_SFT 1 +#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK 0x1 +#define CCI_AUD_SDM_7BIT_SEL_2ND_MASK_SFT (0x1 << 1) +#define CCI_SCRAMBLER_EN_2ND_SFT 0 +#define CCI_SCRAMBLER_EN_2ND_MASK 0x1 +#define CCI_SCRAMBLER_EN_2ND_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON10 */ +#define AUD_SDM_TEST_L_2ND_SFT 8 +#define AUD_SDM_TEST_L_2ND_MASK 0xff +#define AUD_SDM_TEST_L_2ND_MASK_SFT (0xff << 8) +#define AUD_SDM_TEST_R_2ND_SFT 0 +#define AUD_SDM_TEST_R_2ND_MASK 0xff +#define AUD_SDM_TEST_R_2ND_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_CON11 */ +#define CCI_AUD_DAC_ANA_MUTE_2ND_SFT 7 +#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK 0x1 +#define CCI_AUD_DAC_ANA_MUTE_2ND_MASK_SFT (0x1 << 7) +#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_SFT 6 +#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK 0x1 +#define CCI_AUD_DAC_ANA_RSTB_SEL_2ND_MASK_SFT (0x1 << 6) +#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_SFT 4 +#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK 0x1 +#define CCI_AUDIO_FIFO_CLKIN_INV_2ND_MASK_SFT (0x1 << 4) +#define CCI_AUDIO_FIFO_ENABLE_2ND_SFT 3 +#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK 0x1 +#define CCI_AUDIO_FIFO_ENABLE_2ND_MASK_SFT (0x1 << 3) +#define CCI_ACD_MODE_2ND_SFT 2 +#define CCI_ACD_MODE_2ND_MASK 0x1 +#define CCI_ACD_MODE_2ND_MASK_SFT (0x1 << 2) +#define CCI_AFIFO_CLK_PWDB_2ND_SFT 1 +#define CCI_AFIFO_CLK_PWDB_2ND_MASK 0x1 +#define CCI_AFIFO_CLK_PWDB_2ND_MASK_SFT (0x1 << 1) +#define CCI_ACD_FUNC_RSTB_2ND_SFT 0 +#define CCI_ACD_FUNC_RSTB_2ND_MASK 0x1 +#define CCI_ACD_FUNC_RSTB_2ND_MASK_SFT (0x1 << 0) + +/* AFUNC_AUD_CON12 */ +#define SPLITTER2_DITHER_EN_2ND_SFT 9 +#define SPLITTER2_DITHER_EN_2ND_MASK 0x1 +#define SPLITTER2_DITHER_EN_2ND_MASK_SFT (0x1 << 9) +#define SPLITTER1_DITHER_EN_2ND_SFT 8 +#define SPLITTER1_DITHER_EN_2ND_MASK 0x1 +#define SPLITTER1_DITHER_EN_2ND_MASK_SFT (0x1 << 8) +#define SPLITTER2_DITHER_GAIN_2ND_SFT 4 +#define SPLITTER2_DITHER_GAIN_2ND_MASK 0xf +#define SPLITTER2_DITHER_GAIN_2ND_MASK_SFT (0xf << 4) +#define SPLITTER1_DITHER_GAIN_2ND_SFT 0 +#define SPLITTER1_DITHER_GAIN_2ND_MASK 0xf +#define SPLITTER1_DITHER_GAIN_2ND_MASK_SFT (0xf << 0) + +/* AFUNC_AUD_MON0 */ +#define AUD_SCR_OUT_L_SFT 8 +#define AUD_SCR_OUT_L_MASK 0xff +#define AUD_SCR_OUT_L_MASK_SFT (0xff << 8) +#define AUD_SCR_OUT_R_SFT 0 +#define AUD_SCR_OUT_R_MASK 0xff +#define AUD_SCR_OUT_R_MASK_SFT (0xff << 0) + +/* AFUNC_AUD_MON1 */ +#define AUD_SCR_OUT_L_2ND_SFT 8 +#define AUD_SCR_OUT_L_2ND_MASK 0xff +#define AUD_SCR_OUT_L_2ND_MASK_SFT (0xff << 8) +#define AUD_SCR_OUT_R_2ND_SFT 0 +#define AUD_SCR_OUT_R_2ND_MASK 0xff +#define AUD_SCR_OUT_R_2ND_MASK_SFT (0xff << 0) + +/* AUDRC_TUNE_MON0 */ +#define ASYNC_TEST_OUT_BCK_SFT 15 +#define ASYNC_TEST_OUT_BCK_MASK 0x1 +#define ASYNC_TEST_OUT_BCK_MASK_SFT (0x1 << 15) +#define RGS_AUDRCTUNE1READ_SFT 8 +#define RGS_AUDRCTUNE1READ_MASK 0x1f +#define RGS_AUDRCTUNE1READ_MASK_SFT (0x1f << 8) +#define RGS_AUDRCTUNE0READ_SFT 0 +#define RGS_AUDRCTUNE0READ_MASK 0x1f +#define RGS_AUDRCTUNE0READ_MASK_SFT (0x1f << 0) + +/* AFE_ADDA_MTKAIF_FIFO_CFG0 */ +#define AFE_RESERVED_SFT 1 +#define AFE_RESERVED_MASK 0x7fff +#define AFE_RESERVED_MASK_SFT (0x7fff << 1) +#define RG_MTKAIF_RXIF_FIFO_INTEN_SFT 0 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK 0x1 +#define RG_MTKAIF_RXIF_FIFO_INTEN_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_FIFO_LOG_MON1 */ +#define MTKAIF_RXIF_WR_FULL_STATUS_SFT 1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK 0x1 +#define MTKAIF_RXIF_WR_FULL_STATUS_MASK_SFT (0x1 << 1) +#define MTKAIF_RXIF_RD_EMPTY_STATUS_SFT 0 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK 0x1 +#define MTKAIF_RXIF_RD_EMPTY_STATUS_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_MON0 */ +#define MTKAIFTX_V3_SYNC_OUT_SFT 15 +#define MTKAIFTX_V3_SYNC_OUT_MASK 0x1 +#define MTKAIFTX_V3_SYNC_OUT_MASK_SFT (0x1 << 15) +#define MTKAIFTX_V3_SDATA_OUT3_SFT 14 +#define MTKAIFTX_V3_SDATA_OUT3_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT3_MASK_SFT (0x1 << 14) +#define MTKAIFTX_V3_SDATA_OUT2_SFT 13 +#define MTKAIFTX_V3_SDATA_OUT2_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT2_MASK_SFT (0x1 << 13) +#define MTKAIFTX_V3_SDATA_OUT1_SFT 12 +#define MTKAIFTX_V3_SDATA_OUT1_MASK 0x1 +#define MTKAIFTX_V3_SDATA_OUT1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_FIFO_STATUS_SFT 0 +#define MTKAIF_RXIF_FIFO_STATUS_MASK 0xfff +#define MTKAIF_RXIF_FIFO_STATUS_MASK_SFT (0xfff << 0) + +/* AFE_ADDA_MTKAIF_MON1 */ +#define MTKAIFRX_V3_SYNC_IN_SFT 15 +#define MTKAIFRX_V3_SYNC_IN_MASK 0x1 +#define MTKAIFRX_V3_SYNC_IN_MASK_SFT (0x1 << 15) +#define MTKAIFRX_V3_SDATA_IN3_SFT 14 +#define MTKAIFRX_V3_SDATA_IN3_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN3_MASK_SFT (0x1 << 14) +#define MTKAIFRX_V3_SDATA_IN2_SFT 13 +#define MTKAIFRX_V3_SDATA_IN2_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN2_MASK_SFT (0x1 << 13) +#define MTKAIFRX_V3_SDATA_IN1_SFT 12 +#define MTKAIFRX_V3_SDATA_IN1_MASK 0x1 +#define MTKAIFRX_V3_SDATA_IN1_MASK_SFT (0x1 << 12) +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_SFT 11 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK 0x1 +#define MTKAIF_RXIF_SEARCH_FAIL_FLAG_MASK_SFT (0x1 << 11) +#define MTKAIF_RXIF_INVALID_FLAG_SFT 8 +#define MTKAIF_RXIF_INVALID_FLAG_MASK 0x1 +#define MTKAIF_RXIF_INVALID_FLAG_MASK_SFT (0x1 << 8) +#define MTKAIF_RXIF_INVALID_CYCLE_SFT 0 +#define MTKAIF_RXIF_INVALID_CYCLE_MASK 0xff +#define MTKAIF_RXIF_INVALID_CYCLE_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_MON2 */ +#define MTKAIF_TXIF_IN_CH2_SFT 8 +#define MTKAIF_TXIF_IN_CH2_MASK 0xff +#define MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_TXIF_IN_CH1_SFT 0 +#define MTKAIF_TXIF_IN_CH1_MASK 0xff +#define MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0) + +/* AFE_ADDA6_MTKAIF_MON3 */ +#define ADDA6_MTKAIF_TXIF_IN_CH2_SFT 8 +#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK 0xff +#define ADDA6_MTKAIF_TXIF_IN_CH2_MASK_SFT (0xff << 8) +#define ADDA6_MTKAIF_TXIF_IN_CH1_SFT 0 +#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK 0xff +#define ADDA6_MTKAIF_TXIF_IN_CH1_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_MON4 */ +#define MTKAIF_RXIF_OUT_CH2_SFT 8 +#define MTKAIF_RXIF_OUT_CH2_MASK 0xff +#define MTKAIF_RXIF_OUT_CH2_MASK_SFT (0xff << 8) +#define MTKAIF_RXIF_OUT_CH1_SFT 0 +#define MTKAIF_RXIF_OUT_CH1_MASK 0xff +#define MTKAIF_RXIF_OUT_CH1_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_MON5 */ +#define MTKAIF_RXIF_OUT_CH3_SFT 0 +#define MTKAIF_RXIF_OUT_CH3_MASK 0xff +#define MTKAIF_RXIF_OUT_CH3_MASK_SFT (0xff << 0) + +/* AFE_ADDA_MTKAIF_CFG0 */ +#define RG_MTKAIF_RXIF_CLKINV_SFT 15 +#define RG_MTKAIF_RXIF_CLKINV_MASK 0x1 +#define RG_MTKAIF_RXIF_CLKINV_MASK_SFT (0x1 << 15) +#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_SFT 9 +#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define RG_ADDA6_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 9) +#define RG_MTKAIF_RXIF_PROTOCOL2_SFT 8 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_PROTOCOL2_MASK_SFT (0x1 << 8) +#define RG_MTKAIF_BYPASS_SRC_MODE_SFT 6 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK 0x3 +#define RG_MTKAIF_BYPASS_SRC_MODE_MASK_SFT (0x3 << 6) +#define RG_MTKAIF_BYPASS_SRC_TEST_SFT 5 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK 0x1 +#define RG_MTKAIF_BYPASS_SRC_TEST_MASK_SFT (0x1 << 5) +#define RG_MTKAIF_TXIF_PROTOCOL2_SFT 4 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_TXIF_PROTOCOL2_MASK_SFT (0x1 << 4) +#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_SFT 3 +#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1 +#define RG_ADDA6_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 3) +#define RG_MTKAIF_PMIC_TXIF_8TO5_SFT 2 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK 0x1 +#define RG_MTKAIF_PMIC_TXIF_8TO5_MASK_SFT (0x1 << 2) +#define RG_MTKAIF_LOOPBACK_TEST2_SFT 1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST2_MASK_SFT (0x1 << 1) +#define RG_MTKAIF_LOOPBACK_TEST1_SFT 0 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK 0x1 +#define RG_MTKAIF_LOOPBACK_TEST1_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG0 */ +#define RG_MTKAIF_RXIF_VOICE_MODE_SFT 12 +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_DATA_BIT_SFT 8 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK 0x7 +#define RG_MTKAIF_RXIF_DATA_BIT_MASK_SFT (0x7 << 8) +#define RG_MTKAIF_RXIF_FIFO_RSP_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_MASK_SFT (0x1 << 3) +#define RG_MTKAIF_RXIF_DATA_MODE_SFT 0 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK 0x1 +#define RG_MTKAIF_RXIF_DATA_MODE_MASK_SFT (0x1 << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG1 */ +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_SFT 12 +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_SEARCH_TABLE_MASK_SFT (0xf << 12) +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_SFT 8 +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_INVALID_SYNC_CHECK_ROUND_MASK_SFT (0xf << 8) +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_SFT 4 +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK 0xf +#define RG_MTKAIF_RXIF_SYNC_CHECK_ROUND_MASK_SFT (0xf << 4) +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_SFT 0 +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK 0xf +#define RG_MTKAIF_RXIF_VOICE_MODE_PROTOCOL2_MASK_SFT (0xf << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG2 */ +#define RG_MTKAIF_RXIF_P2_INPUT_SEL_SFT 15 +#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK 0x1 +#define RG_MTKAIF_RXIF_P2_INPUT_SEL_MASK_SFT (0x1 << 15) +#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_SFT 14 +#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK 0x1 +#define RG_MTKAIF_RXIF_SYNC_WORD2_DISABLE_MASK_SFT (0x1 << 14) +#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_SFT 13 +#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK 0x1 +#define RG_MTKAIF_RXIF_SYNC_WORD1_DISABLE_MASK_SFT (0x1 << 13) +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_SFT 12 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK 0x1 +#define RG_MTKAIF_RXIF_CLEAR_SYNC_FAIL_MASK_SFT (0x1 << 12) +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_SFT 0 +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK 0xfff +#define RG_MTKAIF_RXIF_SYNC_CNT_TABLE_MASK_SFT (0xfff << 0) + +/* AFE_ADDA_MTKAIF_RX_CFG3 */ +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_SFT 7 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK 0x1 +#define RG_MTKAIF_RXIF_LOOPBACK_USE_NLE_MASK_SFT (0x1 << 7) +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_SFT 4 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK 0x7 +#define RG_MTKAIF_RXIF_FIFO_RSP_PROTOCOL2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_SFT 3 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK 0x1 +#define RG_MTKAIF_RXIF_DETECT_ON_PROTOCOL2_MASK_SFT (0x1 << 3) + +/* AFE_ADDA_MTKAIF_SYNCWORD_CFG0 */ +#define RG_MTKAIF_RX_SYNC_WORD2_SFT 4 +#define RG_MTKAIF_RX_SYNC_WORD2_MASK 0x7 +#define RG_MTKAIF_RX_SYNC_WORD2_MASK_SFT (0x7 << 4) +#define RG_MTKAIF_RX_SYNC_WORD1_SFT 0 +#define RG_MTKAIF_RX_SYNC_WORD1_MASK 0x7 +#define RG_MTKAIF_RX_SYNC_WORD1_MASK_SFT (0x7 << 0) + +/* AFE_ADDA_MTKAIF_SYNCWORD_CFG1 */ +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_SFT 12 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK 0x7 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 12) +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_SFT 8 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK 0x7 +#define RG_ADDA6_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 8) +#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_SFT 4 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK 0x7 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD2_MASK_SFT (0x7 << 4) +#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_SFT 0 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK 0x7 +#define RG_ADDA_MTKAIF_TX_SYNC_WORD1_MASK_SFT (0x7 << 0) + +/* AFE_SGEN_CFG0 */ +#define SGEN_AMP_DIV_CH1_CTL_SFT 12 +#define SGEN_AMP_DIV_CH1_CTL_MASK 0xf +#define SGEN_AMP_DIV_CH1_CTL_MASK_SFT (0xf << 12) +#define SGEN_DAC_EN_CTL_SFT 7 +#define SGEN_DAC_EN_CTL_MASK 0x1 +#define SGEN_DAC_EN_CTL_MASK_SFT (0x1 << 7) +#define SGEN_MUTE_SW_CTL_SFT 6 +#define SGEN_MUTE_SW_CTL_MASK 0x1 +#define SGEN_MUTE_SW_CTL_MASK_SFT (0x1 << 6) +#define R_AUD_SDM_MUTE_L_SFT 5 +#define R_AUD_SDM_MUTE_L_MASK 0x1 +#define R_AUD_SDM_MUTE_L_MASK_SFT (0x1 << 5) +#define R_AUD_SDM_MUTE_R_SFT 4 +#define R_AUD_SDM_MUTE_R_MASK 0x1 +#define R_AUD_SDM_MUTE_R_MASK_SFT (0x1 << 4) +#define R_AUD_SDM_MUTE_L_2ND_SFT 3 +#define R_AUD_SDM_MUTE_L_2ND_MASK 0x1 +#define R_AUD_SDM_MUTE_L_2ND_MASK_SFT (0x1 << 3) +#define R_AUD_SDM_MUTE_R_2ND_SFT 2 +#define R_AUD_SDM_MUTE_R_2ND_MASK 0x1 +#define R_AUD_SDM_MUTE_R_2ND_MASK_SFT (0x1 << 2) + +/* AFE_SGEN_CFG1 */ +#define C_SGEN_RCH_INV_5BIT_SFT 15 +#define C_SGEN_RCH_INV_5BIT_MASK 0x1 +#define C_SGEN_RCH_INV_5BIT_MASK_SFT (0x1 << 15) +#define C_SGEN_RCH_INV_8BIT_SFT 14 +#define C_SGEN_RCH_INV_8BIT_MASK 0x1 +#define C_SGEN_RCH_INV_8BIT_MASK_SFT (0x1 << 14) +#define SGEN_FREQ_DIV_CH1_CTL_SFT 0 +#define SGEN_FREQ_DIV_CH1_CTL_MASK 0x1f +#define SGEN_FREQ_DIV_CH1_CTL_MASK_SFT (0x1f << 0) + +/* AFE_ADC_ASYNC_FIFO_CFG */ +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_SFT 5 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5) +#define RG_UL_ASYNC_FIFO_SOFT_RST_SFT 4 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK 0x1 +#define RG_UL_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4) +#define RG_AMIC_UL_ADC_CLK_SEL_SFT 1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK 0x1 +#define RG_AMIC_UL_ADC_CLK_SEL_MASK_SFT (0x1 << 1) + +/* AFE_ADC_ASYNC_FIFO_CFG1 */ +#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_SFT 5 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK 0x1 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_EN_MASK_SFT (0x1 << 5) +#define RG_UL2_ASYNC_FIFO_SOFT_RST_SFT 4 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK 0x1 +#define RG_UL2_ASYNC_FIFO_SOFT_RST_MASK_SFT (0x1 << 4) + +/* AFE_DCCLK_CFG0 */ +#define DCCLK_DIV_SFT 5 +#define DCCLK_DIV_MASK 0x7ff +#define DCCLK_DIV_MASK_SFT (0x7ff << 5) +#define DCCLK_INV_SFT 4 +#define DCCLK_INV_MASK 0x1 +#define DCCLK_INV_MASK_SFT (0x1 << 4) +#define DCCLK_REF_CK_SEL_SFT 2 +#define DCCLK_REF_CK_SEL_MASK 0x3 +#define DCCLK_REF_CK_SEL_MASK_SFT (0x3 << 2) +#define DCCLK_PDN_SFT 1 +#define DCCLK_PDN_MASK 0x1 +#define DCCLK_PDN_MASK_SFT (0x1 << 1) +#define DCCLK_GEN_ON_SFT 0 +#define DCCLK_GEN_ON_MASK 0x1 +#define DCCLK_GEN_ON_MASK_SFT (0x1 << 0) + +/* AFE_DCCLK_CFG1 */ +#define RESYNC_SRC_SEL_SFT 10 +#define RESYNC_SRC_SEL_MASK 0x3 +#define RESYNC_SRC_SEL_MASK_SFT (0x3 << 10) +#define RESYNC_SRC_CK_INV_SFT 9 +#define RESYNC_SRC_CK_INV_MASK 0x1 +#define RESYNC_SRC_CK_INV_MASK_SFT (0x1 << 9) +#define DCCLK_RESYNC_BYPASS_SFT 8 +#define DCCLK_RESYNC_BYPASS_MASK 0x1 +#define DCCLK_RESYNC_BYPASS_MASK_SFT (0x1 << 8) +#define DCCLK_PHASE_SEL_SFT 4 +#define DCCLK_PHASE_SEL_MASK 0xf +#define DCCLK_PHASE_SEL_MASK_SFT (0xf << 4) + +/* AUDIO_DIG_CFG */ +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_SFT 15 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO2_LOOPBACK_MASK_SFT (0x1 << 15) +#define RG_AUD_PAD_TOP_PHASE_MODE2_SFT 8 +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE2_MASK_SFT (0x7f << 8) +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_SFT 7 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO_LOOPBACK_MASK_SFT (0x1 << 7) +#define RG_AUD_PAD_TOP_PHASE_MODE_SFT 0 +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE_MASK_SFT (0x7f << 0) + +/* AUDIO_DIG_CFG1 */ +#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_SFT 7 +#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK 0x1 +#define RG_AUD_PAD_TOP_DAT_MISO3_LOOPBACK_MASK_SFT (0x1 << 7) +#define RG_AUD_PAD_TOP_PHASE_MODE3_SFT 0 +#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK 0x7f +#define RG_AUD_PAD_TOP_PHASE_MODE3_MASK_SFT (0x7f << 0) + +/* AFE_AUD_PAD_TOP */ +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_SFT 12 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK 0x7 +#define RG_AUD_PAD_TOP_TX_FIFO_RSP_MASK_SFT (0x7 << 12) +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_SFT 11 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK 0x1 +#define RG_AUD_PAD_TOP_MTKAIF_CLK_PROTOCOL2_MASK_SFT (0x1 << 11) +#define RG_AUD_PAD_TOP_TX_FIFO_ON_SFT 8 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK 0x1 +#define RG_AUD_PAD_TOP_TX_FIFO_ON_MASK_SFT (0x1 << 8) + +/* AFE_AUD_PAD_TOP_MON */ +#define ADDA_AUD_PAD_TOP_MON_SFT 0 +#define ADDA_AUD_PAD_TOP_MON_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON_MASK_SFT (0xffff << 0) + +/* AFE_AUD_PAD_TOP_MON1 */ +#define ADDA_AUD_PAD_TOP_MON1_SFT 0 +#define ADDA_AUD_PAD_TOP_MON1_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON1_MASK_SFT (0xffff << 0) + +/* AFE_AUD_PAD_TOP_MON2 */ +#define ADDA_AUD_PAD_TOP_MON2_SFT 0 +#define ADDA_AUD_PAD_TOP_MON2_MASK 0xffff +#define ADDA_AUD_PAD_TOP_MON2_MASK_SFT (0xffff << 0) + +/* AFE_DL_NLE_CFG */ +#define NLE_RCH_HPGAIN_SEL_SFT 10 +#define NLE_RCH_HPGAIN_SEL_MASK 0x1 +#define NLE_RCH_HPGAIN_SEL_MASK_SFT (0x1 << 10) +#define NLE_RCH_CH_SEL_SFT 9 +#define NLE_RCH_CH_SEL_MASK 0x1 +#define NLE_RCH_CH_SEL_MASK_SFT (0x1 << 9) +#define NLE_RCH_ON_SFT 8 +#define NLE_RCH_ON_MASK 0x1 +#define NLE_RCH_ON_MASK_SFT (0x1 << 8) +#define NLE_LCH_HPGAIN_SEL_SFT 2 +#define NLE_LCH_HPGAIN_SEL_MASK 0x1 +#define NLE_LCH_HPGAIN_SEL_MASK_SFT (0x1 << 2) +#define NLE_LCH_CH_SEL_SFT 1 +#define NLE_LCH_CH_SEL_MASK 0x1 +#define NLE_LCH_CH_SEL_MASK_SFT (0x1 << 1) +#define NLE_LCH_ON_SFT 0 +#define NLE_LCH_ON_MASK 0x1 +#define NLE_LCH_ON_MASK_SFT (0x1 << 0) + +/* AFE_DL_NLE_MON */ +#define NLE_MONITOR_SFT 0 +#define NLE_MONITOR_MASK 0x3fff +#define NLE_MONITOR_MASK_SFT (0x3fff << 0) + +/* AFE_CG_EN_MON */ +#define CK_CG_EN_MON_SFT 0 +#define CK_CG_EN_MON_MASK 0x3f +#define CK_CG_EN_MON_MASK_SFT (0x3f << 0) + +/* AFE_MIC_ARRAY_CFG */ +#define RG_AMIC_ADC1_SOURCE_SEL_SFT 10 +#define RG_AMIC_ADC1_SOURCE_SEL_MASK 0x3 +#define RG_AMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 10) +#define RG_AMIC_ADC2_SOURCE_SEL_SFT 8 +#define RG_AMIC_ADC2_SOURCE_SEL_MASK 0x3 +#define RG_AMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 8) +#define RG_AMIC_ADC3_SOURCE_SEL_SFT 6 +#define RG_AMIC_ADC3_SOURCE_SEL_MASK 0x3 +#define RG_AMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 6) +#define RG_DMIC_ADC1_SOURCE_SEL_SFT 4 +#define RG_DMIC_ADC1_SOURCE_SEL_MASK 0x3 +#define RG_DMIC_ADC1_SOURCE_SEL_MASK_SFT (0x3 << 4) +#define RG_DMIC_ADC2_SOURCE_SEL_SFT 2 +#define RG_DMIC_ADC2_SOURCE_SEL_MASK 0x3 +#define RG_DMIC_ADC2_SOURCE_SEL_MASK_SFT (0x3 << 2) +#define RG_DMIC_ADC3_SOURCE_SEL_SFT 0 +#define RG_DMIC_ADC3_SOURCE_SEL_MASK 0x3 +#define RG_DMIC_ADC3_SOURCE_SEL_MASK_SFT (0x3 << 0) + +/* AFE_CHOP_CFG0 */ +#define RG_CHOP_DIV_SEL_SFT 4 +#define RG_CHOP_DIV_SEL_MASK 0x1f +#define RG_CHOP_DIV_SEL_MASK_SFT (0x1f << 4) +#define RG_CHOP_DIV_EN_SFT 0 +#define RG_CHOP_DIV_EN_MASK 0x1 +#define RG_CHOP_DIV_EN_MASK_SFT (0x1 << 0) + +/* AFE_MTKAIF_MUX_CFG */ +#define RG_ADDA6_EN_SEL_SFT 12 +#define RG_ADDA6_EN_SEL_MASK 0x1 +#define RG_ADDA6_EN_SEL_MASK_SFT (0x1 << 12) +#define RG_ADDA6_CH2_SEL_SFT 10 +#define RG_ADDA6_CH2_SEL_MASK 0x3 +#define RG_ADDA6_CH2_SEL_MASK_SFT (0x3 << 10) +#define RG_ADDA6_CH1_SEL_SFT 8 +#define RG_ADDA6_CH1_SEL_MASK 0x3 +#define RG_ADDA6_CH1_SEL_MASK_SFT (0x3 << 8) +#define RG_ADDA_EN_SEL_SFT 4 +#define RG_ADDA_EN_SEL_MASK 0x1 +#define RG_ADDA_EN_SEL_MASK_SFT (0x1 << 4) +#define RG_ADDA_CH2_SEL_SFT 2 +#define RG_ADDA_CH2_SEL_MASK 0x3 +#define RG_ADDA_CH2_SEL_MASK_SFT (0x3 << 2) +#define RG_ADDA_CH1_SEL_SFT 0 +#define RG_ADDA_CH1_SEL_MASK 0x3 +#define RG_ADDA_CH1_SEL_MASK_SFT (0x3 << 0) + +/* AFE_PMIC_NEWIF_CFG3 */ +#define RG_UP8X_SYNC_WORD_SFT 0 +#define RG_UP8X_SYNC_WORD_MASK 0xffff +#define RG_UP8X_SYNC_WORD_MASK_SFT (0xffff << 0) + +/* AFE_NCP_CFG0 */ +#define RG_NCP_CK1_VALID_CNT_SFT 9 +#define RG_NCP_CK1_VALID_CNT_MASK 0x7f +#define RG_NCP_CK1_VALID_CNT_MASK_SFT (0x7f << 9) +#define RG_NCP_ADITH_SFT 8 +#define RG_NCP_ADITH_MASK 0x1 +#define RG_NCP_ADITH_MASK_SFT (0x1 << 8) +#define RG_NCP_DITHER_EN_SFT 7 +#define RG_NCP_DITHER_EN_MASK 0x1 +#define RG_NCP_DITHER_EN_MASK_SFT (0x1 << 7) +#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_SFT 4 +#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK 0x7 +#define RG_NCP_DITHER_FIXED_CK0_ACK1_2P_MASK_SFT (0x7 << 4) +#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_SFT 1 +#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK 0x7 +#define RG_NCP_DITHER_FIXED_CK0_ACK2_2P_MASK_SFT (0x7 << 1) +#define RG_NCP_ON_SFT 0 +#define RG_NCP_ON_MASK 0x1 +#define RG_NCP_ON_MASK_SFT (0x1 << 0) + +/* AFE_NCP_CFG1 */ +#define RG_XY_VAL_CFG_EN_SFT 15 +#define RG_XY_VAL_CFG_EN_MASK 0x1 +#define RG_XY_VAL_CFG_EN_MASK_SFT (0x1 << 15) +#define RG_X_VAL_CFG_SFT 8 +#define RG_X_VAL_CFG_MASK 0x7f +#define RG_X_VAL_CFG_MASK_SFT (0x7f << 8) +#define RG_Y_VAL_CFG_SFT 0 +#define RG_Y_VAL_CFG_MASK 0x7f +#define RG_Y_VAL_CFG_MASK_SFT (0x7f << 0) + +/* AFE_NCP_CFG2 */ +#define RG_NCP_NONCLK_SET_SFT 1 +#define RG_NCP_NONCLK_SET_MASK 0x1 +#define RG_NCP_NONCLK_SET_MASK_SFT (0x1 << 1) +#define RG_NCP_PDDIS_EN_SFT 0 +#define RG_NCP_PDDIS_EN_MASK 0x1 +#define RG_NCP_PDDIS_EN_MASK_SFT (0x1 << 0) + +/* AUDENC_ANA_CON0 */ +#define RG_AUDPREAMPLON_SFT 0 +#define RG_AUDPREAMPLON_MASK 0x1 +#define RG_AUDPREAMPLON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPLDCCEN_SFT 1 +#define RG_AUDPREAMPLDCCEN_MASK 0x1 +#define RG_AUDPREAMPLDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPLDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPLDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPLPGATEST_SFT 3 +#define RG_AUDPREAMPLPGATEST_MASK 0x1 +#define RG_AUDPREAMPLPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPLVSCALE_SFT 4 +#define RG_AUDPREAMPLVSCALE_MASK 0x3 +#define RG_AUDPREAMPLVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPLINPUTSEL_SFT 6 +#define RG_AUDPREAMPLINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPLINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPLGAIN_SFT 8 +#define RG_AUDPREAMPLGAIN_MASK 0x7 +#define RG_AUDPREAMPLGAIN_MASK_SFT (0x7 << 8) +#define RG_BULKL_VCM_EN_SFT 11 +#define RG_BULKL_VCM_EN_MASK 0x1 +#define RG_BULKL_VCM_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADCLPWRUP_SFT 12 +#define RG_AUDADCLPWRUP_MASK 0x1 +#define RG_AUDADCLPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCLINPUTSEL_SFT 13 +#define RG_AUDADCLINPUTSEL_MASK 0x3 +#define RG_AUDADCLINPUTSEL_MASK_SFT (0x3 << 13) + +/* AUDENC_ANA_CON1 */ +#define RG_AUDPREAMPRON_SFT 0 +#define RG_AUDPREAMPRON_MASK 0x1 +#define RG_AUDPREAMPRON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMPRDCCEN_SFT 1 +#define RG_AUDPREAMPRDCCEN_MASK 0x1 +#define RG_AUDPREAMPRDCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPRDCPRECHARGE_SFT 2 +#define RG_AUDPREAMPRDCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMPRDCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMPRPGATEST_SFT 3 +#define RG_AUDPREAMPRPGATEST_MASK 0x1 +#define RG_AUDPREAMPRPGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMPRVSCALE_SFT 4 +#define RG_AUDPREAMPRVSCALE_MASK 0x3 +#define RG_AUDPREAMPRVSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMPRINPUTSEL_SFT 6 +#define RG_AUDPREAMPRINPUTSEL_MASK 0x3 +#define RG_AUDPREAMPRINPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMPRGAIN_SFT 8 +#define RG_AUDPREAMPRGAIN_MASK 0x7 +#define RG_AUDPREAMPRGAIN_MASK_SFT (0x7 << 8) +#define RG_BULKR_VCM_EN_SFT 11 +#define RG_BULKR_VCM_EN_MASK 0x1 +#define RG_BULKR_VCM_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADCRPWRUP_SFT 12 +#define RG_AUDADCRPWRUP_MASK 0x1 +#define RG_AUDADCRPWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADCRINPUTSEL_SFT 13 +#define RG_AUDADCRINPUTSEL_MASK 0x3 +#define RG_AUDADCRINPUTSEL_MASK_SFT (0x3 << 13) + +/* AUDENC_ANA_CON2 */ +#define RG_AUDPREAMP3ON_SFT 0 +#define RG_AUDPREAMP3ON_MASK 0x1 +#define RG_AUDPREAMP3ON_MASK_SFT (0x1 << 0) +#define RG_AUDPREAMP3DCCEN_SFT 1 +#define RG_AUDPREAMP3DCCEN_MASK 0x1 +#define RG_AUDPREAMP3DCCEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMP3DCPRECHARGE_SFT 2 +#define RG_AUDPREAMP3DCPRECHARGE_MASK 0x1 +#define RG_AUDPREAMP3DCPRECHARGE_MASK_SFT (0x1 << 2) +#define RG_AUDPREAMP3PGATEST_SFT 3 +#define RG_AUDPREAMP3PGATEST_MASK 0x1 +#define RG_AUDPREAMP3PGATEST_MASK_SFT (0x1 << 3) +#define RG_AUDPREAMP3VSCALE_SFT 4 +#define RG_AUDPREAMP3VSCALE_MASK 0x3 +#define RG_AUDPREAMP3VSCALE_MASK_SFT (0x3 << 4) +#define RG_AUDPREAMP3INPUTSEL_SFT 6 +#define RG_AUDPREAMP3INPUTSEL_MASK 0x3 +#define RG_AUDPREAMP3INPUTSEL_MASK_SFT (0x3 << 6) +#define RG_AUDPREAMP3GAIN_SFT 8 +#define RG_AUDPREAMP3GAIN_MASK 0x7 +#define RG_AUDPREAMP3GAIN_MASK_SFT (0x7 << 8) +#define RG_BULK3_VCM_EN_SFT 11 +#define RG_BULK3_VCM_EN_MASK 0x1 +#define RG_BULK3_VCM_EN_MASK_SFT (0x1 << 11) +#define RG_AUDADC3PWRUP_SFT 12 +#define RG_AUDADC3PWRUP_MASK 0x1 +#define RG_AUDADC3PWRUP_MASK_SFT (0x1 << 12) +#define RG_AUDADC3INPUTSEL_SFT 13 +#define RG_AUDADC3INPUTSEL_MASK 0x3 +#define RG_AUDADC3INPUTSEL_MASK_SFT (0x3 << 13) + +/* AUDENC_ANA_CON3 */ +#define RG_AUDULHALFBIAS_SFT 0 +#define RG_AUDULHALFBIAS_MASK 0x1 +#define RG_AUDULHALFBIAS_MASK_SFT (0x1 << 0) +#define RG_AUDGLBVOWLPWEN_SFT 1 +#define RG_AUDGLBVOWLPWEN_MASK 0x1 +#define RG_AUDGLBVOWLPWEN_MASK_SFT (0x1 << 1) +#define RG_AUDPREAMPLPEN_SFT 2 +#define RG_AUDPREAMPLPEN_MASK 0x1 +#define RG_AUDPREAMPLPEN_MASK_SFT (0x1 << 2) +#define RG_AUDADC1STSTAGELPEN_SFT 3 +#define RG_AUDADC1STSTAGELPEN_MASK 0x1 +#define RG_AUDADC1STSTAGELPEN_MASK_SFT (0x1 << 3) +#define RG_AUDADC2NDSTAGELPEN_SFT 4 +#define RG_AUDADC2NDSTAGELPEN_MASK 0x1 +#define RG_AUDADC2NDSTAGELPEN_MASK_SFT (0x1 << 4) +#define RG_AUDADCFLASHLPEN_SFT 5 +#define RG_AUDADCFLASHLPEN_MASK 0x1 +#define RG_AUDADCFLASHLPEN_MASK_SFT (0x1 << 5) +#define RG_AUDPREAMPIDDTEST_SFT 6 +#define RG_AUDPREAMPIDDTEST_MASK 0x3 +#define RG_AUDPREAMPIDDTEST_MASK_SFT (0x3 << 6) +#define RG_AUDADC1STSTAGEIDDTEST_SFT 8 +#define RG_AUDADC1STSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8) +#define RG_AUDADC2NDSTAGEIDDTEST_SFT 10 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK 0x3 +#define RG_AUDADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10) +#define RG_AUDADCREFBUFIDDTEST_SFT 12 +#define RG_AUDADCREFBUFIDDTEST_MASK 0x3 +#define RG_AUDADCREFBUFIDDTEST_MASK_SFT (0x3 << 12) +#define RG_AUDADCFLASHIDDTEST_SFT 14 +#define RG_AUDADCFLASHIDDTEST_MASK 0x3 +#define RG_AUDADCFLASHIDDTEST_MASK_SFT (0x3 << 14) + +/* AUDENC_ANA_CON4 */ +#define RG_AUDRULHALFBIAS_SFT 0 +#define RG_AUDRULHALFBIAS_MASK 0x1 +#define RG_AUDRULHALFBIAS_MASK_SFT (0x1 << 0) +#define RG_AUDGLBRVOWLPWEN_SFT 1 +#define RG_AUDGLBRVOWLPWEN_MASK 0x1 +#define RG_AUDGLBRVOWLPWEN_MASK_SFT (0x1 << 1) +#define RG_AUDRPREAMPLPEN_SFT 2 +#define RG_AUDRPREAMPLPEN_MASK 0x1 +#define RG_AUDRPREAMPLPEN_MASK_SFT (0x1 << 2) +#define RG_AUDRADC1STSTAGELPEN_SFT 3 +#define RG_AUDRADC1STSTAGELPEN_MASK 0x1 +#define RG_AUDRADC1STSTAGELPEN_MASK_SFT (0x1 << 3) +#define RG_AUDRADC2NDSTAGELPEN_SFT 4 +#define RG_AUDRADC2NDSTAGELPEN_MASK 0x1 +#define RG_AUDRADC2NDSTAGELPEN_MASK_SFT (0x1 << 4) +#define RG_AUDRADCFLASHLPEN_SFT 5 +#define RG_AUDRADCFLASHLPEN_MASK 0x1 +#define RG_AUDRADCFLASHLPEN_MASK_SFT (0x1 << 5) +#define RG_AUDRPREAMPIDDTEST_SFT 6 +#define RG_AUDRPREAMPIDDTEST_MASK 0x3 +#define RG_AUDRPREAMPIDDTEST_MASK_SFT (0x3 << 6) +#define RG_AUDRADC1STSTAGEIDDTEST_SFT 8 +#define RG_AUDRADC1STSTAGEIDDTEST_MASK 0x3 +#define RG_AUDRADC1STSTAGEIDDTEST_MASK_SFT (0x3 << 8) +#define RG_AUDRADC2NDSTAGEIDDTEST_SFT 10 +#define RG_AUDRADC2NDSTAGEIDDTEST_MASK 0x3 +#define RG_AUDRADC2NDSTAGEIDDTEST_MASK_SFT (0x3 << 10) +#define RG_AUDRADCREFBUFIDDTEST_SFT 12 +#define RG_AUDRADCREFBUFIDDTEST_MASK 0x3 +#define RG_AUDRADCREFBUFIDDTEST_MASK_SFT (0x3 << 12) +#define RG_AUDRADCFLASHIDDTEST_SFT 14 +#define RG_AUDRADCFLASHIDDTEST_MASK 0x3 +#define RG_AUDRADCFLASHIDDTEST_MASK_SFT (0x3 << 14) + +/* AUDENC_ANA_CON5 */ +#define RG_AUDADCCLKRSTB_SFT 0 +#define RG_AUDADCCLKRSTB_MASK 0x1 +#define RG_AUDADCCLKRSTB_MASK_SFT (0x1 << 0) +#define RG_AUDADCCLKSEL_SFT 1 +#define RG_AUDADCCLKSEL_MASK 0x3 +#define RG_AUDADCCLKSEL_MASK_SFT (0x3 << 1) +#define RG_AUDADCCLKSOURCE_SFT 3 +#define RG_AUDADCCLKSOURCE_MASK 0x3 +#define RG_AUDADCCLKSOURCE_MASK_SFT (0x3 << 3) +#define RG_AUDADCCLKGENMODE_SFT 5 +#define RG_AUDADCCLKGENMODE_MASK 0x3 +#define RG_AUDADCCLKGENMODE_MASK_SFT (0x3 << 5) +#define RG_AUDPREAMP_ACCFS_SFT 7 +#define RG_AUDPREAMP_ACCFS_MASK 0x1 +#define RG_AUDPREAMP_ACCFS_MASK_SFT (0x1 << 7) +#define RG_AUDPREAMPAAFEN_SFT 8 +#define RG_AUDPREAMPAAFEN_MASK 0x1 +#define RG_AUDPREAMPAAFEN_MASK_SFT (0x1 << 8) +#define RG_DCCVCMBUFLPMODSEL_SFT 9 +#define RG_DCCVCMBUFLPMODSEL_MASK 0x1 +#define RG_DCCVCMBUFLPMODSEL_MASK_SFT (0x1 << 9) +#define RG_DCCVCMBUFLPSWEN_SFT 10 +#define RG_DCCVCMBUFLPSWEN_MASK 0x1 +#define RG_DCCVCMBUFLPSWEN_MASK_SFT (0x1 << 10) +#define RG_AUDSPAREPGA_SFT 11 +#define RG_AUDSPAREPGA_MASK 0x1f +#define RG_AUDSPAREPGA_MASK_SFT (0x1f << 11) + +/* AUDENC_ANA_CON6 */ +#define RG_AUDADC1STSTAGESDENB_SFT 0 +#define RG_AUDADC1STSTAGESDENB_MASK 0x1 +#define RG_AUDADC1STSTAGESDENB_MASK_SFT (0x1 << 0) +#define RG_AUDADC2NDSTAGERESET_SFT 1 +#define RG_AUDADC2NDSTAGERESET_MASK 0x1 +#define RG_AUDADC2NDSTAGERESET_MASK_SFT (0x1 << 1) +#define RG_AUDADC3RDSTAGERESET_SFT 2 +#define RG_AUDADC3RDSTAGERESET_MASK 0x1 +#define RG_AUDADC3RDSTAGERESET_MASK_SFT (0x1 << 2) +#define RG_AUDADCFSRESET_SFT 3 +#define RG_AUDADCFSRESET_MASK 0x1 +#define RG_AUDADCFSRESET_MASK_SFT (0x1 << 3) +#define RG_AUDADCWIDECM_SFT 4 +#define RG_AUDADCWIDECM_MASK 0x1 +#define RG_AUDADCWIDECM_MASK_SFT (0x1 << 4) +#define RG_AUDADCNOPATEST_SFT 5 +#define RG_AUDADCNOPATEST_MASK 0x1 +#define RG_AUDADCNOPATEST_MASK_SFT (0x1 << 5) +#define RG_AUDADCBYPASS_SFT 6 +#define RG_AUDADCBYPASS_MASK 0x1 +#define RG_AUDADCBYPASS_MASK_SFT (0x1 << 6) +#define RG_AUDADCFFBYPASS_SFT 7 +#define RG_AUDADCFFBYPASS_MASK 0x1 +#define RG_AUDADCFFBYPASS_MASK_SFT (0x1 << 7) +#define RG_AUDADCDACFBCURRENT_SFT 8 +#define RG_AUDADCDACFBCURRENT_MASK 0x1 +#define RG_AUDADCDACFBCURRENT_MASK_SFT (0x1 << 8) +#define RG_AUDADCDACIDDTEST_SFT 9 +#define RG_AUDADCDACIDDTEST_MASK 0x3 +#define RG_AUDADCDACIDDTEST_MASK_SFT (0x3 << 9) +#define RG_AUDADCDACNRZ_SFT 11 +#define RG_AUDADCDACNRZ_MASK 0x1 +#define RG_AUDADCDACNRZ_MASK_SFT (0x1 << 11) +#define RG_AUDADCNODEM_SFT 12 +#define RG_AUDADCNODEM_MASK 0x1 +#define RG_AUDADCNODEM_MASK_SFT (0x1 << 12) +#define RG_AUDADCDACTEST_SFT 13 +#define RG_AUDADCDACTEST_MASK 0x1 +#define RG_AUDADCDACTEST_MASK_SFT (0x1 << 13) +#define RG_AUDADCDAC0P25FS_SFT 14 +#define RG_AUDADCDAC0P25FS_MASK 0x1 +#define RG_AUDADCDAC0P25FS_MASK_SFT (0x1 << 14) +#define RG_AUDADCRDAC0P25FS_SFT 15 +#define RG_AUDADCRDAC0P25FS_MASK 0x1 +#define RG_AUDADCRDAC0P25FS_MASK_SFT (0x1 << 15) + +/* AUDENC_ANA_CON7 */ +#define RG_AUDADCTESTDATA_SFT 0 +#define RG_AUDADCTESTDATA_MASK 0xffff +#define RG_AUDADCTESTDATA_MASK_SFT (0xffff << 0) + +/* AUDENC_ANA_CON8 */ +#define RG_AUDRCTUNEL_SFT 0 +#define RG_AUDRCTUNEL_MASK 0x1f +#define RG_AUDRCTUNEL_MASK_SFT (0x1f << 0) +#define RG_AUDRCTUNELSEL_SFT 5 +#define RG_AUDRCTUNELSEL_MASK 0x1 +#define RG_AUDRCTUNELSEL_MASK_SFT (0x1 << 5) +#define RG_AUDRCTUNER_SFT 8 +#define RG_AUDRCTUNER_MASK 0x1f +#define RG_AUDRCTUNER_MASK_SFT (0x1f << 8) +#define RG_AUDRCTUNERSEL_SFT 13 +#define RG_AUDRCTUNERSEL_MASK 0x1 +#define RG_AUDRCTUNERSEL_MASK_SFT (0x1 << 13) + +/* AUDENC_ANA_CON9 */ +#define RG_AUD3CTUNEL_SFT 0 +#define RG_AUD3CTUNEL_MASK 0x1f +#define RG_AUD3CTUNEL_MASK_SFT (0x1f << 0) +#define RG_AUD3CTUNELSEL_SFT 5 +#define RG_AUD3CTUNELSEL_MASK 0x1 +#define RG_AUD3CTUNELSEL_MASK_SFT (0x1 << 5) +#define RGS_AUDRCTUNE3READ_SFT 6 +#define RGS_AUDRCTUNE3READ_MASK 0x1f +#define RGS_AUDRCTUNE3READ_MASK_SFT (0x1f << 6) +#define RG_AUD3SPARE_SFT 11 +#define RG_AUD3SPARE_MASK 0x1f +#define RG_AUD3SPARE_MASK_SFT (0x1f << 11) + +/* AUDENC_ANA_CON10 */ +#define RGS_AUDRCTUNELREAD_SFT 0 +#define RGS_AUDRCTUNELREAD_MASK 0x1f +#define RGS_AUDRCTUNELREAD_MASK_SFT (0x1f << 0) +#define RGS_AUDRCTUNERREAD_SFT 8 +#define RGS_AUDRCTUNERREAD_MASK 0x1f +#define RGS_AUDRCTUNERREAD_MASK_SFT (0x1f << 8) + +/* AUDENC_ANA_CON11 */ +#define RG_AUDSPAREVA30_SFT 0 +#define RG_AUDSPAREVA30_MASK 0xff +#define RG_AUDSPAREVA30_MASK_SFT (0xff << 0) +#define RG_AUDSPAREVA18_SFT 8 +#define RG_AUDSPAREVA18_MASK 0xff +#define RG_AUDSPAREVA18_MASK_SFT (0xff << 8) + +/* AUDENC_ANA_CON12 */ +#define RG_AUDPGA_DECAP_SFT 0 +#define RG_AUDPGA_DECAP_MASK 0x1 +#define RG_AUDPGA_DECAP_MASK_SFT (0x1 << 0) +#define RG_AUDPGA_CAPRA_SFT 1 +#define RG_AUDPGA_CAPRA_MASK 0x1 +#define RG_AUDPGA_CAPRA_MASK_SFT (0x1 << 1) +#define RG_AUDPGA_ACCCMP_SFT 2 +#define RG_AUDPGA_ACCCMP_MASK 0x1 +#define RG_AUDPGA_ACCCMP_MASK_SFT (0x1 << 2) +#define RG_AUDENC_SPARE2_SFT 3 +#define RG_AUDENC_SPARE2_MASK 0x1fff +#define RG_AUDENC_SPARE2_MASK_SFT (0x1fff << 3) + +/* AUDENC_ANA_CON13 */ +#define RG_AUDDIGMICEN_SFT 0 +#define RG_AUDDIGMICEN_MASK 0x1 +#define RG_AUDDIGMICEN_MASK_SFT (0x1 << 0) +#define RG_AUDDIGMICBIAS_SFT 1 +#define RG_AUDDIGMICBIAS_MASK 0x3 +#define RG_AUDDIGMICBIAS_MASK_SFT (0x3 << 1) +#define RG_DMICHPCLKEN_SFT 3 +#define RG_DMICHPCLKEN_MASK 0x1 +#define RG_DMICHPCLKEN_MASK_SFT (0x1 << 3) +#define RG_AUDDIGMICPDUTY_SFT 4 +#define RG_AUDDIGMICPDUTY_MASK 0x3 +#define RG_AUDDIGMICPDUTY_MASK_SFT (0x3 << 4) +#define RG_AUDDIGMICNDUTY_SFT 6 +#define RG_AUDDIGMICNDUTY_MASK 0x3 +#define RG_AUDDIGMICNDUTY_MASK_SFT (0x3 << 6) +#define RG_DMICMONEN_SFT 8 +#define RG_DMICMONEN_MASK 0x1 +#define RG_DMICMONEN_MASK_SFT (0x1 << 8) +#define RG_DMICMONSEL_SFT 9 +#define RG_DMICMONSEL_MASK 0x7 +#define RG_DMICMONSEL_MASK_SFT (0x7 << 9) + +/* AUDENC_ANA_CON14 */ +#define RG_AUDDIGMIC1EN_SFT 0 +#define RG_AUDDIGMIC1EN_MASK 0x1 +#define RG_AUDDIGMIC1EN_MASK_SFT (0x1 << 0) +#define RG_AUDDIGMICBIAS1_SFT 1 +#define RG_AUDDIGMICBIAS1_MASK 0x3 +#define RG_AUDDIGMICBIAS1_MASK_SFT (0x3 << 1) +#define RG_DMIC1HPCLKEN_SFT 3 +#define RG_DMIC1HPCLKEN_MASK 0x1 +#define RG_DMIC1HPCLKEN_MASK_SFT (0x1 << 3) +#define RG_AUDDIGMIC1PDUTY_SFT 4 +#define RG_AUDDIGMIC1PDUTY_MASK 0x3 +#define RG_AUDDIGMIC1PDUTY_MASK_SFT (0x3 << 4) +#define RG_AUDDIGMIC1NDUTY_SFT 6 +#define RG_AUDDIGMIC1NDUTY_MASK 0x3 +#define RG_AUDDIGMIC1NDUTY_MASK_SFT (0x3 << 6) +#define RG_DMIC1MONEN_SFT 8 +#define RG_DMIC1MONEN_MASK 0x1 +#define RG_DMIC1MONEN_MASK_SFT (0x1 << 8) +#define RG_DMIC1MONSEL_SFT 9 +#define RG_DMIC1MONSEL_MASK 0x7 +#define RG_DMIC1MONSEL_MASK_SFT (0x7 << 9) +#define RG_AUDSPAREVMIC_SFT 12 +#define RG_AUDSPAREVMIC_MASK 0xf +#define RG_AUDSPAREVMIC_MASK_SFT (0xf << 12) + +/* AUDENC_ANA_CON15 */ +#define RG_AUDPWDBMICBIAS0_SFT 0 +#define RG_AUDPWDBMICBIAS0_MASK 0x1 +#define RG_AUDPWDBMICBIAS0_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS0BYPASSEN_SFT 1 +#define RG_AUDMICBIAS0BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS0BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS0LOWPEN_SFT 2 +#define RG_AUDMICBIAS0LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS0LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDPWDBMICBIAS3_SFT 3 +#define RG_AUDPWDBMICBIAS3_MASK 0x1 +#define RG_AUDPWDBMICBIAS3_MASK_SFT (0x1 << 3) +#define RG_AUDMICBIAS0VREF_SFT 4 +#define RG_AUDMICBIAS0VREF_MASK 0x7 +#define RG_AUDMICBIAS0VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS0DCSW0P1EN_SFT 8 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P1EN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS0DCSW0P2EN_SFT 9 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0P2EN_MASK_SFT (0x1 << 9) +#define RG_AUDMICBIAS0DCSW0NEN_SFT 10 +#define RG_AUDMICBIAS0DCSW0NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW0NEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS0DCSW2P1EN_SFT 12 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P1EN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS0DCSW2P2EN_SFT 13 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2P2EN_MASK_SFT (0x1 << 13) +#define RG_AUDMICBIAS0DCSW2NEN_SFT 14 +#define RG_AUDMICBIAS0DCSW2NEN_MASK 0x1 +#define RG_AUDMICBIAS0DCSW2NEN_MASK_SFT (0x1 << 14) + +/* AUDENC_ANA_CON16 */ +#define RG_AUDPWDBMICBIAS1_SFT 0 +#define RG_AUDPWDBMICBIAS1_MASK 0x1 +#define RG_AUDPWDBMICBIAS1_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS1BYPASSEN_SFT 1 +#define RG_AUDMICBIAS1BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS1BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS1LOWPEN_SFT 2 +#define RG_AUDMICBIAS1LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS1LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS1VREF_SFT 4 +#define RG_AUDMICBIAS1VREF_MASK 0x7 +#define RG_AUDMICBIAS1VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS1DCSW1PEN_SFT 8 +#define RG_AUDMICBIAS1DCSW1PEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1PEN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS1DCSW1NEN_SFT 9 +#define RG_AUDMICBIAS1DCSW1NEN_MASK 0x1 +#define RG_AUDMICBIAS1DCSW1NEN_MASK_SFT (0x1 << 9) +#define RG_BANDGAPGEN_SFT 10 +#define RG_BANDGAPGEN_MASK 0x1 +#define RG_BANDGAPGEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIAS1HVEN_SFT 12 +#define RG_AUDMICBIAS1HVEN_MASK 0x1 +#define RG_AUDMICBIAS1HVEN_MASK_SFT (0x1 << 12) +#define RG_AUDMICBIAS1HVVREF_SFT 13 +#define RG_AUDMICBIAS1HVVREF_MASK 0x1 +#define RG_AUDMICBIAS1HVVREF_MASK_SFT (0x1 << 13) + +/* AUDENC_ANA_CON17 */ +#define RG_AUDPWDBMICBIAS2_SFT 0 +#define RG_AUDPWDBMICBIAS2_MASK 0x1 +#define RG_AUDPWDBMICBIAS2_MASK_SFT (0x1 << 0) +#define RG_AUDMICBIAS2BYPASSEN_SFT 1 +#define RG_AUDMICBIAS2BYPASSEN_MASK 0x1 +#define RG_AUDMICBIAS2BYPASSEN_MASK_SFT (0x1 << 1) +#define RG_AUDMICBIAS2LOWPEN_SFT 2 +#define RG_AUDMICBIAS2LOWPEN_MASK 0x1 +#define RG_AUDMICBIAS2LOWPEN_MASK_SFT (0x1 << 2) +#define RG_AUDMICBIAS2VREF_SFT 4 +#define RG_AUDMICBIAS2VREF_MASK 0x7 +#define RG_AUDMICBIAS2VREF_MASK_SFT (0x7 << 4) +#define RG_AUDMICBIAS2DCSW3P1EN_SFT 8 +#define RG_AUDMICBIAS2DCSW3P1EN_MASK 0x1 +#define RG_AUDMICBIAS2DCSW3P1EN_MASK_SFT (0x1 << 8) +#define RG_AUDMICBIAS2DCSW3P2EN_SFT 9 +#define RG_AUDMICBIAS2DCSW3P2EN_MASK 0x1 +#define RG_AUDMICBIAS2DCSW3P2EN_MASK_SFT (0x1 << 9) +#define RG_AUDMICBIAS2DCSW3NEN_SFT 10 +#define RG_AUDMICBIAS2DCSW3NEN_MASK 0x1 +#define RG_AUDMICBIAS2DCSW3NEN_MASK_SFT (0x1 << 10) +#define RG_AUDMICBIASSPARE_SFT 12 +#define RG_AUDMICBIASSPARE_MASK 0xf +#define RG_AUDMICBIASSPARE_MASK_SFT (0xf << 12) + +/* AUDENC_ANA_CON18 */ +#define RG_AUDACCDETMICBIAS0PULLLOW_SFT 0 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS0PULLLOW_MASK_SFT (0x1 << 0) +#define RG_AUDACCDETMICBIAS1PULLLOW_SFT 1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS1PULLLOW_MASK_SFT (0x1 << 1) +#define RG_AUDACCDETMICBIAS2PULLLOW_SFT 2 +#define RG_AUDACCDETMICBIAS2PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS2PULLLOW_MASK_SFT (0x1 << 2) +#define RG_AUDACCDETVIN1PULLLOW_SFT 3 +#define RG_AUDACCDETVIN1PULLLOW_MASK 0x1 +#define RG_AUDACCDETVIN1PULLLOW_MASK_SFT (0x1 << 3) +#define RG_AUDACCDETVTHACAL_SFT 4 +#define RG_AUDACCDETVTHACAL_MASK 0x1 +#define RG_AUDACCDETVTHACAL_MASK_SFT (0x1 << 4) +#define RG_AUDACCDETVTHBCAL_SFT 5 +#define RG_AUDACCDETVTHBCAL_MASK 0x1 +#define RG_AUDACCDETVTHBCAL_MASK_SFT (0x1 << 5) +#define RG_AUDACCDETTVDET_SFT 6 +#define RG_AUDACCDETTVDET_MASK 0x1 +#define RG_AUDACCDETTVDET_MASK_SFT (0x1 << 6) +#define RG_ACCDETSEL_SFT 7 +#define RG_ACCDETSEL_MASK 0x1 +#define RG_ACCDETSEL_MASK_SFT (0x1 << 7) +#define RG_SWBUFMODSEL_SFT 8 +#define RG_SWBUFMODSEL_MASK 0x1 +#define RG_SWBUFMODSEL_MASK_SFT (0x1 << 8) +#define RG_SWBUFSWEN_SFT 9 +#define RG_SWBUFSWEN_MASK 0x1 +#define RG_SWBUFSWEN_MASK_SFT (0x1 << 9) +#define RG_EINT0NOHYS_SFT 10 +#define RG_EINT0NOHYS_MASK 0x1 +#define RG_EINT0NOHYS_MASK_SFT (0x1 << 10) +#define RG_EINT0CONFIGACCDET_SFT 11 +#define RG_EINT0CONFIGACCDET_MASK 0x1 +#define RG_EINT0CONFIGACCDET_MASK_SFT (0x1 << 11) +#define RG_EINT0HIRENB_SFT 12 +#define RG_EINT0HIRENB_MASK 0x1 +#define RG_EINT0HIRENB_MASK_SFT (0x1 << 12) +#define RG_ACCDET2AUXRESBYPASS_SFT 13 +#define RG_ACCDET2AUXRESBYPASS_MASK 0x1 +#define RG_ACCDET2AUXRESBYPASS_MASK_SFT (0x1 << 13) +#define RG_ACCDET2AUXSWEN_SFT 14 +#define RG_ACCDET2AUXSWEN_MASK 0x1 +#define RG_ACCDET2AUXSWEN_MASK_SFT (0x1 << 14) +#define RG_AUDACCDETMICBIAS3PULLLOW_SFT 15 +#define RG_AUDACCDETMICBIAS3PULLLOW_MASK 0x1 +#define RG_AUDACCDETMICBIAS3PULLLOW_MASK_SFT (0x1 << 15) + +/* AUDENC_ANA_CON19 */ +#define RG_EINT1CONFIGACCDET_SFT 0 +#define RG_EINT1CONFIGACCDET_MASK 0x1 +#define RG_EINT1CONFIGACCDET_MASK_SFT (0x1 << 0) +#define RG_EINT1HIRENB_SFT 1 +#define RG_EINT1HIRENB_MASK 0x1 +#define RG_EINT1HIRENB_MASK_SFT (0x1 << 1) +#define RG_EINT1NOHYS_SFT 2 +#define RG_EINT1NOHYS_MASK 0x1 +#define RG_EINT1NOHYS_MASK_SFT (0x1 << 2) +#define RG_EINTCOMPVTH_SFT 4 +#define RG_EINTCOMPVTH_MASK 0xf +#define RG_EINTCOMPVTH_MASK_SFT (0xf << 4) +#define RG_MTEST_EN_SFT 8 +#define RG_MTEST_EN_MASK 0x1 +#define RG_MTEST_EN_MASK_SFT (0x1 << 8) +#define RG_MTEST_SEL_SFT 9 +#define RG_MTEST_SEL_MASK 0x1 +#define RG_MTEST_SEL_MASK_SFT (0x1 << 9) +#define RG_MTEST_CURRENT_SFT 10 +#define RG_MTEST_CURRENT_MASK 0x1 +#define RG_MTEST_CURRENT_MASK_SFT (0x1 << 10) +#define RG_ANALOGFDEN_SFT 12 +#define RG_ANALOGFDEN_MASK 0x1 +#define RG_ANALOGFDEN_MASK_SFT (0x1 << 12) +#define RG_FDVIN1PPULLLOW_SFT 13 +#define RG_FDVIN1PPULLLOW_MASK 0x1 +#define RG_FDVIN1PPULLLOW_MASK_SFT (0x1 << 13) +#define RG_FDEINT0TYPE_SFT 14 +#define RG_FDEINT0TYPE_MASK 0x1 +#define RG_FDEINT0TYPE_MASK_SFT (0x1 << 14) +#define RG_FDEINT1TYPE_SFT 15 +#define RG_FDEINT1TYPE_MASK 0x1 +#define RG_FDEINT1TYPE_MASK_SFT (0x1 << 15) + +/* AUDENC_ANA_CON20 */ +#define RG_EINT0CMPEN_SFT 0 +#define RG_EINT0CMPEN_MASK 0x1 +#define RG_EINT0CMPEN_MASK_SFT (0x1 << 0) +#define RG_EINT0CMPMEN_SFT 1 +#define RG_EINT0CMPMEN_MASK 0x1 +#define RG_EINT0CMPMEN_MASK_SFT (0x1 << 1) +#define RG_EINT0EN_SFT 2 +#define RG_EINT0EN_MASK 0x1 +#define RG_EINT0EN_MASK_SFT (0x1 << 2) +#define RG_EINT0CEN_SFT 3 +#define RG_EINT0CEN_MASK 0x1 +#define RG_EINT0CEN_MASK_SFT (0x1 << 3) +#define RG_EINT0INVEN_SFT 4 +#define RG_EINT0INVEN_MASK 0x1 +#define RG_EINT0INVEN_MASK_SFT (0x1 << 4) +#define RG_EINT0CTURBO_SFT 5 +#define RG_EINT0CTURBO_MASK 0x7 +#define RG_EINT0CTURBO_MASK_SFT (0x7 << 5) +#define RG_EINT1CMPEN_SFT 8 +#define RG_EINT1CMPEN_MASK 0x1 +#define RG_EINT1CMPEN_MASK_SFT (0x1 << 8) +#define RG_EINT1CMPMEN_SFT 9 +#define RG_EINT1CMPMEN_MASK 0x1 +#define RG_EINT1CMPMEN_MASK_SFT (0x1 << 9) +#define RG_EINT1EN_SFT 10 +#define RG_EINT1EN_MASK 0x1 +#define RG_EINT1EN_MASK_SFT (0x1 << 10) +#define RG_EINT1CEN_SFT 11 +#define RG_EINT1CEN_MASK 0x1 +#define RG_EINT1CEN_MASK_SFT (0x1 << 11) +#define RG_EINT1INVEN_SFT 12 +#define RG_EINT1INVEN_MASK 0x1 +#define RG_EINT1INVEN_MASK_SFT (0x1 << 12) +#define RG_EINT1CTURBO_SFT 13 +#define RG_EINT1CTURBO_MASK 0x7 +#define RG_EINT1CTURBO_MASK_SFT (0x7 << 13) + +/* AUDENC_ANA_CON21 */ +#define RG_ACCDETSPARE_SFT 0 +#define RG_ACCDETSPARE_MASK 0xffff +#define RG_ACCDETSPARE_MASK_SFT (0xffff << 0) + +/* AUDENC_ANA_CON22 */ +#define RG_AUDENCSPAREVA30_SFT 0 +#define RG_AUDENCSPAREVA30_MASK 0xff +#define RG_AUDENCSPAREVA30_MASK_SFT (0xff << 0) +#define RG_AUDENCSPAREVA18_SFT 8 +#define RG_AUDENCSPAREVA18_MASK 0xff +#define RG_AUDENCSPAREVA18_MASK_SFT (0xff << 8) + +/* AUDENC_ANA_CON23 */ +#define RG_CLKSQ_EN_SFT 0 +#define RG_CLKSQ_EN_MASK 0x1 +#define RG_CLKSQ_EN_MASK_SFT (0x1 << 0) +#define RG_CLKSQ_IN_SEL_TEST_SFT 1 +#define RG_CLKSQ_IN_SEL_TEST_MASK 0x1 +#define RG_CLKSQ_IN_SEL_TEST_MASK_SFT (0x1 << 1) +#define RG_CM_REFGENSEL_SFT 2 +#define RG_CM_REFGENSEL_MASK 0x1 +#define RG_CM_REFGENSEL_MASK_SFT (0x1 << 2) +#define RG_AUDIO_VOW_EN_SFT 3 +#define RG_AUDIO_VOW_EN_MASK 0x1 +#define RG_AUDIO_VOW_EN_MASK_SFT (0x1 << 3) +#define RG_CLKSQ_EN_VOW_SFT 4 +#define RG_CLKSQ_EN_VOW_MASK 0x1 +#define RG_CLKSQ_EN_VOW_MASK_SFT (0x1 << 4) +#define RG_CLKAND_EN_VOW_SFT 5 +#define RG_CLKAND_EN_VOW_MASK 0x1 +#define RG_CLKAND_EN_VOW_MASK_SFT (0x1 << 5) +#define RG_VOWCLK_SEL_EN_VOW_SFT 6 +#define RG_VOWCLK_SEL_EN_VOW_MASK 0x1 +#define RG_VOWCLK_SEL_EN_VOW_MASK_SFT (0x1 << 6) +#define RG_SPARE_VOW_SFT 7 +#define RG_SPARE_VOW_MASK 0x7 +#define RG_SPARE_VOW_MASK_SFT (0x7 << 7) + +/* AUDDEC_ANA_CON0 */ +#define RG_AUDDACLPWRUP_VAUDP32_SFT 0 +#define RG_AUDDACLPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDDACLPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDDACRPWRUP_VAUDP32_SFT 1 +#define RG_AUDDACRPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDDACRPWRUP_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUD_DAC_PWR_UP_VA32_SFT 2 +#define RG_AUD_DAC_PWR_UP_VA32_MASK 0x1 +#define RG_AUD_DAC_PWR_UP_VA32_MASK_SFT (0x1 << 2) +#define RG_AUD_DAC_PWL_UP_VA32_SFT 3 +#define RG_AUD_DAC_PWL_UP_VA32_MASK 0x1 +#define RG_AUD_DAC_PWL_UP_VA32_MASK_SFT (0x1 << 3) +#define RG_AUDHPLPWRUP_VAUDP32_SFT 4 +#define RG_AUDHPLPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPLPWRUP_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_AUDHPRPWRUP_VAUDP32_SFT 5 +#define RG_AUDHPRPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPRPWRUP_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_SFT 6 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDHPLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_SFT 7 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDHPRPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_SFT 8 +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 8) +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_SFT 10 +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPRMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 10) +#define RG_AUDHPLSCDISABLE_VAUDP32_SFT 12 +#define RG_AUDHPLSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDHPLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 12) +#define RG_AUDHPRSCDISABLE_VAUDP32_SFT 13 +#define RG_AUDHPRSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDHPRSCDISABLE_VAUDP32_MASK_SFT (0x1 << 13) +#define RG_AUDHPLBSCCURRENT_VAUDP32_SFT 14 +#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDHPLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 14) +#define RG_AUDHPRBSCCURRENT_VAUDP32_SFT 15 +#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDHPRBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 15) + +/* AUDDEC_ANA_CON1 */ +#define RG_AUDHPLOUTPWRUP_VAUDP32_SFT 0 +#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPLOUTPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDHPROUTPWRUP_VAUDP32_SFT 1 +#define RG_AUDHPROUTPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPROUTPWRUP_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_SFT 2 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPLOUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 2) +#define RG_AUDHPROUTAUXPWRUP_VAUDP32_SFT 3 +#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHPROUTAUXPWRUP_VAUDP32_MASK_SFT (0x1 << 3) +#define RG_HPLAUXFBRSW_EN_VAUDP32_SFT 4 +#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK 0x1 +#define RG_HPLAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_HPRAUXFBRSW_EN_VAUDP32_SFT 5 +#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK 0x1 +#define RG_HPRAUXFBRSW_EN_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_SFT 6 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK 0x1 +#define RG_HPLSHORT2HPLAUX_EN_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_SFT 7 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK 0x1 +#define RG_HPRSHORT2HPRAUX_EN_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_HPLOUTSTGCTRL_VAUDP32_SFT 8 +#define RG_HPLOUTSTGCTRL_VAUDP32_MASK 0x7 +#define RG_HPLOUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 8) +#define RG_HPROUTSTGCTRL_VAUDP32_SFT 12 +#define RG_HPROUTSTGCTRL_VAUDP32_MASK 0x7 +#define RG_HPROUTSTGCTRL_VAUDP32_MASK_SFT (0x7 << 12) + +/* AUDDEC_ANA_CON2 */ +#define RG_HPLOUTPUTSTBENH_VAUDP32_SFT 0 +#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK 0x7 +#define RG_HPLOUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_HPROUTPUTSTBENH_VAUDP32_SFT 4 +#define RG_HPROUTPUTSTBENH_VAUDP32_MASK 0x7 +#define RG_HPROUTPUTSTBENH_VAUDP32_MASK_SFT (0x7 << 4) +#define RG_AUDHPSTARTUP_VAUDP32_SFT 7 +#define RG_AUDHPSTARTUP_VAUDP32_MASK 0x1 +#define RG_AUDHPSTARTUP_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_AUDREFN_DERES_EN_VAUDP32_SFT 8 +#define RG_AUDREFN_DERES_EN_VAUDP32_MASK 0x1 +#define RG_AUDREFN_DERES_EN_VAUDP32_MASK_SFT (0x1 << 8) +#define RG_HPINPUTSTBENH_VAUDP32_SFT 9 +#define RG_HPINPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_HPINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 9) +#define RG_HPINPUTRESET0_VAUDP32_SFT 10 +#define RG_HPINPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HPINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10) +#define RG_HPOUTPUTRESET0_VAUDP32_SFT 11 +#define RG_HPOUTPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HPOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 11) +#define RG_HPPSHORT2VCM_VAUDP32_SFT 12 +#define RG_HPPSHORT2VCM_VAUDP32_MASK 0x7 +#define RG_HPPSHORT2VCM_VAUDP32_MASK_SFT (0x7 << 12) +#define RG_AUDHPTRIM_EN_VAUDP32_SFT 15 +#define RG_AUDHPTRIM_EN_VAUDP32_MASK 0x1 +#define RG_AUDHPTRIM_EN_VAUDP32_MASK_SFT (0x1 << 15) + +/* AUDDEC_ANA_CON3 */ +#define RG_AUDHPLTRIM_VAUDP32_SFT 0 +#define RG_AUDHPLTRIM_VAUDP32_MASK 0x1f +#define RG_AUDHPLTRIM_VAUDP32_MASK_SFT (0x1f << 0) +#define RG_AUDHPLFINETRIM_VAUDP32_SFT 5 +#define RG_AUDHPLFINETRIM_VAUDP32_MASK 0x7 +#define RG_AUDHPLFINETRIM_VAUDP32_MASK_SFT (0x7 << 5) +#define RG_AUDHPRTRIM_VAUDP32_SFT 8 +#define RG_AUDHPRTRIM_VAUDP32_MASK 0x1f +#define RG_AUDHPRTRIM_VAUDP32_MASK_SFT (0x1f << 8) +#define RG_AUDHPRFINETRIM_VAUDP32_SFT 13 +#define RG_AUDHPRFINETRIM_VAUDP32_MASK 0x7 +#define RG_AUDHPRFINETRIM_VAUDP32_MASK_SFT (0x7 << 13) + +/* AUDDEC_ANA_CON4 */ +#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_SFT 0 +#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK 0x7 +#define RG_AUDHPDIFFINPBIASADJ_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_AUDHPLFCOMPRESSEL_VAUDP32_SFT 4 +#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK 0x7 +#define RG_AUDHPLFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 4) +#define RG_AUDHPHFCOMPRESSEL_VAUDP32_SFT 8 +#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK 0x7 +#define RG_AUDHPHFCOMPRESSEL_VAUDP32_MASK_SFT (0x7 << 8) +#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_SFT 12 +#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPHFCOMPBUFGAINSEL_VAUDP32_MASK_SFT (0x3 << 12) +#define RG_AUDHPCOMP_EN_VAUDP32_SFT 15 +#define RG_AUDHPCOMP_EN_VAUDP32_MASK 0x1 +#define RG_AUDHPCOMP_EN_VAUDP32_MASK_SFT (0x1 << 15) + +/* AUDDEC_ANA_CON5 */ +#define RG_AUDHPDECMGAINADJ_VAUDP32_SFT 0 +#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK 0x7 +#define RG_AUDHPDECMGAINADJ_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_AUDHPDEDMGAINADJ_VAUDP32_SFT 4 +#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK 0x7 +#define RG_AUDHPDEDMGAINADJ_VAUDP32_MASK_SFT (0x7 << 4) + +/* AUDDEC_ANA_CON6 */ +#define RG_AUDHSPWRUP_VAUDP32_SFT 0 +#define RG_AUDHSPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDHSPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDHSPWRUP_IBIAS_VAUDP32_SFT 1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDHSPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUDHSMUXINPUTSEL_VAUDP32_SFT 2 +#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDHSMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2) +#define RG_AUDHSSCDISABLE_VAUDP32_SFT 4 +#define RG_AUDHSSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDHSSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_AUDHSBSCCURRENT_VAUDP32_SFT 5 +#define RG_AUDHSBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDHSBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDHSSTARTUP_VAUDP32_SFT 6 +#define RG_AUDHSSTARTUP_VAUDP32_MASK 0x1 +#define RG_AUDHSSTARTUP_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_HSOUTPUTSTBENH_VAUDP32_SFT 7 +#define RG_HSOUTPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_HSOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_HSINPUTSTBENH_VAUDP32_SFT 8 +#define RG_HSINPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_HSINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8) +#define RG_HSINPUTRESET0_VAUDP32_SFT 9 +#define RG_HSINPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HSINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9) +#define RG_HSOUTPUTRESET0_VAUDP32_SFT 10 +#define RG_HSOUTPUTRESET0_VAUDP32_MASK 0x1 +#define RG_HSOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10) +#define RG_HSOUT_SHORTVCM_VAUDP32_SFT 11 +#define RG_HSOUT_SHORTVCM_VAUDP32_MASK 0x1 +#define RG_HSOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11) + +/* AUDDEC_ANA_CON7 */ +#define RG_AUDLOLPWRUP_VAUDP32_SFT 0 +#define RG_AUDLOLPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDLOLPWRUP_VAUDP32_MASK_SFT (0x1 << 0) +#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_SFT 1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK 0x1 +#define RG_AUDLOLPWRUP_IBIAS_VAUDP32_MASK_SFT (0x1 << 1) +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_SFT 2 +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK 0x3 +#define RG_AUDLOLMUXINPUTSEL_VAUDP32_MASK_SFT (0x3 << 2) +#define RG_AUDLOLSCDISABLE_VAUDP32_SFT 4 +#define RG_AUDLOLSCDISABLE_VAUDP32_MASK 0x1 +#define RG_AUDLOLSCDISABLE_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_AUDLOLBSCCURRENT_VAUDP32_SFT 5 +#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK 0x1 +#define RG_AUDLOLBSCCURRENT_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDLOSTARTUP_VAUDP32_SFT 6 +#define RG_AUDLOSTARTUP_VAUDP32_MASK 0x1 +#define RG_AUDLOSTARTUP_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_LOINPUTSTBENH_VAUDP32_SFT 7 +#define RG_LOINPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_LOINPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 7) +#define RG_LOOUTPUTSTBENH_VAUDP32_SFT 8 +#define RG_LOOUTPUTSTBENH_VAUDP32_MASK 0x1 +#define RG_LOOUTPUTSTBENH_VAUDP32_MASK_SFT (0x1 << 8) +#define RG_LOINPUTRESET0_VAUDP32_SFT 9 +#define RG_LOINPUTRESET0_VAUDP32_MASK 0x1 +#define RG_LOINPUTRESET0_VAUDP32_MASK_SFT (0x1 << 9) +#define RG_LOOUTPUTRESET0_VAUDP32_SFT 10 +#define RG_LOOUTPUTRESET0_VAUDP32_MASK 0x1 +#define RG_LOOUTPUTRESET0_VAUDP32_MASK_SFT (0x1 << 10) +#define RG_LOOUT_SHORTVCM_VAUDP32_SFT 11 +#define RG_LOOUT_SHORTVCM_VAUDP32_MASK 0x1 +#define RG_LOOUT_SHORTVCM_VAUDP32_MASK_SFT (0x1 << 11) +#define RG_AUDDACTPWRUP_VAUDP32_SFT 12 +#define RG_AUDDACTPWRUP_VAUDP32_MASK 0x1 +#define RG_AUDDACTPWRUP_VAUDP32_MASK_SFT (0x1 << 12) +#define RG_AUD_DAC_PWT_UP_VA32_SFT 13 +#define RG_AUD_DAC_PWT_UP_VA32_MASK 0x1 +#define RG_AUD_DAC_PWT_UP_VA32_MASK_SFT (0x1 << 13) + +/* AUDDEC_ANA_CON8 */ +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_SFT 0 +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK 0xf +#define RG_AUDTRIMBUF_INPUTMUXSEL_VAUDP32_MASK_SFT (0xf << 0) +#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_SFT 4 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK 0x3 +#define RG_AUDTRIMBUF_GAINSEL_VAUDP32_MASK_SFT (0x3 << 4) +#define RG_AUDTRIMBUF_EN_VAUDP32_SFT 6 +#define RG_AUDTRIMBUF_EN_VAUDP32_MASK 0x1 +#define RG_AUDTRIMBUF_EN_VAUDP32_MASK_SFT (0x1 << 6) +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_SFT 8 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPSPKDET_INPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 8) +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_SFT 10 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK 0x3 +#define RG_AUDHPSPKDET_OUTPUTMUXSEL_VAUDP32_MASK_SFT (0x3 << 10) +#define RG_AUDHPSPKDET_EN_VAUDP32_SFT 12 +#define RG_AUDHPSPKDET_EN_VAUDP32_MASK 0x1 +#define RG_AUDHPSPKDET_EN_VAUDP32_MASK_SFT (0x1 << 12) + +/* AUDDEC_ANA_CON9 */ +#define RG_ABIDEC_RSVD0_VA32_SFT 0 +#define RG_ABIDEC_RSVD0_VA32_MASK 0xff +#define RG_ABIDEC_RSVD0_VA32_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD0_VAUDP32_SFT 8 +#define RG_ABIDEC_RSVD0_VAUDP32_MASK 0xff +#define RG_ABIDEC_RSVD0_VAUDP32_MASK_SFT (0xff << 8) + +/* AUDDEC_ANA_CON10 */ +#define RG_ABIDEC_RSVD1_VAUDP32_SFT 0 +#define RG_ABIDEC_RSVD1_VAUDP32_MASK 0xff +#define RG_ABIDEC_RSVD1_VAUDP32_MASK_SFT (0xff << 0) +#define RG_ABIDEC_RSVD2_VAUDP32_SFT 8 +#define RG_ABIDEC_RSVD2_VAUDP32_MASK 0xff +#define RG_ABIDEC_RSVD2_VAUDP32_MASK_SFT (0xff << 8) + +/* AUDDEC_ANA_CON11 */ +#define RG_AUDZCDMUXSEL_VAUDP32_SFT 0 +#define RG_AUDZCDMUXSEL_VAUDP32_MASK 0x7 +#define RG_AUDZCDMUXSEL_VAUDP32_MASK_SFT (0x7 << 0) +#define RG_AUDZCDCLKSEL_VAUDP32_SFT 3 +#define RG_AUDZCDCLKSEL_VAUDP32_MASK 0x1 +#define RG_AUDZCDCLKSEL_VAUDP32_MASK_SFT (0x1 << 3) +#define RG_AUDBIASADJ_0_VAUDP32_SFT 7 +#define RG_AUDBIASADJ_0_VAUDP32_MASK 0x1ff +#define RG_AUDBIASADJ_0_VAUDP32_MASK_SFT (0x1ff << 7) + +/* AUDDEC_ANA_CON12 */ +#define RG_AUDBIASADJ_1_VAUDP32_SFT 0 +#define RG_AUDBIASADJ_1_VAUDP32_MASK 0xff +#define RG_AUDBIASADJ_1_VAUDP32_MASK_SFT (0xff << 0) +#define RG_AUDIBIASPWRDN_VAUDP32_SFT 8 +#define RG_AUDIBIASPWRDN_VAUDP32_MASK 0x1 +#define RG_AUDIBIASPWRDN_VAUDP32_MASK_SFT (0x1 << 8) + +/* AUDDEC_ANA_CON13 */ +#define RG_RSTB_DECODER_VA32_SFT 0 +#define RG_RSTB_DECODER_VA32_MASK 0x1 +#define RG_RSTB_DECODER_VA32_MASK_SFT (0x1 << 0) +#define RG_SEL_DECODER_96K_VA32_SFT 1 +#define RG_SEL_DECODER_96K_VA32_MASK 0x1 +#define RG_SEL_DECODER_96K_VA32_MASK_SFT (0x1 << 1) +#define RG_SEL_DELAY_VCORE_SFT 2 +#define RG_SEL_DELAY_VCORE_MASK 0x1 +#define RG_SEL_DELAY_VCORE_MASK_SFT (0x1 << 2) +#define RG_AUDGLB_PWRDN_VA32_SFT 4 +#define RG_AUDGLB_PWRDN_VA32_MASK 0x1 +#define RG_AUDGLB_PWRDN_VA32_MASK_SFT (0x1 << 4) +#define RG_AUDGLB_LP_VOW_EN_VA32_SFT 5 +#define RG_AUDGLB_LP_VOW_EN_VA32_MASK 0x1 +#define RG_AUDGLB_LP_VOW_EN_VA32_MASK_SFT (0x1 << 5) +#define RG_AUDGLB_LP2_VOW_EN_VA32_SFT 6 +#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK 0x1 +#define RG_AUDGLB_LP2_VOW_EN_VA32_MASK_SFT (0x1 << 6) + +/* AUDDEC_ANA_CON14 */ +#define RG_LCLDO_DEC_EN_VA32_SFT 0 +#define RG_LCLDO_DEC_EN_VA32_MASK 0x1 +#define RG_LCLDO_DEC_EN_VA32_MASK_SFT (0x1 << 0) +#define RG_LCLDO_DEC_PDDIS_EN_VA18_SFT 1 +#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK 0x1 +#define RG_LCLDO_DEC_PDDIS_EN_VA18_MASK_SFT (0x1 << 1) +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_SFT 2 +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK 0x1 +#define RG_LCLDO_DEC_REMOTE_SENSE_VA18_MASK_SFT (0x1 << 2) +#define RG_NVREG_EN_VAUDP32_SFT 4 +#define RG_NVREG_EN_VAUDP32_MASK 0x1 +#define RG_NVREG_EN_VAUDP32_MASK_SFT (0x1 << 4) +#define RG_NVREG_PULL0V_VAUDP32_SFT 5 +#define RG_NVREG_PULL0V_VAUDP32_MASK 0x1 +#define RG_NVREG_PULL0V_VAUDP32_MASK_SFT (0x1 << 5) +#define RG_AUDPMU_RSVD_VA18_SFT 8 +#define RG_AUDPMU_RSVD_VA18_MASK 0xff +#define RG_AUDPMU_RSVD_VA18_MASK_SFT (0xff << 8) + +/* MT6359_ZCD_CON0 */ +#define RG_AUDZCDENABLE_SFT 0 +#define RG_AUDZCDENABLE_MASK 0x1 +#define RG_AUDZCDENABLE_MASK_SFT (0x1 << 0) +#define RG_AUDZCDGAINSTEPTIME_SFT 1 +#define RG_AUDZCDGAINSTEPTIME_MASK 0x7 +#define RG_AUDZCDGAINSTEPTIME_MASK_SFT (0x7 << 1) +#define RG_AUDZCDGAINSTEPSIZE_SFT 4 +#define RG_AUDZCDGAINSTEPSIZE_MASK 0x3 +#define RG_AUDZCDGAINSTEPSIZE_MASK_SFT (0x3 << 4) +#define RG_AUDZCDTIMEOUTMODESEL_SFT 6 +#define RG_AUDZCDTIMEOUTMODESEL_MASK 0x1 +#define RG_AUDZCDTIMEOUTMODESEL_MASK_SFT (0x1 << 6) + +/* MT6359_ZCD_CON1 */ +#define RG_AUDLOLGAIN_SFT 0 +#define RG_AUDLOLGAIN_MASK 0x1f +#define RG_AUDLOLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDLORGAIN_SFT 7 +#define RG_AUDLORGAIN_MASK 0x1f +#define RG_AUDLORGAIN_MASK_SFT (0x1f << 7) + +/* MT6359_ZCD_CON2 */ +#define RG_AUDHPLGAIN_SFT 0 +#define RG_AUDHPLGAIN_MASK 0x1f +#define RG_AUDHPLGAIN_MASK_SFT (0x1f << 0) +#define RG_AUDHPRGAIN_SFT 7 +#define RG_AUDHPRGAIN_MASK 0x1f +#define RG_AUDHPRGAIN_MASK_SFT (0x1f << 7) + +/* MT6359_ZCD_CON3 */ +#define RG_AUDHSGAIN_SFT 0 +#define RG_AUDHSGAIN_MASK 0x1f +#define RG_AUDHSGAIN_MASK_SFT (0x1f << 0) + +/* MT6359_ZCD_CON4 */ +#define RG_AUDIVLGAIN_SFT 0 +#define RG_AUDIVLGAIN_MASK 0x7 +#define RG_AUDIVLGAIN_MASK_SFT (0x7 << 0) +#define RG_AUDIVRGAIN_SFT 8 +#define RG_AUDIVRGAIN_MASK 0x7 +#define RG_AUDIVRGAIN_MASK_SFT (0x7 << 8) + +/* MT6359_ZCD_CON5 */ +#define RG_AUDINTGAIN1_SFT 0 +#define RG_AUDINTGAIN1_MASK 0x3f +#define RG_AUDINTGAIN1_MASK_SFT (0x3f << 0) +#define RG_AUDINTGAIN2_SFT 8 +#define RG_AUDINTGAIN2_MASK 0x3f +#define RG_AUDINTGAIN2_MASK_SFT (0x3f << 8) + +/* audio register */ +#define MT6359_GPIO_DIR0 0x88 +#define MT6359_GPIO_DIR0_SET 0x8a +#define MT6359_GPIO_DIR0_CLR 0x8c +#define MT6359_GPIO_DIR1 0x8e +#define MT6359_GPIO_DIR1_SET 0x90 +#define MT6359_GPIO_DIR1_CLR 0x92 + +#define MT6359_DCXO_CW11 0x7a6 +#define MT6359_DCXO_CW12 0x7a8 +#define MT6359_LDO_VAUD18_CON0 0x1c98 + +#define MT6359_GPIO_MODE0 0xcc +#define MT6359_GPIO_MODE0_SET 0xce +#define MT6359_GPIO_MODE0_CLR 0xd0 +#define MT6359_GPIO_MODE1 0xd2 +#define MT6359_GPIO_MODE1_SET 0xd4 +#define MT6359_GPIO_MODE1_CLR 0xd6 +#define MT6359_GPIO_MODE2 0xd8 +#define MT6359_GPIO_MODE2_SET 0xda +#define MT6359_GPIO_MODE2_CLR 0xdc +#define MT6359_GPIO_MODE3 0xde +#define MT6359_GPIO_MODE3_SET 0xe0 +#define MT6359_GPIO_MODE3_CLR 0xe2 +#define MT6359_GPIO_MODE4 0xe4 +#define MT6359_GPIO_MODE4_SET 0xe6 +#define MT6359_GPIO_MODE4_CLR 0xe8 + +#define MT6359_AUD_TOP_ID 0x2300 +#define MT6359_AUD_TOP_REV0 0x2302 +#define MT6359_AUD_TOP_DBI 0x2304 +#define MT6359_AUD_TOP_DXI 0x2306 +#define MT6359_AUD_TOP_CKPDN_TPM0 0x2308 +#define MT6359_AUD_TOP_CKPDN_TPM1 0x230a +#define MT6359_AUD_TOP_CKPDN_CON0 0x230c +#define MT6359_AUD_TOP_CKPDN_CON0_SET 0x230e +#define MT6359_AUD_TOP_CKPDN_CON0_CLR 0x2310 +#define MT6359_AUD_TOP_CKSEL_CON0 0x2312 +#define MT6359_AUD_TOP_CKSEL_CON0_SET 0x2314 +#define MT6359_AUD_TOP_CKSEL_CON0_CLR 0x2316 +#define MT6359_AUD_TOP_CKTST_CON0 0x2318 +#define MT6359_AUD_TOP_CLK_HWEN_CON0 0x231a +#define MT6359_AUD_TOP_CLK_HWEN_CON0_SET 0x231c +#define MT6359_AUD_TOP_CLK_HWEN_CON0_CLR 0x231e +#define MT6359_AUD_TOP_RST_CON0 0x2320 +#define MT6359_AUD_TOP_RST_CON0_SET 0x2322 +#define MT6359_AUD_TOP_RST_CON0_CLR 0x2324 +#define MT6359_AUD_TOP_RST_BANK_CON0 0x2326 +#define MT6359_AUD_TOP_INT_CON0 0x2328 +#define MT6359_AUD_TOP_INT_CON0_SET 0x232a +#define MT6359_AUD_TOP_INT_CON0_CLR 0x232c +#define MT6359_AUD_TOP_INT_MASK_CON0 0x232e +#define MT6359_AUD_TOP_INT_MASK_CON0_SET 0x2330 +#define MT6359_AUD_TOP_INT_MASK_CON0_CLR 0x2332 +#define MT6359_AUD_TOP_INT_STATUS0 0x2334 +#define MT6359_AUD_TOP_INT_RAW_STATUS0 0x2336 +#define MT6359_AUD_TOP_INT_MISC_CON0 0x2338 +#define MT6359_AUD_TOP_MON_CON0 0x233a +#define MT6359_AUDIO_DIG_DSN_ID 0x2380 +#define MT6359_AUDIO_DIG_DSN_REV0 0x2382 +#define MT6359_AUDIO_DIG_DSN_DBI 0x2384 +#define MT6359_AUDIO_DIG_DSN_DXI 0x2386 +#define MT6359_AFE_UL_DL_CON0 0x2388 +#define MT6359_AFE_DL_SRC2_CON0_L 0x238a +#define MT6359_AFE_UL_SRC_CON0_H 0x238c +#define MT6359_AFE_UL_SRC_CON0_L 0x238e +#define MT6359_AFE_ADDA6_L_SRC_CON0_H 0x2390 +#define MT6359_AFE_ADDA6_UL_SRC_CON0_L 0x2392 +#define MT6359_AFE_TOP_CON0 0x2394 +#define MT6359_AUDIO_TOP_CON0 0x2396 +#define MT6359_AFE_MON_DEBUG0 0x2398 +#define MT6359_AFUNC_AUD_CON0 0x239a +#define MT6359_AFUNC_AUD_CON1 0x239c +#define MT6359_AFUNC_AUD_CON2 0x239e +#define MT6359_AFUNC_AUD_CON3 0x23a0 +#define MT6359_AFUNC_AUD_CON4 0x23a2 +#define MT6359_AFUNC_AUD_CON5 0x23a4 +#define MT6359_AFUNC_AUD_CON6 0x23a6 +#define MT6359_AFUNC_AUD_CON7 0x23a8 +#define MT6359_AFUNC_AUD_CON8 0x23aa +#define MT6359_AFUNC_AUD_CON9 0x23ac +#define MT6359_AFUNC_AUD_CON10 0x23ae +#define MT6359_AFUNC_AUD_CON11 0x23b0 +#define MT6359_AFUNC_AUD_CON12 0x23b2 +#define MT6359_AFUNC_AUD_MON0 0x23b4 +#define MT6359_AFUNC_AUD_MON1 0x23b6 +#define MT6359_AUDRC_TUNE_MON0 0x23b8 +#define MT6359_AFE_ADDA_MTKAIF_FIFO_CFG0 0x23ba +#define MT6359_AFE_ADDA_MTKAIF_FIFO_LOG_MON1 0x23bc +#define MT6359_AFE_ADDA_MTKAIF_MON0 0x23be +#define MT6359_AFE_ADDA_MTKAIF_MON1 0x23c0 +#define MT6359_AFE_ADDA_MTKAIF_MON2 0x23c2 +#define MT6359_AFE_ADDA6_MTKAIF_MON3 0x23c4 +#define MT6359_AFE_ADDA_MTKAIF_MON4 0x23c6 +#define MT6359_AFE_ADDA_MTKAIF_MON5 0x23c8 +#define MT6359_AFE_ADDA_MTKAIF_CFG0 0x23ca +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG0 0x23cc +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG1 0x23ce +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG2 0x23d0 +#define MT6359_AFE_ADDA_MTKAIF_RX_CFG3 0x23d2 +#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG0 0x23d4 +#define MT6359_AFE_ADDA_MTKAIF_SYNCWORD_CFG1 0x23d6 +#define MT6359_AFE_SGEN_CFG0 0x23d8 +#define MT6359_AFE_SGEN_CFG1 0x23da +#define MT6359_AFE_ADC_ASYNC_FIFO_CFG 0x23dc +#define MT6359_AFE_ADC_ASYNC_FIFO_CFG1 0x23de +#define MT6359_AFE_DCCLK_CFG0 0x23e0 +#define MT6359_AFE_DCCLK_CFG1 0x23e2 +#define MT6359_AUDIO_DIG_CFG 0x23e4 +#define MT6359_AUDIO_DIG_CFG1 0x23e6 +#define MT6359_AFE_AUD_PAD_TOP 0x23e8 +#define MT6359_AFE_AUD_PAD_TOP_MON 0x23ea +#define MT6359_AFE_AUD_PAD_TOP_MON1 0x23ec +#define MT6359_AFE_AUD_PAD_TOP_MON2 0x23ee +#define MT6359_AFE_DL_NLE_CFG 0x23f0 +#define MT6359_AFE_DL_NLE_MON 0x23f2 +#define MT6359_AFE_CG_EN_MON 0x23f4 +#define MT6359_AFE_MIC_ARRAY_CFG 0x23f6 +#define MT6359_AFE_CHOP_CFG0 0x23f8 +#define MT6359_AFE_MTKAIF_MUX_CFG 0x23fa +#define MT6359_AUDIO_DIG_2ND_DSN_ID 0x2400 +#define MT6359_AUDIO_DIG_2ND_DSN_REV0 0x2402 +#define MT6359_AUDIO_DIG_2ND_DSN_DBI 0x2404 +#define MT6359_AUDIO_DIG_2ND_DSN_DXI 0x2406 +#define MT6359_AFE_PMIC_NEWIF_CFG3 0x2408 +#define MT6359_AUDIO_DIG_3RD_DSN_ID 0x2480 +#define MT6359_AUDIO_DIG_3RD_DSN_REV0 0x2482 +#define MT6359_AUDIO_DIG_3RD_DSN_DBI 0x2484 +#define MT6359_AUDIO_DIG_3RD_DSN_DXI 0x2486 +#define MT6359_AFE_NCP_CFG0 0x24de +#define MT6359_AFE_NCP_CFG1 0x24e0 +#define MT6359_AFE_NCP_CFG2 0x24e2 +#define MT6359_AUDENC_DSN_ID 0x2500 +#define MT6359_AUDENC_DSN_REV0 0x2502 +#define MT6359_AUDENC_DSN_DBI 0x2504 +#define MT6359_AUDENC_DSN_FPI 0x2506 +#define MT6359_AUDENC_ANA_CON0 0x2508 +#define MT6359_AUDENC_ANA_CON1 0x250a +#define MT6359_AUDENC_ANA_CON2 0x250c +#define MT6359_AUDENC_ANA_CON3 0x250e +#define MT6359_AUDENC_ANA_CON4 0x2510 +#define MT6359_AUDENC_ANA_CON5 0x2512 +#define MT6359_AUDENC_ANA_CON6 0x2514 +#define MT6359_AUDENC_ANA_CON7 0x2516 +#define MT6359_AUDENC_ANA_CON8 0x2518 +#define MT6359_AUDENC_ANA_CON9 0x251a +#define MT6359_AUDENC_ANA_CON10 0x251c +#define MT6359_AUDENC_ANA_CON11 0x251e +#define MT6359_AUDENC_ANA_CON12 0x2520 +#define MT6359_AUDENC_ANA_CON13 0x2522 +#define MT6359_AUDENC_ANA_CON14 0x2524 +#define MT6359_AUDENC_ANA_CON15 0x2526 +#define MT6359_AUDENC_ANA_CON16 0x2528 +#define MT6359_AUDENC_ANA_CON17 0x252a +#define MT6359_AUDENC_ANA_CON18 0x252c +#define MT6359_AUDENC_ANA_CON19 0x252e +#define MT6359_AUDENC_ANA_CON20 0x2530 +#define MT6359_AUDENC_ANA_CON21 0x2532 +#define MT6359_AUDENC_ANA_CON22 0x2534 +#define MT6359_AUDENC_ANA_CON23 0x2536 +#define MT6359_AUDDEC_DSN_ID 0x2580 +#define MT6359_AUDDEC_DSN_REV0 0x2582 +#define MT6359_AUDDEC_DSN_DBI 0x2584 +#define MT6359_AUDDEC_DSN_FPI 0x2586 +#define MT6359_AUDDEC_ANA_CON0 0x2588 +#define MT6359_AUDDEC_ANA_CON1 0x258a +#define MT6359_AUDDEC_ANA_CON2 0x258c +#define MT6359_AUDDEC_ANA_CON3 0x258e +#define MT6359_AUDDEC_ANA_CON4 0x2590 +#define MT6359_AUDDEC_ANA_CON5 0x2592 +#define MT6359_AUDDEC_ANA_CON6 0x2594 +#define MT6359_AUDDEC_ANA_CON7 0x2596 +#define MT6359_AUDDEC_ANA_CON8 0x2598 +#define MT6359_AUDDEC_ANA_CON9 0x259a +#define MT6359_AUDDEC_ANA_CON10 0x259c +#define MT6359_AUDDEC_ANA_CON11 0x259e +#define MT6359_AUDDEC_ANA_CON12 0x25a0 +#define MT6359_AUDDEC_ANA_CON13 0x25a2 +#define MT6359_AUDDEC_ANA_CON14 0x25a4 +#define MT6359_AUDZCD_DSN_ID 0x2600 +#define MT6359_AUDZCD_DSN_REV0 0x2602 +#define MT6359_AUDZCD_DSN_DBI 0x2604 +#define MT6359_AUDZCD_DSN_FPI 0x2606 +#define MT6359_ZCD_CON0 0x2608 +#define MT6359_ZCD_CON1 0x260a +#define MT6359_ZCD_CON2 0x260c +#define MT6359_ZCD_CON3 0x260e +#define MT6359_ZCD_CON4 0x2610 +#define MT6359_ZCD_CON5 0x2612 +#define MT6359_ACCDET_DSN_DIG_ID 0x2680 +#define MT6359_ACCDET_DSN_DIG_REV0 0x2682 +#define MT6359_ACCDET_DSN_DBI 0x2684 +#define MT6359_ACCDET_DSN_FPI 0x2686 +#define MT6359_ACCDET_CON0 0x2688 +#define MT6359_ACCDET_CON1 0x268a +#define MT6359_ACCDET_CON2 0x268c +#define MT6359_ACCDET_CON3 0x268e +#define MT6359_ACCDET_CON4 0x2690 +#define MT6359_ACCDET_CON5 0x2692 +#define MT6359_ACCDET_CON6 0x2694 +#define MT6359_ACCDET_CON7 0x2696 +#define MT6359_ACCDET_CON8 0x2698 +#define MT6359_ACCDET_CON9 0x269a +#define MT6359_ACCDET_CON10 0x269c +#define MT6359_ACCDET_CON11 0x269e +#define MT6359_ACCDET_CON12 0x26a0 +#define MT6359_ACCDET_CON13 0x26a2 +#define MT6359_ACCDET_CON14 0x26a4 +#define MT6359_ACCDET_CON15 0x26a6 +#define MT6359_ACCDET_CON16 0x26a8 +#define MT6359_ACCDET_CON17 0x26aa +#define MT6359_ACCDET_CON18 0x26ac +#define MT6359_ACCDET_CON19 0x26ae +#define MT6359_ACCDET_CON20 0x26b0 +#define MT6359_ACCDET_CON21 0x26b2 +#define MT6359_ACCDET_CON22 0x26b4 +#define MT6359_ACCDET_CON23 0x26b6 +#define MT6359_ACCDET_CON24 0x26b8 +#define MT6359_ACCDET_CON25 0x26ba +#define MT6359_ACCDET_CON26 0x26bc +#define MT6359_ACCDET_CON27 0x26be +#define MT6359_ACCDET_CON28 0x26c0 +#define MT6359_ACCDET_CON29 0x26c2 +#define MT6359_ACCDET_CON30 0x26c4 +#define MT6359_ACCDET_CON31 0x26c6 +#define MT6359_ACCDET_CON32 0x26c8 +#define MT6359_ACCDET_CON33 0x26ca +#define MT6359_ACCDET_CON34 0x26cc +#define MT6359_ACCDET_CON35 0x26ce +#define MT6359_ACCDET_CON36 0x26d0 +#define MT6359_ACCDET_CON37 0x26d2 +#define MT6359_ACCDET_CON38 0x26d4 +#define MT6359_ACCDET_CON39 0x26d6 +#define MT6359_ACCDET_CON40 0x26d8 +#define MT6359_MAX_REGISTER MT6359_ZCD_CON5 + +/* dl bias */ +#define DRBIAS_MASK 0x7 +#define DRBIAS_HP_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 0) +#define DRBIAS_HP_MASK_SFT (DRBIAS_MASK << DRBIAS_HP_SFT) +#define DRBIAS_HS_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 3) +#define DRBIAS_HS_MASK_SFT (DRBIAS_MASK << DRBIAS_HS_SFT) +#define DRBIAS_LO_SFT (RG_AUDBIASADJ_0_VAUDP32_SFT + 6) +#define DRBIAS_LO_MASK_SFT (DRBIAS_MASK << DRBIAS_LO_SFT) +#define IBIAS_MASK 0x3 +#define IBIAS_HP_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 0) +#define IBIAS_HP_MASK_SFT (IBIAS_MASK << IBIAS_HP_SFT) +#define IBIAS_HS_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 2) +#define IBIAS_HS_MASK_SFT (IBIAS_MASK << IBIAS_HS_SFT) +#define IBIAS_LO_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 4) +#define IBIAS_LO_MASK_SFT (IBIAS_MASK << IBIAS_LO_SFT) +#define IBIAS_ZCD_SFT (RG_AUDBIASADJ_1_VAUDP32_SFT + 6) +#define IBIAS_ZCD_MASK_SFT (IBIAS_MASK << IBIAS_ZCD_SFT) + +/* dl gain */ +#define DL_GAIN_N_10DB_REG (DL_GAIN_N_10DB << 7 | DL_GAIN_N_10DB) +#define DL_GAIN_N_22DB_REG (DL_GAIN_N_22DB << 7 | DL_GAIN_N_22DB) +#define DL_GAIN_N_40DB_REG (DL_GAIN_N_40DB << 7 | DL_GAIN_N_40DB) +#define DL_GAIN_REG_MASK 0x0f9f + +/* mic type mux */ +#define MT_SOC_ENUM_EXT_ID(xname, xenum, xhandler_get, xhandler_put, id) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .device = id,\ + .info = snd_soc_info_enum_double, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = (unsigned long)&(xenum) } + +enum { + MT6359_MTKAIF_PROTOCOL_1 = 0, + MT6359_MTKAIF_PROTOCOL_2, + MT6359_MTKAIF_PROTOCOL_2_CLK_P2, +}; + +enum { + MT6359_AIF_1 = 0, /* dl: hp, rcv, hp+lo */ + MT6359_AIF_2, /* dl: lo only */ + MT6359_AIF_NUM, +}; + +enum { + AUDIO_ANALOG_VOLUME_HSOUTL, + AUDIO_ANALOG_VOLUME_HSOUTR, + AUDIO_ANALOG_VOLUME_HPOUTL, + AUDIO_ANALOG_VOLUME_HPOUTR, + AUDIO_ANALOG_VOLUME_LINEOUTL, + AUDIO_ANALOG_VOLUME_LINEOUTR, + AUDIO_ANALOG_VOLUME_MICAMP1, + AUDIO_ANALOG_VOLUME_MICAMP2, + AUDIO_ANALOG_VOLUME_MICAMP3, + AUDIO_ANALOG_VOLUME_TYPE_MAX +}; + +enum { + MUX_MIC_TYPE_0, /* ain0, micbias 0 */ + MUX_MIC_TYPE_1, /* ain1, micbias 1 */ + MUX_MIC_TYPE_2, /* ain2/3, micbias 2 */ + MUX_PGA_L, + MUX_PGA_R, + MUX_PGA_3, + MUX_HP, + MUX_NUM, +}; + +enum { + DEVICE_HP, + DEVICE_LO, + DEVICE_RCV, + DEVICE_MIC1, + DEVICE_MIC2, + DEVICE_NUM +}; + +enum { + HP_GAIN_CTL_ZCD = 0, + HP_GAIN_CTL_NLE, + HP_GAIN_CTL_NUM, +}; + +enum { + HP_MUX_OPEN = 0, + HP_MUX_HPSPK, + HP_MUX_HP, + HP_MUX_TEST_MODE, + HP_MUX_HP_IMPEDANCE, + HP_MUX_MASK = 0x7, +}; + +enum { + RCV_MUX_OPEN = 0, + RCV_MUX_MUTE, + RCV_MUX_VOICE_PLAYBACK, + RCV_MUX_TEST_MODE, + RCV_MUX_MASK = 0x3, +}; + +enum { + LO_MUX_OPEN = 0, + LO_MUX_L_DAC, + LO_MUX_3RD_DAC, + LO_MUX_TEST_MODE, + LO_MUX_MASK = 0x3, +}; + +/* Supply widget subseq */ +enum { + /* common */ + SUPPLY_SEQ_CLK_BUF, + SUPPLY_SEQ_LDO_VAUD18, + SUPPLY_SEQ_AUD_GLB, + SUPPLY_SEQ_HP_PULL_DOWN, + SUPPLY_SEQ_CLKSQ, + SUPPLY_SEQ_ADC_CLKGEN, + SUPPLY_SEQ_TOP_CK, + SUPPLY_SEQ_TOP_CK_LAST, + SUPPLY_SEQ_DCC_CLK, + SUPPLY_SEQ_MIC_BIAS, + SUPPLY_SEQ_DMIC, + SUPPLY_SEQ_AUD_TOP, + SUPPLY_SEQ_AUD_TOP_LAST, + SUPPLY_SEQ_DL_SDM_FIFO_CLK, + SUPPLY_SEQ_DL_SDM, + SUPPLY_SEQ_DL_NCP, + SUPPLY_SEQ_AFE, + /* playback */ + SUPPLY_SEQ_DL_SRC, + SUPPLY_SEQ_DL_ESD_RESIST, + SUPPLY_SEQ_HP_DAMPING_OFF_RESET_CMFB, + SUPPLY_SEQ_HP_MUTE, + SUPPLY_SEQ_DL_LDO_REMOTE_SENSE, + SUPPLY_SEQ_DL_LDO, + SUPPLY_SEQ_DL_NV, + SUPPLY_SEQ_HP_ANA_TRIM, + SUPPLY_SEQ_DL_IBIST, + /* capture */ + SUPPLY_SEQ_UL_PGA, + SUPPLY_SEQ_UL_ADC, + SUPPLY_SEQ_UL_MTKAIF, + SUPPLY_SEQ_UL_SRC_DMIC, + SUPPLY_SEQ_UL_SRC, +}; + +enum { + CH_L = 0, + CH_R, + NUM_CH, +}; + +enum { + DRBIAS_4UA = 0, + DRBIAS_5UA, + DRBIAS_6UA, + DRBIAS_7UA, + DRBIAS_8UA, + DRBIAS_9UA, + DRBIAS_10UA, + DRBIAS_11UA, +}; + +enum { + IBIAS_4UA = 0, + IBIAS_5UA, + IBIAS_6UA, + IBIAS_7UA, +}; + +enum { + IBIAS_ZCD_3UA = 0, + IBIAS_ZCD_4UA, + IBIAS_ZCD_5UA, + IBIAS_ZCD_6UA, +}; + +enum { + MIC_BIAS_1P7 = 0, + MIC_BIAS_1P8, + MIC_BIAS_1P9, + MIC_BIAS_2P0, + MIC_BIAS_2P1, + MIC_BIAS_2P5, + MIC_BIAS_2P6, + MIC_BIAS_2P7, +}; + +/* dl pga gain */ +enum { + DL_GAIN_8DB = 0, + DL_GAIN_0DB = 8, + DL_GAIN_N_1DB = 9, + DL_GAIN_N_10DB = 18, + DL_GAIN_N_22DB = 30, + DL_GAIN_N_40DB = 0x1f, +}; + +/* Mic Type MUX */ +enum { + MIC_TYPE_MUX_IDLE = 0, + MIC_TYPE_MUX_ACC, + MIC_TYPE_MUX_DMIC, + MIC_TYPE_MUX_DCC, + MIC_TYPE_MUX_DCC_ECM_DIFF, + MIC_TYPE_MUX_DCC_ECM_SINGLE, +}; + +/* UL SRC MUX */ +enum { + UL_SRC_MUX_AMIC = 0, + UL_SRC_MUX_DMIC, +}; + +/* MISO MUX */ +enum { + MISO_MUX_UL1_CH1 = 0, + MISO_MUX_UL1_CH2, + MISO_MUX_UL2_CH1, + MISO_MUX_UL2_CH2, +}; + +/* DMIC MUX */ +enum { + DMIC_MUX_DMIC_DATA0 = 0, + DMIC_MUX_DMIC_DATA1_L, + DMIC_MUX_DMIC_DATA1_L_1, + DMIC_MUX_DMIC_DATA1_R, +}; + +/* ADC L MUX */ +enum { + ADC_MUX_IDLE = 0, + ADC_MUX_AIN0, + ADC_MUX_PREAMPLIFIER, + ADC_MUX_IDLE1, +}; + +/* PGA L MUX */ +enum { + PGA_L_MUX_NONE = 0, + PGA_L_MUX_AIN0, + PGA_L_MUX_AIN1, +}; + +/* PGA R MUX */ +enum { + PGA_R_MUX_NONE = 0, + PGA_R_MUX_AIN2, + PGA_R_MUX_AIN3, + PGA_R_MUX_AIN0, +}; + +/* PGA 3 MUX */ +enum { + PGA_3_MUX_NONE = 0, + PGA_3_MUX_AIN3, + PGA_3_MUX_AIN2, +}; + +struct mt6359_priv { + struct device *dev; + struct regmap *regmap; + unsigned int dl_rate[MT6359_AIF_NUM]; + unsigned int ul_rate[MT6359_AIF_NUM]; + int ana_gain[AUDIO_ANALOG_VOLUME_TYPE_MAX]; + unsigned int mux_select[MUX_NUM]; + unsigned int dmic_one_wire_mode; + int dev_counter[DEVICE_NUM]; + int hp_gain_ctl; + int hp_hifi_mode; + int mtkaif_protocol; + struct regulator *avdd_reg; +}; + +#define CODEC_MT6359_NAME "mtk-codec-mt6359" +#define IS_DCC_BASE(type) ((type) == MIC_TYPE_MUX_DCC || \ + (type) == MIC_TYPE_MUX_DCC_ECM_DIFF || \ + (type) == MIC_TYPE_MUX_DCC_ECM_SINGLE) + +#endif/* end _MT6359_H_ */ -- cgit From 1eb629363aa354e63f55f6680b0724165cf2088d Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 19 Aug 2020 20:44:04 +0800 Subject: ASoC: SOF: Intel: hda: import SOUNDWIRE_INIT namespace Make sure the SoundWire driver can be loaded Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200819124404.3734-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 63ca920c8e6e..c0b75d682750 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1207,3 +1207,4 @@ MODULE_LICENSE("Dual BSD/GPL"); MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC); MODULE_IMPORT_NS(SND_SOC_SOF_HDA_AUDIO_CODEC_I915); MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); +MODULE_IMPORT_NS(SOUNDWIRE_INTEL_INIT); -- cgit From a467f2f8ad5f9a21f92b3fa6ad2aac90fa7054fe Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 19 Aug 2020 17:01:03 +0100 Subject: ASoC: qcom: add missing out of memory check on drvdata->clks allocation Currently drvdata->clks is not being checked for an allocation failure, leading to potential null pointer dereferencing. Fix this by adding a check and returning -ENOMEM if an error occurred. Fixes: 1220f6a76e77 ("ASoC: qcom: Add common array to initialize soc based core clocks") Signed-off-by: Colin Ian King Reviewed-by: Rohit kumar Addresses-Coverity: ("Dereference null return value") Link: https://lore.kernel.org/r/20200819160103.164893-1-colin.king@canonical.com Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-apq8016.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index dd9e3dd014f6..5c8ae225cd5d 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -168,6 +168,8 @@ static int apq8016_lpass_init(struct platform_device *pdev) drvdata->clks = devm_kcalloc(dev, variant->num_clks, sizeof(*drvdata->clks), GFP_KERNEL); + if (!drvdata->clks) + return -ENOMEM; drvdata->num_clks = variant->num_clks; for (i = 0; i < drvdata->num_clks; i++) -- cgit From a115ab9b8b93e7f0ff28a4fc869a3222ae921edd Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 19 Aug 2020 20:44:29 +0800 Subject: ASoC: SOF: Intel: add build support for SoundWire Select SoundWire capabilities on newer Intel platforms, starting with CannonLake/CoffeeLake/CometLake. As done for HDaudio, the SoundWire link is an opt-in capability. We explicitly test for ACPI to avoid warnings on unmet dependencies on the SoundWire side. Signed-off-by: Pierre-Louis Bossart Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200819124429.3785-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/Kconfig | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index 3aaf25e4f766..f644010cc568 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -166,6 +166,7 @@ config SND_SOC_SOF_CANNONLAKE_SUPPORT config SND_SOC_SOF_CANNONLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -181,6 +182,7 @@ config SND_SOC_SOF_COFFEELAKE_SUPPORT config SND_SOC_SOF_COFFEELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -196,6 +198,7 @@ config SND_SOC_SOF_ICELAKE_SUPPORT config SND_SOC_SOF_ICELAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -203,6 +206,7 @@ config SND_SOC_SOF_ICELAKE config SND_SOC_SOF_COMETLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -229,6 +233,7 @@ config SND_SOC_SOF_TIGERLAKE_SUPPORT config SND_SOC_SOF_TIGERLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -244,6 +249,7 @@ config SND_SOC_SOF_ELKHARTLAKE_SUPPORT config SND_SOC_SOF_ELKHARTLAKE tristate select SND_SOC_SOF_HDA_COMMON + select SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -329,6 +335,29 @@ config SND_SOC_SOF_HDA This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + bool "SOF support for SoundWire" + depends on SOUNDWIRE && ACPI + help + This adds support for SoundWire with Sound Open Firmware + for Intel(R) platforms. + Say Y if you want to enable SoundWire links with SOF. + If unsure select "N". + +config SND_SOC_SOF_INTEL_SOUNDWIRE_LINK_BASELINE + tristate + select SND_SOC_SOF_INTEL_SOUNDWIRE if SND_SOC_SOF_INTEL_SOUNDWIRE_LINK + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + +config SND_SOC_SOF_INTEL_SOUNDWIRE + tristate + select SOUNDWIRE_INTEL + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level + endif ## SND_SOC_SOF_INTEL_PCI endif ## SND_SOC_SOF_INTEL_TOPLEVEL -- cgit From b50747558855ff94523dbb7f08a8c9fadfdd9110 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Fri, 21 Aug 2020 09:11:53 +0200 Subject: ASoC: fsl-asoc-card: add support for TLV320AIC32x4 codec The TLV320AIC32x4 is commonly used on TQ-Systems starterkit mainboards for i.MX-based SoMs (i.MX6Q/DL, i.MX6UL, i.MX7) and LS1021A. Signed-off-by: Matthias Schiffer Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/20200821071153.7317-2-matthias.schiffer@ew.tq-group.com Signed-off-by: Mark Brown --- sound/soc/fsl/Kconfig | 2 +- sound/soc/fsl/fsl-asoc-card.c | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index 1c4ca5ec8caf..3f76ff71ea47 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -324,7 +324,7 @@ config SND_SOC_FSL_ASOC_CARD help ALSA SoC Audio support with ASRC feature for Freescale SoCs that have ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888, - CS4271, CS4272 and SGTL5000. + CS4271, CS4272, SGTL5000 and TLV320AIC32x4. Say Y if you want to add support for Freescale Generic ASoC Sound Card. config SND_SOC_IMX_AUDMIX diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c index 32f8f756e6bb..a2dd3b6b7fec 100644 --- a/sound/soc/fsl/fsl-asoc-card.c +++ b/sound/soc/fsl/fsl-asoc-card.c @@ -617,6 +617,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev) codec_dai_name = "sgtl5000"; priv->codec_priv.mclk_id = SGTL5000_SYSCLK; priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + } else if (of_device_is_compatible(np, "fsl,imx-audio-tlv320aic32x4")) { + codec_dai_name = "tlv320aic32x4-hifi"; + priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; } else if (of_device_is_compatible(np, "fsl,imx-audio-wm8962")) { codec_dai_name = "wm8962"; priv->codec_priv.mclk_id = WM8962_SYSCLK_MCLK; @@ -860,6 +863,7 @@ static const struct of_device_id fsl_asoc_card_dt_ids[] = { { .compatible = "fsl,imx-audio-ac97", }, { .compatible = "fsl,imx-audio-cs42888", }, { .compatible = "fsl,imx-audio-cs427x", }, + { .compatible = "fsl,imx-audio-tlv320aic32x4", }, { .compatible = "fsl,imx-audio-sgtl5000", }, { .compatible = "fsl,imx-audio-wm8962", }, { .compatible = "fsl,imx-audio-wm8960", }, -- cgit From 751365035b4f360369ed6b0990283fd25d4ee32c Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Thu, 20 Aug 2020 21:45:42 +0800 Subject: ASoC: intel: sof_sdw: add .exit callback function We may allocate some resources in sof_sdw_codec_info .init function. Adding a corresponding .exit function can help to release these resources. Signed-off-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Reviewed-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200820134542.8682-1-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 31 +++++++++++++++++++++++++++++++ sound/soc/intel/boards/sof_sdw_common.h | 1 + 2 files changed, 32 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 2463d432bf4d..4bc1ed757009 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -1032,12 +1032,43 @@ static int mc_probe(struct platform_device *pdev) return ret; } +static int mc_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + struct snd_soc_dai_link *link; + int ret; + int i, j; + + for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) { + if (!codec_info_list[i].exit) + continue; + /* + * We don't need to call .exit function if there is no matched + * dai link found. + */ + for_each_card_prelinks(card, j, link) { + if (!strcmp(link->codecs[0].dai_name, + codec_info_list[i].dai_name)) { + ret = codec_info_list[i].exit(&pdev->dev, link); + if (ret) + dev_warn(&pdev->dev, + "codec exit failed %d\n", + ret); + break; + } + } + } + + return 0; +} + static struct platform_driver sof_sdw_driver = { .driver = { .name = "sof_sdw", .pm = &snd_soc_pm_ops, }, .probe = mc_probe, + .remove = mc_remove, }; module_platform_driver(sof_sdw_driver); diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 12e32439ba46..76d6c0c3839d 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -63,6 +63,7 @@ struct sof_sdw_codec_info { struct sof_sdw_codec_info *info, bool playback); + int (*exit)(struct device *dev, struct snd_soc_dai_link *dai_link); bool late_probe; int (*codec_card_late_probe)(struct snd_soc_card *card); }; -- cgit From fcea8b023a5f06ea0180ae65b01520b0414ee325 Mon Sep 17 00:00:00 2001 From: Adam Thomson Date: Fri, 21 Aug 2020 15:22:59 +0100 Subject: ASoC: da7219: Fix I/O voltage range configuration during probe Previous improvements around handling device and codec level probe functionality added the possibility of the voltage level being undefined for the scenario where the IO voltage retrieved from the regulator supply was below 1.2V, whereas previously the code defaulted to the 2.5V to 3.6V range in that case. This commit restores the default value to avoid this happening. Fixes: aa5b18d1c290 ("ASoC: da7219: Move soft reset handling to codec level probe") Reported-by: Pierre-Louis Bossart Signed-off-by: Adam Thomson Link: https://lore.kernel.org/r/20200821142259.C2ECE3FB96@swsrvapps-01.diasemi.com Signed-off-by: Mark Brown --- sound/soc/codecs/da7219.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 301d8be2c5f7..0b3b7909efc9 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -1864,6 +1864,9 @@ static int da7219_handle_supplies(struct snd_soc_component *component, return ret; } + /* Default to upper range */ + *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; + /* Determine VDDIO voltage provided */ vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer; ret = regulator_get_voltage(vddio); @@ -1871,8 +1874,6 @@ static int da7219_handle_supplies(struct snd_soc_component *component, dev_warn(component->dev, "Invalid VDDIO voltage\n"); else if (ret < 2800000) *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V; - else - *io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V; /* Enable main supplies */ ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies); -- cgit From 14b51ccd26742811ced3ee5e52ee6544f5d6105c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 23 Aug 2020 13:32:51 +0200 Subject: ALSA: usb-audio: Correct wrongly matching entries with audio class The commit 51ab5d77dcb8 ("ALSA: usb-audio: Properly match with audio interface class") converted the quirk entries that have both vid/pid pair and bInterface fields to match with all those with a new macro USB_AUDIO_CLASS(). However, it turned out that those are false conversions; all those (but the unknown KeithMcMillen device) are actually with vendor-specific interface class, hence the conversions broke the matching. This patch corrects those entries to the right one, USB_DEVICE_VENDOR_SPEC() (and USB_DEVICE() for KeithMcMillen to be sure), and drop the unused USB_AUDIO_CLASS macro again. Fixes: 51ab5d77dcb8 ("ALSA: usb-audio: Properly match with audio interface class") Reported-by: Alexander Tsoy Link: https://lore.kernel.org/r/20200823113251.10175-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/quirks-table.h | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 969c79d0c688..7fe9d3e75d59 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -35,14 +35,6 @@ .bInterfaceClass = USB_CLASS_AUDIO, \ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL -/* Another standard entry matching with vid/pid and the audio class */ -#define USB_AUDIO_CLASS(vend, prod) \ - .match_flags = USB_DEVICE_ID_MATCH_DEVICE | \ - USB_DEVICE_ID_MATCH_INT_CLASS, \ - .idVendor = vend, \ - .idProduct = prod, \ - .bInterfaceClass = USB_CLASS_AUDIO - /* FTDI devices */ { USB_DEVICE(0x0403, 0xb8d8), @@ -77,13 +69,13 @@ }, /* E-Mu 0202 USB */ -{ USB_AUDIO_CLASS(0x041e, 0x3f02) }, +{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f02) }, /* E-Mu 0404 USB */ -{ USB_AUDIO_CLASS(0x041e, 0x3f04) }, +{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f04) }, /* E-Mu Tracker Pre */ -{ USB_AUDIO_CLASS(0x041e, 0x3f0a) }, +{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f0a) }, /* E-Mu 0204 USB */ -{ USB_AUDIO_CLASS(0x041e, 0x3f19) }, +{ USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f19) }, /* * HP Wireless Audio @@ -2756,7 +2748,7 @@ YAMAHA_DEVICE(0x7010, "UB99"), }, /* KeithMcMillen Stringport */ -{ USB_AUDIO_CLASS(0x1f38, 0x0001) }, +{ USB_DEVICE(0x1f38, 0x0001) }, /* FIXME: should be more restrictive matching */ /* Miditech devices */ { @@ -2979,7 +2971,7 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), }, { /* Tascam US122 MKII - playback-only support */ - USB_AUDIO_CLASS(0x0644, 0x8021), + USB_DEVICE_VENDOR_SPEC(0x0644, 0x8021), .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { .vendor_name = "TASCAM", .product_name = "US122 MKII", @@ -3611,4 +3603,3 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"), #undef USB_DEVICE_VENDOR_SPEC #undef USB_AUDIO_DEVICE -#undef USB_AUDIO_CLASS -- cgit From 69a785da525e8bdfaa8a9c000fb3af714f0d719b Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 21 Aug 2020 14:55:48 -0500 Subject: ASoC: Intel: modify SoundWire version id in acpi match table The SoundWire version id of the existing RT1308, RT711, and RT715 codecs should be 2 (index for SoundWire 1.1), it was mistakenly set as 1 which pointed to the wrong version (SoundWire 1.0). This off-by-one error had no functional impact so far since the version number was not used, however in future patches this version will be required. Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-cml-match.c | 10 +++++----- sound/soc/intel/common/soc-acpi-intel-icl-match.c | 10 +++++----- sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index dee1f0fa998b..51535cd60a0a 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -112,7 +112,7 @@ static const struct snd_soc_acpi_link_adr cml_rvp[] = { static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { - .adr = 0x000010025D071100, + .adr = 0x000020025D071100, .num_endpoints = 1, .endpoints = &single_endpoint, } @@ -120,7 +120,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { { - .adr = 0x000110025D130800, + .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &single_endpoint, } @@ -128,7 +128,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { { - .adr = 0x000110025D130800, + .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &spk_l_endpoint, } @@ -136,7 +136,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { { - .adr = 0x000210025D130800, + .adr = 0x000220025D130800, .num_endpoints = 1, .endpoints = &spk_r_endpoint, } @@ -144,7 +144,7 @@ static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { { - .adr = 0x000310025D071500, + .adr = 0x000320025D071500, .num_endpoints = 1, .endpoints = &single_endpoint, } diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index 6927bbbc66fc..ebe13197410f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -73,7 +73,7 @@ static const struct snd_soc_acpi_link_adr icl_rvp[] = { static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { - .adr = 0x000010025D071100, + .adr = 0x000020025D071100, .num_endpoints = 1, .endpoints = &single_endpoint, } @@ -81,7 +81,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { { - .adr = 0x000110025D130800, + .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &single_endpoint, } @@ -89,7 +89,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { { - .adr = 0x000110025D130800, + .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &spk_l_endpoint, } @@ -97,7 +97,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { { - .adr = 0x000210025D130800, + .adr = 0x000220025D130800, .num_endpoints = 1, .endpoints = &spk_r_endpoint, } @@ -105,7 +105,7 @@ static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { { - .adr = 0x000310025D071500, + .adr = 0x000320025D071500, .num_endpoints = 1, .endpoints = &single_endpoint, } diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 2ffa608d987d..472f58ddda1f 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -37,7 +37,7 @@ static const struct snd_soc_acpi_endpoint spk_r_endpoint = { static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { { - .adr = 0x000010025D071100, + .adr = 0x000020025D071100, .num_endpoints = 1, .endpoints = &single_endpoint, } -- cgit From 6f7cf9125ed43daaf4c5dd34ebcd96c978fcd2b2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 21 Aug 2020 14:55:49 -0500 Subject: ASoC: Intel: soc-acpi: cnl: add support for rt5682 on SoundWire link2 Add one of the configurations for rt5682 w/ the Up Extreme Advanced Audio mode using the SoundWire link2. Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 33 ++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 6a0bcc1a8429..7d61e0da808b 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -27,8 +27,39 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[] = { }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_machines); +static const struct snd_soc_acpi_endpoint single_endpoint = { + .num = 0, + .aggregated = 0, + .group_position = 0, + .group_id = 0, +}; + +static const struct snd_soc_acpi_adr_device rt5682_2_adr[] = { + { + .adr = 0x000220025D568200, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_link_adr up_extreme_rt5682_2[] = { + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt5682_2_adr), + .adr_d = rt5682_2_adr, + }, + {} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_sdw_machines[] = { - {}, + { + .link_mask = BIT(2), + .links = up_extreme_rt5682_2, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-cnl.ri", + .sof_tplg_filename = "sof-cnl-rt5682-sdw2.tplg" + }, + {} }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_cnl_sdw_machines); -- cgit From b161a12192f4b88cede8a563a6ebd2336d905d18 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 21 Aug 2020 14:55:50 -0500 Subject: ASoC: Intel: sof-soundwire: add support for rt5682 on link2 The UpExtreme board provides support for SoundWire link2 in 2 of the 3 advanced modes. Let's use it w/ rt5682. Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 4bc1ed757009..100c0a83a6ad 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -173,6 +173,11 @@ static struct snd_soc_codec_conf codec_conf[] = { .dlc = COMP_CODEC_CONF("sdw:0:25d:5682:0"), .name_prefix = "rt5682", }, + /* rt5682 on link2 */ + { + .dlc = COMP_CODEC_CONF("sdw:2:25d:5682:0"), + .name_prefix = "rt5682", + }, }; static struct snd_soc_dai_link_component dmic_component[] = { -- cgit From 6cb8bd60ba5ca9eb8f05f3f8351bbbcb6b92c525 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 21 Aug 2020 14:55:51 -0500 Subject: ASoC: Intel: soc-acpi: mirror CML and TGL configurations Some TGL devices use the same audio hardware as on CML platforms, with RT711 on link0, RT1308 on link1 and optionally link2, and RT715 on link 3. To clarify configurations, the rt1308 configurations are split between single amp on link1 and dual amps on link1. The case with two amps on different links is already identified with the group1 attribute. Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-cml-match.c | 6 +- sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 100 +++++++++++++++++++++- 2 files changed, 100 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index 51535cd60a0a..8ac01a2d5886 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -118,7 +118,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { } }; -static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { +static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = { { .adr = 0x000120025D130800, .num_endpoints = 1, @@ -182,8 +182,8 @@ static const struct snd_soc_acpi_link_adr cml_3_in_1_mono_amp[] = { }, { .mask = BIT(1), - .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr_d = rt1308_1_adr, + .num_adr = ARRAY_SIZE(rt1308_1_single_adr), + .adr_d = rt1308_1_single_adr, }, { .mask = BIT(3), diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 472f58ddda1f..aabb49617d37 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -43,7 +43,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { } }; -static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { +static const struct snd_soc_acpi_adr_device rt1308_1_dual_adr[] = { { .adr = 0x000120025D130800, .num_endpoints = 1, @@ -56,6 +56,38 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = { + { + .adr = 0x000120025D130800, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { + { + .adr = 0x000120025D130800, + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { + { + .adr = 0x000220025D130800, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { + { + .adr = 0x000320025D071500, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + static const struct snd_soc_acpi_adr_device mx8373_1_adr[] = { { .adr = 0x000123019F837300, @@ -94,8 +126,8 @@ static const struct snd_soc_acpi_link_adr tgl_rvp[] = { }, { .mask = BIT(1), - .num_adr = ARRAY_SIZE(rt1308_1_adr), - .adr_d = rt1308_1_adr, + .num_adr = ARRAY_SIZE(rt1308_1_dual_adr), + .adr_d = rt1308_1_dual_adr, }, {} }; @@ -114,6 +146,49 @@ static const struct snd_soc_acpi_link_adr tgl_chromebook_base[] = { {} }; +static const struct snd_soc_acpi_link_adr tgl_3_in_1_default[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr_d = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_group1_adr), + .adr_d = rt1308_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1308_2_group1_adr), + .adr_d = rt1308_2_group1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt715_3_adr), + .adr_d = rt715_3_adr, + }, + {} +}; + +static const struct snd_soc_acpi_link_adr tgl_3_in_1_mono_amp[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_0_adr), + .adr_d = rt711_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1308_1_single_adr), + .adr_d = rt1308_1_single_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt715_3_adr), + .adr_d = rt715_3_adr, + }, + {} +}; + static struct snd_soc_acpi_codecs tgl_max98373_amp = { .num_codecs = 1, .codecs = {"MX98373"} @@ -150,6 +225,25 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { + { + .link_mask = 0xF, /* 4 active links required */ + .links = tgl_3_in_1_default, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1308-rt715.tplg", + }, + { + /* + * link_mask should be 0xB, but all links are enabled by BIOS. + * This entry will be selected if there is no rt1308 exposed + * on link2 since it will fail to match the above entry. + */ + .link_mask = 0xF, + .links = tgl_3_in_1_mono_amp, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1308-mono-rt715.tplg", + }, { .link_mask = 0x3, /* rt711 on link 0 and 2 rt1308s on link 1 */ .links = tgl_rvp, -- cgit From 44751fc5f0de07b0a1ab57e9beab9789638d76d2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 21 Aug 2020 14:55:52 -0500 Subject: ASoC: Intel: soc-acpi: add support for SDCA boards The description and board layout is similar to previous ones for CometLake and TigerLake, except for a bump to SoundWire 1.2 and updates to part numbers to reflect the SDCA (SoundWire Device Class for Audio) hardware support. Note that one of the RT1316 amplifiers uses a non-zero UniqueID which is not required and will be ignored. Reviewed-by: Ranjani Sridharan Reviewed-by: Jaska Uimonen Reviewed-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-cml-match.c | 63 +++++++++++++++++++++++ sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 63 +++++++++++++++++++++++ 2 files changed, 126 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index 8ac01a2d5886..ec01884ef93d 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -150,6 +150,38 @@ static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { + { + .adr = 0x000030025D071101, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { + { + .adr = 0x000131025D131601, /* unique ID is set for some reason */ + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { + { + .adr = 0x000230025D131601, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt714_3_adr[] = { + { + .adr = 0x000330025D071401, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + static const struct snd_soc_acpi_link_adr cml_3_in_1_default[] = { { .mask = BIT(0), @@ -193,6 +225,30 @@ static const struct snd_soc_acpi_link_adr cml_3_in_1_mono_amp[] = { {} }; +static const struct snd_soc_acpi_link_adr cml_3_in_1_sdca[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group1_adr), + .adr_d = rt1316_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group1_adr), + .adr_d = rt1316_2_group1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt714_3_adr), + .adr_d = rt714_3_adr, + }, + {} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { { .link_mask = 0xF, /* 4 active links required */ @@ -201,6 +257,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_cml_sdw_machines[] = { .sof_fw_filename = "sof-cml.ri", .sof_tplg_filename = "sof-cml-rt711-rt1308-rt715.tplg", }, + { + .link_mask = 0xF, /* 4 active links required */ + .links = cml_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-cml.ri", + .sof_tplg_filename = "sof-cml-rt711-rt1316-rt714.tplg", + }, { /* * link_mask should be 0xB, but all links are enabled by BIOS. diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index aabb49617d37..6816847bee40 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -109,6 +109,38 @@ static const struct snd_soc_acpi_adr_device rt5682_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { + { + .adr = 0x000030025D071101, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { + { + .adr = 0x000131025D131601, /* unique ID is set for some reason */ + .num_endpoints = 1, + .endpoints = &spk_l_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { + { + .adr = 0x000230025D131601, + .num_endpoints = 1, + .endpoints = &spk_r_endpoint, + } +}; + +static const struct snd_soc_acpi_adr_device rt714_3_adr[] = { + { + .adr = 0x000330025D071401, + .num_endpoints = 1, + .endpoints = &single_endpoint, + } +}; + static const struct snd_soc_acpi_link_adr tgl_i2s_rt1308[] = { { .mask = BIT(0), @@ -189,6 +221,30 @@ static const struct snd_soc_acpi_link_adr tgl_3_in_1_mono_amp[] = { {} }; +static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca[] = { + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt711_sdca_0_adr), + .adr_d = rt711_sdca_0_adr, + }, + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt1316_1_group1_adr), + .adr_d = rt1316_1_group1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1316_2_group1_adr), + .adr_d = rt1316_2_group1_adr, + }, + { + .mask = BIT(3), + .num_adr = ARRAY_SIZE(rt714_3_adr), + .adr_d = rt714_3_adr, + }, + {} +}; + static struct snd_soc_acpi_codecs tgl_max98373_amp = { .num_codecs = 1, .codecs = {"MX98373"} @@ -244,6 +300,13 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { .sof_fw_filename = "sof-tgl.ri", .sof_tplg_filename = "sof-tgl-rt711-rt1308-mono-rt715.tplg", }, + { + .link_mask = 0xF, /* 4 active links required */ + .links = tgl_3_in_1_sdca, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt711-rt1316-rt714.tplg", + }, { .link_mask = 0x3, /* rt711 on link 0 and 2 rt1308s on link 1 */ .links = tgl_rvp, -- cgit From e300486ad94d2608ebc3aaed4e03e86eeeb97084 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 21 Aug 2020 14:55:53 -0500 Subject: ASoC: Intel: tgl_max98373: fix a runtime pm issue in multi-thread case When the playback & capture streams are stopped simultaneously, the SOF PCI device will remain pm_runtime active. The root-cause is a race condition with two threads reaching the trigger function at the same time. They see another stream is active so the dapm pin is not disabled, so the codec remains active as well as the parent PCI device. For max98373, the capture stream provides feedback when playback is working and it is unused when playback is stopped. So the dapm pin should be set only when playback is active. Reviewed-by: Ranjani Sridharan Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_maxim_common.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_maxim_common.c b/sound/soc/intel/boards/sof_maxim_common.c index 1a6961592029..b6e63ea13d64 100644 --- a/sound/soc/intel/boards/sof_maxim_common.c +++ b/sound/soc/intel/boards/sof_maxim_common.c @@ -66,6 +66,10 @@ int max98373_trigger(struct snd_pcm_substream *substream, int cmd) int j; int ret = 0; + /* set spk pin by playback only */ + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return 0; + for_each_rtd_codec_dais(rtd, j, codec_dai) { struct snd_soc_component *component = codec_dai->component; struct snd_soc_dapm_context *dapm = @@ -86,9 +90,6 @@ int max98373_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - /* Make sure no streams are active before disable pin */ - if (snd_soc_dai_active(codec_dai) != 1) - break; ret = snd_soc_dapm_disable_pin(dapm, pin_name); if (!ret) snd_soc_dapm_sync(dapm); -- cgit From 65fae64d79d2cbad43819c1ab83390594fac7fcb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 21 Aug 2020 14:55:54 -0500 Subject: ASoC: codecs: max98373-sdw: add missing test on resume All existing SoundWire codecs follow the same pattern on resume, except for this codec which doesn't test if the hardware is initialized. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/max98373-sdw.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 5fe724728e84..6469612c42cb 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -256,6 +256,9 @@ static __maybe_unused int max98373_resume(struct device *dev) struct max98373_priv *max98373 = dev_get_drvdata(dev); unsigned long time; + if (!max98373->hw_init) + return 0; + if (!slave->unattach_request) goto regmap_sync; -- cgit From 2e2d287bbe613be4848e83be9aa8e99e8b9f7dc0 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 21 Aug 2020 14:55:55 -0500 Subject: ASoC: Intel: sof_sdw: check SoundWire version when matching codec Some codecs with the same part id but different SoundWire versions have different configurations. So we have to separate them in codec_info_list[]. Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 35 ++++++++++++++++++--------------- sound/soc/intel/boards/sof_sdw_common.h | 1 + 2 files changed, 20 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 100c0a83a6ad..dca981f7c7d3 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -253,18 +253,25 @@ static struct sof_sdw_codec_info codec_info_list[] = { }, }; -static inline int find_codec_info_part(unsigned int part_id) +static inline int find_codec_info_part(u64 adr) { + unsigned int part_id, sdw_version; int i; + part_id = SDW_PART_ID(adr); + sdw_version = SDW_VERSION(adr); for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) - if (part_id == codec_info_list[i].id) - break; + /* + * A codec info is for all sdw version with the part id if + * version_id is not specified in the codec info. + */ + if (part_id == codec_info_list[i].id && + (!codec_info_list[i].version_id || + sdw_version == codec_info_list[i].version_id)) + return i; - if (i == ARRAY_SIZE(codec_info_list)) - return -EINVAL; + return -EINVAL; - return i; } static inline int find_codec_info_acpi(const u8 *acpi_id) @@ -310,13 +317,12 @@ static int get_sdw_dailink_info(const struct snd_soc_acpi_link_adr *links, for (link = links; link->num_adr; link++) { const struct snd_soc_acpi_endpoint *endpoint; - int part_id, codec_index; + int codec_index; int stream; u64 adr; adr = link->adr_d->adr; - part_id = SDW_PART_ID(adr); - codec_index = find_codec_info_part(part_id); + codec_index = find_codec_info_part(adr); if (codec_index < 0) return codec_index; @@ -444,7 +450,7 @@ static int create_codec_dai_name(struct device *dev, if (!codec[comp_index].name) return -ENOMEM; - codec_index = find_codec_info_part(part_id); + codec_index = find_codec_info_part(adr); if (codec_index < 0) return codec_index; @@ -468,11 +474,9 @@ static int set_codec_init_func(const struct snd_soc_acpi_link_adr *link, * same group. */ for (i = 0; i < link->num_adr; i++) { - unsigned int part_id; int codec_index; - part_id = SDW_PART_ID(link->adr_d[i].adr); - codec_index = find_codec_info_part(part_id); + codec_index = find_codec_info_part(link->adr_d[i].adr); if (codec_index < 0) return codec_index; @@ -576,7 +580,7 @@ static int create_sdw_dailink(struct device *dev, int *be_index, struct snd_soc_dai_link_component *codecs; int cpu_dai_id[SDW_MAX_CPU_DAIS]; int cpu_dai_num, cpu_dai_index; - unsigned int part_id, group_id; + unsigned int group_id; int codec_idx = 0; int i = 0, j = 0; int codec_index; @@ -618,8 +622,7 @@ static int create_sdw_dailink(struct device *dev, int *be_index, } /* find codec info to create BE DAI */ - part_id = SDW_PART_ID(link->adr_d[0].adr); - codec_index = find_codec_info_part(part_id); + codec_index = find_codec_info_part(link->adr_d[0].adr); if (codec_index < 0) return codec_index; diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 76d6c0c3839d..cb1320348d0b 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -52,6 +52,7 @@ enum { struct sof_sdw_codec_info { const int id; + const int version_id; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; const bool direction[2]; // playback & capture support -- cgit From 535df653f75583a25c6bbb2eaaa0b121b47460c4 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 21 Aug 2020 14:55:56 -0500 Subject: ASoC: Intel: sof_sdw: rename id as part_id The "id" field in sof_sdw_codec_info struct is actually the "part id". Rename to prevent confusions. Reviewed-by: Ranjani Sridharan Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 14 +++++++------- sound/soc/intel/boards/sof_sdw_common.h | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index dca981f7c7d3..ca231459ac10 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -212,20 +212,20 @@ static const struct snd_soc_ops sdw_ops = { static struct sof_sdw_codec_info codec_info_list[] = { { - .id = 0x700, + .part_id = 0x700, .direction = {true, true}, .dai_name = "rt700-aif1", .init = sof_sdw_rt700_init, }, { - .id = 0x711, + .part_id = 0x711, .direction = {true, true}, .dai_name = "rt711-aif1", .init = sof_sdw_rt711_init, .exit = sof_sdw_rt711_exit, }, { - .id = 0x1308, + .part_id = 0x1308, .acpi_id = "10EC1308", .direction = {true, false}, .dai_name = "rt1308-aif", @@ -233,20 +233,20 @@ static struct sof_sdw_codec_info codec_info_list[] = { .init = sof_sdw_rt1308_init, }, { - .id = 0x715, + .part_id = 0x715, .direction = {false, true}, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, }, { - .id = 0x8373, + .part_id = 0x8373, .direction = {true, true}, .dai_name = "max98373-aif1", .init = sof_sdw_mx8373_init, .codec_card_late_probe = sof_sdw_mx8373_late_probe, }, { - .id = 0x5682, + .part_id = 0x5682, .direction = {true, true}, .dai_name = "rt5682-sdw", .init = sof_sdw_rt5682_init, @@ -265,7 +265,7 @@ static inline int find_codec_info_part(u64 adr) * A codec info is for all sdw version with the part id if * version_id is not specified in the codec info. */ - if (part_id == codec_info_list[i].id && + if (part_id == codec_info_list[i].part_id && (!codec_info_list[i].version_id || sdw_version == codec_info_list[i].version_id)) return i; diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index cb1320348d0b..ce0680a7b180 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -51,7 +51,7 @@ enum { #define SOF_SDW_NO_AGGREGATION BIT(12) struct sof_sdw_codec_info { - const int id; + const int part_id; const int version_id; int amp_num; const u8 acpi_id[ACPI_ID_LEN]; -- cgit From 626200df2498ff3044170d0f02b463ac0ec899c5 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Fri, 21 Aug 2020 14:55:58 -0500 Subject: SoC: Intel: sof_sdw: Add support for product Ripto Ripto is another product based on TGL with the same audio hardware configuration as Volteer. Reviewed-by: Bard Liao Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-12-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index ca231459ac10..44d06fa48b24 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -123,6 +123,15 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { .driver_data = (void *)(SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC | SOF_SDW_FOUR_SPK), }, + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Google"), + DMI_MATCH(DMI_PRODUCT_NAME, "Ripto"), + }, + .driver_data = (void *)(SOF_SDW_TGL_HDMI | SOF_SDW_PCH_DMIC | + SOF_SDW_FOUR_SPK), + }, {} }; -- cgit From 3e1734b64ce786c54dc98adcfe67941e6011d735 Mon Sep 17 00:00:00 2001 From: Sathyanarayana Nujella Date: Fri, 21 Aug 2020 14:55:59 -0500 Subject: ASoC: Intel: sof_rt5682: override quirk data for tgl_max98373_rt5682 A Chrome System based on tgl_max98373_rt5682 has different SSP interface configurations. Using DMI data of this variant DUT, override quirk data. Reviewed-by: Guennadi Liakhovetski Signed-off-by: Sathyanarayana Nujella Signed-off-by: Mac Chiang Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-13-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 0129d23694ed..9a6f10ede427 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -119,6 +119,19 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { .driver_data = (void *)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0)), }, + { + .callback = sof_rt5682_quirk_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Terrador"), + }, + .driver_data = (void *)(SOF_RT5682_MCLK_EN | + SOF_RT5682_SSP_CODEC(0) | + SOF_SPEAKER_AMP_PRESENT | + SOF_MAX98373_SPEAKER_AMP_PRESENT | + SOF_RT5682_SSP_AMP(2) | + SOF_RT5682_NUM_HDMIDEV(4)), + }, {} }; -- cgit From 5253a73d567dcd75e62834ff5f502ea9470e5722 Mon Sep 17 00:00:00 2001 From: Sathyanarayana Nujella Date: Fri, 21 Aug 2020 14:56:00 -0500 Subject: ASoC: SOF: Add topology filename override based on dmi data match Add topology filename override based on system DMI data matching, typically to account for a different hardware layout. In ACPI based systems, the tplg_filename is pre-defined in an ACPI machine table. When a DMI quirk is detected, the sof_pdata->tplg_filename is not set with the hard-coded ACPI value, and instead is set with the DMI-specific filename. Reviewed-by: Guennadi Liakhovetski Signed-off-by: Sathyanarayana Nujella Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-14-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 8 +++++++- sound/soc/sof/sof-pci-dev.c | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c0b75d682750..b8157c1f37f3 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -1179,7 +1179,13 @@ void hda_machine_select(struct snd_sof_dev *sdev) mach = snd_soc_acpi_find_machine(desc->machines); if (mach) { - sof_pdata->tplg_filename = mach->sof_tplg_filename; + /* + * If tplg file name is overridden, use it instead of + * the one set in mach table + */ + if (!sof_pdata->tplg_filename) + sof_pdata->tplg_filename = mach->sof_tplg_filename; + sof_pdata->machine = mach; if (mach->link_mask) { diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index aa3532ba1434..f3a8140773db 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -35,8 +35,28 @@ static int sof_pci_debug; module_param_named(sof_pci_debug, sof_pci_debug, int, 0444); MODULE_PARM_DESC(sof_pci_debug, "SOF PCI debug options (0x0 all off)"); +static const char *sof_override_tplg_name; + #define SOF_PCI_DISABLE_PM_RUNTIME BIT(0) +static int sof_tplg_cb(const struct dmi_system_id *id) +{ + sof_override_tplg_name = id->driver_data; + return 1; +} + +static const struct dmi_system_id sof_tplg_table[] = { + { + .callback = sof_tplg_cb, + .matches = { + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Terrador"), + }, + .driver_data = "sof-tgl-rt5682-ssp0-max98373-ssp2.tplg", + }, + {} +}; + static const struct dmi_system_id community_key_platforms[] = { { .ident = "Up Squared", @@ -347,6 +367,10 @@ static int sof_pci_probe(struct pci_dev *pci, sof_pdata->tplg_filename_prefix = sof_pdata->desc->default_tplg_path; + dmi_check_system(sof_tplg_table); + if (sof_override_tplg_name) + sof_pdata->tplg_filename = sof_override_tplg_name; + #if IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE) /* set callback to enable runtime_pm */ sof_pdata->sof_probe_complete = sof_pci_probe_complete; -- cgit From b75bea4b8834c1253b00d5f8df2dd3ce09d2e305 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 21 Aug 2020 14:56:01 -0500 Subject: ASoC: intel: sof_sdw: add rt711 rt1316 rt714 SDCA codec support. Add rt711, rt1316, and rt714 SDCA codecs support in sof_sdw machine driver. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-15-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 3 + sound/soc/intel/boards/Makefile | 7 +- sound/soc/intel/boards/sof_sdw.c | 37 ++++++ sound/soc/intel/boards/sof_sdw_common.h | 19 +++ sound/soc/intel/boards/sof_sdw_rt1316.c | 113 ++++++++++++++++++ sound/soc/intel/boards/sof_sdw_rt711_sdca.c | 174 ++++++++++++++++++++++++++++ sound/soc/intel/boards/sof_sdw_rt715_sdca.c | 42 +++++++ 7 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 sound/soc/intel/boards/sof_sdw_rt1316.c create mode 100644 sound/soc/intel/boards/sof_sdw_rt711_sdca.c create mode 100644 sound/soc/intel/boards/sof_sdw_rt715_sdca.c (limited to 'sound') diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index d96fc1313434..12dd41796e82 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -569,9 +569,12 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH select SND_SOC_MAX98373_SDW select SND_SOC_RT700_SDW select SND_SOC_RT711_SDW + select SND_SOC_RT711_SDCA_SDW select SND_SOC_RT1308_SDW select SND_SOC_RT1308 + select SND_SOC_RT1316_SDW select SND_SOC_RT715_SDW + select SND_SOC_RT715_SDCA_SDW select SND_SOC_RT5682_SDW select SND_SOC_DMIC help diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index dc04acb911b6..de7cc9b86354 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -35,9 +35,10 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o hda_dsp_common.o snd-soc-ehl-rt5660-objs := ehl_rt5660.o hda_dsp_common.o snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_max98373.o \ - sof_sdw_rt711.o sof_sdw_rt700.o \ - sof_sdw_rt1308.o sof_sdw_rt715.o \ - sof_sdw_rt5682.o \ + sof_sdw_rt1308.o sof_sdw_rt1316.o \ + sof_sdw_rt5682.o sof_sdw_rt700.o \ + sof_sdw_rt711.o sof_sdw_rt711_sdca.o \ + sof_sdw_rt715.o sof_sdw_rt715_sdca.o \ sof_maxim_common.o \ sof_sdw_dmic.o sof_sdw_hdmi.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 44d06fa48b24..00d10e83872a 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -141,6 +141,10 @@ static struct snd_soc_codec_conf codec_conf[] = { .dlc = COMP_CODEC_CONF("sdw:0:25d:711:0"), .name_prefix = "rt711", }, + { + .dlc = COMP_CODEC_CONF("sdw:0:25d:711:1"), + .name_prefix = "rt711", + }, /* rt1308 w/ I2S connection */ { .dlc = COMP_CODEC_CONF("i2c-10EC1308:00"), @@ -187,6 +191,18 @@ static struct snd_soc_codec_conf codec_conf[] = { .dlc = COMP_CODEC_CONF("sdw:2:25d:5682:0"), .name_prefix = "rt5682", }, + { + .dlc = COMP_CODEC_CONF("sdw:1:25d:1316:1"), + .name_prefix = "rt1316-1", + }, + { + .dlc = COMP_CODEC_CONF("sdw:2:25d:1316:1"), + .name_prefix = "rt1316-2", + }, + { + .dlc = COMP_CODEC_CONF("sdw:3:25d:714:1"), + .name_prefix = "rt714", + }, }; static struct snd_soc_dai_link_component dmic_component[] = { @@ -228,6 +244,15 @@ static struct sof_sdw_codec_info codec_info_list[] = { }, { .part_id = 0x711, + .version_id = 3, + .direction = {true, true}, + .dai_name = "rt711-sdca-aif1", + .init = sof_sdw_rt711_sdca_init, + .exit = sof_sdw_rt711_sdca_exit, + }, + { + .part_id = 0x711, + .version_id = 2, .direction = {true, true}, .dai_name = "rt711-aif1", .init = sof_sdw_rt711_init, @@ -241,6 +266,18 @@ static struct sof_sdw_codec_info codec_info_list[] = { .ops = &sof_sdw_rt1308_i2s_ops, .init = sof_sdw_rt1308_init, }, + { + .part_id = 0x1316, + .direction = {true, true}, + .dai_name = "rt1316-aif", + .init = sof_sdw_rt1316_init, + }, + { + .part_id = 0x714, + .direction = {false, true}, + .dai_name = "rt715-aif2", + .init = sof_sdw_rt715_sdca_init, + }, { .part_id = 0x715, .direction = {false, true}, diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index ce0680a7b180..6a5d46589baf 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -96,6 +96,13 @@ int sof_sdw_rt711_init(const struct snd_soc_acpi_link_adr *link, bool playback); int sof_sdw_rt711_exit(struct device *dev, struct snd_soc_dai_link *dai_link); +/* RT711-SDCA support */ +int sof_sdw_rt711_sdca_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); +int sof_sdw_rt711_sdca_exit(struct device *dev, struct snd_soc_dai_link *dai_link); + /* RT700 support */ int sof_sdw_rt700_init(const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, @@ -110,12 +117,24 @@ int sof_sdw_rt1308_init(const struct snd_soc_acpi_link_adr *link, struct sof_sdw_codec_info *info, bool playback); +/* RT1316 support */ +int sof_sdw_rt1316_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + /* RT715 support */ int sof_sdw_rt715_init(const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback); +/* RT715-SDCA support */ +int sof_sdw_rt715_sdca_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback); + /* MAX98373 support */ int sof_sdw_mx8373_init(const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, diff --git a/sound/soc/intel/boards/sof_sdw_rt1316.c b/sound/soc/intel/boards/sof_sdw_rt1316.c new file mode 100644 index 000000000000..2c566330f236 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt1316.c @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt1316 - Helpers to handle RT1316 from generic machine driver + */ + +#include +#include +#include +#include +#include +#include +#include "sof_sdw_common.h" + +static const struct snd_soc_dapm_widget rt1316_widgets[] = { + SND_SOC_DAPM_SPK("Speaker", NULL), +}; + +/* + * dapm routes for rt1316 will be registered dynamically according + * to the number of rt1316 used. The first two entries will be registered + * for one codec case, and the last two entries are also registered + * if two 1316s are used. + */ +static const struct snd_soc_dapm_route rt1316_map[] = { + { "Speaker", NULL, "rt1316-1 SPOL" }, + { "Speaker", NULL, "rt1316-1 SPOR" }, + { "Speaker", NULL, "rt1316-2 SPOL" }, + { "Speaker", NULL, "rt1316-2 SPOR" }, +}; + +static const struct snd_kcontrol_new rt1316_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static int first_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_add_card_controls(card, rt1316_controls, + ARRAY_SIZE(rt1316_controls)); + if (ret) { + dev_err(card->dev, "rt1316 controls addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt1316_widgets, + ARRAY_SIZE(rt1316_widgets)); + if (ret) { + dev_err(card->dev, "rt1316 widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map, 2); + if (ret) + dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); + + return ret; +} + +static int second_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + int ret; + + ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map + 2, 2); + if (ret) + dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); + + return ret; +} + +static int all_spk_init(struct snd_soc_pcm_runtime *rtd) +{ + int ret; + + ret = first_spk_init(rtd); + if (ret) + return ret; + + return second_spk_init(rtd); +} + +int sof_sdw_rt1316_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* Count amp number and do init on playback link only. */ + if (!playback) + return 0; + + info->amp_num++; + if (info->amp_num == 1) + dai_links->init = first_spk_init; + + if (info->amp_num == 2) { + /* + * if two 1316s are in one dai link, the init function + * in this dai link will be first set for the first speaker, + * and it should be reset to initialize all speakers when + * the second speaker is found. + */ + if (dai_links->init) + dai_links->init = all_spk_init; + else + dai_links->init = second_spk_init; + } + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt711_sdca.c b/sound/soc/intel/boards/sof_sdw_rt711_sdca.c new file mode 100644 index 000000000000..19496f0f9110 --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt711_sdca.c @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt711_sdca - Helpers to handle RT711-SDCA from generic machine driver + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sof_sdw_common.h" + +/* + * Note this MUST be called before snd_soc_register_card(), so that the props + * are in place before the codec component driver's probe function parses them. + */ +static int rt711_sdca_add_codec_device_props(const char *sdw_dev_name) +{ + struct property_entry props[MAX_NO_PROPS] = {}; + struct device *sdw_dev; + int ret; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_dev_name); + if (!sdw_dev) + return -EPROBE_DEFER; + + if (SOF_RT711_JDSRC(sof_sdw_quirk)) { + props[0] = PROPERTY_ENTRY_U32("realtek,jd-src", + SOF_RT711_JDSRC(sof_sdw_quirk)); + } + + ret = device_add_properties(sdw_dev, props); + put_device(sdw_dev); + + return ret; +} + +static const struct snd_soc_dapm_widget rt711_sdca_widgets[] = { + SND_SOC_DAPM_HP("Headphone", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), +}; + +static const struct snd_soc_dapm_route rt711_sdca_map[] = { + /* Headphones */ + { "Headphone", NULL, "rt711 HP" }, + { "rt711 MIC2", NULL, "Headset Mic" }, +}; + +static const struct snd_kcontrol_new rt711_sdca_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), +}; + +static struct snd_soc_jack_pin rt711_sdca_jack_pins[] = { + { + .pin = "Headphone", + .mask = SND_JACK_HEADPHONE, + }, + { + .pin = "Headset Mic", + .mask = SND_JACK_MICROPHONE, + }, +}; + +static int rt711_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct mc_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); + struct snd_soc_component *component = codec_dai->component; + struct snd_soc_jack *jack; + int ret; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s hs:rt711-sdca", + card->components); + if (!card->components) + return -ENOMEM; + + ret = snd_soc_add_card_controls(card, rt711_sdca_controls, + ARRAY_SIZE(rt711_sdca_controls)); + if (ret) { + dev_err(card->dev, "rt711-sdca controls addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_new_controls(&card->dapm, rt711_sdca_widgets, + ARRAY_SIZE(rt711_sdca_widgets)); + if (ret) { + dev_err(card->dev, "rt711-sdca widgets addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(&card->dapm, rt711_sdca_map, + ARRAY_SIZE(rt711_sdca_map)); + + if (ret) { + dev_err(card->dev, "rt711-sdca map addition failed: %d\n", ret); + return ret; + } + + ret = snd_soc_card_jack_new(rtd->card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | + SND_JACK_BTN_1 | SND_JACK_BTN_2 | + SND_JACK_BTN_3, + &ctx->sdw_headset, + rt711_sdca_jack_pins, + ARRAY_SIZE(rt711_sdca_jack_pins)); + if (ret) { + dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n", + ret); + return ret; + } + + jack = &ctx->sdw_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); + + ret = snd_soc_component_set_jack(component, jack, NULL); + + if (ret) + dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n", + ret); + + return ret; +} + +int sof_sdw_rt711_sdca_exit(struct device *dev, struct snd_soc_dai_link *dai_link) +{ + struct device *sdw_dev; + + sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, + dai_link->codecs[0].name); + if (!sdw_dev) + return -EINVAL; + + device_remove_properties(sdw_dev); + put_device(sdw_dev); + + return 0; +} + +int sof_sdw_rt711_sdca_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + int ret; + + /* + * headset should be initialized once. + * Do it with dai link for playback. + */ + if (!playback) + return 0; + + ret = rt711_sdca_add_codec_device_props(dai_links->codecs[0].name); + if (ret < 0) + return ret; + + dai_links->init = rt711_sdca_rtd_init; + + return 0; +} diff --git a/sound/soc/intel/boards/sof_sdw_rt715_sdca.c b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c new file mode 100644 index 000000000000..c056e56a139b --- /dev/null +++ b/sound/soc/intel/boards/sof_sdw_rt715_sdca.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2020 Intel Corporation + +/* + * sof_sdw_rt715_sdca - Helpers to handle RT715-SDCA from generic machine driver + */ + +#include +#include +#include +#include +#include "sof_sdw_common.h" + +static int rt715_sdca_rtd_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s mic:rt715-sdca", + card->components); + if (!card->components) + return -ENOMEM; + + return 0; +} + +int sof_sdw_rt715_sdca_init(const struct snd_soc_acpi_link_adr *link, + struct snd_soc_dai_link *dai_links, + struct sof_sdw_codec_info *info, + bool playback) +{ + /* + * DAI ID is fixed at SDW_DMIC_DAI_ID for 715-SDCA to + * keep sdw DMIC and HDMI setting static in UCM + */ + if (sof_sdw_quirk & SOF_RT715_DAI_ID_FIX) + dai_links->id = SDW_DMIC_DAI_ID; + + dai_links->init = rt715_sdca_rtd_init; + + return 0; +} -- cgit From 3f2c656491af6698cb3f18d5bd34afa1b54096d2 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Fri, 21 Aug 2020 14:56:03 -0500 Subject: ASoC: Intel: sof_sdw: clean-up inclusion of header files "struct snd_soc_dapm_widget" and "struct snd_kcontrol_new" are used in most of these .c files. Adding the header files to prevent from depending on Reported-by: Guennadi Liakhovetski Reviewed-by: Guennadi Liakhovetski Reviewed-by: Rander Wang Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200821195603.215535-17-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_dmic.c | 1 + sound/soc/intel/boards/sof_sdw_max98373.c | 2 ++ sound/soc/intel/boards/sof_sdw_rt1308.c | 2 ++ sound/soc/intel/boards/sof_sdw_rt5682.c | 2 ++ sound/soc/intel/boards/sof_sdw_rt700.c | 2 ++ sound/soc/intel/boards/sof_sdw_rt711.c | 2 ++ 6 files changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw_dmic.c b/sound/soc/intel/boards/sof_sdw_dmic.c index 89b0824b2381..19df0f7a1d85 100644 --- a/sound/soc/intel/boards/sof_sdw_dmic.c +++ b/sound/soc/intel/boards/sof_sdw_dmic.c @@ -7,6 +7,7 @@ #include #include +#include #include "sof_sdw_common.h" static const struct snd_soc_dapm_widget dmic_widgets[] = { diff --git a/sound/soc/intel/boards/sof_sdw_max98373.c b/sound/soc/intel/boards/sof_sdw_max98373.c index 6437872a9b3d..905582aaf58c 100644 --- a/sound/soc/intel/boards/sof_sdw_max98373.c +++ b/sound/soc/intel/boards/sof_sdw_max98373.c @@ -6,8 +6,10 @@ #include #include +#include #include #include +#include #include "sof_sdw_common.h" #include "sof_maxim_common.h" diff --git a/sound/soc/intel/boards/sof_sdw_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt1308.c index 3655e890acec..dba2fd28d77f 100644 --- a/sound/soc/intel/boards/sof_sdw_rt1308.c +++ b/sound/soc/intel/boards/sof_sdw_rt1308.c @@ -7,8 +7,10 @@ #include #include +#include #include #include +#include #include "sof_sdw_common.h" #include "../../codecs/rt1308.h" diff --git a/sound/soc/intel/boards/sof_sdw_rt5682.c b/sound/soc/intel/boards/sof_sdw_rt5682.c index da354ba83939..5fa1a59615b6 100644 --- a/sound/soc/intel/boards/sof_sdw_rt5682.c +++ b/sound/soc/intel/boards/sof_sdw_rt5682.c @@ -10,8 +10,10 @@ #include #include #include +#include #include #include +#include #include #include "sof_sdw_common.h" diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index 5f50491ba5ee..bff69cfe27f4 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -8,8 +8,10 @@ #include #include #include +#include #include #include +#include #include #include "sof_sdw_common.h" diff --git a/sound/soc/intel/boards/sof_sdw_rt711.c b/sound/soc/intel/boards/sof_sdw_rt711.c index 606009fa3901..04074c09dded 100644 --- a/sound/soc/intel/boards/sof_sdw_rt711.c +++ b/sound/soc/intel/boards/sof_sdw_rt711.c @@ -10,8 +10,10 @@ #include #include #include +#include #include #include +#include #include #include "sof_sdw_common.h" -- cgit From 2ff6d5a108c6b7c07d1093c38e0def015edd325d Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Sun, 23 Aug 2020 21:54:37 +0800 Subject: ASoC: ak5558: Add regulator support "AVDD" is for analog power supply, "DVDD" is for digital power supply, they can improve the power management. As the regulator is enabled in pm runtime resume, which is behind the component driver probe, so accessing registers in component driver probe will fail. Fix this issue by enabling regcache_cache_only after pm_runtime_enable. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1598190877-9213-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak5558.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 8179512129d3..2f076d5ee284 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,8 +23,15 @@ #include "ak5558.h" +#define AK5558_NUM_SUPPLIES 2 +static const char *ak5558_supply_names[AK5558_NUM_SUPPLIES] = { + "DVDD", + "AVDD", +}; + /* AK5558 Codec Private Data */ struct ak5558_priv { + struct regulator_bulk_data supplies[AK5558_NUM_SUPPLIES]; struct snd_soc_component component; struct regmap *regmap; struct i2c_client *i2c; @@ -299,12 +307,22 @@ static int __maybe_unused ak5558_runtime_suspend(struct device *dev) regcache_cache_only(ak5558->regmap, true); ak5558_power_off(ak5558); + regulator_bulk_disable(ARRAY_SIZE(ak5558->supplies), + ak5558->supplies); return 0; } static int __maybe_unused ak5558_runtime_resume(struct device *dev) { struct ak5558_priv *ak5558 = dev_get_drvdata(dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(ak5558->supplies), + ak5558->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable supplies: %d\n", ret); + return ret; + } ak5558_power_off(ak5558); ak5558_power_on(ak5558); @@ -350,6 +368,7 @@ static int ak5558_i2c_probe(struct i2c_client *i2c) { struct ak5558_priv *ak5558; int ret = 0; + int i; ak5558 = devm_kzalloc(&i2c->dev, sizeof(*ak5558), GFP_KERNEL); if (!ak5558) @@ -367,6 +386,16 @@ static int ak5558_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak5558->reset_gpiod)) return PTR_ERR(ak5558->reset_gpiod); + for (i = 0; i < ARRAY_SIZE(ak5558->supplies); i++) + ak5558->supplies[i].supply = ak5558_supply_names[i]; + + ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(ak5558->supplies), + ak5558->supplies); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); + return ret; + } + ret = devm_snd_soc_register_component(&i2c->dev, &soc_codec_dev_ak5558, &ak5558_dai, 1); @@ -374,6 +403,7 @@ static int ak5558_i2c_probe(struct i2c_client *i2c) return ret; pm_runtime_enable(&i2c->dev); + regcache_cache_only(ak5558->regmap, true); return 0; } -- cgit From c1e47e8919da525c803d1557a30e44441db1e5ee Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Mon, 24 Aug 2020 15:58:07 +0800 Subject: ASoC: fsl_sai: Add -EPROBE_DEFER check for regmap init Regmap initialization may return -EPROBE_DEFER for clock may not be ready, so check -EPROBE_DEFER error type before start another Regmap initialization. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1598255887-1391-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index f6969a5d49e3..62c5fdb678fc 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -959,7 +959,7 @@ static int fsl_sai_probe(struct platform_device *pdev) "bus", base, &fsl_sai_regmap_config); /* Compatible with old DTB cases */ - if (IS_ERR(sai->regmap)) + if (IS_ERR(sai->regmap) && PTR_ERR(sai->regmap) != -EPROBE_DEFER) sai->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "sai", base, &fsl_sai_regmap_config); if (IS_ERR(sai->regmap)) { -- cgit From c81a4ef725362dbce890a24d62e745f57172718a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 24 Aug 2020 15:09:05 -0500 Subject: ASoC: SOF: topology: (cosmetic) remove redundant variable initialisations Remove two cases of redundant variable initialisation. Reviewed-by: Kai Vehmanen Signed-off-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 13e10a0c0b05..3b4e09089643 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2083,12 +2083,12 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_dapm_widget *widget = swidget->widget; struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_process *process = NULL; + struct sof_ipc_comp_process *process; struct sof_widget_data *wdata = NULL; size_t ipc_data_size = 0; size_t ipc_size; int offset = 0; - int ret = 0; + int ret; int i; if (type == SOF_COMP_NONE) { -- cgit From f738d8156d05b0331543ec097dd5d3c83465d68f Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Mon, 24 Aug 2020 15:09:06 -0500 Subject: ASoC: SOF: (cosmetic) use the "bool" type where it makes sense Several fields in struct snd_sof_dev are used as boolean flags, use the "bool" type for them. Reviewed-by: Kai Vehmanen Signed-off-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 64f28e082049..e2070072791c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -370,7 +370,7 @@ struct snd_sof_dev { /* DSP firmware boot */ wait_queue_head_t boot_wait; enum snd_sof_fw_state fw_state; - u32 first_boot; + bool first_boot; /* work queue in case the probe is implemented in two steps */ struct work_struct probe_work; @@ -431,10 +431,10 @@ struct snd_sof_dev { int dma_trace_pages; wait_queue_head_t trace_sleep; u32 host_offset; - u32 dtrace_is_supported; /* set with Kconfig or module parameter */ - u32 dtrace_is_enabled; - u32 dtrace_error; - u32 dtrace_draining; + bool dtrace_is_supported; /* set with Kconfig or module parameter */ + bool dtrace_is_enabled; + bool dtrace_error; + bool dtrace_draining; bool msi_enabled; -- cgit From f3e9ed5e90603db73a699bed18311f15cba71531 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 24 Aug 2020 15:09:07 -0500 Subject: ASoC: SOF: topology: remove unnecessary memory alloc for sdev->private Looks like it was left over from the previous implementation of DMIC PDM token parsing. It is not used anymore. Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 3b4e09089643..36dc87514dce 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2944,14 +2944,6 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, return ret; } - /* - * alloc memory for private member - * Used to track the pdm config array index currently being parsed - */ - sdev->private = kzalloc(sizeof(u32), GFP_KERNEL); - if (!sdev->private) - return -ENOMEM; - /* get DMIC PDM tokens */ ret = sof_parse_token_sets(scomp, &config->dmic.pdm[0], dmic_pdm_tokens, ARRAY_SIZE(dmic_pdm_tokens), private->array, @@ -2962,7 +2954,7 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, if (ret != 0) { dev_err(scomp->dev, "error: parse dmic pdm tokens failed %d\n", le32_to_cpu(private->size)); - goto err; + return ret; } /* set IPC header size */ @@ -3007,9 +2999,6 @@ static int sof_link_dmic_load(struct snd_soc_component *scomp, int index, dev_err(scomp->dev, "error: failed to save DAI config for DMIC%d\n", config->dai_index); -err: - kfree(sdev->private); - return ret; } -- cgit From f46ff50660e88235bd3dc993565c6440544a8e06 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 24 Aug 2020 15:09:08 -0500 Subject: ASoC: SOF: topology: reduce the log level for unhandled widgets Virtual widgets are added to topology to be compatible with legacy machine drivers. Reduce the log level for messages printed when such widgets are ignored by the SOF driver. Reviewed-by: Guennadi Liakhovetski Signed-off-by: Ranjani Sridharan Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 36dc87514dce..707fbac3e64f 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2374,8 +2374,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, case snd_soc_dapm_dai_link: case snd_soc_dapm_kcontrol: default: - dev_warn(scomp->dev, "warning: widget type %d name %s not handled\n", - swidget->id, tw->name); + dev_dbg(scomp->dev, "widget type %d name %s not handled\n", swidget->id, tw->name); break; } -- cgit From 277ff2364b3e2a8351ac2a605bfdaba058ac7586 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Aug 2020 15:09:09 -0500 Subject: ASoC: SOF: IPC: reduce verbosity of IPC pointer updates When using dynamic debug, the console is swamped with verbose position pointer logs, which really don't add much information. Move then to vdbg to keep traces usable and allow for easier end-user support. Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/ipc.c | 16 +++++++++++----- sound/soc/sof/pcm.c | 6 +++--- 2 files changed, 14 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 36e2d4d43da4..fd2b96ae4943 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -54,6 +54,7 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) u8 *str2 = NULL; u32 glb; u32 type; + bool vdbg = false; glb = cmd & SOF_GLB_TYPE_MASK; type = cmd & SOF_CMD_TYPE_MASK; @@ -146,6 +147,7 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) case SOF_IPC_STREAM_TRIG_XRUN: str2 = "TRIG_XRUN"; break; case SOF_IPC_STREAM_POSITION: + vdbg = true; str2 = "POSITION"; break; case SOF_IPC_STREAM_VORBIS_PARAMS: str2 = "VORBIS_PARAMS"; break; @@ -183,10 +185,14 @@ static void ipc_log_header(struct device *dev, u8 *text, u32 cmd) str = "unknown GLB command"; break; } - if (str2) - dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); - else + if (str2) { + if (vdbg) + dev_vdbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); + else + dev_dbg(dev, "%s: 0x%x: %s: %s\n", text, cmd, str, str2); + } else { dev_dbg(dev, "%s: 0x%x: %s\n", text, cmd, str); + } } #else static inline void ipc_log_header(struct device *dev, u8 *text, u32 cmd) @@ -449,8 +455,8 @@ static void ipc_period_elapsed(struct snd_sof_dev *sdev, u32 msg_id) stream = &spcm->stream[direction]; snd_sof_ipc_msg_data(sdev, stream->substream, &posn, sizeof(posn)); - dev_dbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", - posn.host_posn, posn.dai_posn, posn.wallclock); + dev_vdbg(sdev->dev, "posn : host 0x%llx dai 0x%llx wall 0x%llx\n", + posn.host_posn, posn.dai_posn, posn.wallclock); memcpy(&stream->posn, &posn, sizeof(posn)); diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index d730e437e4ba..30e667daffe4 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -446,9 +446,9 @@ static snd_pcm_uframes_t sof_pcm_pointer(struct snd_soc_component *component, dai = bytes_to_frames(substream->runtime, spcm->stream[substream->stream].posn.dai_posn); - dev_dbg(component->dev, - "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", - spcm->pcm.pcm_id, substream->stream, host, dai); + dev_vdbg(component->dev, + "PCM: stream %d dir %d DMA position %lu DAI position %lu\n", + spcm->pcm.pcm_id, substream->stream, host, dai); return host; } -- cgit From 71d551f5aaed0ae3566bfc1c6c8b31e74cb5cd06 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Aug 2020 15:09:10 -0500 Subject: ASoC: SOF: acpi: add dev_dbg() log for probe completion When the probe relies on a workqueue, the completion is not signaled by a return value. Mirror the log already present for PCI probe, so that CI checks can test if the probe actually worked by filtering the console logs. Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-acpi-dev.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/sof-acpi-dev.c b/sound/soc/sof/sof-acpi-dev.c index 8aecc46b3647..a78b76ef37b2 100644 --- a/sound/soc/sof/sof-acpi-dev.c +++ b/sound/soc/sof/sof-acpi-dev.c @@ -106,6 +106,8 @@ static const struct dev_pm_ops sof_acpi_pm = { static void sof_acpi_probe_complete(struct device *dev) { + dev_dbg(dev, "Completing SOF ACPI probe"); + if (sof_acpi_debug & SOF_ACPI_DISABLE_PM_RUNTIME) return; -- cgit From d5d023592e29b20aa17ba001495ebbab75b220f2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Aug 2020 15:09:11 -0500 Subject: ASoC: SOF: Intel: add dev_dbg log when driver is not selected Mirror change suggested in legacy HDaudio driver. On SKL+ Intel platforms, the driver selection is handled by the snd_intel_dspcfg, and when the HDaudio legacy driver is not selected, be it with the auto-selection or user preferences with a kernel parameter, the probe aborts with no logs, only a -ENODEV return value. Having no dmesg trace, even with dynamic debug enabled, makes support more complicated than it needs to be, and even experienced users can be fooled. A simple dev_dbg() trace solves this problem. BugLink: https://github.com/thesofproject/linux/issues/2330 Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-pci-dev.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index f3a8140773db..7b85c88cba9c 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -302,10 +302,10 @@ static int sof_pci_probe(struct pci_dev *pci, int ret; ret = snd_intel_dsp_driver_probe(pci); - if (ret != SND_INTEL_DSP_DRIVER_ANY && - ret != SND_INTEL_DSP_DRIVER_SOF) + if (ret != SND_INTEL_DSP_DRIVER_ANY && ret != SND_INTEL_DSP_DRIVER_SOF) { + dev_dbg(&pci->dev, "SOF PCI driver not selected, aborting probe\n"); return -ENODEV; - + } dev_dbg(&pci->dev, "PCI DSP detected"); /* get ops for platform */ -- cgit From 135ab457e776d042c481d70d8954f6775ce35958 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 24 Aug 2020 15:09:12 -0500 Subject: ASoC: Intel: use consistent HDAudio spelling in comments/docs We use HDaudio and HDAudio, pick one to make searches easier. No functionality change Reported-by: Guennadi Liakhovetski Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200824200912.46852-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 2 +- sound/soc/intel/skylake/skl.c | 6 +++--- sound/soc/sof/Kconfig | 2 +- sound/soc/sof/intel/Kconfig | 2 +- sound/soc/sof/pcm.c | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 82805a8681e5..0e48c4f532ce 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -209,7 +209,7 @@ config SND_SOC_INTEL_SKYLAKE_SSP_CLK config SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC bool "HDAudio codec support" help - If you have Intel Skylake or Kabylake with HDaudio codec + If you have Intel Skylake or Kabylake with HDAudio codec and DMIC present then enable this option by saying Y. config SND_SOC_INTEL_SKYLAKE_COMMON diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 63182bfd7941..ea00cf61d1a8 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -925,7 +925,7 @@ static int skl_first_init(struct hdac_bus *bus) /* check if PPCAP exists */ if (!bus->ppcap) { - dev_err(bus->dev, "bus ppcap not set, HDaudio or DSP not present?\n"); + dev_err(bus->dev, "bus ppcap not set, HDAudio or DSP not present?\n"); return -ENODEV; } @@ -986,7 +986,7 @@ static int skl_probe(struct pci_dev *pci, return -ENODEV; break; case SND_SKL_PCI_BIND_LEGACY: - dev_info(&pci->dev, "Module parameter forced binding with HDaudio legacy, aborting probe\n"); + dev_info(&pci->dev, "Module parameter forced binding with HDAudio legacy, aborting probe\n"); return -ENODEV; case SND_SKL_PCI_BIND_ASOC: dev_info(&pci->dev, "Module parameter forced binding with SKL driver, bypassed detection logic\n"); @@ -1021,7 +1021,7 @@ static int skl_probe(struct pci_dev *pci, err = -ENODEV; goto out_free; #else - dev_warn(bus->dev, "no nhlt info found, continuing to try to enable HDaudio codec\n"); + dev_warn(bus->dev, "no nhlt info found, continuing to try to enable HDAudio codec\n"); #endif } else { diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig index 4dda4b62509f..8c1f0829de40 100644 --- a/sound/soc/sof/Kconfig +++ b/sound/soc/sof/Kconfig @@ -73,7 +73,7 @@ config SND_SOC_SOF_NOCODEC_SUPPORT option if no known codec is detected. This is typically only enabled for developers or devices where the sound card is controlled externally - This option is mutually exclusive with the Intel HDaudio support, + This option is mutually exclusive with the Intel HDAudio support, selecting it may have negative impacts and prevent e.g. microphone functionality from being enabled on Intel CoffeeLake and later platforms. diff --git a/sound/soc/sof/intel/Kconfig b/sound/soc/sof/intel/Kconfig index f644010cc568..a066e08860cb 100644 --- a/sound/soc/sof/intel/Kconfig +++ b/sound/soc/sof/intel/Kconfig @@ -311,7 +311,7 @@ config SND_SOC_SOF_HDA_ALWAYS_ENABLE_DMI_L1 bool "SOF enable DMI Link L1" help This option enables DMI L1 for both playback and capture - and disables known workarounds for specific HDaudio platforms. + and disables known workarounds for specific HDAudio platforms. Only use to look into power optimizations on platforms not affected by DMI L1 issues. This option is not recommended. Say Y if you want to enable DMI Link L1 diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 30e667daffe4..4c5082b7eea9 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -704,7 +704,7 @@ static int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, break; case SOF_DAI_INTEL_HDA: /* - * HDaudio does not follow the default trigger + * HDAudio does not follow the default trigger * sequence due to firmware implementation */ for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { -- cgit From 672072976bf0db6e0aca5382bcf03bc90f439923 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Aug 2020 13:46:23 +0300 Subject: ASoC: hdac_hdmi: tidy up a memset() The ARRAY_SIZE() is the number of the elements but we want to use the number of bytes. Fortunately, in this case the value is the same so it doesn't affect runtime. Signed-off-by: Dan Carpenter Link: https://lore.kernel.org/r/20200825104623.GA278587@mwanda Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 869d1547ae5d..c61cce53f513 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -1474,7 +1474,7 @@ static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol, struct hdac_hdmi_port *port; struct hdac_hdmi_eld *eld; - memset(ucontrol->value.bytes.data, 0, ARRAY_SIZE(ucontrol->value.bytes.data)); + memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data)); pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device); if (!pcm) { -- cgit From bf2aa9ccc8e598d788132ded9c7e94c6af3a9d1e Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:20 -0400 Subject: ALSA: hda/ca0132 - Cleanup ca0132_mmio_init function. Cleanup the ca0132_mmio_init function, separating into two separate functions, one for Sound Blaster Z/ZxR/Recon3D, and another for the AE-5. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-2-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 158 +++++++++++++++++++++++++++---------------- 1 file changed, 99 insertions(+), 59 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index b7dbf2e7f77a..7491e2044638 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -8108,78 +8108,118 @@ static void r3di_pre_dsp_setup(struct hda_codec *codec) * what they do, or if they're necessary. Could possibly * be removed. Figure they're better to leave in. */ -static void ca0132_mmio_init(struct hda_codec *codec) +static const unsigned int ca0113_mmio_init_address_sbz[] = { + 0x400, 0x408, 0x40c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, + 0xc0c, 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04 +}; + +static const unsigned int ca0113_mmio_init_data_sbz[] = { + 0x00000030, 0x00000000, 0x00000003, 0x00000003, 0x00000003, + 0x00000003, 0x000000c1, 0x000000f1, 0x00000001, 0x000000c7, + 0x000000c1, 0x00000080 +}; + +static const unsigned int ca0113_mmio_init_data_zxr[] = { + 0x00000030, 0x00000000, 0x00000000, 0x00000003, 0x00000003, + 0x00000003, 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, + 0x000000c1, 0x00000080 +}; + +static const unsigned int ca0113_mmio_init_address_ae5[] = { + 0x400, 0x42c, 0x46c, 0x4ac, 0x4ec, 0x43c, 0x47c, 0x4bc, 0x4fc, 0x408, + 0x100, 0x410, 0x40c, 0x100, 0x100, 0x830, 0x86c, 0x800, 0x86c, 0x800, + 0x804, 0x20c, 0x01c, 0xc0c, 0xc00, 0xc04, 0xc0c, 0xc0c, 0xc0c, 0xc0c, + 0xc08, 0xc08, 0xc08, 0xc08, 0xc08, 0xc04, 0x01c +}; + +static const unsigned int ca0113_mmio_init_data_ae5[] = { + 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001, + 0x00000600, 0x00000014, 0x00000001, 0x0000060f, 0x0000070f, + 0x00000aff, 0x00000000, 0x0000006b, 0x00000001, 0x0000006b, + 0x00000057, 0x00800000, 0x00880680, 0x00000080, 0x00000030, + 0x00000000, 0x00000000, 0x00000003, 0x00000003, 0x00000003, + 0x00000001, 0x000000f1, 0x00000001, 0x000000c7, 0x000000c1, + 0x00000080, 0x00880680 +}; + +static void ca0132_mmio_init_sbz(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; + unsigned int tmp[2], i, count, cur_addr; + const unsigned int *addr, *data; - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00000001, spec->mem_base + 0x400); - else - writel(0x00000000, spec->mem_base + 0x400); + addr = ca0113_mmio_init_address_sbz; + for (i = 0; i < 3; i++) + writel(0x00000000, spec->mem_base + addr[i]); - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00000001, spec->mem_base + 0x408); - else - writel(0x00000000, spec->mem_base + 0x408); + cur_addr = i; + switch (ca0132_quirk(spec)) { + case QUIRK_ZXR: + tmp[0] = 0x00880480; + tmp[1] = 0x00000080; + break; + case QUIRK_SBZ: + tmp[0] = 0x00820680; + tmp[1] = 0x00000083; + break; + case QUIRK_R3D: + tmp[0] = 0x00880680; + tmp[1] = 0x00000083; + break; + default: + tmp[0] = 0x00000000; + tmp[1] = 0x00000000; + break; + } - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00000001, spec->mem_base + 0x40c); - else - writel(0x00000000, spec->mem_base + 0x40C); + for (i = 0; i < 2; i++) + writel(tmp[i], spec->mem_base + addr[cur_addr + i]); - if (ca0132_quirk(spec) == QUIRK_ZXR) - writel(0x00880640, spec->mem_base + 0x01C); - else - writel(0x00880680, spec->mem_base + 0x01C); + cur_addr += i; - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00000080, spec->mem_base + 0xC0C); - else - writel(0x00000083, spec->mem_base + 0xC0C); + switch (ca0132_quirk(spec)) { + case QUIRK_ZXR: + count = ARRAY_SIZE(ca0113_mmio_init_data_zxr); + data = ca0113_mmio_init_data_zxr; + break; + default: + count = ARRAY_SIZE(ca0113_mmio_init_data_sbz); + data = ca0113_mmio_init_data_sbz; + break; + } - writel(0x00000030, spec->mem_base + 0xC00); - writel(0x00000000, spec->mem_base + 0xC04); + for (i = 0; i < count; i++) + writel(data[i], spec->mem_base + addr[cur_addr + i]); +} - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00000000, spec->mem_base + 0xC0C); - else - writel(0x00000003, spec->mem_base + 0xC0C); +static void ca0132_mmio_init_ae5(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + const unsigned int *addr, *data; + unsigned int i, count; - writel(0x00000003, spec->mem_base + 0xC0C); - writel(0x00000003, spec->mem_base + 0xC0C); - writel(0x00000003, spec->mem_base + 0xC0C); + addr = ca0113_mmio_init_address_ae5; + data = ca0113_mmio_init_data_ae5; + count = ARRAY_SIZE(ca0113_mmio_init_data_ae5); - if (ca0132_quirk(spec) == QUIRK_AE5) - writel(0x00000001, spec->mem_base + 0xC08); - else - writel(0x000000C1, spec->mem_base + 0xC08); + for (i = 0; i < count; i++) + writel(data[i], spec->mem_base + addr[i]); +} - writel(0x000000F1, spec->mem_base + 0xC08); - writel(0x00000001, spec->mem_base + 0xC08); - writel(0x000000C7, spec->mem_base + 0xC08); - writel(0x000000C1, spec->mem_base + 0xC08); - writel(0x00000080, spec->mem_base + 0xC04); +static void ca0132_mmio_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; - if (ca0132_quirk(spec) == QUIRK_AE5) { - writel(0x00000000, spec->mem_base + 0x42c); - writel(0x00000000, spec->mem_base + 0x46c); - writel(0x00000000, spec->mem_base + 0x4ac); - writel(0x00000000, spec->mem_base + 0x4ec); - writel(0x00000000, spec->mem_base + 0x43c); - writel(0x00000000, spec->mem_base + 0x47c); - writel(0x00000000, spec->mem_base + 0x4bc); - writel(0x00000000, spec->mem_base + 0x4fc); - writel(0x00000600, spec->mem_base + 0x100); - writel(0x00000014, spec->mem_base + 0x410); - writel(0x0000060f, spec->mem_base + 0x100); - writel(0x0000070f, spec->mem_base + 0x100); - writel(0x00000aff, spec->mem_base + 0x830); - writel(0x00000000, spec->mem_base + 0x86c); - writel(0x0000006b, spec->mem_base + 0x800); - writel(0x00000001, spec->mem_base + 0x86c); - writel(0x0000006b, spec->mem_base + 0x800); - writel(0x00000057, spec->mem_base + 0x804); - writel(0x00800000, spec->mem_base + 0x20c); + switch (ca0132_quirk(spec)) { + case QUIRK_R3D: + case QUIRK_SBZ: + case QUIRK_ZXR: + ca0132_mmio_init_sbz(codec); + break; + case QUIRK_AE5: + ca0132_mmio_init_ae5(codec); + break; } } -- cgit From 896e361e82423aed4490f485dc25de1958c724ed Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:21 -0400 Subject: ALSA: hda/ca0132 - Add speaker tuning initialization commands. Add speaker tuning initialization DSP commands, and also define previously unknown DSP command values. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-3-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 119 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 7491e2044638..2e664aeee1c4 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -589,6 +589,60 @@ static const struct ct_eq_preset ca0132_alt_eq_presets[] = { } }; +/* + * Definitions for the DSP req's to handle speaker tuning. These all belong to + * module ID 0x96, the output effects module. + */ +enum speaker_tuning_reqs { + /* + * Currently, this value is always set to 0.0f. However, on Windows, + * when selecting certain headphone profiles on the new Sound Blaster + * connect software, the QUERY_SPEAKER_EQ_ADDRESS req on mid 0x80 is + * sent. This gets the speaker EQ address area, which is then used to + * send over (presumably) an equalizer profile for the specific + * headphone setup. It is sent using the same method the DSP + * firmware is uploaded with, which I believe is why the 'ctspeq.bin' + * file exists in linux firmware tree but goes unused. It would also + * explain why the QUERY_SPEAKER_EQ_ADDRESS req is defined but unused. + * Once this profile is sent over, SPEAKER_TUNING_USE_SPEAKER_EQ is + * set to 1.0f. + */ + SPEAKER_TUNING_USE_SPEAKER_EQ = 0x1f, + SPEAKER_TUNING_ENABLE_CENTER_EQ = 0x20, + SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL = 0x21, + SPEAKER_TUNING_FRONT_RIGHT_VOL_LEVEL = 0x22, + SPEAKER_TUNING_CENTER_VOL_LEVEL = 0x23, + SPEAKER_TUNING_LFE_VOL_LEVEL = 0x24, + SPEAKER_TUNING_REAR_LEFT_VOL_LEVEL = 0x25, + SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL = 0x26, + SPEAKER_TUNING_SURROUND_LEFT_VOL_LEVEL = 0x27, + SPEAKER_TUNING_SURROUND_RIGHT_VOL_LEVEL = 0x28, + /* + * Inversion is used when setting headphone virtualization to line + * out. Not sure why this is, but it's the only place it's ever used. + */ + SPEAKER_TUNING_FRONT_LEFT_INVERT = 0x29, + SPEAKER_TUNING_FRONT_RIGHT_INVERT = 0x2a, + SPEAKER_TUNING_CENTER_INVERT = 0x2b, + SPEAKER_TUNING_LFE_INVERT = 0x2c, + SPEAKER_TUNING_REAR_LEFT_INVERT = 0x2d, + SPEAKER_TUNING_REAR_RIGHT_INVERT = 0x2e, + SPEAKER_TUNING_SURROUND_LEFT_INVERT = 0x2f, + SPEAKER_TUNING_SURROUND_RIGHT_INVERT = 0x30, + /* Delay is used when setting surround speaker distance in Windows. */ + SPEAKER_TUNING_FRONT_LEFT_DELAY = 0x31, + SPEAKER_TUNING_FRONT_RIGHT_DELAY = 0x32, + SPEAKER_TUNING_CENTER_DELAY = 0x33, + SPEAKER_TUNING_LFE_DELAY = 0x34, + SPEAKER_TUNING_REAR_LEFT_DELAY = 0x35, + SPEAKER_TUNING_REAR_RIGHT_DELAY = 0x36, + SPEAKER_TUNING_SURROUND_LEFT_DELAY = 0x37, + SPEAKER_TUNING_SURROUND_RIGHT_DELAY = 0x38, + /* Of these two, only mute seems to ever be used. */ + SPEAKER_TUNING_MAIN_VOLUME = 0x39, + SPEAKER_TUNING_MUTE = 0x3a, +}; + /* DSP command sequences for ca0132_alt_select_out */ #define ALT_OUT_SET_MAX_COMMANDS 9 /* Max number of commands in sequence */ struct ca0132_alt_out_set { @@ -6874,6 +6928,67 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) } } +/* + * Default speaker tuning values setup for alternative codecs. + */ +static const unsigned int sbz_default_delay_values[] = { + /* Non-zero values are floating point 0.000198. */ + 0x394f9e38, 0x394f9e38, 0x00000000, 0x00000000, 0x00000000, 0x00000000 +}; + +static const unsigned int zxr_default_delay_values[] = { + /* Non-zero values are floating point 0.000220. */ + 0x00000000, 0x00000000, 0x3966afcd, 0x3966afcd, 0x3966afcd, 0x3966afcd +}; + +static const unsigned int ae5_default_delay_values[] = { + /* Non-zero values are floating point 0.000100. */ + 0x00000000, 0x00000000, 0x38d1b717, 0x38d1b717, 0x38d1b717, 0x38d1b717 +}; + +/* + * If we never change these, probably only need them on initialization. + */ +static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int i, tmp, start_req, end_req; + const unsigned int *values; + + switch (ca0132_quirk(spec)) { + case QUIRK_SBZ: + values = sbz_default_delay_values; + break; + case QUIRK_ZXR: + values = zxr_default_delay_values; + break; + case QUIRK_AE5: + values = ae5_default_delay_values; + break; + default: + values = sbz_default_delay_values; + break; + } + + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_ENABLE_CENTER_EQ, tmp); + + start_req = SPEAKER_TUNING_FRONT_LEFT_VOL_LEVEL; + end_req = SPEAKER_TUNING_REAR_RIGHT_VOL_LEVEL; + for (i = start_req; i < end_req + 1; i++) + dspio_set_uint_param(codec, 0x96, i, tmp); + + start_req = SPEAKER_TUNING_FRONT_LEFT_INVERT; + end_req = SPEAKER_TUNING_REAR_RIGHT_INVERT; + for (i = start_req; i < end_req + 1; i++) + dspio_set_uint_param(codec, 0x96, i, tmp); + + + for (i = 0; i < 6; i++) + dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_FRONT_LEFT_DELAY + i, values[i]); +} + /* * Creates a dummy stream to bind the output to. This seems to have to be done * after changing the main outputs source and destination streams. @@ -7373,6 +7488,8 @@ static void sbz_setup_defaults(struct hda_codec *codec) } } + ca0132_alt_init_speaker_tuning(codec); + ca0132_alt_create_dummy_stream(codec); } @@ -7440,6 +7557,8 @@ static void ae5_setup_defaults(struct hda_codec *codec) } } + ca0132_alt_init_speaker_tuning(codec); + ca0132_alt_create_dummy_stream(codec); } -- cgit From 01464a566eed527dbf977e2430afa249c257c008 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:22 -0400 Subject: ALSA: hda/ca0132 - Add surround channel config control. Add a surround channel configuration enumeration control. Setting up different channel configurations allows the DSP to upmix stereo audio into multi-channel audio, and allows for redirection of bass to a subwoofer. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-4-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 110 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 2e664aeee1c4..1a5fb30b69ab 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -38,6 +38,8 @@ #define FLOAT_ONE 0x3f800000 #define FLOAT_TWO 0x40000000 #define FLOAT_THREE 0x40400000 +#define FLOAT_FIVE 0x40a00000 +#define FLOAT_SIX 0x40c00000 #define FLOAT_EIGHT 0x41000000 #define FLOAT_MINUS_5 0xc0a00000 @@ -143,7 +145,8 @@ enum { MIC_BOOST_ENUM, AE5_HEADPHONE_GAIN_ENUM, AE5_SOUND_FILTER_ENUM, - ZXR_HEADPHONE_GAIN + ZXR_HEADPHONE_GAIN, + SPEAKER_CHANNEL_CFG_ENUM, #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; @@ -686,6 +689,39 @@ static const struct ca0132_alt_out_set alt_out_presets[] = { } }; +/* Surround output channel count configuration structures. */ +#define SPEAKER_CHANNEL_CFG_COUNT 5 +enum { + SPEAKER_CHANNELS_2_0, + SPEAKER_CHANNELS_2_1, + SPEAKER_CHANNELS_4_0, + SPEAKER_CHANNELS_4_1, + SPEAKER_CHANNELS_5_1, +}; + +struct ca0132_alt_speaker_channel_cfg { + char *name; + unsigned int val; +}; + +static const struct ca0132_alt_speaker_channel_cfg speaker_channel_cfgs[] = { + { .name = "2.0", + .val = FLOAT_ONE + }, + { .name = "2.1", + .val = FLOAT_TWO + }, + { .name = "4.0", + .val = FLOAT_FIVE + }, + { .name = "4.1", + .val = FLOAT_SIX + }, + { .name = "5.1", + .val = FLOAT_EIGHT + } +}; + /* * DSP volume setting structs. Req 1 is left volume, req 2 is right volume, * and I don't know what the third req is, but it's always zero. I assume it's @@ -1063,6 +1099,7 @@ struct ca0132_spec { /* ca0132_alt control related values */ unsigned char in_enum_val; unsigned char out_enum_val; + unsigned char channel_cfg_val; unsigned char mic_boost_enum_val; unsigned char smart_volume_setting; long fx_ctl_val[EFFECT_LEVEL_SLIDERS]; @@ -4476,7 +4513,8 @@ static int ca0132_alt_select_out(struct hda_codec *codec) snd_hda_set_pin_ctl(codec, spec->out_pins[3], pin_ctl | PIN_OUT); - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); + tmp = speaker_channel_cfgs[spec->channel_cfg_val].val; + dspio_set_uint_param(codec, 0x80, 0x04, tmp); break; } /* @@ -5582,6 +5620,54 @@ static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, return 1; } +/* Select surround output type: 2.1, 4.0, 4.1, or 5.1. */ +static int ca0132_alt_speaker_channel_cfg_get_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned int items = SPEAKER_CHANNEL_CFG_COUNT; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = items; + if (uinfo->value.enumerated.item >= items) + uinfo->value.enumerated.item = items - 1; + strcpy(uinfo->value.enumerated.name, + speaker_channel_cfgs[uinfo->value.enumerated.item].name); + return 0; +} + +static int ca0132_alt_speaker_channel_cfg_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->channel_cfg_val; + return 0; +} + +static int ca0132_alt_speaker_channel_cfg_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = SPEAKER_CHANNEL_CFG_COUNT; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_speaker_channels: sel=%d, channels=%s\n", + sel, speaker_channel_cfgs[sel].name); + + spec->channel_cfg_val = sel; + + if (spec->out_enum_val == SURROUND_OUT) + ca0132_alt_select_out(codec); + + return 1; +} + /* * Smart Volume output setting control. Three different settings, Normal, * which takes the value from the smart volume slider. The two others, loud @@ -6226,6 +6312,23 @@ static int ca0132_alt_add_output_enum(struct hda_codec *codec) snd_ctl_new1(&knew, codec)); } +/* + * Add a control for selecting channel count on speaker output. Setting this + * allows the DSP to do bass redirection and channel upmixing on surround + * configurations. + */ +static int ca0132_alt_add_speaker_channel_cfg_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("Surround Channel Config", + SPEAKER_CHANNEL_CFG_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ca0132_alt_speaker_channel_cfg_get_info; + knew.get = ca0132_alt_speaker_channel_cfg_get; + knew.put = ca0132_alt_speaker_channel_cfg_put; + return snd_hda_ctl_add(codec, SPEAKER_CHANNEL_CFG_ENUM, + snd_ctl_new1(&knew, codec)); +} + /* * Create an Input Source enumerated control for the alternate ca0132 codecs * because the front microphone has no auto-detect, and Line-in has to be set @@ -6530,6 +6633,9 @@ static int ca0132_build_controls(struct hda_codec *codec) */ if (ca0132_use_alt_functions(spec)) { err = ca0132_alt_add_output_enum(codec); + if (err < 0) + return err; + err = ca0132_alt_add_speaker_channel_cfg_enum(codec); if (err < 0) return err; err = ca0132_alt_add_mic_boost_enum(codec); -- cgit From 670c5f484a4403e5c218442d5a0e337a2224741f Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:23 -0400 Subject: ALSA: hda/ca0132 - Add full-range speaker selection controls. Add functions for setting full-range speakers and controls to enable/disable the setting. Setting a speaker to full-range means that the channels won't have their bass redirected to the LFE channel. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-5-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 117 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 1a5fb30b69ab..469cefe9a51a 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -147,6 +147,8 @@ enum { AE5_SOUND_FILTER_ENUM, ZXR_HEADPHONE_GAIN, SPEAKER_CHANNEL_CFG_ENUM, + SPEAKER_FULL_RANGE_FRONT, + SPEAKER_FULL_RANGE_REAR, #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; @@ -592,6 +594,24 @@ static const struct ct_eq_preset ca0132_alt_eq_presets[] = { } }; +/* + * DSP reqs for handling full-range speakers/bass redirection. If a speaker is + * set as not being full range, and bass redirection is enabled, all + * frequencies below the crossover frequency are redirected to the LFE + * channel. If the surround configuration has no LFE channel, this can't be + * enabled. X-Bass must be disabled when using these. + */ +enum speaker_range_reqs { + SPEAKER_BASS_REDIRECT = 0x15, + SPEAKER_BASS_REDIRECT_XOVER_FREQ = 0x16, + /* Between 0x16-0x1a are the X-Bass reqs. */ + SPEAKER_FULL_RANGE_FRONT_L_R = 0x1a, + SPEAKER_FULL_RANGE_CENTER_LFE = 0x1b, + SPEAKER_FULL_RANGE_REAR_L_R = 0x1c, + SPEAKER_FULL_RANGE_SURROUND_L_R = 0x1d, + SPEAKER_BASS_REDIRECT_SUB_GAIN = 0x1e, +}; + /* * Definitions for the DSP req's to handle speaker tuning. These all belong to * module ID 0x96, the output effects module. @@ -1100,6 +1120,7 @@ struct ca0132_spec { unsigned char in_enum_val; unsigned char out_enum_val; unsigned char channel_cfg_val; + unsigned char speaker_range_val[2]; unsigned char mic_boost_enum_val; unsigned char smart_volume_setting; long fx_ctl_val[EFFECT_LEVEL_SLIDERS]; @@ -4259,6 +4280,50 @@ static void ae5_mmio_select_out(struct hda_codec *codec) ae5_ca0113_output_presets[spec->cur_out_type].vals[i]); } +static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + int err; + + /* 2.0/4.0 setup has no LFE channel, so setting full-range does nothing. */ + if (spec->channel_cfg_val == SPEAKER_CHANNELS_4_0 + || spec->channel_cfg_val == SPEAKER_CHANNELS_2_0) + return 0; + + /* Set front L/R full range. Zero for full-range, one for redirection. */ + tmp = spec->speaker_range_val[0] ? FLOAT_ZERO : FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_FRONT_L_R, tmp); + if (err < 0) + return err; + + /* When setting full-range rear, both rear and center/lfe are set. */ + tmp = spec->speaker_range_val[1] ? FLOAT_ZERO : FLOAT_ONE; + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_CENTER_LFE, tmp); + if (err < 0) + return err; + + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_REAR_L_R, tmp); + if (err < 0) + return err; + + /* + * Only the AE series cards set this value when setting full-range, + * and it's always 1.0f. + */ + if (ca0132_quirk(spec) == QUIRK_AE5) { + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_FULL_RANGE_SURROUND_L_R, FLOAT_ONE); + if (err < 0) + return err; + } + + return 0; +} + /* * These are the commands needed to setup output on each of the different card * types. @@ -4539,6 +4604,9 @@ static int ca0132_alt_select_out(struct hda_codec *codec) goto exit; } + if (spec->cur_out_type == SURROUND_OUT) + err = ca0132_alt_set_full_range_speaker(codec); + exit: snd_hda_power_down_pm(codec); @@ -5269,6 +5337,7 @@ static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol, long *valp = ucontrol->value.integer.value; *valp = spec->xbass_xover_freq; + return 0; } @@ -5894,6 +5963,11 @@ static int ca0132_switch_get(struct snd_kcontrol *kcontrol, return 0; } + if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { + *valp = spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT]; + return 0; + } + return 0; } @@ -5972,6 +6046,14 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, goto exit; } + if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { + spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT] = *valp; + if (spec->cur_out_type == SURROUND_OUT) + ca0132_alt_set_full_range_speaker(codec); + + changed = 0; + } + exit: snd_hda_power_down(codec); return changed; @@ -6329,6 +6411,31 @@ static int ca0132_alt_add_speaker_channel_cfg_enum(struct hda_codec *codec) snd_ctl_new1(&knew, codec)); } +/* + * Full range front stereo and rear surround switches. When these are set to + * full range, the lower frequencies from these channels are no longer + * redirected to the LFE channel. + */ +static int ca0132_alt_add_front_full_range_switch(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO("Full-Range Front Speakers", + SPEAKER_FULL_RANGE_FRONT, 1, HDA_OUTPUT); + + return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_FRONT, + snd_ctl_new1(&knew, codec)); +} + +static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO("Full-Range Rear Speakers", + SPEAKER_FULL_RANGE_REAR, 1, HDA_OUTPUT); + + return snd_hda_ctl_add(codec, SPEAKER_FULL_RANGE_REAR, + snd_ctl_new1(&knew, codec)); +} + /* * Create an Input Source enumerated control for the alternate ca0132 codecs * because the front microphone has no auto-detect, and Line-in has to be set @@ -6636,6 +6743,12 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; err = ca0132_alt_add_speaker_channel_cfg_enum(codec); + if (err < 0) + return err; + err = ca0132_alt_add_front_full_range_switch(codec); + if (err < 0) + return err; + err = ca0132_alt_add_rear_full_range_switch(codec); if (err < 0) return err; err = ca0132_alt_add_mic_boost_enum(codec); @@ -7982,6 +8095,10 @@ static void ca0132_init_chip(struct hda_codec *codec) * ca0132 codecs. Also sets x-bass crossover frequency to 80hz. */ if (ca0132_use_alt_controls(spec)) { + /* Set speakers to default to full range. */ + spec->speaker_range_val[0] = 1; + spec->speaker_range_val[1] = 1; + spec->xbass_xover_freq = 8; for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) spec->fx_ctl_val[i] = effect_slider_defaults[i]; -- cgit From f49b3063ad0d8638da2e7eb994549863f2c74923 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:24 -0400 Subject: ALSA: hda/ca0132 - Add bass redirection controls. Add bass redirection controls for surround outputs. This uses the DSP to redirect audio below the bass redirection crossover frequency to the LFE channel from the front/rear L/R speakers. This only goes into effect if the speakers aren't set as full range, and only if the surround configuration has an LFE channel. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-6-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 130 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 126 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 469cefe9a51a..8ad2fc5ab30b 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -149,6 +149,8 @@ enum { SPEAKER_CHANNEL_CFG_ENUM, SPEAKER_FULL_RANGE_FRONT, SPEAKER_FULL_RANGE_REAR, + BASS_REDIRECTION, + BASS_REDIRECTION_XOVER, #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; @@ -1123,6 +1125,8 @@ struct ca0132_spec { unsigned char speaker_range_val[2]; unsigned char mic_boost_enum_val; unsigned char smart_volume_setting; + unsigned char bass_redirection_val; + long bass_redirect_xover_freq; long fx_ctl_val[EFFECT_LEVEL_SLIDERS]; long xbass_xover_freq; long eq_preset_val; @@ -4324,6 +4328,35 @@ static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec) return 0; } +static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec, + bool val) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + int err; + + if (val && spec->channel_cfg_val != SPEAKER_CHANNELS_4_0 && + spec->channel_cfg_val != SPEAKER_CHANNELS_2_0) + tmp = FLOAT_ONE; + else + tmp = FLOAT_ZERO; + + err = dspio_set_uint_param(codec, 0x96, SPEAKER_BASS_REDIRECT, tmp); + if (err < 0) + return err; + + /* If it is enabled, make sure to set the crossover frequency. */ + if (tmp) { + tmp = float_xbass_xover_lookup[spec->xbass_xover_freq]; + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_BASS_REDIRECT_XOVER_FREQ, tmp); + if (err < 0) + return err; + } + + return 0; +} + /* * These are the commands needed to setup output on each of the different card * types. @@ -4593,6 +4626,15 @@ static int ca0132_alt_select_out(struct hda_codec *codec) ca0132_effects_set(codec, X_BASS, spec->effects_switch[X_BASS - EFFECT_START_NID]); + if (spec->cur_out_type == SURROUND_OUT) + err = ca0132_alt_surround_set_bass_redirection(codec, + spec->bass_redirection_val); + else + err = ca0132_alt_surround_set_bass_redirection(codec, 0); + + if (err < 0) + goto exit; + /* run through the output dsp commands for the selected output. */ for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) { err = dspio_set_uint_param(codec, @@ -5282,6 +5324,18 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, return ret; } /* End of control change helpers. */ + +static void ca0132_alt_bass_redirection_xover_set(struct hda_codec *codec, + long idx) +{ + snd_hda_power_up(codec); + + dspio_set_param(codec, 0x96, 0x20, SPEAKER_BASS_REDIRECT_XOVER_FREQ, + &(float_xbass_xover_lookup[idx]), sizeof(unsigned int)); + + snd_hda_power_down(codec); +} + /* * Below I've added controls to mess with the effect levels, I've only enabled * them on the Sound Blaster Z, but they would probably also work on the @@ -5290,6 +5344,7 @@ static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, */ /* Sets DSP effect level from the sliders above the controls */ + static int ca0132_alt_slider_ctl_set(struct hda_codec *codec, hda_nid_t nid, const unsigned int *lookup, int idx) { @@ -5335,8 +5390,12 @@ static int ca0132_alt_xbass_xover_slider_ctl_get(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; long *valp = ucontrol->value.integer.value; + hda_nid_t nid = get_amp_nid(kcontrol); - *valp = spec->xbass_xover_freq; + if (nid == BASS_REDIRECTION_XOVER) + *valp = spec->bass_redirect_xover_freq; + else + *valp = spec->xbass_xover_freq; return 0; } @@ -5391,16 +5450,25 @@ static int ca0132_alt_xbass_xover_slider_put(struct snd_kcontrol *kcontrol, struct ca0132_spec *spec = codec->spec; hda_nid_t nid = get_amp_nid(kcontrol); long *valp = ucontrol->value.integer.value; + long *cur_val; int idx; + if (nid == BASS_REDIRECTION_XOVER) + cur_val = &spec->bass_redirect_xover_freq; + else + cur_val = &spec->xbass_xover_freq; + /* any change? */ - if (spec->xbass_xover_freq == *valp) + if (*cur_val == *valp) return 0; - spec->xbass_xover_freq = *valp; + *cur_val = *valp; idx = *valp; - ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx); + if (nid == BASS_REDIRECTION_XOVER) + ca0132_alt_bass_redirection_xover_set(codec, *cur_val); + else + ca0132_alt_slider_ctl_set(codec, nid, float_xbass_xover_lookup, idx); return 0; } @@ -5968,6 +6036,11 @@ static int ca0132_switch_get(struct snd_kcontrol *kcontrol, return 0; } + if (nid == BASS_REDIRECTION) { + *valp = spec->bass_redirection_val; + return 0; + } + return 0; } @@ -6054,6 +6127,14 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, changed = 0; } + if (nid == BASS_REDIRECTION) { + spec->bass_redirection_val = *valp; + if (spec->cur_out_type == SURROUND_OUT) + ca0132_alt_surround_set_bass_redirection(codec, *valp); + + changed = 0; + } + exit: snd_hda_power_down(codec); return changed; @@ -6436,6 +6517,39 @@ static int ca0132_alt_add_rear_full_range_switch(struct hda_codec *codec) snd_ctl_new1(&knew, codec)); } +/* + * Bass redirection redirects audio below the crossover frequency to the LFE + * channel on speakers that are set as not being full-range. On configurations + * without an LFE channel, it does nothing. Bass redirection seems to be the + * replacement for X-Bass on configurations with an LFE channel. + */ +static int ca0132_alt_add_bass_redirection_crossover(struct hda_codec *codec) +{ + const char *namestr = "Bass Redirection Crossover"; + struct snd_kcontrol_new knew = + HDA_CODEC_VOLUME_MONO(namestr, BASS_REDIRECTION_XOVER, 1, 0, + HDA_OUTPUT); + + knew.tlv.c = NULL; + knew.info = ca0132_alt_xbass_xover_slider_info; + knew.get = ca0132_alt_xbass_xover_slider_ctl_get; + knew.put = ca0132_alt_xbass_xover_slider_put; + + return snd_hda_ctl_add(codec, BASS_REDIRECTION_XOVER, + snd_ctl_new1(&knew, codec)); +} + +static int ca0132_alt_add_bass_redirection_switch(struct hda_codec *codec) +{ + const char *namestr = "Bass Redirection"; + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO(namestr, BASS_REDIRECTION, 1, + HDA_OUTPUT); + + return snd_hda_ctl_add(codec, BASS_REDIRECTION, + snd_ctl_new1(&knew, codec)); +} + /* * Create an Input Source enumerated control for the alternate ca0132 codecs * because the front microphone has no auto-detect, and Line-in has to be set @@ -6749,6 +6863,12 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; err = ca0132_alt_add_rear_full_range_switch(codec); + if (err < 0) + return err; + err = ca0132_alt_add_bass_redirection_crossover(codec); + if (err < 0) + return err; + err = ca0132_alt_add_bass_redirection_switch(codec); if (err < 0) return err; err = ca0132_alt_add_mic_boost_enum(codec); @@ -8102,6 +8222,8 @@ static void ca0132_init_chip(struct hda_codec *codec) spec->xbass_xover_freq = 8; for (i = 0; i < EFFECT_LEVEL_SLIDERS; i++) spec->fx_ctl_val[i] = effect_slider_defaults[i]; + + spec->bass_redirect_xover_freq = 8; } spec->voicefx_val = 0; -- cgit From ed8156c86f958744ba2088d57b1959b86fb32344 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:25 -0400 Subject: ALSA: hda/ca0132 - Remove surround output selection. Remove the surround output selection and merge it with the speaker output selection. Now that the extra commands that were being run on surround output setting are known, there's no need to have it be separate. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-7-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 151 +++++++++++++------------------------------ 1 file changed, 46 insertions(+), 105 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 8ad2fc5ab30b..5743bdd7cc88 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -82,11 +82,10 @@ MODULE_FIRMWARE(R3DI_EFX_FILE); static const char *const dirstr[2] = { "Playback", "Capture" }; -#define NUM_OF_OUTPUTS 3 +#define NUM_OF_OUTPUTS 2 enum { SPEAKER_OUT, HEADPHONE_OUT, - SURROUND_OUT }; enum { @@ -699,16 +698,6 @@ static const struct ca0132_alt_out_set alt_out_presets[] = { 0x00000000, 0x00000000, 0x00000000, 0x00000000 } }, - { .name = "Surround", - .commands = 8, - .mids = { 0x96, 0x8F, 0x96, 0x96, - 0x96, 0x96, 0x96, 0x96 }, - .reqs = { 0x18, 0x01, 0x1F, 0x15, - 0x3A, 0x1A, 0x1B, 0x1C }, - .vals = { 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000 } - } }; /* Surround output channel count configuration structures. */ @@ -785,10 +774,6 @@ static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, .vals = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } - }, - { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, - .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, - .vals = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f } } }; @@ -4440,42 +4425,6 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) break; } break; - case SURROUND_OUT: - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - ca0113_mmio_gpio_set(codec, 7, false); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0d, 0x18); - break; - case QUIRK_ZXR: - ca0113_mmio_gpio_set(codec, 2, true); - ca0113_mmio_gpio_set(codec, 3, true); - ca0113_mmio_gpio_set(codec, 5, false); - zxr_headphone_gain_set(codec, 0); - chipio_set_control_param(codec, 0x0d, 0x24); - break; - case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0d, 0x24); - r3di_gpio_out_set(codec, R3DI_LINE_OUT); - break; - case QUIRK_R3D: - ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0d, 0x24); - break; - case QUIRK_AE5: - ae5_mmio_select_out(codec); - ae5_headphone_gain_set(codec, 2); - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x29, tmp); - dspio_set_uint_param(codec, 0x96, 0x2a, tmp); - chipio_set_control_param(codec, 0x0d, 0xa4); - chipio_write(codec, 0x18b03c, 0x00000012); - break; - default: - break; - } - break; } } @@ -4492,11 +4441,10 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) static int ca0132_alt_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; + unsigned int tmp, outfx_set, i; unsigned int pin_ctl; int jack_present; int auto_jack; - unsigned int i; - unsigned int tmp; int err; /* Default Headphone is rear headphone */ hda_nid_t headphone_nid = spec->out_pins[1]; @@ -4523,6 +4471,8 @@ static int ca0132_alt_select_out(struct hda_codec *codec) } else spec->cur_out_type = spec->out_enum_val; + outfx_set = spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]; + /* Begin DSP output switch */ tmp = FLOAT_ONE; err = dspio_set_uint_param(codec, 0x96, 0x3A, tmp); @@ -4536,6 +4486,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) codec_dbg(codec, "%s speaker\n", __func__); /* disable headphone node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); snd_hda_set_pin_ctl(codec, spec->out_pins[1], @@ -4549,15 +4500,34 @@ static int ca0132_alt_select_out(struct hda_codec *codec) snd_hda_codec_write(codec, spec->out_pins[0], 0, AC_VERB_SET_EAPD_BTLENABLE, 0x01); - /* If PlayEnhancement is enabled, set different source */ - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); + /* enable center/lfe out node */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[2], + pin_ctl | PIN_OUT); + /* Now set rear surround node as out. */ + pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + snd_hda_set_pin_ctl(codec, spec->out_pins[3], + pin_ctl | PIN_OUT); + + /* + * Without PlayEnhancement being enabled, if we've got a 2.0 + * setup, set it to floating point eight to disable any DSP + * processing effects. + */ + if (!outfx_set && spec->channel_cfg_val == SPEAKER_CHANNELS_2_0) + tmp = FLOAT_EIGHT; else - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); + tmp = speaker_channel_cfgs[spec->channel_cfg_val].val; + + err = dspio_set_uint_param(codec, 0x80, 0x04, tmp); + if (err < 0) + goto exit; + break; case HEADPHONE_OUT: codec_dbg(codec, "%s hp\n", __func__); - snd_hda_codec_write(codec, spec->out_pins[0], 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); @@ -4568,7 +4538,6 @@ static int ca0132_alt_select_out(struct hda_codec *codec) pin_ctl & ~PIN_HP); /* enable headphone, either front or rear */ - if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp)) headphone_nid = spec->out_pins[2]; else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp)) @@ -4579,54 +4548,22 @@ static int ca0132_alt_select_out(struct hda_codec *codec) snd_hda_set_pin_ctl(codec, headphone_nid, pin_ctl | PIN_HP); - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + if (outfx_set) dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); else dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO); break; - case SURROUND_OUT: - codec_dbg(codec, "%s surround\n", __func__); - - /* enable line out node */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[0], - pin_ctl | PIN_OUT); - /* Disable headphone out */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[1], - pin_ctl & ~PIN_HP); - /* Enable EAPD on line out */ - snd_hda_codec_write(codec, spec->out_pins[0], 0, - AC_VERB_SET_EAPD_BTLENABLE, 0x01); - /* enable center/lfe out node */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[2], - pin_ctl | PIN_OUT); - /* Now set rear surround node as out. */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[3], - pin_ctl | PIN_OUT); - - tmp = speaker_channel_cfgs[spec->channel_cfg_val].val; - dspio_set_uint_param(codec, 0x80, 0x04, tmp); - break; } /* - * Surround always sets it's scp command to req 0x04 to FLOAT_EIGHT. - * With this set though, X_BASS cannot be enabled. So, if we have OutFX - * enabled, we need to make sure X_BASS is off, otherwise everything - * sounds all muffled. Running ca0132_effects_set with X_BASS as the - * effect should sort this out. + * If output effects are enabled, set the X-Bass effect value again to + * make sure that it's properly enabled/disabled for speaker + * configurations with an LFE channel. */ - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + if (outfx_set) ca0132_effects_set(codec, X_BASS, spec->effects_switch[X_BASS - EFFECT_START_NID]); - if (spec->cur_out_type == SURROUND_OUT) + if (spec->cur_out_type == SPEAKER_OUT) err = ca0132_alt_surround_set_bass_redirection(codec, spec->bass_redirection_val); else @@ -4646,7 +4583,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) goto exit; } - if (spec->cur_out_type == SURROUND_OUT) + if (spec->cur_out_type == SPEAKER_OUT) err = ca0132_alt_set_full_range_speaker(codec); exit: @@ -5054,7 +4991,7 @@ static int ca0132_voicefx_set(struct hda_codec *codec, int enable) static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) { struct ca0132_spec *spec = codec->spec; - unsigned int on, tmp; + unsigned int on, tmp, channel_cfg; int num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; int err = 0; int idx = nid - EFFECT_START_NID; @@ -5067,8 +5004,12 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) /* if PE if off, turn off out effects. */ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) val = 0; - if (spec->cur_out_type == SURROUND_OUT && nid == X_BASS) - val = 0; + if (spec->cur_out_type == SPEAKER_OUT && nid == X_BASS) { + channel_cfg = spec->channel_cfg_val; + if (channel_cfg != SPEAKER_CHANNELS_2_0 && + channel_cfg != SPEAKER_CHANNELS_4_0) + val = 0; + } } /* for in effect, qualify with CrystalVoice */ @@ -5799,7 +5740,7 @@ static int ca0132_alt_speaker_channel_cfg_put(struct snd_kcontrol *kcontrol, spec->channel_cfg_val = sel; - if (spec->out_enum_val == SURROUND_OUT) + if (spec->out_enum_val == SPEAKER_OUT) ca0132_alt_select_out(codec); return 1; @@ -6121,7 +6062,7 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, if (nid == SPEAKER_FULL_RANGE_FRONT || nid == SPEAKER_FULL_RANGE_REAR) { spec->speaker_range_val[nid - SPEAKER_FULL_RANGE_FRONT] = *valp; - if (spec->cur_out_type == SURROUND_OUT) + if (spec->cur_out_type == SPEAKER_OUT) ca0132_alt_set_full_range_speaker(codec); changed = 0; @@ -6129,7 +6070,7 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, if (nid == BASS_REDIRECTION) { spec->bass_redirection_val = *valp; - if (spec->cur_out_type == SURROUND_OUT) + if (spec->cur_out_type == SPEAKER_OUT) ca0132_alt_surround_set_bass_redirection(codec, *valp); changed = 0; -- cgit From 8e00dc7cedb37c43a2eeb2147d67409ae2e8abf5 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:26 -0400 Subject: ALSA: hda/ca0132 - Clean up ca0132_alt_out_select. Remove the output structures that were in use before and instead set the DSP commands line by line. Now that the commands use is known, it makes the functionality more clear this way. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-8-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 142 +++++++++++++++++-------------------------- 1 file changed, 57 insertions(+), 85 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 5743bdd7cc88..39e333866be3 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -83,6 +83,7 @@ MODULE_FIRMWARE(R3DI_EFX_FILE); static const char *const dirstr[2] = { "Playback", "Capture" }; #define NUM_OF_OUTPUTS 2 +static const char *const out_type_str[2] = { "Speakers", "Headphone" }; enum { SPEAKER_OUT, HEADPHONE_OUT, @@ -667,39 +668,6 @@ enum speaker_tuning_reqs { SPEAKER_TUNING_MUTE = 0x3a, }; -/* DSP command sequences for ca0132_alt_select_out */ -#define ALT_OUT_SET_MAX_COMMANDS 9 /* Max number of commands in sequence */ -struct ca0132_alt_out_set { - char *name; /*preset name*/ - unsigned char commands; - unsigned int mids[ALT_OUT_SET_MAX_COMMANDS]; - unsigned int reqs[ALT_OUT_SET_MAX_COMMANDS]; - unsigned int vals[ALT_OUT_SET_MAX_COMMANDS]; -}; - -static const struct ca0132_alt_out_set alt_out_presets[] = { - { .name = "Line Out", - .commands = 7, - .mids = { 0x96, 0x96, 0x96, 0x8F, - 0x96, 0x96, 0x96 }, - .reqs = { 0x19, 0x17, 0x18, 0x01, - 0x1F, 0x15, 0x3A }, - .vals = { 0x3F000000, 0x42A00000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000 } - }, - { .name = "Headphone", - .commands = 7, - .mids = { 0x96, 0x96, 0x96, 0x8F, - 0x96, 0x96, 0x96 }, - .reqs = { 0x19, 0x17, 0x18, 0x01, - 0x1F, 0x15, 0x3A }, - .vals = { 0x3F000000, 0x42A00000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, - 0x00000000 } - }, -}; - /* Surround output channel count configuration structures. */ #define SPEAKER_CHANNEL_CFG_COUNT 5 enum { @@ -4428,21 +4396,31 @@ static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) } } +static void ca0132_set_out_node_pincfg(struct hda_codec *codec, hda_nid_t nid, + bool out_enable, bool hp_enable) +{ + unsigned int pin_ctl; + + pin_ctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + + pin_ctl = hp_enable ? pin_ctl | PIN_HP_AMP : pin_ctl & ~PIN_HP_AMP; + pin_ctl = out_enable ? pin_ctl | PIN_OUT : pin_ctl & ~PIN_OUT; + snd_hda_set_pin_ctl(codec, nid, pin_ctl); +} + /* * This function behaves similarly to the ca0132_select_out funciton above, * except with a few differences. It adds the ability to select the current * output with an enumerated control "output source" if the auto detect * mute switch is set to off. If the auto detect mute switch is enabled, it * will detect either headphone or lineout(SPEAKER_OUT) from jack detection. - * It also adds the ability to auto-detect the front headphone port. The only - * way to select surround is to disable auto detect, and set Surround with the - * enumerated control. + * It also adds the ability to auto-detect the front headphone port. */ static int ca0132_alt_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - unsigned int tmp, outfx_set, i; - unsigned int pin_ctl; + unsigned int tmp, outfx_set; int jack_present; int auto_jack; int err; @@ -4473,9 +4451,8 @@ static int ca0132_alt_select_out(struct hda_codec *codec) outfx_set = spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]; - /* Begin DSP output switch */ - tmp = FLOAT_ONE; - err = dspio_set_uint_param(codec, 0x96, 0x3A, tmp); + /* Begin DSP output switch, mute DSP volume. */ + err = dspio_set_uint_param(codec, 0x96, SPEAKER_TUNING_MUTE, FLOAT_ONE); if (err < 0) goto exit; @@ -4485,31 +4462,18 @@ static int ca0132_alt_select_out(struct hda_codec *codec) case SPEAKER_OUT: codec_dbg(codec, "%s speaker\n", __func__); - /* disable headphone node */ - - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[1], - pin_ctl & ~PIN_HP); - /* enable line-out node */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[0], - pin_ctl | PIN_OUT); /* Enable EAPD */ snd_hda_codec_write(codec, spec->out_pins[0], 0, AC_VERB_SET_EAPD_BTLENABLE, 0x01); - /* enable center/lfe out node */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[2], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[2], - pin_ctl | PIN_OUT); - /* Now set rear surround node as out. */ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[3], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[3], - pin_ctl | PIN_OUT); + /* Disable headphone node. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[1], 0, 0); + /* Set front L-R to output. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 1, 0); + /* Set Center/LFE to output. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 1, 0); + /* Set rear surround to output. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 1, 0); /* * Without PlayEnhancement being enabled, if we've got a 2.0 @@ -4531,11 +4495,10 @@ static int ca0132_alt_select_out(struct hda_codec *codec) snd_hda_codec_write(codec, spec->out_pins[0], 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); - /* disable speaker*/ - pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, spec->out_pins[0], - pin_ctl & ~PIN_HP); + /* Disable all speaker nodes. */ + ca0132_set_out_node_pincfg(codec, spec->out_pins[0], 0, 0); + ca0132_set_out_node_pincfg(codec, spec->out_pins[2], 0, 0); + ca0132_set_out_node_pincfg(codec, spec->out_pins[3], 0, 0); /* enable headphone, either front or rear */ if (snd_hda_jack_detect(codec, spec->unsol_tag_front_hp)) @@ -4543,15 +4506,15 @@ static int ca0132_alt_select_out(struct hda_codec *codec) else if (snd_hda_jack_detect(codec, spec->unsol_tag_hp)) headphone_nid = spec->out_pins[1]; - pin_ctl = snd_hda_codec_read(codec, headphone_nid, 0, - AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - snd_hda_set_pin_ctl(codec, headphone_nid, - pin_ctl | PIN_HP); + ca0132_set_out_node_pincfg(codec, headphone_nid, 1, 1); if (outfx_set) - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); + err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); else - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO); + err = dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ZERO); + + if (err < 0) + goto exit; break; } /* @@ -4563,29 +4526,38 @@ static int ca0132_alt_select_out(struct hda_codec *codec) ca0132_effects_set(codec, X_BASS, spec->effects_switch[X_BASS - EFFECT_START_NID]); + /* Set speaker EQ bypass attenuation to 0. */ + err = dspio_set_uint_param(codec, 0x8f, 0x01, FLOAT_ZERO); + if (err < 0) + goto exit; + + /* + * Although unused on all cards but the AE series, this is always set + * to zero when setting the output. + */ + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_USE_SPEAKER_EQ, FLOAT_ZERO); + if (err < 0) + goto exit; + if (spec->cur_out_type == SPEAKER_OUT) err = ca0132_alt_surround_set_bass_redirection(codec, spec->bass_redirection_val); else err = ca0132_alt_surround_set_bass_redirection(codec, 0); + /* Unmute DSP now that we're done with output selection. */ + err = dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_MUTE, FLOAT_ZERO); if (err < 0) goto exit; - /* run through the output dsp commands for the selected output. */ - for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) { - err = dspio_set_uint_param(codec, - alt_out_presets[spec->cur_out_type].mids[i], - alt_out_presets[spec->cur_out_type].reqs[i], - alt_out_presets[spec->cur_out_type].vals[i]); - + if (spec->cur_out_type == SPEAKER_OUT) { + err = ca0132_alt_set_full_range_speaker(codec); if (err < 0) goto exit; } - if (spec->cur_out_type == SPEAKER_OUT) - err = ca0132_alt_set_full_range_speaker(codec); - exit: snd_hda_power_down_pm(codec); @@ -5659,7 +5631,7 @@ static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol, if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; strcpy(uinfo->value.enumerated.name, - alt_out_presets[uinfo->value.enumerated.item].name); + out_type_str[uinfo->value.enumerated.item]); return 0; } @@ -5686,7 +5658,7 @@ static int ca0132_alt_output_select_put(struct snd_kcontrol *kcontrol, return 0; codec_dbg(codec, "ca0132_alt_output_select: sel=%d, preset=%s\n", - sel, alt_out_presets[sel].name); + sel, out_type_str[sel]); spec->out_enum_val = sel; -- cgit From def3f0a5c7007c899a9a4d4034130e9d175d952d Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:27 -0400 Subject: ALSA: hda/ca0132 - Add quirk output selection structures. Add structures containing the changes that need to happen on output selection for each quirk. This should streamline the addition of new quirks. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-9-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 336 +++++++++++++++++++++++++++++++------------ 1 file changed, 241 insertions(+), 95 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 39e333866be3..ab84ea397552 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1256,6 +1256,164 @@ static const struct snd_pci_quirk ca0132_quirks[] = { {} }; +/* Output selection quirk info structures. */ +#define MAX_QUIRK_MMIO_GPIO_SET_VALS 3 +#define MAX_QUIRK_SCP_SET_VALS 2 +struct ca0132_alt_out_set_info { + unsigned int dac2port; /* ParamID 0x0d value. */ + + bool has_hda_gpio; + char hda_gpio_pin; + char hda_gpio_set; + + unsigned int mmio_gpio_count; + char mmio_gpio_pin[MAX_QUIRK_MMIO_GPIO_SET_VALS]; + char mmio_gpio_set[MAX_QUIRK_MMIO_GPIO_SET_VALS]; + + unsigned int scp_cmds_count; + unsigned int scp_cmd_mid[MAX_QUIRK_SCP_SET_VALS]; + unsigned int scp_cmd_req[MAX_QUIRK_SCP_SET_VALS]; + unsigned int scp_cmd_val[MAX_QUIRK_SCP_SET_VALS]; + + bool has_chipio_write; + unsigned int chipio_write_addr; + unsigned int chipio_write_data; +}; + +struct ca0132_alt_out_set_quirk_data { + int quirk_id; + + bool has_headphone_gain; + bool is_ae_series; + + struct ca0132_alt_out_set_info out_set_info[NUM_OF_OUTPUTS]; +}; + +static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = { + { .quirk_id = QUIRK_R3DI, + .has_headphone_gain = false, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x24, + .has_hda_gpio = true, + .hda_gpio_pin = 2, + .hda_gpio_set = 1, + .mmio_gpio_count = 0, + .scp_cmds_count = 0, + .has_chipio_write = false, + }, + /* Headphones. */ + { .dac2port = 0x21, + .has_hda_gpio = true, + .hda_gpio_pin = 2, + .hda_gpio_set = 0, + .mmio_gpio_count = 0, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_R3D, + .has_headphone_gain = false, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x24, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 1 }, + .mmio_gpio_set = { 1 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + }, + /* Headphones. */ + { .dac2port = 0x21, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 1 }, + .mmio_gpio_set = { 0 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_SBZ, + .has_headphone_gain = false, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x18, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 7, 4, 1 }, + .mmio_gpio_set = { 0, 1, 1 }, + .scp_cmds_count = 0, + .has_chipio_write = false, }, + /* Headphones. */ + { .dac2port = 0x12, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 7, 4, 1 }, + .mmio_gpio_set = { 1, 1, 0 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_ZXR, + .has_headphone_gain = true, + .is_ae_series = false, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x24, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 2, 3, 5 }, + .mmio_gpio_set = { 1, 1, 0 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + }, + /* Headphones. */ + { .dac2port = 0x21, + .has_hda_gpio = false, + .mmio_gpio_count = 3, + .mmio_gpio_pin = { 2, 3, 5 }, + .mmio_gpio_set = { 0, 1, 1 }, + .scp_cmds_count = 0, + .has_chipio_write = false, + } }, + }, + { .quirk_id = QUIRK_AE5, + .has_headphone_gain = true, + .is_ae_series = true, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0xa4, + .has_hda_gpio = false, + .mmio_gpio_count = 0, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000012 + }, + /* Headphones. */ + { .dac2port = 0xa1, + .has_hda_gpio = false, + .mmio_gpio_count = 0, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000012 + } }, + } +}; + /* * CA0132 codec access */ @@ -3513,26 +3671,6 @@ static void r3di_gpio_mic_set(struct hda_codec *codec, AC_VERB_SET_GPIO_DATA, cur_gpio); } -static void r3di_gpio_out_set(struct hda_codec *codec, - enum r3di_out_select cur_out) -{ - unsigned int cur_gpio; - - /* Get the current GPIO Data setup */ - cur_gpio = snd_hda_codec_read(codec, 0x01, 0, AC_VERB_GET_GPIO_DATA, 0); - - switch (cur_out) { - case R3DI_HEADPHONE_OUT: - cur_gpio &= ~(1 << R3DI_OUT_SELECT_BIT); - break; - case R3DI_LINE_OUT: - cur_gpio |= (1 << R3DI_OUT_SELECT_BIT); - break; - } - snd_hda_codec_write(codec, codec->core.afg, 0, - AC_VERB_SET_GPIO_DATA, cur_gpio); -} - static void r3di_gpio_dsp_status_set(struct hda_codec *codec, enum r3di_dsp_status dsp_status) { @@ -4314,86 +4452,93 @@ static int ca0132_alt_surround_set_bass_redirection(struct hda_codec *codec, * These are the commands needed to setup output on each of the different card * types. */ -static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) +static void ca0132_alt_select_out_get_quirk_data(struct hda_codec *codec, + const struct ca0132_alt_out_set_quirk_data **quirk_data) { struct ca0132_spec *spec = codec->spec; - unsigned int tmp; + int quirk = ca0132_quirk(spec); + unsigned int i; - switch (spec->cur_out_type) { - case SPEAKER_OUT: - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - ca0113_mmio_gpio_set(codec, 7, false); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0d, 0x18); - break; - case QUIRK_ZXR: - ca0113_mmio_gpio_set(codec, 2, true); - ca0113_mmio_gpio_set(codec, 3, true); - ca0113_mmio_gpio_set(codec, 5, false); - zxr_headphone_gain_set(codec, 0); - chipio_set_control_param(codec, 0x0d, 0x24); - break; - case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0d, 0x24); - r3di_gpio_out_set(codec, R3DI_LINE_OUT); - break; - case QUIRK_R3D: - chipio_set_control_param(codec, 0x0d, 0x24); - ca0113_mmio_gpio_set(codec, 1, true); - break; - case QUIRK_AE5: - ae5_mmio_select_out(codec); - ae5_headphone_gain_set(codec, 2); - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x96, 0x29, tmp); - dspio_set_uint_param(codec, 0x96, 0x2a, tmp); - chipio_set_control_param(codec, 0x0d, 0xa4); - chipio_write(codec, 0x18b03c, 0x00000012); - break; - default: - break; + *quirk_data = NULL; + for (i = 0; i < ARRAY_SIZE(quirk_out_set_data); i++) { + if (quirk_out_set_data[i].quirk_id == quirk) { + *quirk_data = &quirk_out_set_data[i]; + return; } - break; - case HEADPHONE_OUT: - switch (ca0132_quirk(spec)) { - case QUIRK_SBZ: - ca0113_mmio_gpio_set(codec, 7, true); - ca0113_mmio_gpio_set(codec, 4, true); - ca0113_mmio_gpio_set(codec, 1, false); - chipio_set_control_param(codec, 0x0d, 0x12); - break; - case QUIRK_ZXR: - ca0113_mmio_gpio_set(codec, 2, false); - ca0113_mmio_gpio_set(codec, 3, false); - ca0113_mmio_gpio_set(codec, 5, true); - zxr_headphone_gain_set(codec, spec->zxr_gain_set); - chipio_set_control_param(codec, 0x0d, 0x21); - break; - case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0d, 0x21); - r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT); - break; - case QUIRK_R3D: - chipio_set_control_param(codec, 0x0d, 0x21); - ca0113_mmio_gpio_set(codec, 0x1, false); - break; - case QUIRK_AE5: - ae5_mmio_select_out(codec); - ae5_headphone_gain_set(codec, - spec->ae5_headphone_gain_val); - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x96, 0x29, tmp); - dspio_set_uint_param(codec, 0x96, 0x2a, tmp); - chipio_set_control_param(codec, 0x0d, 0xa1); - chipio_write(codec, 0x18b03c, 0x00000012); - break; - default: - break; + } +} + +static int ca0132_alt_select_out_quirk_set(struct hda_codec *codec) +{ + const struct ca0132_alt_out_set_quirk_data *quirk_data; + const struct ca0132_alt_out_set_info *out_info; + struct ca0132_spec *spec = codec->spec; + unsigned int i, gpio_data; + int err; + + ca0132_alt_select_out_get_quirk_data(codec, &quirk_data); + if (!quirk_data) + return 0; + + out_info = &quirk_data->out_set_info[spec->cur_out_type]; + if (quirk_data->is_ae_series) + ae5_mmio_select_out(codec); + + if (out_info->has_hda_gpio) { + gpio_data = snd_hda_codec_read(codec, codec->core.afg, 0, + AC_VERB_GET_GPIO_DATA, 0); + + if (out_info->hda_gpio_set) + gpio_data |= (1 << out_info->hda_gpio_pin); + else + gpio_data &= ~(1 << out_info->hda_gpio_pin); + + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_GPIO_DATA, gpio_data); + } + + if (out_info->mmio_gpio_count) { + for (i = 0; i < out_info->mmio_gpio_count; i++) { + ca0113_mmio_gpio_set(codec, out_info->mmio_gpio_pin[i], + out_info->mmio_gpio_set[i]); } - break; } + + if (out_info->scp_cmds_count) { + for (i = 0; i < out_info->scp_cmds_count; i++) { + err = dspio_set_uint_param(codec, + out_info->scp_cmd_mid[i], + out_info->scp_cmd_req[i], + out_info->scp_cmd_val[i]); + if (err < 0) + return err; + } + } + + chipio_set_control_param(codec, 0x0d, out_info->dac2port); + + if (out_info->has_chipio_write) { + chipio_write(codec, out_info->chipio_write_addr, + out_info->chipio_write_data); + } + + if (quirk_data->has_headphone_gain) { + if (spec->cur_out_type != HEADPHONE_OUT) { + if (quirk_data->is_ae_series) + ae5_headphone_gain_set(codec, 2); + else + zxr_headphone_gain_set(codec, 0); + } else { + if (quirk_data->is_ae_series) + ae5_headphone_gain_set(codec, + spec->ae5_headphone_gain_val); + else + zxr_headphone_gain_set(codec, + spec->zxr_gain_set); + } + } + + return 0; } static void ca0132_set_out_node_pincfg(struct hda_codec *codec, hda_nid_t nid, @@ -4456,7 +4601,8 @@ static int ca0132_alt_select_out(struct hda_codec *codec) if (err < 0) goto exit; - ca0132_alt_select_out_quirk_handler(codec); + if (ca0132_alt_select_out_quirk_set(codec) < 0) + goto exit; switch (spec->cur_out_type) { case SPEAKER_OUT: -- cgit From b7a8b9e8e797236a62716118c513e13a2670fd07 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:28 -0400 Subject: ALSA: hda/ca0132 - Fix Recon3D Center/LFE output. Properly set the GPIO pin to un-mute the Center/LFE channel on the Recon3D. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-10-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index ab84ea397552..138403fd1639 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7819,6 +7819,12 @@ static void r3d_setup_defaults(struct hda_codec *codec) if (ca0132_quirk(spec) == QUIRK_R3DI) r3di_gpio_dsp_status_set(codec, R3DI_DSP_DOWNLOADED); + /* Disable mute on Center/LFE. */ + if (ca0132_quirk(spec) == QUIRK_R3D) { + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 4, true); + } + /* Setup effect defaults */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; for (idx = 0; idx < num_fx; idx++) { -- cgit From 620f08eea6d6961b789af3fa3ea86725c8c93ece Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:29 -0400 Subject: ALSA: hda/ca0132 - Add new quirk ID for SoundBlaster AE-7. Add a new PCI subsystem ID for the SoundBlaster AE-7 card. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-11-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 138403fd1639..284f63dc2749 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1134,6 +1134,7 @@ enum { QUIRK_R3DI, QUIRK_R3D, QUIRK_AE5, + QUIRK_AE7, }; #ifdef CONFIG_PCI @@ -1253,6 +1254,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), SND_PCI_QUIRK(0x1102, 0x0018, "Recon3D", QUIRK_R3D), SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5), + SND_PCI_QUIRK(0x1102, 0x0081, "Sound Blaster AE-7", QUIRK_AE7), {} }; -- cgit From a35e37a3a2c5fc9a6dcf1e0445e8472cffac37c1 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:30 -0400 Subject: ALSA: hda/ca0132 - Add SoundBlaster AE-7 pincfg. Add AE-7 pincfg, based on the values set within Windows. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-12-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 284f63dc2749..6791aaf18e63 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -1238,6 +1238,20 @@ static const struct hda_pintbl r3di_pincfgs[] = { {} }; +static const struct hda_pintbl ae7_pincfgs[] = { + { 0x0b, 0x01017010 }, + { 0x0c, 0x014510f0 }, + { 0x0d, 0x414510f0 }, + { 0x0e, 0x01c520f0 }, + { 0x0f, 0x01017114 }, + { 0x10, 0x01017011 }, + { 0x11, 0x018170ff }, + { 0x12, 0x01a170f0 }, + { 0x13, 0x908700f0 }, + { 0x18, 0x500000f0 }, + {} +}; + static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1028, 0x057b, "Alienware M17x R4", QUIRK_ALIENWARE_M17XR4), SND_PCI_QUIRK(0x1028, 0x0685, "Alienware 15 2015", QUIRK_ALIENWARE), @@ -9105,6 +9119,10 @@ static void ca0132_config(struct hda_codec *codec) codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__); snd_hda_apply_pincfgs(codec, ae5_pincfgs); break; + case QUIRK_AE7: + codec_dbg(codec, "%s: QUIRK_AE7 applied.\n", __func__); + snd_hda_apply_pincfgs(codec, ae7_pincfgs); + break; default: break; } @@ -9186,6 +9204,7 @@ static void ca0132_config(struct hda_codec *codec) spec->dig_in = 0x09; break; case QUIRK_AE5: + case QUIRK_AE7: spec->num_outputs = 2; spec->out_pins[0] = 0x0B; /* Line out */ spec->out_pins[1] = 0x11; /* Rear headphone out */ -- cgit From 76d257d67f4104bd3089be1a486047e3f3b40c24 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:31 -0400 Subject: ALSA: hda/ca0132 - Set AE-7 bools and select mixer. Set the boolean values used for desktop cards, and select the desktop mixer. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-13-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 6791aaf18e63..bd5d4f0bd6f5 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -9403,6 +9403,10 @@ static int patch_ca0132(struct hda_codec *codec) spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Sound BlasterX AE-5"); break; + case QUIRK_AE7: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Sound Blaster AE-7"); + break; default: spec->mixers[0] = ca0132_mixer; break; @@ -9413,6 +9417,7 @@ static int patch_ca0132(struct hda_codec *codec) case QUIRK_SBZ: case QUIRK_R3D: case QUIRK_AE5: + case QUIRK_AE7: case QUIRK_ZXR: spec->use_alt_controls = true; spec->use_alt_functions = true; -- cgit From 4e356d56df9dbd4631ce2e40a5fb491829f4e8ce Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:32 -0400 Subject: ALSA: hda/ca0132 - Add ca0132_mmio_init data for SoundBlaster AE-7. Modify the AE-5 ca0132_mmio_init function to add AE-7 specific writes. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-14-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index bd5d4f0bd6f5..10aaa4806946 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -8748,8 +8748,26 @@ static void ca0132_mmio_init_ae5(struct hda_codec *codec) data = ca0113_mmio_init_data_ae5; count = ARRAY_SIZE(ca0113_mmio_init_data_ae5); - for (i = 0; i < count; i++) + if (ca0132_quirk(spec) == QUIRK_AE7) { + writel(0x00000680, spec->mem_base + 0x1c); + writel(0x00880680, spec->mem_base + 0x1c); + } + + for (i = 0; i < count; i++) { + /* + * AE-7 shares all writes with the AE-5, except that it writes + * a different value to 0x20c. + */ + if (i == 21 && ca0132_quirk(spec) == QUIRK_AE7) { + writel(0x00800001, spec->mem_base + addr[i]); + continue; + } + writel(data[i], spec->mem_base + addr[i]); + } + + if (ca0132_quirk(spec) == QUIRK_AE5) + writel(0x00880680, spec->mem_base + 0x1c); } static void ca0132_mmio_init(struct hda_codec *codec) -- cgit From 77bdbae90445771f76510e8c6a4ac0b722a19472 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:33 -0400 Subject: ALSA: hda/ca0132 - Add pre-init function for SoundBlaster AE-7. Add pre DSP initialization function for the AE-7. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-15-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 70 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 10aaa4806946..cd46112c827e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -8786,6 +8786,16 @@ static void ca0132_mmio_init(struct hda_codec *codec) } } +static const unsigned int ca0132_ae5_register_set_addresses[] = { + 0x304, 0x304, 0x304, 0x304, 0x100, 0x304, 0x100, 0x304, 0x100, 0x304, + 0x100, 0x304, 0x86c, 0x800, 0x86c, 0x800, 0x804 +}; + +static const unsigned char ca0132_ae5_register_set_data[] = { + 0x0f, 0x0e, 0x1f, 0x0c, 0x3f, 0x08, 0x7f, 0x00, 0xff, 0x00, 0x6b, + 0x01, 0x6b, 0x57 +}; + /* * This function writes to some SFR's, does some region2 writes, and then * eventually resets the codec with the 0x7ff verb. Not quite sure why it does @@ -8794,6 +8804,18 @@ static void ca0132_mmio_init(struct hda_codec *codec) static void ae5_register_set(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; + unsigned int count = ARRAY_SIZE(ca0132_ae5_register_set_addresses); + const unsigned int *addr = ca0132_ae5_register_set_addresses; + const unsigned char *data = ca0132_ae5_register_set_data; + unsigned int i, cur_addr; + unsigned char tmp[3]; + + if (ca0132_quirk(spec) == QUIRK_AE7) { + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x41); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc8); + } chipio_8051_write_direct(codec, 0x93, 0x10); snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, @@ -8801,25 +8823,43 @@ static void ae5_register_set(struct hda_codec *codec) snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); - writeb(0x0f, spec->mem_base + 0x304); - writeb(0x0f, spec->mem_base + 0x304); - writeb(0x0f, spec->mem_base + 0x304); - writeb(0x0f, spec->mem_base + 0x304); - writeb(0x0e, spec->mem_base + 0x100); - writeb(0x1f, spec->mem_base + 0x304); - writeb(0x0c, spec->mem_base + 0x100); - writeb(0x3f, spec->mem_base + 0x304); - writeb(0x08, spec->mem_base + 0x100); - writeb(0x7f, spec->mem_base + 0x304); - writeb(0x00, spec->mem_base + 0x100); - writeb(0xff, spec->mem_base + 0x304); + if (ca0132_quirk(spec) == QUIRK_AE7) { + tmp[0] = 0x03; + tmp[1] = 0x03; + tmp[2] = 0x07; + } else { + tmp[0] = 0x0f; + tmp[1] = 0x0f; + tmp[2] = 0x0f; + } - ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + for (i = cur_addr = 0; i < 3; i++, cur_addr++) + writeb(tmp[i], spec->mem_base + addr[cur_addr]); + + /* + * First writes are in single bytes, final are in 4 bytes. So, we use + * writeb, then writel. + */ + for (i = 0; cur_addr < 12; i++, cur_addr++) + writeb(data[i], spec->mem_base + addr[cur_addr]); + + for (; cur_addr < count; i++, cur_addr++) + writel(data[i], spec->mem_base + addr[cur_addr]); + + writel(0x00800001, spec->mem_base + 0x20c); + + if (ca0132_quirk(spec) == QUIRK_AE7) { + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + } else { + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + } chipio_8051_write_direct(codec, 0x90, 0x00); chipio_8051_write_direct(codec, 0x90, 0x10); - ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + if (ca0132_quirk(spec) == QUIRK_AE5) + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); chipio_write(codec, 0x18b0a4, 0x000000c2); @@ -8918,7 +8958,7 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_power_up_pm(codec); - if (ca0132_quirk(spec) == QUIRK_AE5) + if (ca0132_quirk(spec) == QUIRK_AE5 || ca0132_quirk(spec) == QUIRK_AE7) ae5_register_set(codec); ca0132_init_unsol(codec); -- cgit From cfa736e2f02d768dad1b611abdb85fe6c10d8000 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:34 -0400 Subject: ALSA: hda/ca0132 - Add init data for SoundBlaster AE-7. Add initialization data for the SoundBlaster AE-7 card. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-16-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index cd46112c827e..dc1eb9bfcc5e 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3582,6 +3582,7 @@ static void ca0132_gpio_init(struct hda_codec *codec) switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: + case QUIRK_AE7: snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23); @@ -8911,6 +8912,19 @@ static void ca0132_alt_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->desktop_init_verbs); ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); break; + case QUIRK_AE7: + ca0132_gpio_init(codec); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x49); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x88); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + chipio_write(codec, 0x18b008, 0x000000f8); + chipio_write(codec, 0x18b008, 0x000000f0); + chipio_write(codec, 0x18b030, 0x00000020); + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + break; case QUIRK_ZXR: snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); -- cgit From e5b21888882bb340bf0fbcbcea909b09eef2933f Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:35 -0400 Subject: ALSA: hda/ca0132 - Add DSP setup functions for AE-7. Add DSP setup functions for the Sound Blaster AE-7 post DSP download. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-17-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 290 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 290 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index dc1eb9bfcc5e..8519119ef7a6 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -7378,6 +7378,7 @@ static void ca0132_alt_init_speaker_tuning(struct hda_codec *codec) values = zxr_default_delay_values; break; case QUIRK_AE5: + case QUIRK_AE7: values = ae5_default_delay_values; break; default: @@ -7551,6 +7552,7 @@ static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) switch (ca0132_quirk(spec)) { case QUIRK_SBZ: case QUIRK_AE5: + case QUIRK_AE7: tmp = 0x00000003; dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); tmp = 0x00000000; @@ -7760,6 +7762,206 @@ static void ae5_post_dsp_startup_data(struct hda_codec *codec) mutex_unlock(&spec->chipio_mutex); } +static const unsigned int ae7_port_set_data[] = { + 0x0001e0c0, 0x0001e1c1, 0x0001e4c2, 0x0001e5c3, 0x0001e2c4, 0x0001e3c5, + 0x0001e8c6, 0x0001e9c7, 0x0001ecc8, 0x0001edc9, 0x0001eaca, 0x0001ebcb +}; + +static void ae7_post_dsp_setup_ports(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int i, count, addr; + + mutex_lock(&spec->chipio_mutex); + + chipio_set_stream_channels(codec, 0x0c, 6); + chipio_set_stream_control(codec, 0x0c, 1); + + count = ARRAY_SIZE(ae7_port_set_data); + addr = 0x190030; + for (i = 0; i < count; i++) { + chipio_write_no_mutex(codec, addr, ae7_port_set_data[i]); + + /* Addresses are incremented by 4-bytes. */ + addr += 0x04; + } + + /* + * Port setting always ends with a write of 0x1 to address 0x19042c. + */ + chipio_write_no_mutex(codec, 0x19042c, 0x00000001); + + ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x0d, 0x40); + ca0113_mmio_command_set(codec, 0x48, 0x17, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x19, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x11, 0xff); + ca0113_mmio_command_set(codec, 0x48, 0x12, 0xff); + ca0113_mmio_command_set(codec, 0x48, 0x13, 0xff); + ca0113_mmio_command_set(codec, 0x48, 0x14, 0x7f); + + mutex_unlock(&spec->chipio_mutex); +} + +static void ae7_post_dsp_asi_stream_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81); + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); + + chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); + chipio_set_stream_channels(codec, 0x0c, 6); + chipio_set_stream_control(codec, 0x0c, 1); + + chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00); + chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0); + + chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); + chipio_set_stream_channels(codec, 0x18, 6); + chipio_set_stream_control(codec, 0x18, 1); + + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4); + + mutex_unlock(&spec->chipio_mutex); +} + +static void ae7_post_dsp_pll_setup(struct hda_codec *codec) +{ + const unsigned int addr[] = { 0x41, 0x45, 0x40, 0x43, 0x51 }; + const unsigned int data[] = { 0xc8, 0xcc, 0xcb, 0xc7, 0x8d }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(addr); i++) { + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, addr[i]); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, data[i]); + } +} + +static void ae7_post_dsp_asi_setup_ports(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + const unsigned int target[] = { 0x0b, 0x04, 0x06, 0x0a, 0x0c, 0x11, + 0x12, 0x13, 0x14 }; + const unsigned int data[] = { 0x12, 0x00, 0x48, 0x05, 0x5f, 0xff, + 0xff, 0xff, 0x7f }; + unsigned int i; + + mutex_lock(&spec->chipio_mutex); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + + chipio_write_no_mutex(codec, 0x189000, 0x0001f101); + chipio_write_no_mutex(codec, 0x189004, 0x0001f101); + chipio_write_no_mutex(codec, 0x189024, 0x00014004); + chipio_write_no_mutex(codec, 0x189028, 0x0002000f); + + ae7_post_dsp_pll_setup(codec); + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); + + for (i = 0; i < ARRAY_SIZE(target); i++) + ca0113_mmio_command_set(codec, 0x48, target[i], data[i]); + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + + chipio_set_stream_source_dest(codec, 0x21, 0x64, 0x56); + chipio_set_stream_channels(codec, 0x21, 2); + chipio_set_conn_rate_no_mutex(codec, 0x56, SR_8_000); + + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_NODE_ID, 0x09); + /* + * In the 8051's memory, this param is referred to as 'n2sid', which I + * believe is 'node to streamID'. It seems to be a way to assign a + * stream to a given HDA node. + */ + chipio_set_control_param_no_mutex(codec, 0x20, 0x21); + + chipio_write_no_mutex(codec, 0x18b038, 0x00000088); + + /* + * Now, at this point on Windows, an actual stream is setup and + * seemingly sends data to the HDA node 0x09, which is the digital + * audio input node. This is left out here, because obviously I don't + * know what data is being sent. Interestingly, the AE-5 seems to go + * through the motions of getting here and never actually takes this + * step, but the AE-7 does. + */ + + ca0113_mmio_gpio_set(codec, 0, 1); + ca0113_mmio_gpio_set(codec, 1, 1); + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + chipio_write_no_mutex(codec, 0x18b03c, 0x00000000); + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + + chipio_set_stream_source_dest(codec, 0x05, 0x43, 0x00); + chipio_set_stream_source_dest(codec, 0x18, 0x09, 0xd0); + + chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); + chipio_set_stream_channels(codec, 0x18, 6); + + /* + * Runs again, this has been repeated a few times, but I'm just + * following what the Windows driver does. + */ + ae7_post_dsp_pll_setup(codec); + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); + + mutex_unlock(&spec->chipio_mutex); +} + +/* + * The Windows driver has commands that seem to setup ASI, which I believe to + * be some sort of audio serial interface. My current speculation is that it's + * related to communicating with the new DAC. + */ +static void ae7_post_dsp_asi_setup(struct hda_codec *codec) +{ + chipio_8051_write_direct(codec, 0x93, 0x10); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + + chipio_set_control_param(codec, 3, 3); + chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83); + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + snd_hda_codec_write(codec, 0x17, 0, 0x794, 0x00); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x92); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0xfa); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x22); + + ae7_post_dsp_pll_setup(codec); + ae7_post_dsp_asi_stream_setup(codec); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + + ae7_post_dsp_asi_setup_ports(codec); +} + /* * Setup default parameters for DSP */ @@ -7983,6 +8185,91 @@ static void ae5_setup_defaults(struct hda_codec *codec) ca0132_alt_create_dummy_stream(codec); } +/* + * Setup default parameters for the Sound Blaster AE-7 DSP. + */ +static void ae7_setup_defaults(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + int num_fx; + int idx, i; + + if (spec->dsp_state != DSP_DOWNLOADED) + return; + + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec); + ae7_post_dsp_setup_ports(codec); + + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_FRONT_LEFT_INVERT, tmp); + dspio_set_uint_param(codec, 0x96, + SPEAKER_TUNING_FRONT_RIGHT_INVERT, tmp); + + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + + /* New, unknown SCP req's */ + dspio_set_uint_param(codec, 0x80, 0x0d, tmp); + dspio_set_uint_param(codec, 0x80, 0x0e, tmp); + + ca0113_mmio_gpio_set(codec, 0, false); + + /* Internal loopback off */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x37, 0x08, tmp); + dspio_set_uint_param(codec, 0x37, 0x10, tmp); + + /*remove DSP headroom*/ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x3C, tmp); + + /* set WUH source */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x31, 0x00, tmp); + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); + + /* Set speaker source? */ + dspio_set_uint_param(codec, 0x32, 0x00, tmp); + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + + /* + * This is the second time we've called this, but this is seemingly + * what Windows does. + */ + ca0132_alt_init_analog_mics(codec); + + ae7_post_dsp_asi_setup(codec); + + /* + * Not sure why, but these are both set to 1. They're only set to 0 + * upon shutdown. + */ + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 1, true); + + /* Volume control related. */ + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x04); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x04); + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x80); + + /* out, in effects + voicefx */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; + for (idx = 0; idx < num_fx; idx++) { + for (i = 0; i <= ca0132_effects[idx].params; i++) { + dspio_set_uint_param(codec, + ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[i], + ca0132_effects[idx].def_vals[i]); + } + } + + ca0132_alt_init_speaker_tuning(codec); + + ca0132_alt_create_dummy_stream(codec); +} + /* * Initialization of flags in chip */ @@ -9000,6 +9287,9 @@ static int ca0132_init(struct hda_codec *codec) case QUIRK_AE5: ae5_setup_defaults(codec); break; + case QUIRK_AE7: + ae7_setup_defaults(codec); + break; default: ca0132_setup_defaults(codec); ca0132_init_analog_mic2(codec); -- cgit From 91b94a933f28b0b819224bc638866b0844317776 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:36 -0400 Subject: ALSA: hda/ca0132 - Add output selection for SoundBlaster AE-7. Add output selection quirk table information for SoundBlaster AE-7, and slightly modify the AE-5's ca0113 command table to accommodate the AE-7. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-18-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 88 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 68 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 8519119ef7a6..dcc8d29d934c 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -727,22 +727,29 @@ static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = { }; /* Values for ca0113_mmio_command_set for selecting output. */ -#define AE5_CA0113_OUT_SET_COMMANDS 6 -struct ae5_ca0113_output_set { - unsigned int group[AE5_CA0113_OUT_SET_COMMANDS]; - unsigned int target[AE5_CA0113_OUT_SET_COMMANDS]; - unsigned int vals[AE5_CA0113_OUT_SET_COMMANDS]; +#define AE_CA0113_OUT_SET_COMMANDS 6 +struct ae_ca0113_output_set { + unsigned int group[AE_CA0113_OUT_SET_COMMANDS]; + unsigned int target[AE_CA0113_OUT_SET_COMMANDS]; + unsigned int vals[NUM_OF_OUTPUTS][AE_CA0113_OUT_SET_COMMANDS]; }; -static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { - { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, - .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, - .vals = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f } - }, - { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, - .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, - .vals = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } - } +static const struct ae_ca0113_output_set ae5_ca0113_output_presets = { + .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + /* Speakers. */ + .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f }, + /* Headphones. */ + { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } }, +}; + +static const struct ae_ca0113_output_set ae7_ca0113_output_presets = { + .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + /* Speakers. */ + .vals = { { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f }, + /* Headphones. */ + { 0x3f, 0x3f, 0x00, 0x00, 0x02, 0x00 } }, }; /* ae5 ca0113 command sequences to set headphone gain levels. */ @@ -1427,6 +1434,41 @@ static const struct ca0132_alt_out_set_quirk_data quirk_out_set_data[] = { .chipio_write_addr = 0x0018b03c, .chipio_write_data = 0x00000012 } }, + }, + { .quirk_id = QUIRK_AE7, + .has_headphone_gain = true, + .is_ae_series = true, + .out_set_info = { + /* Speakers. */ + { .dac2port = 0x58, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 0 }, + .mmio_gpio_set = { 1 }, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ZERO, FLOAT_ZERO }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000000 + }, + /* Headphones. */ + { .dac2port = 0x58, + .has_hda_gpio = false, + .mmio_gpio_count = 1, + .mmio_gpio_pin = { 0 }, + .mmio_gpio_set = { 1 }, + .scp_cmds_count = 2, + .scp_cmd_mid = { 0x96, 0x96 }, + .scp_cmd_req = { SPEAKER_TUNING_FRONT_LEFT_INVERT, + SPEAKER_TUNING_FRONT_RIGHT_INVERT }, + .scp_cmd_val = { FLOAT_ONE, FLOAT_ONE }, + .has_chipio_write = true, + .chipio_write_addr = 0x0018b03c, + .chipio_write_data = 0x00000010 + } }, } }; @@ -4383,18 +4425,24 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); static void ae5_mmio_select_out(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; + const struct ae_ca0113_output_set *out_cmds; unsigned int i; - for (i = 0; i < AE5_CA0113_OUT_SET_COMMANDS; i++) - ca0113_mmio_command_set(codec, - ae5_ca0113_output_presets[spec->cur_out_type].group[i], - ae5_ca0113_output_presets[spec->cur_out_type].target[i], - ae5_ca0113_output_presets[spec->cur_out_type].vals[i]); + if (ca0132_quirk(spec) == QUIRK_AE5) + out_cmds = &ae5_ca0113_output_presets; + else + out_cmds = &ae7_ca0113_output_presets; + + for (i = 0; i < AE_CA0113_OUT_SET_COMMANDS; i++) + ca0113_mmio_command_set(codec, out_cmds->group[i], + out_cmds->target[i], + out_cmds->vals[spec->cur_out_type][i]); } static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; + int quirk = ca0132_quirk(spec); unsigned int tmp; int err; @@ -4426,7 +4474,7 @@ static int ca0132_alt_set_full_range_speaker(struct hda_codec *codec) * Only the AE series cards set this value when setting full-range, * and it's always 1.0f. */ - if (ca0132_quirk(spec) == QUIRK_AE5) { + if (quirk == QUIRK_AE5 || quirk == QUIRK_AE7) { err = dspio_set_uint_param(codec, 0x96, SPEAKER_FULL_RANGE_SURROUND_L_R, FLOAT_ONE); if (err < 0) -- cgit From ed93f9750c6c2ed371347d0aac3dcd31cb9cf256 Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:37 -0400 Subject: ALSA: hda/ca0132 - Add AE-7 microphone selection commands. Add AE-7 quirk data for setting of microphone. The AE-7 has no front panel connector, so only rear-mic/line-in have new commands. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-19-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index dcc8d29d934c..8c6e38734489 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -4997,6 +4997,15 @@ static int ca0132_alt_select_in(struct hda_codec *codec) ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); tmp = FLOAT_THREE; break; + case QUIRK_AE7: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + tmp = FLOAT_THREE; + chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, + SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, + SR_96_000); + dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO); + break; default: tmp = FLOAT_ONE; break; @@ -5042,6 +5051,14 @@ static int ca0132_alt_select_in(struct hda_codec *codec) case QUIRK_AE5: ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); break; + case QUIRK_AE7: + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x3f); + chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, + SR_96_000); + chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, + SR_96_000); + dspio_set_uint_param(codec, 0x80, 0x01, FLOAT_ZERO); + break; default: break; } @@ -5051,7 +5068,10 @@ static int ca0132_alt_select_in(struct hda_codec *codec) if (ca0132_quirk(spec) == QUIRK_R3DI) chipio_set_conn_rate(codec, 0x0F, SR_96_000); - tmp = FLOAT_ZERO; + if (ca0132_quirk(spec) == QUIRK_AE7) + tmp = FLOAT_THREE; + else + tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x00, tmp); switch (ca0132_quirk(spec)) { -- cgit From 24a28eaeb23b2ff7791350bb44875ca4dc3bef3f Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:38 -0400 Subject: ALSA: hda/ca0132 - Add AE-7 custom controls. Add headphone gain and DAC filter controls, which use the same commands as the AE-5. Also, change input source enumerated control item count to exclude front microphone. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-20-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 8c6e38734489..52f6d3740e0a 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -5839,6 +5839,13 @@ static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol, int sel = ucontrol->value.enumerated.item[0]; unsigned int items = IN_SRC_NUM_OF_INPUTS; + /* + * The AE-7 has no front microphone, so limit items to 2: rear mic and + * line-in. + */ + if (ca0132_quirk(spec) == QUIRK_AE7) + items = 2; + if (sel >= items) return 0; @@ -7029,20 +7036,25 @@ static int ca0132_build_controls(struct hda_codec *codec) } } - if (ca0132_quirk(spec) == QUIRK_AE5) { + switch (ca0132_quirk(spec)) { + case QUIRK_AE5: + case QUIRK_AE7: err = ae5_add_headphone_gain_enum(codec); if (err < 0) return err; err = ae5_add_sound_filter_enum(codec); if (err < 0) return err; - } - - if (ca0132_quirk(spec) == QUIRK_ZXR) { + break; + case QUIRK_ZXR: err = zxr_add_headphone_gain_switch(codec); if (err < 0) return err; + break; + default: + break; } + #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); #endif -- cgit From 685a04a53747d07d22f5139c04ae35745200a91d Mon Sep 17 00:00:00 2001 From: Connor McAdams Date: Tue, 25 Aug 2020 16:10:39 -0400 Subject: ALSA: hda/ca0132 - Add AE-7 exit commands. Add exit commands for the AE-7. Signed-off-by: Connor McAdams Link: https://lore.kernel.org/r/20200825201040.30339-21-conmanx360@gmail.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 52f6d3740e0a..9779978e4bc7 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -8838,6 +8838,32 @@ static void ae5_exit_chip(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, 0x724, 0x83); } +static void ae7_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x18, 0); + chipio_set_stream_source_dest(codec, 0x21, 0xc8, 0xc8); + chipio_set_stream_channels(codec, 0x21, 0); + chipio_set_control_param(codec, CONTROL_PARAM_NODE_ID, 0x09); + chipio_set_control_param(codec, 0x20, 0x01); + + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + + chipio_set_stream_control(codec, 0x18, 0); + chipio_set_stream_control(codec, 0x0c, 0); + + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); + snd_hda_codec_write(codec, 0x15, 0, 0x724, 0x83); + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x00); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 1, false); + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); +} + static void zxr_exit_chip(struct hda_codec *codec) { chipio_set_stream_control(codec, 0x03, 0); @@ -9457,6 +9483,9 @@ static void ca0132_free(struct hda_codec *codec) case QUIRK_AE5: ae5_exit_chip(codec); break; + case QUIRK_AE7: + ae7_exit_chip(codec); + break; case QUIRK_R3DI: r3di_gpio_shutdown(codec); break; -- cgit From 76ab546cd8f0c64d4603b2faad4558c5b670561e Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Tue, 25 Aug 2020 16:58:51 -0700 Subject: ASoC: SOF: IPC: make sof_ipc_window monosized This step is needed to add possibility to pack sof_ipc_window inside another one in used FW build tools - for example in extended manifest. Structure reusability leads to easy parsing function reuse, so source code is shorter and easier to maintain. Using structures with constant size is less tricky and properly supported by each toolchain by contrast to variable size elements. This is minor ABI change - backward compatibility is kept. Signed-off-by: Karol Trzcinski Reviewed-by: Guennadi Liakhovetski Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235854.1588034-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index b94fa5f5d480..25dc28ebafb7 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -20,13 +20,12 @@ static int get_ext_windows(struct snd_sof_dev *sdev, { const struct sof_ipc_window *w = container_of(ext_hdr, struct sof_ipc_window, ext_hdr); - size_t w_size = struct_size(w, window, w->num_windows); if (w->num_windows == 0 || w->num_windows > SOF_IPC_MAX_ELEMS) return -EINVAL; if (sdev->info_window) { - if (memcmp(sdev->info_window, w, w_size)) { + if (memcmp(sdev->info_window, w, ext_hdr->hdr.size)) { dev_err(sdev->dev, "error: mismatch between window descriptor from extended manifest and mailbox"); return -EINVAL; } @@ -34,7 +33,7 @@ static int get_ext_windows(struct snd_sof_dev *sdev, } /* keep a local copy of the data */ - sdev->info_window = kmemdup(w, w_size, GFP_KERNEL); + sdev->info_window = kmemdup(w, ext_hdr->hdr.size, GFP_KERNEL); if (!sdev->info_window) return -ENOMEM; -- cgit From e9157a449aa3f72fdbded9ce780c666bf0951cf3 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Tue, 25 Aug 2020 16:58:52 -0700 Subject: ASoC: SOF: loader: fix memory leak in get_ext_windows sdev->info_window is allocated with kmemdup and never freed, use devm_ version since this is only used for first boot. Fixes: 8d809c15acf23 ('ASoC: SOF: ext_manifest: parse windows') Cc: Karol Trzcinski Signed-off-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Reviewed-by: Kai Vehmanen Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Bard Liao Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235854.1588034-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 25dc28ebafb7..42e0e3e58869 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -33,7 +33,8 @@ static int get_ext_windows(struct snd_sof_dev *sdev, } /* keep a local copy of the data */ - sdev->info_window = kmemdup(w, ext_hdr->hdr.size, GFP_KERNEL); + sdev->info_window = devm_kmemdup(sdev->dev, w, ext_hdr->hdr.size, + GFP_KERNEL); if (!sdev->info_window) return -ENOMEM; -- cgit From 60b7c1ba289b8ebe4f275b0b381f711e5b60184b Mon Sep 17 00:00:00 2001 From: Karol Trzcinski Date: Tue, 25 Aug 2020 16:58:53 -0700 Subject: ASoC: SOF: ext_manifest: Parse debug ABI version The debug ABI can be extracted from the extended manifest content. This information known at build time does not need to be provided in a mailbox. Signed-off-by: Karol Trzcinski Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235854.1588034-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index 42e0e3e58869..b8e72084dfeb 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -176,6 +176,22 @@ static int ext_man_get_cc_info(struct snd_sof_dev *sdev, return get_cc_info(sdev, &cc->cc_version.ext_hdr); } +static int ext_man_get_dbg_abi_info(struct snd_sof_dev *sdev, + const struct sof_ext_man_elem_header *hdr) +{ + const struct ext_man_dbg_abi *dbg_abi = + container_of(hdr, struct ext_man_dbg_abi, hdr); + + if (sdev->first_boot) + dev_dbg(sdev->dev, + "Firmware: DBG_ABI %d:%d:%d\n", + SOF_ABI_VERSION_MAJOR(dbg_abi->dbg_abi.abi_dbg_version), + SOF_ABI_VERSION_MINOR(dbg_abi->dbg_abi.abi_dbg_version), + SOF_ABI_VERSION_PATCH(dbg_abi->dbg_abi.abi_dbg_version)); + + return 0; +} + static ssize_t snd_sof_ext_man_size(const struct firmware *fw) { const struct sof_ext_man_header *head; @@ -255,6 +271,9 @@ static int snd_sof_fw_ext_man_parse(struct snd_sof_dev *sdev, case SOF_EXT_MAN_ELEM_CC_VERSION: ret = ext_man_get_cc_info(sdev, elem_hdr); break; + case SOF_EXT_MAN_ELEM_DBG_ABI: + ret = ext_man_get_dbg_abi_info(sdev, elem_hdr); + break; default: dev_warn(sdev->dev, "warning: unknown sof_ext_man header type %d size 0x%X\n", elem_hdr->type, elem_hdr->size); -- cgit From e17b7389dcc4239b806cd8789a812ee1b8b7b134 Mon Sep 17 00:00:00 2001 From: Iulian Olaru Date: Tue, 25 Aug 2020 16:58:54 -0700 Subject: ASoC: SOF: loader: Add debug box region This patch adds an IPC initiated debug box region in the snd_sof_dev structure, defined in soc/sof/sof-priv.h. It is initialized at loading, in the sof_get_windows function from soc/sof/loader.c, in a similar manner with the stream box and host box. This region is useful because the firmware will put an error message here so the kernel can read it in case of a dsp oops. Signed-off-by: Iulian Olaru Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235854.1588034-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/loader.c | 9 +++++++++ sound/soc/sof/sof-priv.h | 1 + 2 files changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/loader.c b/sound/soc/sof/loader.c index b8e72084dfeb..68ed454f7ddf 100644 --- a/sound/soc/sof/loader.c +++ b/sound/soc/sof/loader.c @@ -310,6 +310,8 @@ static void sof_get_windows(struct snd_sof_dev *sdev) u32 outbox_size = 0; u32 stream_size = 0; u32 inbox_size = 0; + u32 debug_size = 0; + u32 debug_offset = 0; int window_offset; int bar; int i; @@ -363,6 +365,8 @@ static void sof_get_windows(struct snd_sof_dev *sdev) SOF_DEBUGFS_ACCESS_D0_ONLY); break; case SOF_IPC_REGION_DEBUG: + debug_offset = window_offset + elem->offset; + debug_size = elem->size; snd_sof_debugfs_io_item(sdev, sdev->bar[bar] + window_offset + @@ -412,12 +416,17 @@ static void sof_get_windows(struct snd_sof_dev *sdev) sdev->stream_box.offset = stream_offset; sdev->stream_box.size = stream_size; + sdev->debug_box.offset = debug_offset; + sdev->debug_box.size = debug_size; + dev_dbg(sdev->dev, " mailbox upstream 0x%x - size 0x%x\n", inbox_offset, inbox_size); dev_dbg(sdev->dev, " mailbox downstream 0x%x - size 0x%x\n", outbox_offset, outbox_size); dev_dbg(sdev->dev, " stream region 0x%x - size 0x%x\n", stream_offset, stream_size); + dev_dbg(sdev->dev, " debug region 0x%x - size 0x%x\n", + debug_offset, debug_size); } /* check for ABI compatibility and create memory windows on first boot */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index e2070072791c..53d26be88f64 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -383,6 +383,7 @@ struct snd_sof_dev { struct snd_sof_mailbox dsp_box; /* DSP initiated IPC */ struct snd_sof_mailbox host_box; /* Host initiated IPC */ struct snd_sof_mailbox stream_box; /* Stream position update */ + struct snd_sof_mailbox debug_box; /* Debug info updates */ struct snd_sof_ipc_msg *msg; int ipc_irq; u32 next_comp_id; /* monotonic - reset during S3 */ -- cgit From 53ec753137f2a407c949cef3158e89ece7688637 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 25 Aug 2020 16:50:34 -0700 Subject: ASoC: SOF: Intel: hda: report error only for the last ROM init iteration The FW boot sequence includes multiple attempts for ROM init. When it does take more than one attempt, we should not log the errors encountered during the failed attempts and only log them during the final iteration. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Kai Vehmanen Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-2-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 42 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 441d05cda604..9cb219b87b3e 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -79,7 +79,7 @@ error: * reset/stall and then turn it off */ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, - u32 fwsize, int stream_tag) + u32 fwsize, int stream_tag, int iteration) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -90,7 +90,8 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, /* step 1: power up corex */ ret = hda_dsp_core_power_up(sdev, chip->cores_mask); if (ret < 0) { - dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); + if (iteration == HDA_FW_BOOT_ATTEMPTS) + dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); goto err; } @@ -112,7 +113,9 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, /* step 3: unset core 0 reset state & unstall/run core 0 */ ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); if (ret < 0) { - dev_err(sdev->dev, "error: dsp core start failed %d\n", ret); + if (iteration == HDA_FW_BOOT_ATTEMPTS) + dev_err(sdev->dev, + "error: dsp core start failed %d\n", ret); ret = -EIO; goto err; } @@ -126,8 +129,10 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, HDA_DSP_INIT_TIMEOUT_US); if (ret < 0) { - dev_err(sdev->dev, "error: %s: timeout for HIPCIE done\n", - __func__); + if (iteration == HDA_FW_BOOT_ATTEMPTS) + dev_err(sdev->dev, + "error: %s: timeout for HIPCIE done\n", + __func__); goto err; } @@ -141,7 +146,9 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, ret = hda_dsp_core_power_down(sdev, chip->cores_mask & ~(HDA_DSP_CORE_MASK(0))); if (ret < 0) { - dev_err(sdev->dev, "error: dsp core x power down failed\n"); + if (iteration == HDA_FW_BOOT_ATTEMPTS) + dev_err(sdev->dev, + "error: dsp core x power down failed\n"); goto err; } @@ -159,9 +166,10 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, if (!ret) return 0; - dev_err(sdev->dev, - "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", - __func__); + if (iteration == HDA_FW_BOOT_ATTEMPTS) + dev_err(sdev->dev, + "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", + __func__); err: hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); @@ -329,25 +337,25 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) /* try ROM init a few times before giving up */ for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) { + dev_dbg(sdev->dev, + "Attempting iteration %d of Core En/ROM load...\n", i); + ret = cl_dsp_init(sdev, stripped_firmware.data, - stripped_firmware.size, tag); + stripped_firmware.size, tag, i + 1); /* don't retry anymore if successful */ if (!ret) break; + } - dev_dbg(sdev->dev, "iteration %d of Core En/ROM load failed: %d\n", + if (i == HDA_FW_BOOT_ATTEMPTS) { + dev_err(sdev->dev, "error: dsp init failed after %d attempts with err: %d\n", i, ret); - dev_dbg(sdev->dev, "Error code=0x%x: FW status=0x%x\n", + dev_err(sdev->dev, "ROM error=0x%x: FW status=0x%x\n", snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_ERROR), snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_STATUS)); - } - - if (i == HDA_FW_BOOT_ATTEMPTS) { - dev_err(sdev->dev, "error: dsp init failed after %d attempts with err: %d\n", - i, ret); goto cleanup; } -- cgit From 6c63c954e1c52f1262f986f36d95f557c6f8fa94 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 25 Aug 2020 16:50:35 -0700 Subject: ASoC: SOF: fix a runtime pm issue in SOF when HDMI codec doesn't work When hda_codec_probe() doesn't initialize audio component, we disable the codec and keep going. However,the resources are not released. The child_count of SOF device is increased in snd_hdac_ext_bus_device_init but is not decrease in error case, so SOF can't get suspended. snd_hdac_ext_bus_device_exit will be invoked in HDA framework if it gets a error. Now copy this behavior to release resources and decrease SOF device child_count to release SOF device. Signed-off-by: Rander Wang Reviewed-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index 55811b99e47a..f6ba3b593e1f 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -151,7 +151,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, if (!hdev->bus->audio_component) { dev_dbg(sdev->dev, "iDisp hw present but no driver\n"); - return -ENOENT; + goto error; } hda_priv->need_display_power = true; } @@ -174,7 +174,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, * other return codes without modification */ if (ret == 0) - ret = -ENOENT; + goto error; } return ret; -- cgit From 3dca35e35b42b3405ddad7ee95c02a2d8cf28592 Mon Sep 17 00:00:00 2001 From: Jaska Uimonen Date: Tue, 25 Aug 2020 16:50:36 -0700 Subject: ASoC: SOF: intel: hda: support also devices with 1 and 3 dmics Currently the dmic check code supports only devices with 2 or 4 dmics. With other dmic counts the function will return 0. Lately we've seen devices with only 1 dmic thus enable also configurations with 1, and possibly 3, dmics. Add also topology postfix -1ch and -3ch for new dmic configuration. Signed-off-by: Jaska Uimonen Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index b8157c1f37f3..0e8285b34a7d 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -544,7 +544,7 @@ static int check_nhlt_dmic(struct snd_sof_dev *sdev) if (nhlt) { dmic_num = intel_nhlt_get_dmic_geo(sdev->dev, nhlt); intel_nhlt_free(nhlt); - if (dmic_num == 2 || dmic_num == 4) + if (dmic_num >= 1 && dmic_num <= 4) return dmic_num; } @@ -992,9 +992,15 @@ static int hda_generic_machine_select(struct snd_sof_dev *sdev) dmic_num = hda_dmic_num; switch (dmic_num) { + case 1: + dmic_str = "-1ch"; + break; case 2: dmic_str = "-2ch"; break; + case 3: + dmic_str = "-3ch"; + break; case 4: dmic_str = "-4ch"; break; -- cgit From 878694dcbe51794c7a68195b92a3707ed4ff5826 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Tue, 25 Aug 2020 16:50:37 -0700 Subject: ASoC: SOF: topology: fix the ipc_size calculation for process component The topology private struct is used for token parsing and its size should not be included to the ipc_size, fix it here though it didn't cause any real issue as the Firmware won't use this wrong-added data. Signed-off-by: Keyon Jie Reviewed-by: Guennadi Liakhovetski Reviewed-by: Jaska Uimonen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 707fbac3e64f..95e63d138326 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2114,9 +2114,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, goto out; } - ipc_size = sizeof(struct sof_ipc_comp_process) + - le32_to_cpu(private->size) + - ipc_data_size; + ipc_size = sizeof(struct sof_ipc_comp_process) + ipc_data_size; /* we are exceeding max ipc size, config needs to be sent separately */ if (ipc_size > SOF_IPC_MSG_MAX_SIZE) { -- cgit From 29c8e4398f02adacd429c7847dacc8aea5a0c2f1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 25 Aug 2020 16:50:38 -0700 Subject: ASoC: SOF: Intel: hda: add extended rom status dump to error log Dump the extended ROM status information to the error logs to aid with remote support. The analysis of these logs requires access to non-public technical information. Reviewed-by: Kai Vehmanen Reviewed-by: Jaska Uimonen Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 0e8285b34a7d..de8e85920402 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -37,6 +37,7 @@ #include "shim.h" #define EXCEPT_MAX_HDR_SIZE 0x400 +#define HDA_EXT_ROM_STATUS_SIZE 8 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) @@ -414,6 +415,22 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags) } } +/* dump the first 8 dwords representing the extended ROM status */ +static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev) +{ + char msg[128]; + int len = 0; + u32 value; + int i; + + for (i = 0; i < HDA_EXT_ROM_STATUS_SIZE; i++) { + value = snd_sof_dsp_read(sdev, HDA_DSP_BAR, HDA_DSP_SRAM_REG_ROM_STATUS + i * 0x4); + len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value); + } + + dev_err(sdev->dev, "error: extended rom status:%s", msg); +} + void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { struct sof_ipc_dsp_oops_xtensa xoops; @@ -437,6 +454,7 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) } else { dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n", status, panic); + hda_dsp_dump_ext_rom_status(sdev); hda_dsp_get_status(sdev); } } -- cgit From 17b3f99a360d76fed46e7ec1e12d6670bd7ca884 Mon Sep 17 00:00:00 2001 From: Iulian Olaru Date: Tue, 25 Aug 2020 16:50:39 -0700 Subject: ASoC: SOF: imx: Replace sdev->private with sdev->pdata->hw_pdata The correct way to save private data is to use sdev->pdata->hw_pdata. Removed superfluous type-casts. Signed-off-by: Iulian Olaru Reviewed-by: Daniel Baluta Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx8.c | 10 +++++----- sound/soc/sof/imx/imx8m.c | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index bc0628c7b88c..325bf59e27c1 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -126,7 +126,7 @@ static struct imx_dsp_ops dsp_ops = { static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - struct imx8_priv *priv = (struct imx8_priv *)sdev->private; + struct imx8_priv *priv = sdev->pdata->hw_pdata; sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); @@ -140,7 +140,7 @@ static int imx8_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) */ static int imx8x_run(struct snd_sof_dev *sdev) { - struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private; + struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata; int ret; ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, @@ -180,7 +180,7 @@ static int imx8x_run(struct snd_sof_dev *sdev) static int imx8_run(struct snd_sof_dev *sdev) { - struct imx8_priv *dsp_priv = (struct imx8_priv *)sdev->private; + struct imx8_priv *dsp_priv = sdev->pdata->hw_pdata; int ret; ret = imx_sc_misc_set_control(dsp_priv->sc_ipc, IMX_SC_R_DSP, @@ -213,7 +213,7 @@ static int imx8_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; - sdev->private = priv; + sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -339,7 +339,7 @@ exit_unroll_pm: static int imx8_remove(struct snd_sof_dev *sdev) { - struct imx8_priv *priv = (struct imx8_priv *)sdev->private; + struct imx8_priv *priv = sdev->pdata->hw_pdata; int i; platform_device_unregister(priv->ipc_dev); diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index 86320941fcee..c4f1ca939068 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -99,7 +99,7 @@ static struct imx_dsp_ops imx8m_dsp_ops = { static int imx8m_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { - struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private; + struct imx8m_priv *priv = sdev->pdata->hw_pdata; sof_mailbox_write(sdev, sdev->host_box.offset, msg->msg_data, msg->msg_size); @@ -133,7 +133,7 @@ static int imx8m_probe(struct snd_sof_dev *sdev) if (!priv) return -ENOMEM; - sdev->private = priv; + sdev->pdata->hw_pdata = priv; priv->dev = sdev->dev; priv->sdev = sdev; @@ -209,7 +209,7 @@ exit_pdev_unregister: static int imx8m_remove(struct snd_sof_dev *sdev) { - struct imx8m_priv *priv = (struct imx8m_priv *)sdev->private; + struct imx8m_priv *priv = sdev->pdata->hw_pdata; platform_device_unregister(priv->ipc_dev); -- cgit From 5a1fa00ad74bd5e77feab84a6d64c06f267df415 Mon Sep 17 00:00:00 2001 From: Iulian Olaru Date: Tue, 25 Aug 2020 16:50:40 -0700 Subject: ASoC: SOF: sof-of-dev: Add .arch_ops field Add .arch_ops field in the sof_imx8x_ops structure. The inclusion of this field will allow the usage of functions from sof/core.c in order to print debug information such as the registers and a stack dump in case of a firmware ops. The SND_SOC_SOF_XTENSA is added in the imx/Kconfig file so the compilation is successful. Signed-off-by: Iulian Olaru Reviewed-by: Pierre-Louis Bossart Reviewed-by: Daniel Baluta Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200825235040.1586478-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/Kconfig | 2 ++ sound/soc/sof/imx/imx8.c | 7 +++++++ sound/soc/sof/imx/imx8m.c | 4 ++++ 3 files changed, 13 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 8230285baa43..23bfd79d09c3 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -30,6 +30,7 @@ config SND_SOC_SOF_IMX8_SUPPORT config SND_SOC_SOF_IMX8 tristate + select SND_SOC_SOF_XTENSA help This option is not user-selectable but automagically handled by 'select' statements at a higher level @@ -44,6 +45,7 @@ config SND_SOC_SOF_IMX8M_SUPPORT config SND_SOC_SOF_IMX8M tristate + select SND_SOC_SOF_XTENSA help This option is not user-selectable but automagically handled by 'select' statements at a higher level diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 325bf59e27c1..3b9ffc760cb5 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -424,6 +424,9 @@ struct snd_sof_dsp_ops sof_imx8_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* Firmware ops */ + .arch_ops = &sof_xtensa_arch_ops, + /* DAI drivers */ .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), @@ -464,6 +467,9 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* Firmware ops */ + .arch_ops = &sof_xtensa_arch_ops, + /* DAI drivers */ .drv = imx8_dai, .num_drv = ARRAY_SIZE(imx8_dai), @@ -477,4 +483,5 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { }; EXPORT_SYMBOL(sof_imx8x_ops); +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index c4f1ca939068..ca23ac99a63d 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -277,6 +277,9 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* Firmware ops */ + .arch_ops = &sof_xtensa_arch_ops, + /* DAI drivers */ .drv = imx8m_dai, .num_drv = ARRAY_SIZE(imx8m_dai), @@ -289,4 +292,5 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { }; EXPORT_SYMBOL(sof_imx8m_ops); +MODULE_IMPORT_NS(SND_SOC_SOF_XTENSA); MODULE_LICENSE("Dual BSD/GPL"); -- cgit From 0ff06df0be60db920f3d7218fa2bded9fd8de3f6 Mon Sep 17 00:00:00 2001 From: Yong Zhi Date: Wed, 26 Aug 2020 11:45:26 -0700 Subject: ASoC: SOF: Intel: hda: Remove unused parameters in cl_dsp_init() cl_dsp_init() doesn't use the fwdata and fwsize parameters. Remove it, and update caller accordingly. Signed-off-by: Yong Zhi Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-3-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 9cb219b87b3e..914699f550b1 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -78,8 +78,7 @@ error: * status on core 1, so power up core 1 also momentarily, keep it in * reset/stall and then turn it off */ -static int cl_dsp_init(struct snd_sof_dev *sdev, const void *fwdata, - u32 fwsize, int stream_tag, int iteration) +static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -340,8 +339,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "Attempting iteration %d of Core En/ROM load...\n", i); - ret = cl_dsp_init(sdev, stripped_firmware.data, - stripped_firmware.size, tag, i + 1); + ret = cl_dsp_init(sdev, tag, i + 1); /* don't retry anymore if successful */ if (!ret) -- cgit From aca961f196e5da10d99f4b19e5c27e5a5d7731a3 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 26 Aug 2020 11:45:27 -0700 Subject: ASoC: SOF: Intel: hda: Add helper function to program ICCMAX stream For some platforms, the recommended HW sequence for FW boot involves starting a specially crafted capture stream before powering on the DSP cores. Add a helper function to define the minimal recommended stream programming sequence for this stream. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-4-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-stream.c | 69 ++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/intel/hda.h | 4 +++ 2 files changed, 73 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 1bda14c3590c..0e09ede922c7 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -23,6 +23,8 @@ #include "../sof-audio.h" #include "hda.h" +#define HDA_LTRP_GB_VALUE_US 95 + /* * set up one of BDL entries for a stream */ @@ -322,6 +324,73 @@ int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, return 0; } +/* minimal recommended programming for ICCMAX stream */ +int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *hstream = &stream->hstream; + int sd_offset = SOF_STREAM_SD_OFFSET(hstream); + int ret; + u32 mask = 0x1 << hstream->index; + + if (!stream) { + dev_err(sdev->dev, "error: no stream available\n"); + return -ENODEV; + } + + if (hstream->posbuf) + *hstream->posbuf = 0; + + /* reset BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + 0x0); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + 0x0); + + hstream->frags = 0; + + ret = hda_dsp_stream_setup_bdl(sdev, dmab, hstream); + if (ret < 0) { + dev_err(sdev->dev, "error: set up of BDL failed\n"); + return ret; + } + + /* program BDL address */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPL, + (u32)hstream->bdl.addr); + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_BDLPU, + upper_32_bits(hstream->bdl.addr)); + + /* program cyclic buffer length */ + snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_CBL, + hstream->bufsize); + + /* program last valid index */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, + sd_offset + SOF_HDA_ADSP_REG_CL_SD_LVI, + 0xffff, (hstream->frags - 1)); + + /* decouple host and link DMA, enable DSP features */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, + mask, mask); + + /* Follow HW recommendation to set the guardband value to 95us during FW boot */ + snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, HDA_LTRP_GB_VALUE_US); + + /* start DMA */ + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, SOF_HDA_SD_CTL_DMA_START); + + return 0; +} + /* * prepare for common hdac registers settings, for both code loader * and normal stream. diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index fe452f0d0ec7..100eaaeecb4d 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -246,6 +246,7 @@ /* Intel Vendor Specific Registers */ #define HDA_VS_INTEL_EM2 0x1030 #define HDA_VS_INTEL_EM2_L1SEN BIT(13) +#define HDA_VS_INTEL_LTRP_GB_MASK 0x3F /* HIPCI */ #define HDA_DSP_REG_HIPCI_BUSY BIT(31) @@ -546,6 +547,9 @@ int hda_dsp_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, struct snd_dma_buffer *dmab, struct snd_pcm_hw_params *params); +int hda_dsp_iccmax_stream_hw_params(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, + struct snd_dma_buffer *dmab, + struct snd_pcm_hw_params *params); int hda_dsp_stream_trigger(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream, int cmd); irqreturn_t hda_dsp_stream_threaded_handler(int irq, void *context); -- cgit From c07fa3fcbd28b6d8383a05f0570d472894c6e38f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 26 Aug 2020 11:45:28 -0700 Subject: ASoC: SOF: Intel: hda: modify the signature of get_stream_with_tag() Modify the signature of get_stream_with_tag() to add the direction as an argument to extend it for using with capture streams. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-5-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 914699f550b1..804f5f64aa33 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -205,18 +205,15 @@ static int cl_trigger(struct snd_sof_dev *sdev, } static struct hdac_ext_stream *get_stream_with_tag(struct snd_sof_dev *sdev, - int tag) + int tag, int direction) { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_stream *s; /* get stream with tag */ - list_for_each_entry(s, &bus->stream_list, list) { - if (s->direction == SNDRV_PCM_STREAM_PLAYBACK && - s->stream_tag == tag) { + list_for_each_entry(s, &bus->stream_list, list) + if (s->direction == direction && s->stream_tag == tag) return stream_to_hdac_ext_stream(s); - } - } return NULL; } @@ -322,7 +319,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) } /* get stream with tag */ - stream = get_stream_with_tag(sdev, tag); + stream = get_stream_with_tag(sdev, tag, SNDRV_PCM_STREAM_PLAYBACK); if (!stream) { dev_err(sdev->dev, "error: could not get stream with stream tag %d\n", -- cgit From d43e381390d0aa1992ce204bb6a9af791edf3d45 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 26 Aug 2020 11:45:29 -0700 Subject: ASoC: SOF: Intel: hda: define macro for code loader stream format This will be used for the ICCMAX stream as well. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-6-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 804f5f64aa33..9b4844aa3023 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -22,6 +22,7 @@ #include "hda.h" #define HDA_FW_BOOT_ATTEMPTS 3 +#define HDA_CL_STREAM_FORMAT 0x40 static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, unsigned int size, struct snd_dma_buffer *dmab, @@ -309,7 +310,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->boot_wait); /* prepare DMA for code loader stream */ - tag = cl_stream_prepare(sdev, 0x40, stripped_firmware.size, + tag = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); if (tag < 0) { -- cgit From acf705a425f0e9cd5cab06231479c0a239e5a671 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 26 Aug 2020 11:45:30 -0700 Subject: ASoC: SOF: Intel: hda: Define FW boot sequence with ICCMAX Define the FW boot sequence for platforms that are recommended to use ICCMAX. This function uses the existing prepare and cleanup functions for creating a specially crafted capture stream before powering up the DSP cores. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-7-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 92 +++++++++++++++++++++++++++++++++------- sound/soc/sof/intel/hda.h | 1 + 2 files changed, 78 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 9b4844aa3023..5464f899e16f 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -17,6 +17,7 @@ #include #include +#include #include #include "../ops.h" #include "hda.h" @@ -33,11 +34,6 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, struct pci_dev *pci = to_pci_dev(sdev->dev); int ret; - if (direction != SNDRV_PCM_STREAM_PLAYBACK) { - dev_err(sdev->dev, "error: code loading DMA is playback only\n"); - return -EINVAL; - } - dsp_stream = hda_dsp_stream_get(sdev, direction); if (!dsp_stream) { @@ -58,14 +54,21 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, hstream->format_val = format; hstream->bufsize = size; - ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); - if (ret < 0) { - dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); - goto error; + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + ret = hda_dsp_iccmax_stream_hw_params(sdev, dsp_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: iccmax stream prepare failed: %x\n", ret); + goto error; + } + } else { + ret = hda_dsp_stream_hw_params(sdev, dsp_stream, dmab, NULL); + if (ret < 0) { + dev_err(sdev->dev, "error: hdac prepare failed: %x\n", ret); + goto error; + } + hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); } - hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); - return hstream->stream_tag; error: @@ -224,12 +227,15 @@ static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, { struct hdac_stream *hstream = &stream->hstream; int sd_offset = SOF_STREAM_SD_OFFSET(hstream); - int ret; + int ret = 0; - ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + if (hstream->direction == SNDRV_PCM_STREAM_PLAYBACK) + ret = hda_dsp_stream_spib_config(sdev, stream, HDA_DSP_SPIB_DISABLE, 0); + else + snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, sd_offset, + SOF_HDA_SD_CTL_DMA_START, 0); - hda_dsp_stream_put(sdev, SNDRV_PCM_STREAM_PLAYBACK, - hstream->stream_tag); + hda_dsp_stream_put(sdev, hstream->direction, hstream->stream_tag); hstream->running = 0; hstream->substream = NULL; @@ -287,6 +293,62 @@ static int cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream) return status; } +int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) +{ + struct snd_sof_pdata *plat_data = sdev->pdata; + struct hdac_ext_stream *iccmax_stream; + struct hdac_bus *bus = sof_to_bus(sdev); + struct firmware stripped_firmware; + int ret, ret1; + int iccmax_tag; + u8 original_gb; + + /* save the original LTRP guardband value */ + original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK; + + if (plat_data->fw->size <= plat_data->fw_offset) { + dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); + return -EINVAL; + } + + stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; + + /* prepare capture stream for ICCMAX */ + iccmax_tag = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, + &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); + if (iccmax_tag < 0) { + dev_err(sdev->dev, "error: dma prepare for ICCMAX %x\n", iccmax_tag); + return iccmax_tag; + } + + /* get stream with tag */ + iccmax_stream = get_stream_with_tag(sdev, iccmax_tag, SNDRV_PCM_STREAM_CAPTURE); + if (!iccmax_stream) { + dev_err(sdev->dev, "error: could not get stream with stream tag %d\n", iccmax_tag); + ret = -ENODEV; + } else { + ret = hda_dsp_cl_boot_firmware(sdev); + } + + /* + * Perform iccmax stream cleanup. This should be done even if firmware loading fails. + * If the cleanup also fails, we return the initial error + */ + ret1 = cl_cleanup(sdev, &sdev->dmab_bdl, iccmax_stream); + if (ret1 < 0) { + dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); + + /* set return value to indicate cleanup failure */ + if (!ret) + ret = ret1; + } + + /* restore the original guardband value after FW boot */ + snd_hdac_chip_updateb(bus, VS_LTRP, HDA_VS_INTEL_LTRP_GB_MASK, original_gb); + + return ret; +} + int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) { struct snd_sof_pdata *plat_data = sdev->pdata; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 100eaaeecb4d..a2e6050f0ef0 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -612,6 +612,7 @@ int hda_dsp_ipc_cmd_done(struct snd_sof_dev *sdev, int dir); * DSP Code loader. */ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev); +int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev); int hda_dsp_cl_boot_firmware_skl(struct snd_sof_dev *sdev); /* pre and post fw run ops */ -- cgit From 8b98491a6b8c41a7e4334fb94a58c268d831458e Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 26 Aug 2020 11:45:31 -0700 Subject: ASoC: SOF: Intel: hda: Add sof_tgl_ops for TGL platforms Separate the dsp ops for TGL ops to specify the use of ICCMAX FW boot sequence in the run op. All other ops are identical. Also separate the TGL descriptors into a separate file to make it easier to follow. Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-8-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/Makefile | 2 +- sound/soc/sof/intel/cnl.c | 23 +------ sound/soc/sof/intel/hda-ipc.h | 4 ++ sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/intel/tgl.c | 137 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-pci-dev.c | 2 +- 6 files changed, 147 insertions(+), 22 deletions(-) create mode 100644 sound/soc/sof/intel/tgl.c (limited to 'sound') diff --git a/sound/soc/sof/intel/Makefile b/sound/soc/sof/intel/Makefile index f7e9358f1f06..72d85b25df7d 100644 --- a/sound/soc/sof/intel/Makefile +++ b/sound/soc/sof/intel/Makefile @@ -8,7 +8,7 @@ snd-sof-intel-ipc-objs := intel-ipc.o snd-sof-intel-hda-common-objs := hda.o hda-loader.o hda-stream.o hda-trace.o \ hda-dsp.o hda-ipc.o hda-ctrl.o hda-pcm.o \ hda-dai.o hda-bus.o \ - apl.o cnl.o + apl.o cnl.o tgl.o snd-sof-intel-hda-common-$(CONFIG_SND_SOC_SOF_HDA_PROBES) += hda-compress.o snd-sof-intel-hda-objs := hda-codec.o diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 95234ae59e42..70f14b2aa954 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -29,7 +29,7 @@ static const struct snd_sof_debugfs_map cnl_dsp_debugfs[] = { static void cnl_ipc_host_done(struct snd_sof_dev *sdev); static void cnl_ipc_dsp_done(struct snd_sof_dev *sdev); -static irqreturn_t cnl_ipc_irq_thread(int irq, void *context) +irqreturn_t cnl_ipc_irq_thread(int irq, void *context) { struct snd_sof_dev *sdev = context; u32 hipci; @@ -163,8 +163,7 @@ static bool cnl_compact_ipc_compress(struct snd_sof_ipc_msg *msg, return false; } -static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, - struct snd_sof_ipc_msg *msg) +int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg) { struct sof_intel_hda_dev *hdev = sdev->pdata->hw_pdata; struct sof_ipc_cmd_hdr *hdr; @@ -211,7 +210,7 @@ static int cnl_ipc_send_msg(struct snd_sof_dev *sdev, return 0; } -static void cnl_ipc_dump(struct snd_sof_dev *sdev) +void cnl_ipc_dump(struct snd_sof_dev *sdev) { u32 hipcctl; u32 hipcida; @@ -369,22 +368,6 @@ const struct sof_intel_dsp_desc icl_chip_info = { }; EXPORT_SYMBOL_NS(icl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); -const struct sof_intel_dsp_desc tgl_chip_info = { - /* Tigerlake */ - .cores_num = 4, - .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0), - .ipc_req = CNL_DSP_REG_HIPCIDR, - .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, - .ipc_ack = CNL_DSP_REG_HIPCIDA, - .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, - .ipc_ctl = CNL_DSP_REG_HIPCCTL, - .rom_init_timeout = 300, - .ssp_count = ICL_SSP_COUNT, - .ssp_base_offset = CNL_SSP_BASE_OFFSET, -}; -EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); - const struct sof_intel_dsp_desc ehl_chip_info = { /* Elkhartlake */ .cores_num = 4, diff --git a/sound/soc/sof/intel/hda-ipc.h b/sound/soc/sof/intel/hda-ipc.h index ade4c3191a39..10fbca5939db 100644 --- a/sound/soc/sof/intel/hda-ipc.h +++ b/sound/soc/sof/intel/hda-ipc.h @@ -48,4 +48,8 @@ #define HDA_PM_PG_STREAMING BIT(1) #define HDA_PM_PG_RSVD BIT(0) +irqreturn_t cnl_ipc_irq_thread(int irq, void *context); +int cnl_ipc_send_msg(struct snd_sof_dev *sdev, struct snd_sof_ipc_msg *msg); +void cnl_ipc_dump(struct snd_sof_dev *sdev); + #endif diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index a2e6050f0ef0..5ee2f8354051 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -733,6 +733,7 @@ extern struct snd_soc_dai_driver skl_dai[]; */ extern const struct snd_sof_dsp_ops sof_apl_ops; extern const struct snd_sof_dsp_ops sof_cnl_ops; +extern const struct snd_sof_dsp_ops sof_tgl_ops; extern const struct sof_intel_dsp_desc apl_chip_info; extern const struct sof_intel_dsp_desc cnl_chip_info; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c new file mode 100644 index 000000000000..d0e84b7747a0 --- /dev/null +++ b/sound/soc/sof/intel/tgl.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Authors: Ranjani Sridharan +// + +/* + * Hardware interface for audio DSP on Tigerlake. + */ + +#include "../ops.h" +#include "hda.h" +#include "hda-ipc.h" +#include "../sof-audio.h" + +static const struct snd_sof_debugfs_map tgl_dsp_debugfs[] = { + {"hda", HDA_DSP_HDA_BAR, 0, 0x4000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"pp", HDA_DSP_PP_BAR, 0, 0x1000, SOF_DEBUGFS_ACCESS_ALWAYS}, + {"dsp", HDA_DSP_BAR, 0, 0x10000, SOF_DEBUGFS_ACCESS_ALWAYS}, +}; + +/* Tigerlake ops */ +const struct snd_sof_dsp_ops sof_tgl_ops = { + /* probe and remove */ + .probe = hda_dsp_probe, + .remove = hda_dsp_remove, + + /* Register IO */ + .write = sof_io_write, + .read = sof_io_read, + .write64 = sof_io_write64, + .read64 = sof_io_read64, + + /* Block IO */ + .block_read = sof_block_read, + .block_write = sof_block_write, + + /* doorbell */ + .irq_thread = cnl_ipc_irq_thread, + + /* ipc */ + .send_msg = cnl_ipc_send_msg, + .fw_ready = sof_fw_ready, + .get_mailbox_offset = hda_dsp_ipc_get_mailbox_offset, + .get_window_offset = hda_dsp_ipc_get_window_offset, + + .ipc_msg_data = hda_ipc_msg_data, + .ipc_pcm_params = hda_ipc_pcm_params, + + /* machine driver */ + .machine_select = hda_machine_select, + .machine_register = sof_machine_register, + .machine_unregister = sof_machine_unregister, + .set_mach_params = hda_set_mach_params, + + /* debug */ + .debug_map = tgl_dsp_debugfs, + .debug_map_count = ARRAY_SIZE(tgl_dsp_debugfs), + .dbg_dump = hda_dsp_dump, + .ipc_dump = cnl_ipc_dump, + + /* stream callbacks */ + .pcm_open = hda_dsp_pcm_open, + .pcm_close = hda_dsp_pcm_close, + .pcm_hw_params = hda_dsp_pcm_hw_params, + .pcm_hw_free = hda_dsp_stream_hw_free, + .pcm_trigger = hda_dsp_pcm_trigger, + .pcm_pointer = hda_dsp_pcm_pointer, + +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) + /* probe callbacks */ + .probe_assign = hda_probe_compr_assign, + .probe_free = hda_probe_compr_free, + .probe_set_params = hda_probe_compr_set_params, + .probe_trigger = hda_probe_compr_trigger, + .probe_pointer = hda_probe_compr_pointer, +#endif + + /* firmware loading */ + .load_firmware = snd_sof_load_firmware_raw, + + /* pre/post fw run */ + .pre_fw_run = hda_dsp_pre_fw_run, + .post_fw_run = hda_dsp_post_fw_run, + + /* dsp core power up/down */ + .core_power_up = hda_dsp_enable_core, + .core_power_down = hda_dsp_core_reset_power_down, + + /* firmware run */ + .run = hda_dsp_cl_boot_firmware_iccmax, + + /* trace callback */ + .trace_init = hda_dsp_trace_init, + .trace_release = hda_dsp_trace_release, + .trace_trigger = hda_dsp_trace_trigger, + + /* DAI drivers */ + .drv = skl_dai, + .num_drv = SOF_SKL_NUM_DAIS, + + /* PM */ + .suspend = hda_dsp_suspend, + .resume = hda_dsp_resume, + .runtime_suspend = hda_dsp_runtime_suspend, + .runtime_resume = hda_dsp_runtime_resume, + .runtime_idle = hda_dsp_runtime_idle, + .set_hw_params_upon_resume = hda_dsp_set_hw_params_upon_resume, + .set_power_state = hda_dsp_set_power_state, + + /* ALSA HW info flags */ + .hw_info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + + .arch_ops = &sof_xtensa_arch_ops, +}; +EXPORT_SYMBOL_NS(sof_tgl_ops, SND_SOC_SOF_INTEL_HDA_COMMON); + +const struct sof_intel_dsp_desc tgl_chip_info = { + /* Tigerlake */ + .cores_num = 4, + .init_core_mask = 1, + .cores_mask = HDA_DSP_CORE_MASK(0), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_init_timeout = 300, + .ssp_count = ICL_SSP_COUNT, + .ssp_base_offset = CNL_SSP_BASE_OFFSET, +}; +EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 7b85c88cba9c..ec62d9337d68 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -221,7 +221,7 @@ static const struct sof_dev_desc tgl_desc = { .default_tplg_path = "intel/sof-tplg", .default_fw_filename = "sof-tgl.ri", .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", - .ops = &sof_cnl_ops, + .ops = &sof_tgl_ops, }; #endif -- cgit From 01d42d5a0a70bdcece7228232590f177e7114368 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 26 Aug 2020 11:45:32 -0700 Subject: ASoC: SOF: Intel: hda: Simplify error handling during FW boot Modify cl_stream_prepare() to return a pointer to the prepared stream if successful or ERR_PTR() otherwise. This would simplify the error paths in hda_dsp_cl_boot_firmware() and hda_dsp_cl_boot_firmware_iccmax() to perform the stream cleanup after FW boot. This change also renders the function get_stream_with_tag() redundant. Reviewed-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Ranjani Sridharan Link: https://lore.kernel.org/r/20200826184532.1612070-9-ranjani.sridharan@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 73 +++++++++++----------------------------- 1 file changed, 19 insertions(+), 54 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 5464f899e16f..70727495fdbf 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -25,9 +25,9 @@ #define HDA_FW_BOOT_ATTEMPTS 3 #define HDA_CL_STREAM_FORMAT 0x40 -static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, - unsigned int size, struct snd_dma_buffer *dmab, - int direction) +static struct hdac_ext_stream *cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, + unsigned int size, struct snd_dma_buffer *dmab, + int direction) { struct hdac_ext_stream *dsp_stream; struct hdac_stream *hstream; @@ -38,7 +38,7 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, if (!dsp_stream) { dev_err(sdev->dev, "error: no stream available\n"); - return -ENODEV; + return ERR_PTR(-ENODEV); } hstream = &dsp_stream->hstream; hstream->substream = NULL; @@ -69,12 +69,12 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, hda_dsp_stream_spib_config(sdev, dsp_stream, HDA_DSP_SPIB_ENABLE, size); } - return hstream->stream_tag; + return dsp_stream; error: hda_dsp_stream_put(sdev, direction, hstream->stream_tag); snd_dma_free_pages(dmab); - return ret; + return ERR_PTR(ret); } /* @@ -208,20 +208,6 @@ static int cl_trigger(struct snd_sof_dev *sdev, } } -static struct hdac_ext_stream *get_stream_with_tag(struct snd_sof_dev *sdev, - int tag, int direction) -{ - struct hdac_bus *bus = sof_to_bus(sdev); - struct hdac_stream *s; - - /* get stream with tag */ - list_for_each_entry(s, &bus->stream_list, list) - if (s->direction == direction && s->stream_tag == tag) - return stream_to_hdac_ext_stream(s); - - return NULL; -} - static int cl_cleanup(struct snd_sof_dev *sdev, struct snd_dma_buffer *dmab, struct hdac_ext_stream *stream) { @@ -300,7 +286,6 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) struct hdac_bus *bus = sof_to_bus(sdev); struct firmware stripped_firmware; int ret, ret1; - int iccmax_tag; u8 original_gb; /* save the original LTRP guardband value */ @@ -314,21 +299,14 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; /* prepare capture stream for ICCMAX */ - iccmax_tag = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, - &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); - if (iccmax_tag < 0) { - dev_err(sdev->dev, "error: dma prepare for ICCMAX %x\n", iccmax_tag); - return iccmax_tag; + iccmax_stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, + &sdev->dmab_bdl, SNDRV_PCM_STREAM_CAPTURE); + if (IS_ERR(iccmax_stream)) { + dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n"); + return PTR_ERR(iccmax_stream); } - /* get stream with tag */ - iccmax_stream = get_stream_with_tag(sdev, iccmax_tag, SNDRV_PCM_STREAM_CAPTURE); - if (!iccmax_stream) { - dev_err(sdev->dev, "error: could not get stream with stream tag %d\n", iccmax_tag); - ret = -ENODEV; - } else { - ret = hda_dsp_cl_boot_firmware(sdev); - } + ret = hda_dsp_cl_boot_firmware(sdev); /* * Perform iccmax stream cleanup. This should be done even if firmware loading fails. @@ -356,7 +334,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) const struct sof_intel_dsp_desc *chip_info; struct hdac_ext_stream *stream; struct firmware stripped_firmware; - int ret, ret1, tag, i; + int ret, ret1, i; chip_info = desc->chip_info; @@ -372,23 +350,11 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) init_waitqueue_head(&sdev->boot_wait); /* prepare DMA for code loader stream */ - tag = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, - &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); - - if (tag < 0) { - dev_err(sdev->dev, "error: dma prepare for fw loading err: %x\n", - tag); - return tag; - } - - /* get stream with tag */ - stream = get_stream_with_tag(sdev, tag, SNDRV_PCM_STREAM_PLAYBACK); - if (!stream) { - dev_err(sdev->dev, - "error: could not get stream with stream tag %d\n", - tag); - ret = -ENODEV; - goto err; + stream = cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, + &sdev->dmab, SNDRV_PCM_STREAM_PLAYBACK); + if (IS_ERR(stream)) { + dev_err(sdev->dev, "error: dma prepare for fw loading failed\n"); + return PTR_ERR(stream); } memcpy(sdev->dmab.area, stripped_firmware.data, @@ -399,7 +365,7 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "Attempting iteration %d of Core En/ROM load...\n", i); - ret = cl_dsp_init(sdev, tag, i + 1); + ret = cl_dsp_init(sdev, stream->hstream.stream_tag, i + 1); /* don't retry anymore if successful */ if (!ret) @@ -468,7 +434,6 @@ cleanup: return chip_info->init_core_mask; /* dump dsp registers and disable DSP upon error */ -err: hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); /* disable DSP */ -- cgit From f7660445c8e7fda91e8b944128554249d886b1d4 Mon Sep 17 00:00:00 2001 From: Akshu Agrawal Date: Thu, 27 Aug 2020 00:24:20 +0530 Subject: ASoC: AMD: Clean kernel log from deferred probe error messages While the driver waits for DAIs to be probed and retries probing, have the error messages at debug level instead of error. Signed-off-by: Akshu Agrawal Link: https://lore.kernel.org/r/20200826185454.5545-1-akshu.agrawal@amd.com Signed-off-by: Mark Brown --- sound/soc/amd/acp3x-rt5682-max9836.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/amd/acp3x-rt5682-max9836.c b/sound/soc/amd/acp3x-rt5682-max9836.c index 406526e79af3..1a4e8ca0f99c 100644 --- a/sound/soc/amd/acp3x-rt5682-max9836.c +++ b/sound/soc/amd/acp3x-rt5682-max9836.c @@ -472,12 +472,17 @@ static int acp3x_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { - dev_err(&pdev->dev, + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "devm_snd_soc_register_card(%s) failed: %d\n", card->name, ret); - return ret; + else + dev_dbg(&pdev->dev, + "devm_snd_soc_register_card(%s) probe deferred: %d\n", + card->name, ret); } - return 0; + + return ret; } static const struct acpi_device_id acp3x_audio_acpi_match[] = { -- cgit From a11ffbbac9cc7fdd73b01a0d8227ef8a1d2b95da Mon Sep 17 00:00:00 2001 From: Pavel Dobias Date: Thu, 27 Aug 2020 12:25:28 +0200 Subject: ASoC: max9867: shutdown codec when changing filter type Changing filter type without disabling codec results in filter malfunction. Disable codec when changing filter type. Signed-off-by: Pavel Dobias Link: https://lore.kernel.org/r/20200827102528.29677-1-dobias@2n.cz Signed-off-by: Mark Brown --- sound/soc/codecs/max9867.c | 131 ++++++++++++++++++++++++++++++++++++++++----- sound/soc/codecs/max9867.h | 4 +- 2 files changed, 120 insertions(+), 15 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index fcb31144d69c..aef2746bfb94 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -15,6 +15,14 @@ #include #include "max9867.h" +struct max9867_priv { + struct regmap *regmap; + const struct snd_pcm_hw_constraint_list *constraints; + unsigned int sysclk, pclk; + bool master, dsp_a; + unsigned int adc_dac_active; +}; + static const char *const max9867_spmode[] = { "Stereo Diff", "Mono Diff", "Stereo Cap", "Mono Cap", @@ -32,8 +40,102 @@ static const char *const max9867_adc_dac_filter_text[] = { "Butterworth/8-24" }; -static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7, - max9867_filter_text); +enum max9867_adc_dac { + MAX9867_ADC_LEFT, + MAX9867_ADC_RIGHT, + MAX9867_DAC_LEFT, + MAX9867_DAC_RIGHT, +}; + +static int max9867_adc_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + enum max9867_adc_dac adc_dac; + + if (!strcmp(w->name, "ADCL")) + adc_dac = MAX9867_ADC_LEFT; + else if (!strcmp(w->name, "ADCR")) + adc_dac = MAX9867_ADC_RIGHT; + else if (!strcmp(w->name, "DACL")) + adc_dac = MAX9867_DAC_LEFT; + else if (!strcmp(w->name, "DACR")) + adc_dac = MAX9867_DAC_RIGHT; + else + return 0; + + if (SND_SOC_DAPM_EVENT_ON(event)) + max9867->adc_dac_active |= BIT(adc_dac); + else if (SND_SOC_DAPM_EVENT_OFF(event)) + max9867->adc_dac_active &= ~BIT(adc_dac); + + return 0; +} + +static int max9867_filter_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + unsigned int reg; + int ret; + + ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, ®); + if (ret) + return -EINVAL; + + if (reg & MAX9867_CODECFLTR_MODE) + ucontrol->value.enumerated.item[0] = 1; + else + ucontrol->value.enumerated.item[0] = 0; + + return 0; +} + +static int max9867_filter_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component); + unsigned int reg, mode = ucontrol->value.enumerated.item[0]; + int ret; + + if (mode > 1) + return -EINVAL; + + /* don't allow change if ADC/DAC active */ + if (max9867->adc_dac_active) + return -EBUSY; + + /* read current filter mode */ + ret = regmap_read(max9867->regmap, MAX9867_CODECFLTR, ®); + if (ret) + return -EINVAL; + + if (mode) + mode = MAX9867_CODECFLTR_MODE; + + /* check if change is needed */ + if ((reg & MAX9867_CODECFLTR_MODE) == mode) + return 0; + + /* shutdown codec before switching filter mode */ + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_PWRMAN_SHDN, 0); + + /* switch filter mode */ + regmap_update_bits(max9867->regmap, MAX9867_CODECFLTR, + MAX9867_CODECFLTR_MODE, mode); + + /* out of shutdown now */ + regmap_update_bits(max9867->regmap, MAX9867_PWRMAN, + MAX9867_PWRMAN_SHDN, MAX9867_PWRMAN_SHDN); + + return 0; +} + +static SOC_ENUM_SINGLE_EXT_DECL(max9867_filter, max9867_filter_text); static SOC_ENUM_SINGLE_DECL(max9867_dac_filter, MAX9867_CODECFLTR, 0, max9867_adc_dac_filter_text); static SOC_ENUM_SINGLE_DECL(max9867_adc_filter, MAX9867_CODECFLTR, 4, @@ -76,7 +178,7 @@ static const struct snd_kcontrol_new max9867_snd_controls[] = { SOC_ENUM("Speaker Mode", max9867_spkmode), SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0), SOC_SINGLE("Line ZC Switch", MAX9867_MODECONFIG, 5, 1, 0), - SOC_ENUM("DSP Filter", max9867_filter), + SOC_ENUM_EXT("DSP Filter", max9867_filter, max9867_filter_get, max9867_filter_set), SOC_ENUM("ADC Filter", max9867_adc_filter), SOC_ENUM("DAC Filter", max9867_dac_filter), SOC_SINGLE("Mono Playback Switch", MAX9867_IFC1B, 3, 1, 0), @@ -134,8 +236,12 @@ static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { &max9867_left_dmic_mux), SND_SOC_DAPM_MUX("DMICR Mux", SND_SOC_NOPM, 0, 0, &max9867_right_dmic_mux), - SND_SOC_DAPM_ADC("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_ADC("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC_E("ADCL", "HiFi Capture", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_ADC_E("ADCR", "HiFi Capture", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_MIXER("Digital", SND_SOC_NOPM, 0, 0, max9867_sidetone_mixer_controls, @@ -143,8 +249,12 @@ static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = { 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", SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_DAC("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC_E("DACL", "HiFi Playback", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_DAC_E("DACR", "HiFi Playback", SND_SOC_NOPM, 0, 0, + max9867_adc_dac_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SWITCH("Master Playback", SND_SOC_NOPM, 0, 0, &max9867_line_out_control), SND_SOC_DAPM_OUTPUT("LOUT"), @@ -197,13 +307,6 @@ static const struct snd_pcm_hw_constraint_list max9867_constraints_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) { diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h index 3092c3b99075..b6b880631b13 100644 --- a/sound/soc/codecs/max9867.h +++ b/sound/soc/codecs/max9867.h @@ -44,7 +44,8 @@ #define MAX9867_IFC1B_PCLK_4 0x05 #define MAX9867_IFC1B_PCLK_8 0x06 #define MAX9867_IFC1B_PCLK_16 0x07 -#define MAX9867_CODECFLTR 0x0a +#define MAX9867_CODECFLTR 0x0a +#define MAX9867_CODECFLTR_MODE (1<<7) #define MAX9867_SIDETONE 0x0b #define MAX9867_DACLEVEL 0x0c #define MAX9867_ADCLEVEL 0x0d @@ -58,6 +59,7 @@ #define MAX9867_MICCONFIG 0x15 #define MAX9867_MODECONFIG 0x16 #define MAX9867_PWRMAN 0x17 +#define MAX9867_PWRMAN_SHDN (1<<7) #define MAX9867_REVISION 0xff #define MAX9867_CACHEREGNUM 10 -- cgit From c1c277b2c425f69b9b4f4258d9db18562d9be041 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 27 Aug 2020 08:55:39 +0900 Subject: ASoC: soc-core: add snd_soc_find_dai_with_mutex() commit 25612477d20b52 ("ASoC: soc-dai: set dai_link dpcm_ flags with a helper") added snd_soc_dai_link_set_capabilities(). But it is using snd_soc_find_dai() (A) which is required client_mutex (B). And client_mutex is soc-core.c local. struct snd_soc_dai *snd_soc_find_dai(xxx) { ... (B) lockdep_assert_held(&client_mutex); ... } void snd_soc_dai_link_set_capabilities(xxx) { ... for_each_pcm_streams(direction) { ... for_each_link_cpus(dai_link, i, cpu) { (A) dai = snd_soc_find_dai(cpu); ... } ... for_each_link_codecs(dai_link, i, codec) { (A) dai = snd_soc_find_dai(codec); ... } } ... } Because of these background, we will get WARNING if .config has CONFIG_LOCKDEP. WARNING: CPU: 2 PID: 53 at sound/soc/soc-core.c:814 snd_soc_find_dai+0xf8/0x100 CPU: 2 PID: 53 Comm: kworker/2:1 Not tainted 5.7.0-rc1+ #328 Hardware name: Renesas H3ULCB Kingfisher board based on r8a77951 (DT) Workqueue: events deferred_probe_work_func pstate: 60000005 (nZCv daif -PAN -UAO) pc : snd_soc_find_dai+0xf8/0x100 lr : snd_soc_find_dai+0xf4/0x100 ... Call trace: snd_soc_find_dai+0xf8/0x100 snd_soc_dai_link_set_capabilities+0xa0/0x16c graph_dai_link_of_dpcm+0x390/0x3c0 graph_for_each_link+0x134/0x200 graph_probe+0x144/0x230 platform_drv_probe+0x5c/0xb0 really_probe+0xe4/0x430 driver_probe_device+0x60/0xf4 snd_soc_find_dai() will be used from (X) CPU/Codec/Platform driver with mutex lock, and (Y) Card driver without mutex lock. This snd_soc_dai_link_set_capabilities() is for Card driver, this means called without mutex. This patch adds snd_soc_find_dai_with_mutex() to solve it. Fixes: 25612477d20b52 ("ASoC: soc-dai: set dai_link dpcm_ flags with a helper") Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87blixvuab.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 13 +++++++++++++ sound/soc/soc-dai.c | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bf46f410c8c6..54319ddcf00a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -834,6 +834,19 @@ struct snd_soc_dai *snd_soc_find_dai( } EXPORT_SYMBOL_GPL(snd_soc_find_dai); +struct snd_soc_dai *snd_soc_find_dai_with_mutex( + const struct snd_soc_dai_link_component *dlc) +{ + struct snd_soc_dai *dai; + + mutex_lock(&client_mutex); + dai = snd_soc_find_dai(dlc); + mutex_unlock(&client_mutex); + + return dai; +} +EXPORT_SYMBOL_GPL(snd_soc_find_dai_with_mutex); + static int soc_dai_link_sanity_check(struct snd_soc_card *card, struct snd_soc_dai_link *link) { diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 91a2551e4cef..0dbd312aad08 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -412,14 +412,14 @@ void snd_soc_dai_link_set_capabilities(struct snd_soc_dai_link *dai_link) supported_codec = false; for_each_link_cpus(dai_link, i, cpu) { - dai = snd_soc_find_dai(cpu); + dai = snd_soc_find_dai_with_mutex(cpu); if (dai && snd_soc_dai_stream_valid(dai, direction)) { supported_cpu = true; break; } } for_each_link_codecs(dai_link, i, codec) { - dai = snd_soc_find_dai(codec); + dai = snd_soc_find_dai_with_mutex(codec); if (dai && snd_soc_dai_stream_valid(dai, direction)) { supported_codec = true; break; -- cgit From 45dd9943fce08f1b38352ff9453682253bdf19b7 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Thu, 27 Aug 2020 23:51:00 +0300 Subject: ASoC: core: remove artificial component and DAI name constraint Current fmt_single_name code limits maximum name of a DAI or component to 32 bytes. On some systems corresponding device names might be longer than that (e.g. 17300000.remoteproc:glink-edge:apr:apr-service@8:routing). This will result in duplicate DAI/component names. Rewrite fmt_single_name() to remove such length limitations. Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20200827205100.1479331-1-dmitry.baryshkov@linaro.org Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 54319ddcf00a..681df493bb98 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -44,8 +44,6 @@ #define CREATE_TRACE_POINTS #include -#define NAME_SIZE 32 - static DEFINE_MUTEX(client_mutex); static LIST_HEAD(component_list); static LIST_HEAD(unbind_card_list); @@ -2231,13 +2229,14 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_card); */ static char *fmt_single_name(struct device *dev, int *id) { - char *found, name[NAME_SIZE]; + const char *devname = dev_name(dev); + char *found, *name; int id1, id2; - if (dev_name(dev) == NULL) + if (devname == NULL) return NULL; - strlcpy(name, dev_name(dev), NAME_SIZE); + name = devm_kstrdup(dev, devname, GFP_KERNEL); /* are we a "%s.%d" name (platform and SPI components) */ found = strstr(name, dev->driver->name); @@ -2250,23 +2249,21 @@ static char *fmt_single_name(struct device *dev, int *id) found[strlen(dev->driver->name)] = '\0'; } - } else { - /* I2C component devices are named "bus-addr" */ - if (sscanf(name, "%x-%x", &id1, &id2) == 2) { - char tmp[NAME_SIZE]; + /* I2C component devices are named "bus-addr" */ + } else if (sscanf(name, "%x-%x", &id1, &id2) == 2) { - /* create unique ID number from I2C addr and bus */ - *id = ((id1 & 0xffff) << 16) + id2; + /* create unique ID number from I2C addr and bus */ + *id = ((id1 & 0xffff) << 16) + id2; - /* sanitize component name for DAI link creation */ - snprintf(tmp, NAME_SIZE, "%s.%s", dev->driver->name, - name); - strlcpy(name, tmp, NAME_SIZE); - } else - *id = 0; + devm_kfree(dev, name); + + /* sanitize component name for DAI link creation */ + name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname); + } else { + *id = 0; } - return devm_kstrdup(dev, name, GFP_KERNEL); + return name; } /* -- cgit From ece2a74c5913d244e13c42c61ca2e162932fa3b4 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 28 Aug 2020 06:28:55 -0500 Subject: ASoC: tlv320adcx140: Add digital mic channel enable routing Add the audio routing map to enable the digital mic paths when the analog mic paths are not enabled. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200828112855.10112-1-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 5cd50d841177..5d1b2a03f0ac 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -412,6 +412,16 @@ static const struct snd_soc_dapm_widget adcx140_dapm_widgets[] = { SND_SOC_DAPM_ADC("CH3_ADC", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), SND_SOC_DAPM_ADC("CH4_ADC", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + SND_SOC_DAPM_ADC("CH1_DIG", "CH1 Capture", ADCX140_IN_CH_EN, 7, 0), + SND_SOC_DAPM_ADC("CH2_DIG", "CH2 Capture", ADCX140_IN_CH_EN, 6, 0), + SND_SOC_DAPM_ADC("CH3_DIG", "CH3 Capture", ADCX140_IN_CH_EN, 5, 0), + SND_SOC_DAPM_ADC("CH4_DIG", "CH4 Capture", ADCX140_IN_CH_EN, 4, 0), + SND_SOC_DAPM_ADC("CH5_DIG", "CH5 Capture", ADCX140_IN_CH_EN, 3, 0), + SND_SOC_DAPM_ADC("CH6_DIG", "CH6 Capture", ADCX140_IN_CH_EN, 2, 0), + SND_SOC_DAPM_ADC("CH7_DIG", "CH7 Capture", ADCX140_IN_CH_EN, 1, 0), + SND_SOC_DAPM_ADC("CH8_DIG", "CH8 Capture", ADCX140_IN_CH_EN, 0, 0), + + SND_SOC_DAPM_SWITCH("CH1_ASI_EN", SND_SOC_NOPM, 0, 0, &adcx140_dapm_ch1_en_switch), SND_SOC_DAPM_SWITCH("CH2_ASI_EN", SND_SOC_NOPM, 0, 0, @@ -470,6 +480,15 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = { {"CH3_ASI_EN", "Switch", "CH3_ADC"}, {"CH4_ASI_EN", "Switch", "CH4_ADC"}, + {"CH1_ASI_EN", "Switch", "CH1_DIG"}, + {"CH2_ASI_EN", "Switch", "CH2_DIG"}, + {"CH3_ASI_EN", "Switch", "CH3_DIG"}, + {"CH4_ASI_EN", "Switch", "CH4_DIG"}, + {"CH5_ASI_EN", "Switch", "CH5_DIG"}, + {"CH6_ASI_EN", "Switch", "CH6_DIG"}, + {"CH7_ASI_EN", "Switch", "CH7_DIG"}, + {"CH8_ASI_EN", "Switch", "CH8_DIG"}, + {"CH5_ASI_EN", "Switch", "CH5_OUT"}, {"CH6_ASI_EN", "Switch", "CH6_OUT"}, {"CH7_ASI_EN", "Switch", "CH7_OUT"}, @@ -541,6 +560,15 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = { {"PDM Clk Div Select", "705.6 kHz", "MIC1P Input Mux"}, {"PDM Clk Div Select", "5.6448 MHz", "MIC1P Input Mux"}, + {"MIC1P Input Mux", NULL, "CH1_DIG"}, + {"MIC1M Input Mux", NULL, "CH2_DIG"}, + {"MIC2P Input Mux", NULL, "CH3_DIG"}, + {"MIC2M Input Mux", NULL, "CH4_DIG"}, + {"MIC3P Input Mux", NULL, "CH5_DIG"}, + {"MIC3M Input Mux", NULL, "CH6_DIG"}, + {"MIC4P Input Mux", NULL, "CH7_DIG"}, + {"MIC4M Input Mux", NULL, "CH8_DIG"}, + {"MIC1 Analog Mux", "Line In", "MIC1P"}, {"MIC2 Analog Mux", "Line In", "MIC2P"}, {"MIC3 Analog Mux", "Line In", "MIC3P"}, @@ -554,6 +582,15 @@ static const struct snd_soc_dapm_route adcx140_audio_map[] = { {"MIC3M Input Mux", "Analog", "MIC3M"}, {"MIC4P Input Mux", "Analog", "MIC4P"}, {"MIC4M Input Mux", "Analog", "MIC4M"}, + + {"MIC1P Input Mux", "Digital", "MIC1P"}, + {"MIC1M Input Mux", "Digital", "MIC1M"}, + {"MIC2P Input Mux", "Digital", "MIC2P"}, + {"MIC2M Input Mux", "Digital", "MIC2M"}, + {"MIC3P Input Mux", "Digital", "MIC3P"}, + {"MIC3M Input Mux", "Digital", "MIC3M"}, + {"MIC4P Input Mux", "Digital", "MIC4P"}, + {"MIC4M Input Mux", "Digital", "MIC4M"}, }; static const struct snd_kcontrol_new adcx140_snd_controls[] = { -- cgit From 160c174ff6972bb56bf48ac3335297889839e1f1 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 28 Aug 2020 16:20:27 -0700 Subject: ASoC: rt5682: Prefer async probe The probe of rt5682 is pretty slow. A quick measurement shows that it takes ~650 ms on at least one board. There's no reason to block all other drivers waiting for this probe to finish. Set the flag to allow other drivers to probe while we're probing. Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200828162005.1.I4f67f494c4f759b0e5c7f487e040dfdcf16e0876@changeid Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682-i2c.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 85aba311bdc8..6b4e0eb30c89 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -294,6 +294,7 @@ static struct i2c_driver rt5682_i2c_driver = { .name = "rt5682", .of_match_table = rt5682_of_match, .acpi_match_table = rt5682_acpi_match, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, .probe = rt5682_i2c_probe, .shutdown = rt5682_i2c_shutdown, -- cgit From 931522b90813a6f1c9c673ec9a0eb985b39554dc Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:16 -0500 Subject: ALSA: core: pcm: simplify locking for timers Fix sparse warning: sound/core/pcm.c:999:9: warning: context imbalance in 'snd_pcm_detach_substream' - different lock contexts for basic block There's no real reason to test the same thing twice, and it's simpler have linear sequences. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/pcm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/core/pcm.c b/sound/core/pcm.c index b6d2331a82f7..be5714f1bb58 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -991,11 +991,13 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control))); kfree(runtime->hw_constraints.rules); /* Avoid concurrent access to runtime via PCM timer interface */ - if (substream->timer) + if (substream->timer) { spin_lock_irq(&substream->timer->lock); - substream->runtime = NULL; - if (substream->timer) + substream->runtime = NULL; spin_unlock_irq(&substream->timer->lock); + } else { + substream->runtime = NULL; + } kfree(runtime); put_pid(substream->pid); substream->pid = NULL; -- cgit From b1c14124507b86ab554be75ed5fcb2e50bb33c6c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:17 -0500 Subject: ALSA: core: memalloc: fix fallthrough position Fix cppcheck, the fallthrough only makes sense within the conditional block sound/core/memalloc.c:161:3: style:inconclusive: Statements following return, break, continue, goto or throw will never be executed. [unreachableCode] fallthrough; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/memalloc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index ad74ea9cbff5..0aeeb6244ff6 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -157,8 +157,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, * so if we fail to malloc, try to fetch memory traditionally. */ dmab->dev.type = SNDRV_DMA_TYPE_DEV; -#endif /* CONFIG_GENERIC_ALLOCATOR */ fallthrough; +#endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: case SNDRV_DMA_TYPE_DEV_UC: snd_malloc_dev_pages(dmab, size); -- cgit From b658cbabf8e3e41816f43820f2b03f498e5556d2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:18 -0500 Subject: ALSA: core: pcm_memory: dereference pointer after NULL checks Fix cppcheck warnings: sound/core/pcm_memory.c:380:26: warning: Either the condition '!substream' is redundant or there is possible null pointer dereference: substream. [nullPointerRedundantCheck] struct snd_card *card = substream->pcm->card; ^ sound/core/pcm_memory.c:384:6: note: Assuming that condition '!substream' is not redundant if (PCM_RUNTIME_CHECK(substream)) ^ sound/core/pcm_memory.c:380:26: note: Null pointer dereference struct snd_card *card = substream->pcm->card; ^ sound/core/pcm_memory.c:433:26: warning: Either the condition '!substream' is redundant or there is possible null pointer dereference: substream. [nullPointerRedundantCheck] struct snd_card *card = substream->pcm->card; ^ sound/core/pcm_memory.c:436:6: note: Assuming that condition '!substream' is not redundant if (PCM_RUNTIME_CHECK(substream)) ^ sound/core/pcm_memory.c:433:26: note: Null pointer dereference struct snd_card *card = substream->pcm->card; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/pcm_memory.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c index 1bf6a3d9e0c2..4f03ba8ed0ae 100644 --- a/sound/core/pcm_memory.c +++ b/sound/core/pcm_memory.c @@ -377,7 +377,7 @@ struct page *snd_pcm_sgbuf_ops_page(struct snd_pcm_substream *substream, unsigne */ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) { - struct snd_card *card = substream->pcm->card; + struct snd_card *card; struct snd_pcm_runtime *runtime; struct snd_dma_buffer *dmab = NULL; @@ -387,6 +387,7 @@ int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size) SNDRV_DMA_TYPE_UNKNOWN)) return -EINVAL; runtime = substream->runtime; + card = substream->pcm->card; if (runtime->dma_buffer_p) { /* perphaps, we might free the large DMA memory region -- cgit From 63632563c0dfe036215dcba222a0f899d35f7be6 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:19 -0500 Subject: ALSA: core: timer: remove redundant assignment Cppcheck complains about a possible NULL pointer dereference but it actually looks like the NULL assignment is not needed (same loop is used in other parts of the file without it). Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/timer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/timer.c b/sound/core/timer.c index d9f85f2d66a3..1349f5b5be09 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -173,7 +173,7 @@ EXPORT_SYMBOL(snd_timer_instance_free); */ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) { - struct snd_timer *timer = NULL; + struct snd_timer *timer; list_for_each_entry(timer, &snd_timer_list, device_list) { if (timer->tmr_class != tid->dev_class) -- cgit From 3bcf8eeb7d979402d3db96fb58bed456a3c66668 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:20 -0500 Subject: ALSA: core: timer: clarify operator precedence fix cppcheck warning: sound/core/timer.c:1286:9: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] ? "running" : "stopped"); ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-6-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/timer.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/core/timer.c b/sound/core/timer.c index 1349f5b5be09..b915d39e7acb 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -1281,8 +1281,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry, list_for_each_entry(ti, &timer->open_list_head, open_list) snd_iprintf(buffer, " Client %s : %s\n", ti->owner ? ti->owner : "unknown", - ti->flags & (SNDRV_TIMER_IFLG_START | - SNDRV_TIMER_IFLG_RUNNING) + (ti->flags & (SNDRV_TIMER_IFLG_START | + SNDRV_TIMER_IFLG_RUNNING)) ? "running" : "stopped"); } mutex_unlock(®ister_mutex); -- cgit From cd91fd9f0af5453e66bda9984916ca67686689cf Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:21 -0500 Subject: ALSA: compress_offload: dereference after checking for NULL pointer Fix cppcheck warning and only dereference once the initial checks are done: sound/core/compress_offload.c:516:38: warning: Either the condition '!stream' is redundant or there is possible null pointer dereference: stream. [nullPointerRedundantCheck] struct snd_compr_runtime *runtime = stream->runtime; ^ sound/core/compress_offload.c:518:17: note: Assuming that condition '!stream' is not redundant if (snd_BUG_ON(!(stream) || !(stream)->runtime)) ^ sound/core/compress_offload.c:516:38: note: Null pointer dereference struct snd_compr_runtime *runtime = stream->runtime; ^ Signed-off-by: Pierre-Louis Bossart Acked-by: Vinod Koul Link: https://lore.kernel.org/r/20200902212133.30964-7-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index 0e53f6f31916..e3eb314acb10 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -513,10 +513,11 @@ EXPORT_SYMBOL(snd_compr_malloc_pages); int snd_compr_free_pages(struct snd_compr_stream *stream) { - struct snd_compr_runtime *runtime = stream->runtime; + struct snd_compr_runtime *runtime; if (snd_BUG_ON(!(stream) || !(stream)->runtime)) return -EINVAL; + runtime = stream->runtime; if (runtime->dma_area == NULL) return 0; if (runtime->dma_buffer_p != &stream->dma_buffer) { -- cgit From 9725ce3949a7b3eebf003cb69fa0d7d821179233 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:22 -0500 Subject: ALSA: compress_offload: remove redundant initialization Fix cppcheck warning: sound/core/compress_offload.c:1044:6: style: Redundant initialization for 'ret'. The initialized value is overwritten before it is read. [redundantInitialization] ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, ^ sound/core/compress_offload.c:1034:10: note: ret is initialized int ret = -EINVAL; ^ sound/core/compress_offload.c:1044:6: note: ret is overwritten ret = snd_register_device(SNDRV_DEVICE_TYPE_COMPRESS, ^ Signed-off-by: Pierre-Louis Bossart Acked-by: Vinod Koul Link: https://lore.kernel.org/r/20200902212133.30964-8-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/compress_offload.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index e3eb314acb10..c1fec932c49d 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -1032,7 +1032,7 @@ static const struct file_operations snd_compr_file_ops = { static int snd_compress_dev_register(struct snd_device *device) { - int ret = -EINVAL; + int ret; struct snd_compr *compr; if (snd_BUG_ON(!device || !device->device_data)) -- cgit From bec206db9db59cbc14993583288e7489e8ab02b1 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:23 -0500 Subject: ALSA: core: init: use DECLARE_COMPLETION_ONSTACK() macro Follow recommendation in Documentation/scheduler/completion.rst and use macro to declare local 'struct completion' Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-9-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/init.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/core/init.c b/sound/core/init.c index 0478847ba2b8..764dbe673d48 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -519,10 +519,9 @@ EXPORT_SYMBOL(snd_card_free_when_closed); */ int snd_card_free(struct snd_card *card) { - struct completion released; + DECLARE_COMPLETION_ONSTACK(released); int ret; - init_completion(&released); card->release_completion = &released; ret = snd_card_free_when_closed(card); if (ret) -- cgit From 5656a7a06f0d5d90025f05c59424eb03d66e0039 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:24 -0500 Subject: ALSA: aoa: i2sbus: use DECLARE_COMPLETION_ONSTACK() macro Follow recommendation in Documentation/scheduler/completion.rst and use macro to declare local 'struct completion' Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-10-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/aoa/soundbus/i2sbus/pcm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/aoa/soundbus/i2sbus/pcm.c b/sound/aoa/soundbus/i2sbus/pcm.c index d350dbd24305..1c8e8131a716 100644 --- a/sound/aoa/soundbus/i2sbus/pcm.c +++ b/sound/aoa/soundbus/i2sbus/pcm.c @@ -254,12 +254,11 @@ static void i2sbus_wait_for_stop(struct i2sbus_dev *i2sdev, struct pcm_info *pi) { unsigned long flags; - struct completion done; + DECLARE_COMPLETION_ONSTACK(done); long timeout; spin_lock_irqsave(&i2sdev->low_lock, flags); if (pi->dbdma_ring.stopping) { - init_completion(&done); pi->stop_completion = &done; spin_unlock_irqrestore(&i2sdev->low_lock, flags); timeout = wait_for_completion_timeout(&done, HZ); -- cgit From e9bd25885c7f799134534989b9f13c3db5ce6a16 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:25 -0500 Subject: ALSA: hda: auto_parser: remove shadowed variable declaration Fix cppcheck warning: sound/pci/hda/hda_auto_parser.c:353:7: style: Local variable 'i' shadows outer variable [shadowVariable] int i = 0; ^ sound/pci/hda/hda_auto_parser.c:182:6: note: Shadowed declaration int i; ^ sound/pci/hda/hda_auto_parser.c:353:7: note: Shadow variable int i = 0; ^ It's not clear why a new declaration was added, remove and reuse variable declared with larger scope. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-11-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_auto_parser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c index 824f4ac1a8ce..4dc01647753c 100644 --- a/sound/pci/hda/hda_auto_parser.c +++ b/sound/pci/hda/hda_auto_parser.c @@ -350,7 +350,7 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec, */ if (!cfg->line_outs && cfg->hp_outs > 1 && !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) { - int i = 0; + i = 0; while (i < cfg->hp_outs) { /* The real HPs should have the sequence 0x0f */ if ((hp_out[i].seq & 0x0f) == 0x0f) { -- cgit From 74610eaf310ab9b9b00a015adb54afcae038bc06 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:26 -0500 Subject: ALSA: hda: (cosmetic) align function parameters Fix cppcheck warnings and use same names in headers and C code. Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-12-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.h | 2 +- sound/pci/hda/hda_local.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_jack.h b/sound/pci/hda/hda_jack.h index 727b6d3ba454..8ceaf0ef5df1 100644 --- a/sound/pci/hda/hda_jack.h +++ b/sound/pci/hda/hda_jack.h @@ -77,7 +77,7 @@ int snd_hda_jack_detect_enable(struct hda_codec *codec, hda_nid_t nid, struct hda_jack_callback * snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid, - int dev_id, hda_jack_callback_fn cb); + int dev_id, hda_jack_callback_fn func); /** * snd_hda_jack_detect_enable - enable the jack-detection diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 8c28b1022f49..5beb8aa44ecd 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -100,7 +100,7 @@ int snd_hda_mixer_amp_volume_get(struct snd_kcontrol *kcontrol, int snd_hda_mixer_amp_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_hda_mixer_amp_tlv(struct snd_kcontrol *kcontrol, int op_flag, - unsigned int size, unsigned int __user *tlv); + unsigned int size, unsigned int __user *_tlv); int snd_hda_mixer_amp_switch_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo); int snd_hda_mixer_amp_switch_get(struct snd_kcontrol *kcontrol, @@ -119,7 +119,7 @@ int snd_hda_mixer_amp_switch_put_beep(struct snd_kcontrol *kcontrol, int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch, int dir, int idx, int mask, int val); int snd_hda_codec_amp_stereo(struct hda_codec *codec, hda_nid_t nid, - int dir, int idx, int mask, int val); + int direction, int idx, int mask, int val); int snd_hda_codec_amp_init(struct hda_codec *codec, hda_nid_t nid, int ch, int direction, int idx, int mask, int val); int snd_hda_codec_amp_init_stereo(struct hda_codec *codec, hda_nid_t nid, @@ -198,7 +198,7 @@ int snd_hda_input_mux_put(struct hda_codec *codec, unsigned int *cur_val); int snd_hda_add_imux_item(struct hda_codec *codec, struct hda_input_mux *imux, const char *label, - int index, int *type_index_ret); + int index, int *type_idx); /* * Multi-channel / digital-out PCM helper @@ -642,7 +642,7 @@ unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec, */ int snd_hda_enum_helper_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo, - int num_entries, const char * const *texts); + int num_items, const char * const *texts); #define snd_hda_enum_bool_helper_info(kcontrol, uinfo) \ snd_hda_enum_helper_info(kcontrol, uinfo, 0, NULL) -- cgit From 04d0b5e3b1ba4aca95534767e47d140be1d6643f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:27 -0500 Subject: ALSA: usb: scarless_gen2: fix endianness issue Fix Sparse warning: sound/usb/mixer_scarlett_gen2.c:1949:24: warning: cast to restricted __le32 Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-13-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_scarlett_gen2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c index 0ffff7640892..d33df146d6ce 100644 --- a/sound/usb/mixer_scarlett_gen2.c +++ b/sound/usb/mixer_scarlett_gen2.c @@ -1946,7 +1946,7 @@ static void scarlett2_mixer_interrupt(struct urb *urb) goto requeue; if (len == 8) { - data = le32_to_cpu(*(u32 *)urb->transfer_buffer); + data = le32_to_cpu(*(__le32 *)urb->transfer_buffer); if (data & SCARLETT2_USB_INTERRUPT_VOL_CHANGE) scarlett2_mixer_interrupt_vol_change(mixer); if (data & SCARLETT2_USB_INTERRUPT_BUTTON_CHANGE) -- cgit From 2d7a5c6c69029ae7e386cb3a04086a1e0b61cb8f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:28 -0500 Subject: ALSA: ac97: (cosmetic) align argument names Fix cppcheck warning: sound/ac97/bus.c:133:60: style:inconclusive: Function 'snd_ac97_bus_scan_one' argument 1 names different: declaration 'ac97' definition 'adrv'. [funcArgNamesDifferent] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-14-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/ac97/ac97_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/ac97/ac97_core.h b/sound/ac97/ac97_core.h index 0c5956e4b2f3..5a9677c3d4c3 100644 --- a/sound/ac97/ac97_core.h +++ b/sound/ac97/ac97_core.h @@ -3,7 +3,7 @@ * Copyright (C) 2016 Robert Jarzmik */ -unsigned int snd_ac97_bus_scan_one(struct ac97_controller *ac97, +unsigned int snd_ac97_bus_scan_one(struct ac97_controller *adrv, unsigned int codec_num); static inline bool ac97_ids_match(unsigned int id1, unsigned int id2, -- cgit From a971b42cbf8c0dd553ac0ca7fe82f4b8e9cad088 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:29 -0500 Subject: ALSA: atmel: ac97: clarify operator precedence Fix cppcheck warnings: sound/atmel/ac97c.c:478:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] casr & AC97C_CSR_OVRUN ? " OVRUN" : "", ^ sound/atmel/ac97c.c:479:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] casr & AC97C_CSR_RXRDY ? " RXRDY" : "", ^ sound/atmel/ac97c.c:480:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] casr & AC97C_CSR_UNRUN ? " UNRUN" : "", ^ sound/atmel/ac97c.c:481:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", ^ sound/atmel/ac97c.c:482:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] casr & AC97C_CSR_TXRDY ? " TXRDY" : "", ^ sound/atmel/ac97c.c:524:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] cosr & AC97C_CSR_OVRUN ? " OVRUN" : "", ^ sound/atmel/ac97c.c:525:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] cosr & AC97C_CSR_RXRDY ? " RXRDY" : "", ^ sound/atmel/ac97c.c:526:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", ^ sound/atmel/ac97c.c:527:30: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] cosr & AC97C_CSR_TXRDY ? " TXRDY" : "", ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-15-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/atmel/ac97c.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c index 1006458f7f85..66ecbd4d034e 100644 --- a/sound/atmel/ac97c.c +++ b/sound/atmel/ac97c.c @@ -475,12 +475,12 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) struct snd_pcm_runtime *runtime; int offset, next_period, block_size; dev_dbg(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n", - casr & AC97C_CSR_OVRUN ? " OVRUN" : "", - casr & AC97C_CSR_RXRDY ? " RXRDY" : "", - casr & AC97C_CSR_UNRUN ? " UNRUN" : "", - casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", - casr & AC97C_CSR_TXRDY ? " TXRDY" : "", - !casr ? " NONE" : ""); + (casr & AC97C_CSR_OVRUN) ? " OVRUN" : "", + (casr & AC97C_CSR_RXRDY) ? " RXRDY" : "", + (casr & AC97C_CSR_UNRUN) ? " UNRUN" : "", + (casr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "", + (casr & AC97C_CSR_TXRDY) ? " TXRDY" : "", + !casr ? " NONE" : ""); if ((casr & camr) & AC97C_CSR_ENDTX) { runtime = chip->playback_substream->runtime; block_size = frames_to_bytes(runtime, runtime->period_size); @@ -521,11 +521,11 @@ static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev) if (sr & AC97C_SR_COEVT) { dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n", - cosr & AC97C_CSR_OVRUN ? " OVRUN" : "", - cosr & AC97C_CSR_RXRDY ? " RXRDY" : "", - cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "", - cosr & AC97C_CSR_TXRDY ? " TXRDY" : "", - !cosr ? " NONE" : ""); + (cosr & AC97C_CSR_OVRUN) ? " OVRUN" : "", + (cosr & AC97C_CSR_RXRDY) ? " RXRDY" : "", + (cosr & AC97C_CSR_TXEMPTY) ? " TXEMPTY" : "", + (cosr & AC97C_CSR_TXRDY) ? " TXRDY" : "", + !cosr ? " NONE" : ""); retval = IRQ_HANDLED; } -- cgit From 93fcef86caafba893f2e13782f4c58b386eab311 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:30 -0500 Subject: ALSA: rawmidi: (cosmetic) align function parameters fix cppcheck: sound/core/rawmidi.c:1711:49: style:inconclusive: Function 'snd_rawmidi_free' argument 1 names different: declaration 'rawmidi' definition 'rmidi'. [funcArgNamesDifferent] Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-16-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/core/rawmidi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 2a688b711a9a..c78720a3299c 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -35,7 +35,7 @@ module_param_array(amidi_map, int, NULL, 0444); MODULE_PARM_DESC(amidi_map, "Raw MIDI device number assigned to 2nd OSS device."); #endif /* CONFIG_SND_OSSEMUL */ -static int snd_rawmidi_free(struct snd_rawmidi *rawmidi); +static int snd_rawmidi_free(struct snd_rawmidi *rmidi); static int snd_rawmidi_dev_free(struct snd_device *device); static int snd_rawmidi_dev_register(struct snd_device *device); static int snd_rawmidi_dev_disconnect(struct snd_device *device); -- cgit From b7dcd6ac0225aa3bc4c78fc5f13610789c9a7f15 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:31 -0500 Subject: ALSA: vx: vx_core: clarify operator precedence Fix cppcheck warning sound/drivers/vx/vx_core.c:600:49: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No"); ^ sound/drivers/vx/vx_core.c:602:47: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No"); ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-17-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/drivers/vx/vx_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c index 26d591fe6a6b..d5c65cab195b 100644 --- a/sound/drivers/vx/vx_core.c +++ b/sound/drivers/vx/vx_core.c @@ -597,9 +597,9 @@ static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *b snd_iprintf(buffer, "%s\n", chip->card->longname); snd_iprintf(buffer, "Xilinx Firmware: %s\n", - chip->chip_status & VX_STAT_XILINX_LOADED ? "Loaded" : "No"); + (chip->chip_status & VX_STAT_XILINX_LOADED) ? "Loaded" : "No"); snd_iprintf(buffer, "Device Initialized: %s\n", - chip->chip_status & VX_STAT_DEVICE_INIT ? "Yes" : "No"); + (chip->chip_status & VX_STAT_DEVICE_INIT) ? "Yes" : "No"); snd_iprintf(buffer, "DSP audio info:"); if (chip->audio_info & VX_AUDIO_INFO_REAL_TIME) snd_iprintf(buffer, " realtime"); -- cgit From e408ab068aeca958b83bb6e5ca724356f8c7d78e Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:32 -0500 Subject: ALSA: vx: vx_pcm: remove redundant assignment Fix cppcheck warning: sound/drivers/vx/vx_pcm.c:539:30: style: Variable 'chip->playback_pipes[audio]' is reassigned a value before the old one has been used. [redundantAssignment] chip->playback_pipes[audio] = pipe; ^ sound/drivers/vx/vx_pcm.c:533:31: note: chip->playback_pipes[audio] is assigned chip->playback_pipes[audio] = pipe; ^ sound/drivers/vx/vx_pcm.c:539:30: note: chip->playback_pipes[audio] is overwritten chip->playback_pipes[audio] = pipe; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-18-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/drivers/vx/vx_pcm.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 664b9efa9a50..47361e26be80 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -530,7 +530,6 @@ static int vx_pcm_playback_open(struct snd_pcm_substream *subs) err = vx_alloc_pipe(chip, 0, audio, 2, &pipe); /* stereo playback */ if (err < 0) return err; - chip->playback_pipes[audio] = pipe; } /* open for playback */ pipe->references++; -- cgit From b248b9dd5975c1e16b7918c1dc7eaef7eccfde5f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 2 Sep 2020 16:21:33 -0500 Subject: ALSA: vx: vx_pcm: remove redundant assignment Fix cppcheck warning: sound/drivers/vx/vx_pcm.c:63:7: style: Variable 'buf' is assigned a value that is never used. [unreadVariable] buf = (unsigned char *)runtime->dma_area; ^ Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200902212133.30964-19-pierre-louis.bossart@linux.intel.com Signed-off-by: Takashi Iwai --- sound/drivers/vx/vx_pcm.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/drivers/vx/vx_pcm.c b/sound/drivers/vx/vx_pcm.c index 47361e26be80..3d2e3bcafca8 100644 --- a/sound/drivers/vx/vx_pcm.c +++ b/sound/drivers/vx/vx_pcm.c @@ -60,7 +60,6 @@ static void vx_pcm_read_per_bytes(struct vx_core *chip, struct snd_pcm_runtime * *buf++ = vx_inb(chip, RXL); if (++offset >= pipe->buffer_bytes) { offset = 0; - buf = (unsigned char *)runtime->dma_area; } pipe->hw_ptr = offset; } -- cgit From 770f58d7d2c58b8ff31d3694ce14a785c2e75009 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 1 Sep 2020 19:01:08 +0800 Subject: ASoC: fsl_sai: Support multiple data channel enable bits One data channel is one data line. From imx7ulp, the SAI IP is enhanced to support multiple data channels. If there is only two channels input and slots is 2, then enable one data channel is enough for data transfer. So enable the TCE/RCE and transmit/receive mask register according to the input channels and slots configuration. Move the data channel enablement from startup() to hw_params(). Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1598958068-10552-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 30 ++++++++++++------------------ sound/soc/fsl/fsl_sai.h | 2 +- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 62c5fdb678fc..38c7bcbb361d 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -443,6 +443,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, u32 slots = (channels == 1) ? 2 : channels; u32 slot_width = word_width; int adir = tx ? RX : TX; + u32 pins; int ret; if (sai->slots) @@ -451,6 +452,8 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, if (sai->slot_width) slot_width = sai->slot_width; + pins = DIV_ROUND_UP(channels, slots); + if (!sai->is_slave_mode) { if (sai->bclk_ratio) ret = fsl_sai_set_bclk(cpu_dai, tx, @@ -501,13 +504,17 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, FSL_SAI_CR5_FBT_MASK, val_cr5); } + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), + FSL_SAI_CR3_TRCE_MASK, + FSL_SAI_CR3_TRCE((1 << pins) - 1)); regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, val_cr4); regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs), FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | FSL_SAI_CR5_FBT_MASK, val_cr5); - regmap_write(sai->regmap, FSL_SAI_xMR(tx), ~0UL - ((1 << channels) - 1)); + regmap_write(sai->regmap, FSL_SAI_xMR(tx), + ~0UL - ((1 << min(channels, slots)) - 1)); return 0; } @@ -517,6 +524,10 @@ static int fsl_sai_hw_free(struct snd_pcm_substream *substream, { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int ofs = sai->soc_data->reg_offset; + + regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), + FSL_SAI_CR3_TRCE_MASK, 0); if (!sai->is_slave_mode && sai->mclk_streams & BIT(substream->stream)) { @@ -651,14 +662,9 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int ofs = sai->soc_data->reg_offset; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int ret; - regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), - FSL_SAI_CR3_TRCE_MASK, - FSL_SAI_CR3_TRCE); - /* * EDMA controller needs period size to be a multiple of * tx/rx maxburst @@ -675,17 +681,6 @@ static int fsl_sai_startup(struct snd_pcm_substream *substream, return ret; } -static void fsl_sai_shutdown(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai); - unsigned int ofs = sai->soc_data->reg_offset; - bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; - - regmap_update_bits(sai->regmap, FSL_SAI_xCR3(tx, ofs), - FSL_SAI_CR3_TRCE_MASK, 0); -} - static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .set_bclk_ratio = fsl_sai_set_dai_bclk_ratio, .set_sysclk = fsl_sai_set_dai_sysclk, @@ -695,7 +690,6 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_ops = { .hw_free = fsl_sai_hw_free, .trigger = fsl_sai_trigger, .startup = fsl_sai_startup, - .shutdown = fsl_sai_shutdown, }; static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 6aba7d28f5f3..5f630be74853 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -109,7 +109,7 @@ #define FSL_SAI_CR2_DIV_MASK 0xff /* SAI Transmit and Receive Configuration 3 Register */ -#define FSL_SAI_CR3_TRCE BIT(16) +#define FSL_SAI_CR3_TRCE(x) ((x) << 16) #define FSL_SAI_CR3_TRCE_MASK GENMASK(23, 16) #define FSL_SAI_CR3_WDFL(x) (x) #define FSL_SAI_CR3_WDFL_MASK 0x1f -- cgit From f4c4b1bb2f5a7f034f039c302b56f82344a6dc8c Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 3 Sep 2020 13:53:47 +0800 Subject: ASoC: fsl_sai: Set SAI Channel Mode to Output Mode Transmit data pins will output zero when slots are masked or channels are disabled. In CHMOD TDM mode, transmit data pins are tri-stated when slots are masked or channels are disabled. When data pins are tri-stated, there is noise on some channels when FS clock value is high and data is read while fsclk is transitioning from high to low. Signed-off-by: Cosmin-Gabriel Samoila Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1599112427-22038-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 10 ++++++++-- sound/soc/fsl/fsl_sai.h | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 38c7bcbb361d..b2d65e53dbc4 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -489,6 +489,10 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, val_cr4 |= FSL_SAI_CR4_FRSZ(slots); + /* Set to output mode to avoid tri-stated data pins */ + if (tx) + val_cr4 |= FSL_SAI_CR4_CHMOD; + /* * For SAI master mode, when Tx(Rx) sync with Rx(Tx) clock, Rx(Tx) will * generate bclk and frame clock for Tx(Rx), we should set RCR4(TCR4), @@ -497,7 +501,8 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, if (!sai->is_slave_mode && fsl_sai_dir_is_synced(sai, adir)) { regmap_update_bits(sai->regmap, FSL_SAI_xCR4(!tx, ofs), - FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | + FSL_SAI_CR4_CHMOD_MASK, val_cr4); regmap_update_bits(sai->regmap, FSL_SAI_xCR5(!tx, ofs), FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | @@ -508,7 +513,8 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream, FSL_SAI_CR3_TRCE_MASK, FSL_SAI_CR3_TRCE((1 << pins) - 1)); regmap_update_bits(sai->regmap, FSL_SAI_xCR4(tx, ofs), - FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK, + FSL_SAI_CR4_SYWD_MASK | FSL_SAI_CR4_FRSZ_MASK | + FSL_SAI_CR4_CHMOD_MASK, val_cr4); regmap_update_bits(sai->regmap, FSL_SAI_xCR5(tx, ofs), FSL_SAI_CR5_WNW_MASK | FSL_SAI_CR5_W0W_MASK | diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 5f630be74853..736a437450c8 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -119,6 +119,8 @@ #define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) #define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) #define FSL_SAI_CR4_SYWD_MASK (0x1f << 8) +#define FSL_SAI_CR4_CHMOD BIT(5) +#define FSL_SAI_CR4_CHMOD_MASK BIT(5) #define FSL_SAI_CR4_MF BIT(4) #define FSL_SAI_CR4_FSE BIT(3) #define FSL_SAI_CR4_FSP BIT(1) -- cgit From 0dcdf84289fbf61cd42cbb1e379e2150aa17e31e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 2 Sep 2020 17:07:54 +0300 Subject: ASoC: SOF: add a "core" parameter to widget loading functions We want to be able to explicitly assign cores to individual pipeline components. This patch adds a "core" parameter to widget loading functions to be sent to the DSP for appropriate component scheduling. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200902140756.1427005-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-priv.h | 3 ++ sound/soc/sof/topology.c | 74 ++++++++++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 27 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 53d26be88f64..1c51d99f0459 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -54,6 +54,9 @@ extern int sof_core_debug; (IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) || \ IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_IPC_FLOOD_TEST)) +/* So far the primary core on all DSPs has ID 0 */ +#define SOF_DSP_PRIMARY_CORE 0 + /* DSP power state */ enum sof_dsp_power_states { SOF_DSP_PM_D0, diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 95e63d138326..d41df9337328 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1360,7 +1360,7 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, } static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r, struct snd_sof_dai *dai) @@ -1377,6 +1377,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, comp_dai.comp.id = swidget->comp_id; comp_dai.comp.type = SOF_COMP_DAI; comp_dai.comp.pipeline_id = index; + comp_dai.comp.core = core; comp_dai.config.hdr.size = sizeof(comp_dai.config); ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, @@ -1417,7 +1418,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, */ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1436,6 +1437,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, buffer->comp.id = swidget->comp_id; buffer->comp.type = SOF_COMP_BUFFER; buffer->comp.pipeline_id = index; + buffer->comp.core = core; ret = sof_parse_tokens(scomp, buffer, buffer_tokens, ARRAY_SIZE(buffer_tokens), private->array, @@ -1487,7 +1489,7 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, */ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, enum sof_ipc_stream_direction dir, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) @@ -1507,6 +1509,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, host->comp.id = swidget->comp_id; host->comp.type = SOF_COMP_HOST; host->comp.pipeline_id = index; + host->comp.core = core; host->direction = dir; host->config.hdr.size = sizeof(host->config); @@ -1592,8 +1595,8 @@ int sof_load_pipeline_ipc(struct device *dev, return ret; } -static int sof_widget_load_pipeline(struct snd_soc_component *scomp, - int index, struct snd_sof_widget *swidget, +static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1655,7 +1658,7 @@ err: */ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1674,6 +1677,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, mixer->comp.id = swidget->comp_id; mixer->comp.type = SOF_COMP_MIXER; mixer->comp.pipeline_id = index; + mixer->comp.core = core; mixer->config.hdr.size = sizeof(mixer->config); ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens, @@ -1702,7 +1706,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, * Mux topology */ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1721,6 +1725,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, mux->comp.id = swidget->comp_id; mux->comp.type = SOF_COMP_MUX; mux->comp.pipeline_id = index; + mux->comp.core = core; mux->config.hdr.size = sizeof(mux->config); ret = sof_parse_tokens(scomp, &mux->config, comp_tokens, @@ -1750,7 +1755,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, */ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1779,6 +1784,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, volume->comp.id = swidget->comp_id; volume->comp.type = SOF_COMP_VOLUME; volume->comp.pipeline_id = index; + volume->comp.core = core; volume->config.hdr.size = sizeof(volume->config); ret = sof_parse_tokens(scomp, volume, volume_tokens, @@ -1828,7 +1834,7 @@ err: */ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1847,6 +1853,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, src->comp.id = swidget->comp_id; src->comp.type = SOF_COMP_SRC; src->comp.pipeline_id = index; + src->comp.core = core; src->config.hdr.size = sizeof(src->config); ret = sof_parse_tokens(scomp, src, src_tokens, @@ -1887,7 +1894,7 @@ err: */ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1906,6 +1913,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, asrc->comp.id = swidget->comp_id; asrc->comp.type = SOF_COMP_ASRC; asrc->comp.pipeline_id = index; + asrc->comp.core = core; asrc->config.hdr.size = sizeof(asrc->config); ret = sof_parse_tokens(scomp, asrc, asrc_tokens, @@ -1948,7 +1956,7 @@ err: */ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1967,6 +1975,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, tone->comp.id = swidget->comp_id; tone->comp.type = SOF_COMP_TONE; tone->comp.pipeline_id = index; + tone->comp.core = core; tone->config.hdr.size = sizeof(tone->config); ret = sof_parse_tokens(scomp, tone, tone_tokens, @@ -2204,7 +2213,7 @@ out: */ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, + struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -2219,6 +2228,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, } memset(&config, 0, sizeof(config)); + config.comp.core = core; /* get the process token */ ret = sof_parse_tokens(scomp, &config, process_tokens, @@ -2283,6 +2293,9 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_sof_dai *dai; struct sof_ipc_comp_reply reply; struct snd_sof_control *scontrol; + struct sof_ipc_comp comp = { + .core = SOF_DSP_PRIMARY_CORE, + }; int ret = 0; swidget = kzalloc(sizeof(*swidget), GFP_KERNEL); @@ -2313,8 +2326,8 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return -ENOMEM; } - ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, - dai); + ret = sof_widget_load_dai(scomp, index, swidget, comp.core, + tw, &reply, dai); if (ret == 0) { sof_connect_dai_widget(scomp, w, tw, dai); list_add(&dai->list, &sdev->dai_list); @@ -2324,10 +2337,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } break; case snd_soc_dapm_mixer: - ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_mixer(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_pga: - ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_pga(scomp, index, swidget, comp.core, + tw, &reply); /* Find scontrol for this pga and set readback offset*/ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { @@ -2337,36 +2352,41 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } break; case snd_soc_dapm_buffer: - ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_buffer(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_scheduler: - ret = sof_widget_load_pipeline(scomp, index, swidget, tw, - &reply); + ret = sof_widget_load_pipeline(scomp, index, swidget, + tw, &reply); break; case snd_soc_dapm_aif_out: - ret = sof_widget_load_pcm(scomp, index, swidget, + ret = sof_widget_load_pcm(scomp, index, swidget, comp.core, SOF_IPC_STREAM_CAPTURE, tw, &reply); break; case snd_soc_dapm_aif_in: - ret = sof_widget_load_pcm(scomp, index, swidget, + ret = sof_widget_load_pcm(scomp, index, swidget, comp.core, SOF_IPC_STREAM_PLAYBACK, tw, &reply); break; case snd_soc_dapm_src: - ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_src(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_asrc: - ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_asrc(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_siggen: - ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_siggen(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_effect: - ret = sof_widget_load_process(scomp, index, swidget, tw, - &reply); + ret = sof_widget_load_process(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: - ret = sof_widget_load_mux(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_mux(scomp, index, swidget, comp.core, + tw, &reply); break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: -- cgit From d1c6c4a9fd3da5c735386b0cdb44d79667f10a1b Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Wed, 2 Sep 2020 17:07:55 +0300 Subject: ASoC: SOF: support topology components on secondary cores Currently SOF supports running pipelines on secondary DSP cores in a limited way. This patch represents the next step in SOF multi-core DSP support, it adds checks for core ID to individual topology components. It takes care to power up all the requested cores. More advanced DSP core power management should be added in the future. Signed-off-by: Pan Xiuli Signed-off-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200902140756.1427005-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pm.c | 1 + sound/soc/sof/sof-audio.c | 25 +++++++++ sound/soc/sof/sof-audio.h | 5 ++ sound/soc/sof/topology.c | 128 ++++++++++++++++++++++++++++++++++------------ 4 files changed, 125 insertions(+), 34 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 92e5f9b15f3a..a5f7c7f024a1 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -256,6 +256,7 @@ suspend: /* reset FW state */ sdev->fw_state = SOF_FW_BOOT_NOT_STARTED; + sdev->enabled_cores_mask = 0; return ret; } diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 33d84405cf9c..d05f99cd7919 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -142,6 +142,22 @@ static int sof_restore_kcontrols(struct device *dev) return 0; } +const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, + int pipeline_id) +{ + const struct snd_sof_widget *swidget; + + list_for_each_entry(swidget, &sdev->widget_list, list) + if (swidget->id == snd_soc_dapm_scheduler) { + const struct sof_ipc_pipe_new *pipeline = + swidget->private; + if (pipeline->pipeline_id == pipeline_id) + return pipeline; + } + + return NULL; +} + int sof_restore_pipelines(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); @@ -161,6 +177,15 @@ int sof_restore_pipelines(struct device *dev) if (!swidget->private) continue; + ret = sof_pipeline_core_enable(sdev, swidget); + if (ret < 0) { + dev_err(dev, + "error: failed to enable target core: %d\n", + ret); + + return ret; + } + switch (swidget->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9629994fe463..7f8be8817e69 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -83,6 +83,7 @@ struct snd_sof_widget { int comp_id; int pipeline_id; int complete; + int core; int id; struct snd_soc_dapm_widget *widget; @@ -151,6 +152,8 @@ int snd_sof_complete_pipeline(struct device *dev, int sof_load_pipeline_ipc(struct device *dev, struct sof_ipc_pipe_new *pipeline, struct sof_ipc_comp_reply *r); +int sof_pipeline_core_enable(struct snd_sof_dev *sdev, + const struct snd_sof_widget *swidget); /* * Stream IPC @@ -190,6 +193,8 @@ struct snd_sof_pcm *snd_sof_find_spcm_comp(struct snd_soc_component *scomp, int *direction); struct snd_sof_pcm *snd_sof_find_spcm_pcm_id(struct snd_soc_component *scomp, unsigned int pcm_id); +const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, + int pipeline_id); void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream); void snd_sof_pcm_period_elapsed_work(struct work_struct *work); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d41df9337328..46468fb7b6d1 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -8,6 +8,9 @@ // Author: Liam Girdwood // +#include +#include +#include #include #include #include @@ -715,6 +718,13 @@ static const struct sof_topology_token sai_tokens[] = { offsetof(struct sof_ipc_dai_sai_params, mclk_id), 0}, }; +/* Core tokens */ +static const struct sof_topology_token core_tokens[] = { + {SOF_TKN_COMP_CORE_ID, + SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, + offsetof(struct sof_ipc_comp, core), 0}, +}; + /* * DMIC PDM Tokens * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token @@ -1278,6 +1288,65 @@ static int sof_control_unload(struct snd_soc_component *scomp, * DAI Topology */ +/* Static DSP core power management so far, should be extended in the future */ +static int sof_core_enable(struct snd_sof_dev *sdev, int core) +{ + struct sof_ipc_pm_core_config pm_core_config = { + .hdr = { + .cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE, + .size = sizeof(pm_core_config), + }, + .enable_mask = sdev->enabled_cores_mask | BIT(core), + }; + int ret; + + if (sdev->enabled_cores_mask & BIT(core)) + return 0; + + /* power up the core */ + ret = snd_sof_dsp_core_power_up(sdev, BIT(core)); + if (ret < 0) { + dev_err(sdev->dev, "error: %d powering up core %d\n", + ret, core); + return ret; + } + + /* update enabled cores mask */ + sdev->enabled_cores_mask |= BIT(core); + + /* Now notify DSP that the core has been powered up */ + ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, + &pm_core_config, sizeof(pm_core_config), + &pm_core_config, sizeof(pm_core_config)); + if (ret < 0) + dev_err(sdev->dev, "error: core %d enable ipc failure %d\n", + core, ret); + + return ret; +} + +int sof_pipeline_core_enable(struct snd_sof_dev *sdev, + const struct snd_sof_widget *swidget) +{ + const struct sof_ipc_pipe_new *pipeline; + int ret; + + if (swidget->id == snd_soc_dapm_scheduler) { + pipeline = swidget->private; + } else { + pipeline = snd_sof_pipeline_find(sdev, swidget->pipeline_id); + if (!pipeline) + return -ENOENT; + } + + /* First enable the pipeline core */ + ret = sof_core_enable(sdev, pipeline->core); + if (ret < 0) + return ret; + + return sof_core_enable(sdev, swidget->core); +} + static int sof_connect_dai_widget(struct snd_soc_component *scomp, struct snd_soc_dapm_widget *w, struct snd_soc_tplg_dapm_widget *tw, @@ -1553,44 +1622,15 @@ int sof_load_pipeline_ipc(struct device *dev, struct sof_ipc_comp_reply *r) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct sof_ipc_pm_core_config pm_core_config; - int ret; + int ret = sof_core_enable(sdev, pipeline->core); - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), r, sizeof(*r)); - if (ret < 0) { - dev_err(dev, "error: load pipeline ipc failure\n"); - return ret; - } - - /* power up the core that this pipeline is scheduled on */ - ret = snd_sof_dsp_core_power_up(sdev, 1 << pipeline->core); - if (ret < 0) { - dev_err(dev, "error: powering up pipeline schedule core %d\n", - pipeline->core); + if (ret < 0) return ret; - } - /* update enabled cores mask */ - sdev->enabled_cores_mask |= 1 << pipeline->core; - - /* - * Now notify DSP that the core that this pipeline is scheduled on - * has been powered up - */ - memset(&pm_core_config, 0, sizeof(pm_core_config)); - pm_core_config.enable_mask = sdev->enabled_cores_mask; - - /* configure CORE_ENABLE ipc message */ - pm_core_config.hdr.size = sizeof(pm_core_config); - pm_core_config.hdr.cmd = SOF_IPC_GLB_PM_MSG | SOF_IPC_PM_CORE_ENABLE; - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, - &pm_core_config, sizeof(pm_core_config), - &pm_core_config, sizeof(pm_core_config)); + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), r, sizeof(*r)); if (ret < 0) - dev_err(dev, "error: core enable ipc failure\n"); + dev_err(dev, "error: load pipeline ipc failure\n"); return ret; } @@ -2316,6 +2356,26 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 ? tw->sname : "none"); + ret = sof_parse_tokens(scomp, &comp, core_tokens, + ARRAY_SIZE(core_tokens), tw->priv.array, + le32_to_cpu(tw->priv.size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parsing core tokens failed %d\n", + ret); + kfree(swidget); + return ret; + } + + swidget->core = comp.core; + + /* default is primary core, safe to call for already enabled cores */ + ret = sof_core_enable(sdev, comp.core); + if (ret < 0) { + dev_err(scomp->dev, "error: enable core: %d\n", ret); + kfree(swidget); + return ret; + } + /* handle any special case widgets */ switch (w->id) { case snd_soc_dapm_dai_in: -- cgit From 8c9ff1219aef657954540147522ceaecced71b2b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Wed, 2 Sep 2020 17:07:56 +0300 Subject: ASoC: SOF: topology: fix core enable sequence Core power up involves 2 steps: The first step tries to power up the core by setting the ADSPCS.SPA bit for the host-managed cores. The second step involves sending the IPC to power up other cores that are not host managed. The enabled_cores_mask should be updated only when both these steps are successful. If the IPC to the DSP fails, the host-managed core that was powered in step 1 should be powered off before returning the error. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Keyon Jie Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200902140756.1427005-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 46468fb7b6d1..d47da407a1bd 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1303,7 +1303,7 @@ static int sof_core_enable(struct snd_sof_dev *sdev, int core) if (sdev->enabled_cores_mask & BIT(core)) return 0; - /* power up the core */ + /* power up the core if it is host managed */ ret = snd_sof_dsp_core_power_up(sdev, BIT(core)); if (ret < 0) { dev_err(sdev->dev, "error: %d powering up core %d\n", @@ -1311,16 +1311,24 @@ static int sof_core_enable(struct snd_sof_dev *sdev, int core) return ret; } - /* update enabled cores mask */ - sdev->enabled_cores_mask |= BIT(core); - - /* Now notify DSP that the core has been powered up */ + /* Now notify DSP */ ret = sof_ipc_tx_message(sdev->ipc, pm_core_config.hdr.cmd, &pm_core_config, sizeof(pm_core_config), &pm_core_config, sizeof(pm_core_config)); - if (ret < 0) + if (ret < 0) { dev_err(sdev->dev, "error: core %d enable ipc failure %d\n", core, ret); + goto err; + } + + /* update enabled cores mask */ + sdev->enabled_cores_mask |= BIT(core); + + return ret; +err: + /* power down core if it is host managed and return the original error if this fails too */ + if (snd_sof_dsp_core_power_down(sdev, BIT(core)) < 0) + dev_err(sdev->dev, "error: powering down core %d\n", core); return ret; } -- cgit From ae3a3918edf57bde7651964be04d0807cccae8f2 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 5 Sep 2020 02:28:53 +0800 Subject: ASoC: Intel: sof_sdw: add dailink .trigger callback Add trigger functionality to dailink, so far only .startup() and .shutdown() were implemented at the machine driver level. The companion patch for this patch is the removal of the trigger callback at the DAI level in drivers/soundwire/intel.c Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200904182854.3944-3-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 41 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 00d10e83872a..985dbe1da7a2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -225,6 +225,46 @@ int sdw_startup(struct snd_pcm_substream *substream) return sdw_startup_stream(substream); } +static int sdw_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sdw_stream_runtime *sdw_stream; + struct snd_soc_dai *dai; + int ret; + + /* Find stream from first CPU DAI */ + dai = asoc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + + if (IS_ERR(sdw_stream)) { + dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + return PTR_ERR(sdw_stream); + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + ret = sdw_enable_stream(sdw_stream); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + ret = sdw_disable_stream(sdw_stream); + break; + default: + ret = -EINVAL; + break; + } + + if (ret) + dev_err(rtd->dev, "%s trigger %d failed: %d", __func__, cmd, ret); + + return ret; +} + void sdw_shutdown(struct snd_pcm_substream *substream) { sdw_shutdown_stream(substream); @@ -232,6 +272,7 @@ void sdw_shutdown(struct snd_pcm_substream *substream) static const struct snd_soc_ops sdw_ops = { .startup = sdw_startup, + .trigger = sdw_trigger, .shutdown = sdw_shutdown, }; -- cgit From 06998d49bcac8a9df3341db99c5f81ae4ef51c84 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Sat, 5 Sep 2020 02:28:54 +0800 Subject: ASoC: Intel: sof_sdw: add dailink .prepare and .hw_free callback Add .prepare and .hw_free callback to dailink. The companion patch for this patch is the removal of stream operations in the .prepare and .hw_free callbacks at the DAI level in drivers/soundwire/intel.c Signed-off-by: Pierre-Louis Bossart Reviewed-by: Rander Wang Signed-off-by: Bard Liao Link: https://lore.kernel.org/r/20200904182854.3944-4-yung-chuan.liao@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 985dbe1da7a2..210b66d1f9a2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -225,6 +225,25 @@ int sdw_startup(struct snd_pcm_substream *substream) return sdw_startup_stream(substream); } +static int sdw_prepare(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sdw_stream_runtime *sdw_stream; + struct snd_soc_dai *dai; + + /* Find stream from first CPU DAI */ + dai = asoc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + + if (IS_ERR(sdw_stream)) { + dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + return PTR_ERR(sdw_stream); + } + + return sdw_prepare_stream(sdw_stream); +} + static int sdw_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); @@ -265,6 +284,25 @@ static int sdw_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } +static int sdw_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sdw_stream_runtime *sdw_stream; + struct snd_soc_dai *dai; + + /* Find stream from first CPU DAI */ + dai = asoc_rtd_to_cpu(rtd, 0); + + sdw_stream = snd_soc_dai_get_sdw_stream(dai, substream->stream); + + if (IS_ERR(sdw_stream)) { + dev_err(rtd->dev, "no stream found for DAI %s", dai->name); + return PTR_ERR(sdw_stream); + } + + return sdw_deprepare_stream(sdw_stream); +} + void sdw_shutdown(struct snd_pcm_substream *substream) { sdw_shutdown_stream(substream); @@ -272,7 +310,9 @@ void sdw_shutdown(struct snd_pcm_substream *substream) static const struct snd_soc_ops sdw_ops = { .startup = sdw_startup, + .prepare = sdw_prepare, .trigger = sdw_trigger, + .hw_free = sdw_hw_free, .shutdown = sdw_shutdown, }; -- cgit From b8cbb1cab70342756725c1beded6b81031a95762 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:44 -0500 Subject: ASoC: sun8i-codec: Remove extraneous widgets This driver is for the digital part of the codec only. The analog part, including the microphone inputs, is managed by a separate driver. These widgets look like they were copied from sun4i-codec. Since they do not perform any function in this driver, remove them. Reviewed-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-2-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index e3a1347d7ecd..52ef0f9ec79e 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -455,10 +455,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL, SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0), - - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Mic", NULL), - }; static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { -- cgit From 2455e37adef39bf7fd12df963b86fa7f313f1ad4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:45 -0500 Subject: ASoC: sun8i-codec: Fix AIF1 MODCLK widget name The name should reference "AIF1", not "AFI1". Acked-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-3-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 52ef0f9ec79e..263c1e7c3cc2 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -431,7 +431,7 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { sun8i_input_mixer_controls), /* Clocks */ - SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA, + SND_SOC_DAPM_SUPPLY("MODCLK AIF1", SUN8I_MOD_CLK_ENA, SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA, SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), @@ -464,11 +464,11 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "SYSCLK", NULL, "AIF1 PLL" }, { "RST AIF1", NULL, "SYSCLK" }, - { "MODCLK AFI1", NULL, "RST AIF1" }, - { "AIF1 AD0L", NULL, "MODCLK AFI1" }, - { "AIF1 AD0R", NULL, "MODCLK AFI1" }, - { "AIF1 DA0L", NULL, "MODCLK AFI1" }, - { "AIF1 DA0R", NULL, "MODCLK AFI1" }, + { "MODCLK AIF1", NULL, "RST AIF1" }, + { "AIF1 AD0L", NULL, "MODCLK AIF1" }, + { "AIF1 AD0R", NULL, "MODCLK AIF1" }, + { "AIF1 DA0L", NULL, "MODCLK AIF1" }, + { "AIF1 DA0R", NULL, "MODCLK AIF1" }, { "RST DAC", NULL, "SYSCLK" }, { "MODCLK DAC", NULL, "RST DAC" }, -- cgit From fa5c0ca1f90aaadb6539ec6c407221f2ab7b7608 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:46 -0500 Subject: ASoC: sun8i-codec: Fix AIF1_ADCDAT_CTRL field names They are controlling "AD0" (AIF1 slot 0 ADC), not "DA0". Acked-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-4-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 263c1e7c3cc2..68c8edae9084 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -49,8 +49,8 @@ #define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_16 (1 << 4) #define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT 2 #define SUN8I_AIF1_ADCDAT_CTRL 0x044 -#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA 15 -#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA 14 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14 #define SUN8I_AIF1_DACDAT_CTRL 0x048 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 @@ -407,10 +407,10 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { /* AIF "ADC" Outputs */ SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0, SUN8I_AIF1_ADCDAT_CTRL, - SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0L_ENA, 0), + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0), SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0, SUN8I_AIF1_ADCDAT_CTRL, - SUN8I_AIF1_ADCDAT_CTRL_AIF1_DA0R_ENA, 0), + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), /* ADC Inputs (connected to analog codec DAPM context) */ SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0), -- cgit From 0ba95493023de45744962af41ef5ad90bad7d8bb Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:47 -0500 Subject: ASoC: sun8i-codec: Fix AIF1_MXR_SRC field names Even though they are for the left channel mixer, they are documented as "MXR_SRC". This matches the naming scheme used for the main DAC. The "R" is part of the abbreviation for "mixer", not a reference to the channel. Acked-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-5-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 68c8edae9084..def3a0059c3d 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -55,10 +55,10 @@ #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 #define SUN8I_AIF1_MXR_SRC 0x04c -#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L 15 -#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL 14 -#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL 13 -#define SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR 12 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L 15 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL 14 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL 13 +#define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR 12 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R 11 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR 10 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 @@ -374,18 +374,18 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = { SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF1DA0L, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACL, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0), SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_ADCL, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXL_SRC_AIF2DACR, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR, SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), }; -- cgit From 30aff91ec7840fb72daef7ce389a9414e5db4075 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:48 -0500 Subject: ASoC: sun8i-codec: Fix ADC_DIG_CTRL field name This is the enable bit for the "AD"C, not the "DA"C. Acked-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-6-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index def3a0059c3d..4218a00a9aba 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -64,7 +64,7 @@ #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR 9 #define SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL 8 #define SUN8I_ADC_DIG_CTRL 0x100 -#define SUN8I_ADC_DIG_CTRL_ENDA 15 +#define SUN8I_ADC_DIG_CTRL_ENAD 15 #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 #define SUN8I_DAC_DIG_CTRL 0x120 @@ -393,7 +393,7 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { /* Digital parts of the DACs and ADC */ SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENDA, + SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0), /* AIF "DAC" Inputs */ -- cgit From fcb7b39ee3d877e4eb79fb2abf15644d1b36285c Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:49 -0500 Subject: ASoC: sun8i-codec: Fix field bit number indentation Several fields have inconsistent indentation, presumably because the patch "looked correct" due to the additional "+" character at the beginning of the line. Acked-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-7-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 4218a00a9aba..62d4b1b44e76 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -68,15 +68,15 @@ #define SUN8I_ADC_DIG_CTRL_ADOUT_DTS 2 #define SUN8I_ADC_DIG_CTRL_ADOUT_DLY 1 #define SUN8I_DAC_DIG_CTRL 0x120 -#define SUN8I_DAC_DIG_CTRL_ENDA 15 +#define SUN8I_DAC_DIG_CTRL_ENDA 15 #define SUN8I_DAC_MXR_SRC 0x130 -#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 -#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 -#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL 13 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA0L 15 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF1DA1L 14 +#define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_AIF2DACL 13 #define SUN8I_DAC_MXR_SRC_DACL_MXR_SRC_ADCL 12 -#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R 11 -#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R 10 -#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA0R 11 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF1DA1R 10 +#define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR 8 #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12) -- cgit From f30ef55c332935c1d7c5f4ae3d084bec8d05712e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:50 -0500 Subject: ASoC: sun8i-codec: Sort masks in a consistent order All other definitions are sorted from largest to smallest bit number. This makes the AIF1CLK_CTRL mask constants consistent with them. Acked-by: Chen-Yu Tsai Signed-off-by: Samuel Holland Link: https://lore.kernel.org/r/20200831034852.18841-8-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 62d4b1b44e76..c25cdd3f3057 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -81,10 +81,10 @@ #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12) #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) -#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2) -#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4) -#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) +#define SUN8I_AIF1CLK_CTRL_AIF1_LRCK_DIV_MASK GENMASK(8, 6) +#define SUN8I_AIF1CLK_CTRL_AIF1_WORD_SIZ_MASK GENMASK(5, 4) +#define SUN8I_AIF1CLK_CTRL_AIF1_DATA_FMT_MASK GENMASK(3, 2) struct sun8i_codec_quirks { bool legacy_widgets : 1; -- cgit From efb736fb9eceac6ce335bbaa3d788a05649160b5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:51 -0500 Subject: ASoC: sun8i-codec: Attach the bus clock to the regmap When attached to the regmap, the bus clock is automatically enabled as needed to access device registers. This avoids needing code to manage it separately in the driver. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20200831034852.18841-9-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index c25cdd3f3057..8a7f98910347 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -94,7 +94,6 @@ struct sun8i_codec_quirks { struct sun8i_codec { struct regmap *regmap; struct clk *clk_module; - struct clk *clk_bus; const struct sun8i_codec_quirks *quirks; }; @@ -109,12 +108,6 @@ static int sun8i_codec_runtime_resume(struct device *dev) return ret; } - ret = clk_prepare_enable(scodec->clk_bus); - if (ret) { - dev_err(dev, "Failed to enable the bus clock\n"); - goto err_disable_modclk; - } - regcache_cache_only(scodec->regmap, false); ret = regcache_sync(scodec->regmap); @@ -126,9 +119,6 @@ static int sun8i_codec_runtime_resume(struct device *dev) return 0; err_disable_clk: - clk_disable_unprepare(scodec->clk_bus); - -err_disable_modclk: clk_disable_unprepare(scodec->clk_module); return ret; @@ -142,7 +132,6 @@ static int sun8i_codec_runtime_suspend(struct device *dev) regcache_mark_dirty(scodec->regmap); clk_disable_unprepare(scodec->clk_module); - clk_disable_unprepare(scodec->clk_bus); return 0; } @@ -612,20 +601,14 @@ static int sun8i_codec_probe(struct platform_device *pdev) return PTR_ERR(scodec->clk_module); } - scodec->clk_bus = devm_clk_get(&pdev->dev, "bus"); - if (IS_ERR(scodec->clk_bus)) { - dev_err(&pdev->dev, "Failed to get the bus clock\n"); - return PTR_ERR(scodec->clk_bus); - } - base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) { dev_err(&pdev->dev, "Failed to map the registers\n"); return PTR_ERR(base); } - scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base, - &sun8i_codec_regmap_config); + scodec->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "bus", base, + &sun8i_codec_regmap_config); if (IS_ERR(scodec->regmap)) { dev_err(&pdev->dev, "Failed to create our regmap\n"); return PTR_ERR(scodec->regmap); -- cgit From 6b3bb3c82b94521d6d61c1bf7c766c8c3bddacf5 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 30 Aug 2020 22:48:52 -0500 Subject: ASoC: sun8i-codec: Manage module clock via DAPM By representing the module clock as a DAPM widget, we ensure that the clock is only enabled when the module is actually in use, without additional code in runtime PM hooks. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20200831034852.18841-10-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 8a7f98910347..178f6fb31fd4 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -102,26 +102,15 @@ static int sun8i_codec_runtime_resume(struct device *dev) struct sun8i_codec *scodec = dev_get_drvdata(dev); int ret; - ret = clk_prepare_enable(scodec->clk_module); - if (ret) { - dev_err(dev, "Failed to enable the module clock\n"); - return ret; - } - regcache_cache_only(scodec->regmap, false); ret = regcache_sync(scodec->regmap); if (ret) { dev_err(dev, "Failed to sync regmap cache\n"); - goto err_disable_clk; + return ret; } return 0; - -err_disable_clk: - clk_disable_unprepare(scodec->clk_module); - - return ret; } static int sun8i_codec_runtime_suspend(struct device *dev) @@ -131,8 +120,6 @@ static int sun8i_codec_runtime_suspend(struct device *dev) regcache_cache_only(scodec->regmap, true); regcache_mark_dirty(scodec->regmap); - clk_disable_unprepare(scodec->clk_module); - return 0; } @@ -379,6 +366,8 @@ static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = { }; static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { + SND_SOC_DAPM_CLOCK_SUPPLY("mod"), + /* Digital parts of the DACs and ADC */ SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), @@ -448,6 +437,8 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { /* Clock Routes */ + { "AIF1", NULL, "mod" }, + { "AIF1", NULL, "SYSCLK AIF1" }, { "AIF1 PLL", NULL, "AIF1" }, { "SYSCLK", NULL, "AIF1 PLL" }, -- cgit From f970a77f1d064eeddc32a9ed0fd7db3a66d82fdd Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:30 +0300 Subject: ASoC: SOF: add comp_ext to struct snd_sof_widget Add member comp_ext to struct snd_sof_widget, which will be used for topology extended tokens parsing. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 7f8be8817e69..196cbd322893 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -89,6 +89,9 @@ struct snd_sof_widget { struct snd_soc_dapm_widget *widget; struct list_head list; /* list in sdev widget list */ + /* extended data for UUID components */ + struct sof_ipc_comp_ext comp_ext; + void *private; /* core does not touch this */ }; -- cgit From 92f500cfc329ee3082f20b2f1364788b759ab588 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:31 +0300 Subject: ASoC: SOF: topology: create component extended tokens Add comp_ext_tokens which will be used to parse all extended tokens, these tokens will be stored it to struct snd_sof_widget. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d47da407a1bd..0ba0c3921cf0 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -492,6 +492,16 @@ static int get_token_u16(void *elem, void *object, u32 offset, u32 size) return 0; } +static int get_token_uuid(void *elem, void *object, u32 offset, u32 size) +{ + struct snd_soc_tplg_vendor_uuid_elem *velem = elem; + u8 *dst = (u8 *)object + offset; + + memcpy(dst, velem->uuid, UUID_SIZE); + + return 0; +} + static int get_token_comp_format(void *elem, void *object, u32 offset, u32 size) { struct snd_soc_tplg_vendor_string_elem *velem = elem; @@ -725,6 +735,13 @@ static const struct sof_topology_token core_tokens[] = { offsetof(struct sof_ipc_comp, core), 0}, }; +/* Component extended tokens */ +static const struct sof_topology_token comp_ext_tokens[] = { + {SOF_TKN_COMP_UUID, + SND_SOC_TPLG_TUPLE_TYPE_UUID, get_token_uuid, + offsetof(struct sof_ipc_comp_ext, uuid), 0}, +}; + /* * DMIC PDM Tokens * SOF_TKN_INTEL_DMIC_PDM_CTRL_ID should be the first token -- cgit From 929e427a9c4e59d744fb5d3771b87a741ea3078a Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:32 +0300 Subject: ASoC: SOF: topology: parse comp_ext_tokens for all widgets Parse comp_ext_tokens in the common sof_widget_ready(), and the swidget->comp_ext will be used to construct the COMP_NEW ipc in the subsequent commits. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 0ba0c3921cf0..7d1013e315fc 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2401,6 +2401,16 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return ret; } + ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens, + ARRAY_SIZE(comp_ext_tokens), tw->priv.array, + le32_to_cpu(tw->priv.size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parsing comp_ext_tokens failed %d\n", + ret); + kfree(swidget); + return ret; + } + /* handle any special case widgets */ switch (w->id) { case snd_soc_dapm_dai_in: -- cgit From a905bb0193e7d637bc6b6c86e87f7f9976363d07 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:34 +0300 Subject: ASoC: SOF: topology: add helper for setting up IPC component Add helper to allocate buffer for IPC component, configure the basic settings, and set up the extended data for the subsequent IPC sending. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-7-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 7d1013e315fc..836da1a6f7cc 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1453,6 +1453,51 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, return 0; } +/** + * sof_comp_alloc - allocate and initialize buffer for a new component + * @swidget: pointer to struct snd_sof_widget containing extended data + * @ipc_size: IPC payload size that will be updated depending on valid + * extended data. + * @index: ID of the pipeline the component belongs to + * @core: index of the DSP core that the component should run on + * + * Return: The pointer to the new allocated component, NULL if failed. + */ +static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, + size_t *ipc_size, int index, + int core) +{ + u8 nil_uuid[SOF_UUID_SIZE] = {0}; + struct sof_ipc_comp *comp; + size_t total_size = *ipc_size; + + /* only non-zero UUID is valid */ + if (memcmp(&swidget->comp_ext, nil_uuid, SOF_UUID_SIZE)) + total_size += sizeof(swidget->comp_ext); + + comp = kzalloc(total_size, GFP_KERNEL); + if (!comp) + return NULL; + + /* configure comp new IPC message */ + comp->hdr.size = total_size; + comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; + comp->id = swidget->comp_id; + comp->pipeline_id = index; + comp->core = core; + + /* handle the extended data if needed */ + if (total_size > *ipc_size) { + /* append extended data to the end of the component */ + memcpy((u8 *)comp + *ipc_size, &swidget->comp_ext, sizeof(swidget->comp_ext)); + comp->ext_data_length = sizeof(swidget->comp_ext); + } + + /* update ipc_size and return */ + *ipc_size = total_size; + return comp; +} + static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, int core, struct snd_soc_tplg_dapm_widget *tw, -- cgit From f8ee6c9f5258ce0f7e342d7ecc63d81e45a607fe Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:35 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_dai Append the extended data to the end of the struct sof_ipc_comp_dai, and update the ext_data_offset, to construct the IPC for the topology load and runtime restore. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-8-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 23 ++++++++++++++++++----- sound/soc/sof/topology.c | 46 +++++++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index d05f99cd7919..cd506a4bfc4b 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -165,8 +165,9 @@ int sof_restore_pipelines(struct device *dev) struct snd_sof_route *sroute; struct sof_ipc_pipe_new *pipeline; struct snd_sof_dai *dai; - struct sof_ipc_comp_dai *comp_dai; struct sof_ipc_cmd_hdr *hdr; + struct sof_ipc_comp *comp; + size_t ipc_size; int ret; /* restore pipeline components */ @@ -189,12 +190,24 @@ int sof_restore_pipelines(struct device *dev) switch (swidget->id) { case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: + ipc_size = sizeof(struct sof_ipc_comp_dai) + + sizeof(struct sof_ipc_comp_ext); + comp = kzalloc(ipc_size, GFP_KERNEL); + if (!comp) + return -ENOMEM; + dai = swidget->private; - comp_dai = &dai->comp_dai; - ret = sof_ipc_tx_message(sdev->ipc, - comp_dai->comp.hdr.cmd, - comp_dai, sizeof(*comp_dai), + memcpy(comp, &dai->comp_dai, + sizeof(struct sof_ipc_comp_dai)); + + /* append extended data to the end of the component */ + memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), + &swidget->comp_ext, sizeof(swidget->comp_ext)); + + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, + comp, ipc_size, &r, sizeof(r)); + kfree(comp); break; case snd_soc_dapm_scheduler: diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 836da1a6f7cc..d8732cc5cdeb 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1506,49 +1506,57 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; - struct sof_ipc_comp_dai comp_dai; + struct sof_ipc_comp_dai *comp_dai; + size_t ipc_size = sizeof(*comp_dai); int ret; + comp_dai = (struct sof_ipc_comp_dai *) + sof_comp_alloc(swidget, &ipc_size, index, core); + if (!comp_dai) + return -ENOMEM; + /* configure dai IPC message */ - memset(&comp_dai, 0, sizeof(comp_dai)); - comp_dai.comp.hdr.size = sizeof(comp_dai); - comp_dai.comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - comp_dai.comp.id = swidget->comp_id; - comp_dai.comp.type = SOF_COMP_DAI; - comp_dai.comp.pipeline_id = index; - comp_dai.comp.core = core; - comp_dai.config.hdr.size = sizeof(comp_dai.config); - - ret = sof_parse_tokens(scomp, &comp_dai, dai_tokens, + comp_dai->comp.type = SOF_COMP_DAI; + comp_dai->config.hdr.size = sizeof(comp_dai->config); + + ret = sof_parse_tokens(scomp, comp_dai, dai_tokens, ARRAY_SIZE(dai_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(scomp->dev, "error: parse dai tokens failed %d\n", le32_to_cpu(private->size)); - return ret; + goto finish; } - ret = sof_parse_tokens(scomp, &comp_dai.config, comp_tokens, + ret = sof_parse_tokens(scomp, &comp_dai->config, comp_tokens, ARRAY_SIZE(comp_tokens), private->array, le32_to_cpu(private->size)); if (ret != 0) { dev_err(scomp->dev, "error: parse dai.cfg tokens failed %d\n", private->size); - return ret; + goto finish; } dev_dbg(scomp->dev, "dai %s: type %d index %d\n", - swidget->widget->name, comp_dai.type, comp_dai.dai_index); - sof_dbg_comp_config(scomp, &comp_dai.config); + swidget->widget->name, comp_dai->type, comp_dai->dai_index); + sof_dbg_comp_config(scomp, &comp_dai->config); - ret = sof_ipc_tx_message(sdev->ipc, comp_dai.comp.hdr.cmd, - &comp_dai, sizeof(comp_dai), r, sizeof(*r)); + ret = sof_ipc_tx_message(sdev->ipc, comp_dai->comp.hdr.cmd, + comp_dai, ipc_size, r, sizeof(*r)); if (ret == 0 && dai) { dai->scomp = scomp; - memcpy(&dai->comp_dai, &comp_dai, sizeof(comp_dai)); + + /* + * copy only the sof_ipc_comp_dai to avoid collapsing + * the snd_sof_dai, the extended data is kept in the + * snd_sof_widget. + */ + memcpy(&dai->comp_dai, comp_dai, sizeof(*comp_dai)); } +finish: + kfree(comp_dai); return ret; } -- cgit From f375bb336df3152360fbcdc4b7c49998d43abd0c Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:36 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_mixer Append the extended data to the end of the struct sof_ipc_comp_mixer, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-9-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d8732cc5cdeb..eaad7a60f4b2 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1783,19 +1783,16 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_mixer *mixer; + size_t ipc_size = sizeof(*mixer); int ret; - mixer = kzalloc(sizeof(*mixer), GFP_KERNEL); + mixer = (struct sof_ipc_comp_mixer *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!mixer) return -ENOMEM; /* configure mixer IPC message */ - mixer->comp.hdr.size = sizeof(*mixer); - mixer->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - mixer->comp.id = swidget->comp_id; mixer->comp.type = SOF_COMP_MIXER; - mixer->comp.pipeline_id = index; - mixer->comp.core = core; mixer->config.hdr.size = sizeof(mixer->config); ret = sof_parse_tokens(scomp, &mixer->config, comp_tokens, @@ -1813,7 +1810,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, swidget->private = mixer; ret = sof_ipc_tx_message(sdev->ipc, mixer->comp.hdr.cmd, mixer, - sizeof(*mixer), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret < 0) kfree(mixer); -- cgit From 9fed9d91c00e60cab61844efdb521f4ea5255da0 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:37 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_volume Append the extended data to the end of the struct sof_ipc_comp_volume, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-10-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index eaad7a60f4b2..c908dbbe284a 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1878,11 +1878,13 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_volume *volume; struct snd_sof_control *scontrol; + size_t ipc_size = sizeof(*volume); int min_step; int max_step; int ret; - volume = kzalloc(sizeof(*volume), GFP_KERNEL); + volume = (struct sof_ipc_comp_volume *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!volume) return -ENOMEM; @@ -1894,12 +1896,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, } /* configure volume IPC message */ - volume->comp.hdr.size = sizeof(*volume); - volume->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - volume->comp.id = swidget->comp_id; volume->comp.type = SOF_COMP_VOLUME; - volume->comp.pipeline_id = index; - volume->comp.core = core; volume->config.hdr.size = sizeof(volume->config); ret = sof_parse_tokens(scomp, volume, volume_tokens, @@ -1936,7 +1933,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, } ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume, - sizeof(*volume), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: -- cgit From bbc1364cdd323e6188371744f91d55152e79c14f Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:38 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_host Append the extended data to the end of the struct sof_ipc_comp_host, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-11-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c908dbbe284a..5cf4497a4338 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1644,19 +1644,16 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_host *host; + size_t ipc_size = sizeof(*host); int ret; - host = kzalloc(sizeof(*host), GFP_KERNEL); + host = (struct sof_ipc_comp_host *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!host) return -ENOMEM; /* configure host comp IPC message */ - host->comp.hdr.size = sizeof(*host); - host->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - host->comp.id = swidget->comp_id; host->comp.type = SOF_COMP_HOST; - host->comp.pipeline_id = index; - host->comp.core = core; host->direction = dir; host->config.hdr.size = sizeof(host->config); @@ -1684,7 +1681,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, swidget->private = host; ret = sof_ipc_tx_message(sdev->ipc, host->comp.hdr.cmd, host, - sizeof(*host), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: -- cgit From b64ce2c62ca3d751dc9407e61eef1ba66f6e18c8 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:39 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_src Append the extended data to the end of the struct sof_ipc_comp_src, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-12-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 5cf4497a4338..84f332de8b0d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1950,19 +1950,16 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_src *src; + size_t ipc_size = sizeof(*src); int ret; - src = kzalloc(sizeof(*src), GFP_KERNEL); + src = (struct sof_ipc_comp_src *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!src) return -ENOMEM; /* configure src IPC message */ - src->comp.hdr.size = sizeof(*src); - src->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - src->comp.id = swidget->comp_id; src->comp.type = SOF_COMP_SRC; - src->comp.pipeline_id = index; - src->comp.core = core; src->config.hdr.size = sizeof(src->config); ret = sof_parse_tokens(scomp, src, src_tokens, @@ -1990,7 +1987,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, swidget->private = src; ret = sof_ipc_tx_message(sdev->ipc, src->comp.hdr.cmd, src, - sizeof(*src), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: -- cgit From c7ded588468ada986f1f96645f7e84781e46fc87 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:40 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_asrc Append the extended data to the end of the struct sof_ipc_comp_asrc, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-13-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 84f332de8b0d..476f78839fa8 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2007,19 +2007,16 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_asrc *asrc; + size_t ipc_size = sizeof(*asrc); int ret; - asrc = kzalloc(sizeof(*asrc), GFP_KERNEL); + asrc = (struct sof_ipc_comp_asrc *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!asrc) return -ENOMEM; /* configure ASRC IPC message */ - asrc->comp.hdr.size = sizeof(*asrc); - asrc->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - asrc->comp.id = swidget->comp_id; asrc->comp.type = SOF_COMP_ASRC; - asrc->comp.pipeline_id = index; - asrc->comp.core = core; asrc->config.hdr.size = sizeof(asrc->config); ret = sof_parse_tokens(scomp, asrc, asrc_tokens, @@ -2049,7 +2046,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, swidget->private = asrc; ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc, - sizeof(*asrc), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: -- cgit From 3584ba4c78f719a081ba140db0662aacdd00190f Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:41 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_tone Append the extended data to the end of the struct sof_ipc_comp_tone, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-14-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 476f78839fa8..c61f46350def 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2066,19 +2066,16 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_tone *tone; + size_t ipc_size = sizeof(*tone); int ret; - tone = kzalloc(sizeof(*tone), GFP_KERNEL); + tone = (struct sof_ipc_comp_tone *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!tone) return -ENOMEM; /* configure siggen IPC message */ - tone->comp.hdr.size = sizeof(*tone); - tone->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - tone->comp.id = swidget->comp_id; tone->comp.type = SOF_COMP_TONE; - tone->comp.pipeline_id = index; - tone->comp.core = core; tone->config.hdr.size = sizeof(tone->config); ret = sof_parse_tokens(scomp, tone, tone_tokens, @@ -2106,7 +2103,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, swidget->private = tone; ret = sof_ipc_tx_message(sdev->ipc, tone->comp.hdr.cmd, tone, - sizeof(*tone), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret >= 0) return ret; err: -- cgit From 783898ce68de5261faa4697622f38df8b08251a7 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:42 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_process Append the extended data to the end of the struct sof_ipc_comp_process, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-15-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index c61f46350def..48ea3147124d 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2231,18 +2231,15 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, ipc_data_size = 0; } - process = kzalloc(ipc_size, GFP_KERNEL); + process = (struct sof_ipc_comp_process *) + sof_comp_alloc(swidget, &ipc_size, index, 0); if (!process) { ret = -ENOMEM; goto out; } /* configure iir IPC message */ - process->comp.hdr.size = ipc_size; - process->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - process->comp.id = swidget->comp_id; process->comp.type = type; - process->comp.pipeline_id = index; process->config.hdr.size = sizeof(process->config); ret = sof_parse_tokens(scomp, &process->config, comp_tokens, -- cgit From d2306f4ed181e3bfe91a96a360f9674613c036a6 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:43 +0300 Subject: ASoC: SOF: append extended data to sof_ipc_comp_mux Append the extended data to the end of the struct sof_ipc_comp_mux, construct the ipc for COMP_NEW during the topology load stage. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-16-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 48ea3147124d..69865df60033 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1825,19 +1825,16 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_mux *mux; + size_t ipc_size = sizeof(*mux); int ret; - mux = kzalloc(sizeof(*mux), GFP_KERNEL); + mux = (struct sof_ipc_comp_mux *) + sof_comp_alloc(swidget, &ipc_size, index, core); if (!mux) return -ENOMEM; /* configure mux IPC message */ - mux->comp.hdr.size = sizeof(*mux); - mux->comp.hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; - mux->comp.id = swidget->comp_id; mux->comp.type = SOF_COMP_MUX; - mux->comp.pipeline_id = index; - mux->comp.core = core; mux->config.hdr.size = sizeof(mux->config); ret = sof_parse_tokens(scomp, &mux->config, comp_tokens, @@ -1855,7 +1852,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, swidget->private = mux; ret = sof_ipc_tx_message(sdev->ipc, mux->comp.hdr.cmd, mux, - sizeof(*mux), r, sizeof(*r)); + ipc_size, r, sizeof(*r)); if (ret < 0) kfree(mux); -- cgit From 988d941882336b860d5fae1ee6f487eb110fe6d6 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Fri, 4 Sep 2020 16:27:44 +0300 Subject: ASoC: SOF: topology: make process type optional As components can be now identified with a UUID based mechanism, the process type is no longer required. For new DSP components, process and its component type can be set to SOF_PROCESS_NONE and SOF_COMP_NONE. Allow this combination in topology load, modify the load time check for process type to reflect this. Signed-off-by: Keyon Jie Reviewed-by: Ranjani Sridharan Reviewed-by: Curtis Malainey Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200904132744.1699575-17-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 69865df60033..49fae48961a9 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2197,12 +2197,6 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, int ret; int i; - if (type == SOF_COMP_NONE) { - dev_err(scomp->dev, "error: invalid process comp type %d\n", - type); - return -EINVAL; - } - /* allocate struct for widget control data sizes and types */ if (widget->num_kcontrols) { wdata = kcalloc(widget->num_kcontrols, -- cgit From b40f708deb951c3d46857fe5aed9cea1a506585b Mon Sep 17 00:00:00 2001 From: Michael Sit Wei Hong Date: Fri, 4 Sep 2020 10:09:04 +0800 Subject: ASoC: Intel: KeemBay: Fix warning potential ! vs ~ typo To set platform in slave mode setting the MASTER_MODE bit is not needed. Removing !MASTER_MODE conditional to avoid potential errors and warning. Signed-off-by: Michael Sit Wei Hong Reported-by: Dan Carpenter Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200904020904.19577-1-michael.wei.hong.sit@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/keembay/kmb_platform.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/keembay/kmb_platform.c b/sound/soc/intel/keembay/kmb_platform.c index ca25a6e40cc9..f54b710ee1c2 100644 --- a/sound/soc/intel/keembay/kmb_platform.c +++ b/sound/soc/intel/keembay/kmb_platform.c @@ -515,7 +515,7 @@ static int kmb_dai_hw_params(struct snd_pcm_substream *substream, write_val = ((config->chan_nr / 2) << TDM_CHANNEL_CONFIG_BIT) | (config->data_width << DATA_WIDTH_CONFIG_BIT) | - !MASTER_MODE | TDM_OPERATION; + TDM_OPERATION; writel(write_val, kmb_i2s->pss_base + I2S_GEN_CFG_0); break; -- cgit From 34ce41003bcbff8b20a30eb0938478b156e57a2d Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Tue, 8 Sep 2020 14:32:04 +0300 Subject: ASoC: ti: Kconfig: Allow the j721e machine driver to be used on K3 platform The initial machine driver supports only j721e-cpb and the ivi addon, but other EVMs for different K3 SoC can have similar audio setup which can be supported by the driver with small or no modification. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200908113204.12012-1-peter.ujfalusi@ti.com Signed-off-by: Mark Brown --- sound/soc/ti/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig index 1e6ab87e4460..9775393d46b6 100644 --- a/sound/soc/ti/Kconfig +++ b/sound/soc/ti/Kconfig @@ -221,7 +221,7 @@ config SND_SOC_DM365_VOICE_CODEC_MODULE config SND_SOC_J721E_EVM tristate "SoC Audio support for j721e EVM" - depends on ARCH_K3_J721E_SOC || COMPILE_TEST + depends on ARCH_K3 || COMPILE_TEST depends on I2C select SND_SOC_PCM3168A_I2C select SND_SOC_DAVINCI_MCASP -- cgit From 819b9f6002391925b53817ed96638bd40bd1d34f Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Tue, 8 Sep 2020 12:28:24 +0300 Subject: ASoC: topology: Add support for WO and RO TLV byte kcontrols This patch adds support for write-only and read-only TLV byte kcontrols by checking for appropriate get/put IO handlers. Signed-off-by: Dharageswari R Reviewed-by: Guennadi Liakhovetski Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200908092825.1813847-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index cee998671318..28faaf58326e 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -603,10 +603,11 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, sbe->get = ext_ops[i].get; } - if (sbe->put && sbe->get) - return 0; - else + if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) && !sbe->get) return -EINVAL; + if ((k->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) && !sbe->put) + return -EINVAL; + return 0; } /* try and map vendor specific kcontrol handlers first */ -- cgit From 783560d02dd61aee20d1d00c1c061bcafea30264 Mon Sep 17 00:00:00 2001 From: Dharageswari R Date: Tue, 8 Sep 2020 12:28:25 +0300 Subject: ASoC: SOF: Implement snd_sof_bytes_ext_volatile_get kcontrol IO This patch implements the snd_sof_bytes_ext_volatile_get() to read the actual parameters from DSP by sending the SOF_IPC_COMP_GET_DATA IPC for the kcontrol of type SOF_TPLG_KCTL_BYTES_VOLATILE_RO. Signed-off-by: Dharageswari R Reviewed-by: Guennadi Liakhovetski Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200908092825.1813847-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.h | 2 ++ sound/soc/sof/topology.c | 1 + 3 files changed, 61 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 186eea105bb1..d5e2966cafac 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -353,6 +353,64 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, return 0; } +int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, + unsigned int size) +{ + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; + struct snd_sof_control *scontrol = be->dobj.private; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc_ctrl_data *cdata = scontrol->control_data; + struct snd_ctl_tlv header; + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + size_t data_size; + int ret; + int err; + + ret = pm_runtime_get_sync(scomp->dev); + if (ret < 0) { + dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); + pm_runtime_put_noidle(scomp->dev); + return ret; + } + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + /* get all the component data from DSP */ + ret = snd_sof_ipc_set_get_comp_data(scontrol, SOF_IPC_COMP_GET_DATA, SOF_CTRL_TYPE_DATA_GET, + scontrol->cmd, false); + if (ret < 0) + goto out; + + /* check data size doesn't exceed max coming from topology */ + if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", + cdata->data->size, + be->max - sizeof(const struct sof_abi_hdr)); + ret = -EINVAL; + goto out; + } + + data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + + header.numid = scontrol->cmd; + header.length = data_size; + if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) { + ret = -EFAULT; + goto out; + } + + if (copy_to_user(tlvd->tlv, cdata->data, data_size)) + ret = -EFAULT; +out: + pm_runtime_mark_last_busy(scomp->dev); + err = pm_runtime_put_autosuspend(scomp->dev); + if (err < 0) + dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err); + + return ret; +} + int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, unsigned int size) diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 196cbd322893..9f645a2e5a6c 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -142,6 +142,8 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, unsigned int size); +int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, + unsigned int size); /* * Topology. diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 49fae48961a9..d5efac3af5c2 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -3688,6 +3688,7 @@ static const struct snd_soc_tplg_kcontrol_ops sof_io_ops[] = { /* vendor specific bytes ext handlers available for binding */ static const struct snd_soc_tplg_bytes_ext_ops sof_bytes_ext_ops[] = { {SOF_TPLG_KCTL_BYTES_ID, snd_sof_bytes_ext_get, snd_sof_bytes_ext_put}, + {SOF_TPLG_KCTL_BYTES_VOLATILE_RO, snd_sof_bytes_ext_volatile_get}, }; static struct snd_soc_tplg_ops sof_tplg_ops = { -- cgit From 6835302853169441069e11bc4642300c22009c2e Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 8 Sep 2020 15:00:44 +0800 Subject: ASoC: mt6359: fix failed to parse DT properties Mt6359 platform device is instantiated by mfd_add_devices(). In the case, dev->of_node is NULL so that mt6359_parse_dt() always fails to parse the desired DT properties. Gets the DT properties via dev->parent->of_node. Fixes: 8061734ab654 ("ASoC: mediatek: mt6359: add codec driver") Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20200908070044.1142644-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/codecs/mt6359.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index 72f05335b420..81aafb553bdd 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -2636,8 +2636,13 @@ static int mt6359_parse_dt(struct mt6359_priv *priv) { int ret; struct device *dev = priv->dev; + struct device_node *np; - ret = of_property_read_u32(dev->of_node, "mediatek,dmic-mode", + np = of_get_child_by_name(dev->parent->of_node, "mt6359codec"); + if (!np) + return -EINVAL; + + ret = of_property_read_u32(np, "mediatek,dmic-mode", &priv->dmic_one_wire_mode); if (ret) { dev_warn(priv->dev, "%s() failed to read dmic-mode\n", @@ -2645,7 +2650,7 @@ static int mt6359_parse_dt(struct mt6359_priv *priv) priv->dmic_one_wire_mode = 0; } - ret = of_property_read_u32(dev->of_node, "mediatek,mic-type-0", + ret = of_property_read_u32(np, "mediatek,mic-type-0", &priv->mux_select[MUX_MIC_TYPE_0]); if (ret) { dev_warn(priv->dev, "%s() failed to read mic-type-0\n", @@ -2653,7 +2658,7 @@ static int mt6359_parse_dt(struct mt6359_priv *priv) priv->mux_select[MUX_MIC_TYPE_0] = MIC_TYPE_MUX_IDLE; } - ret = of_property_read_u32(dev->of_node, "mediatek,mic-type-1", + ret = of_property_read_u32(np, "mediatek,mic-type-1", &priv->mux_select[MUX_MIC_TYPE_1]); if (ret) { dev_warn(priv->dev, "%s() failed to read mic-type-1\n", @@ -2661,7 +2666,7 @@ static int mt6359_parse_dt(struct mt6359_priv *priv) priv->mux_select[MUX_MIC_TYPE_1] = MIC_TYPE_MUX_IDLE; } - ret = of_property_read_u32(dev->of_node, "mediatek,mic-type-2", + ret = of_property_read_u32(np, "mediatek,mic-type-2", &priv->mux_select[MUX_MIC_TYPE_2]); if (ret) { dev_warn(priv->dev, "%s() failed to read mic-type-2\n", -- cgit From 1b839d3e15fd48e4278c83190725467713a5b3c6 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 26 Aug 2020 11:51:41 +0200 Subject: ASoC: qcom: common: Parse auxiliary devices from device tree In some cases we need to probe additional audio components that do not appear as part of the DAI links specified in the device tree. Examples for this are auxiliary devices such as analog amplifiers or codecs. The ASoC core provides a way to probe these components by adding them to snd_soc_card->aux_dev. We can use the snd_soc_of_parse_aux_devs() function to parse them from the device tree. As an example for this, some MSM8916 smartphones have an analog speaker amplifier connected to the HPHR output. With the new property this can be modelled as follows: speaker-amp: audio-amplifier { compatible = "simple-audio-amplifier"; enable-gpios = <&msmgpio 114 GPIO_ACTIVE_HIGH>; sound-name-prefix = "Speaker Amp"; }; &sound { aux-devs = <&speaker_amp>; audio-routing = "Speaker Amp IN", "HPHR"; }; Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20200826095141.94017-3-stephan@gerhold.net Signed-off-by: Mark Brown --- sound/soc/qcom/common.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 5194d90ddb96..fe6e778c31c0 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -39,6 +39,10 @@ int qcom_snd_parse_of(struct snd_soc_card *card) return ret; } + ret = snd_soc_of_parse_aux_devs(card, "aux-devs"); + if (ret) + return ret; + /* Populate links */ num_links = of_get_child_count(dev->of_node); -- cgit From e525db7e4b44c5b2b5aac0dad24e23cb58c54d22 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Tue, 25 Aug 2020 21:02:24 +0800 Subject: ASoC: fsl: imx-es8328: add missing put_device() call in imx_es8328_probe() if of_find_device_by_node() succeed, imx_es8328_probe() doesn't have a corresponding put_device(). Thus add a jump target to fix the exception handling for this function implementation. Fixes: 7e7292dba215 ("ASoC: fsl: add imx-es8328 machine driver") Signed-off-by: Yu Kuai Link: https://lore.kernel.org/r/20200825130224.1488694-1-yukuai3@huawei.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-es8328.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/imx-es8328.c b/sound/soc/fsl/imx-es8328.c index 15a27a2cd0ca..fad1eb6253d5 100644 --- a/sound/soc/fsl/imx-es8328.c +++ b/sound/soc/fsl/imx-es8328.c @@ -145,13 +145,13 @@ static int imx_es8328_probe(struct platform_device *pdev) data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; - goto fail; + goto put_device; } comp = devm_kzalloc(dev, 3 * sizeof(*comp), GFP_KERNEL); if (!comp) { ret = -ENOMEM; - goto fail; + goto put_device; } data->dev = dev; @@ -182,12 +182,12 @@ static int imx_es8328_probe(struct platform_device *pdev) ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) { dev_err(dev, "Unable to parse card name\n"); - goto fail; + goto put_device; } ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) { dev_err(dev, "Unable to parse routing: %d\n", ret); - goto fail; + goto put_device; } data->card.num_links = 1; data->card.owner = THIS_MODULE; @@ -196,10 +196,12 @@ static int imx_es8328_probe(struct platform_device *pdev) ret = snd_soc_register_card(&data->card); if (ret) { dev_err(dev, "Unable to register: %d\n", ret); - goto fail; + goto put_device; } platform_set_drvdata(pdev, data); +put_device: + put_device(&ssi_pdev->dev); fail: of_node_put(ssi_np); of_node_put(codec_np); -- cgit From a3d1f931ea4af3a9cae0098a957ce55293ce9ab6 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:47:47 +0200 Subject: ASoC: fsl: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ASoC FSL ESAI CPU DAI driver, a tasklet is still used for offloading the hardware reset function. It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in fsl esai driver with a simple work. The conversion is fairly straightforward. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20200903104749.21435-2-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_esai.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c index 79b861afd986..39637ca78cdb 100644 --- a/sound/soc/fsl/fsl_esai.c +++ b/sound/soc/fsl/fsl_esai.c @@ -41,7 +41,7 @@ struct fsl_esai_soc_data { * @extalclk: esai clock source to derive HCK, SCK and FS * @fsysclk: system clock source to derive HCK, SCK and FS * @spbaclk: SPBA clock (optional, depending on SoC design) - * @task: tasklet to handle the reset operation + * @work: work to handle the reset operation * @soc: soc specific data * @lock: spin lock between hw_reset() and trigger() * @fifo_depth: depth of tx/rx FIFO @@ -67,7 +67,7 @@ struct fsl_esai { struct clk *extalclk; struct clk *fsysclk; struct clk *spbaclk; - struct tasklet_struct task; + struct work_struct work; const struct fsl_esai_soc_data *soc; spinlock_t lock; /* Protect hw_reset and trigger */ u32 fifo_depth; @@ -117,7 +117,7 @@ static irqreturn_t esai_isr(int irq, void *devid) ESAI_xCR_xEIE_MASK, 0); regmap_update_bits(esai_priv->regmap, REG_ESAI_RCR, ESAI_xCR_xEIE_MASK, 0); - tasklet_schedule(&esai_priv->task); + schedule_work(&esai_priv->work); } if (esr & ESAI_ESR_TINIT_MASK) @@ -708,9 +708,9 @@ static void fsl_esai_trigger_stop(struct fsl_esai *esai_priv, bool tx) ESAI_xFCR_xFR, 0); } -static void fsl_esai_hw_reset(struct tasklet_struct *t) +static void fsl_esai_hw_reset(struct work_struct *work) { - struct fsl_esai *esai_priv = from_tasklet(esai_priv, t, task); + struct fsl_esai *esai_priv = container_of(work, struct fsl_esai, work); bool tx = true, rx = false, enabled[2]; unsigned long lock_flags; u32 tfcr, rfcr; @@ -1070,7 +1070,7 @@ static int fsl_esai_probe(struct platform_device *pdev) return ret; } - tasklet_setup(&esai_priv->task, fsl_esai_hw_reset); + INIT_WORK(&esai_priv->work, fsl_esai_hw_reset); pm_runtime_enable(&pdev->dev); @@ -1088,7 +1088,7 @@ static int fsl_esai_remove(struct platform_device *pdev) struct fsl_esai *esai_priv = platform_get_drvdata(pdev); pm_runtime_disable(&pdev->dev); - tasklet_kill(&esai_priv->task); + cancel_work_sync(&esai_priv->work); return 0; } -- cgit From d668e640d50a981e35ccf0c87d2742b0ad26fe0c Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:47:48 +0200 Subject: ASoC: sh: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ASoC SH SIU driver, a tasklet is still used for offloading the hardware reset function. It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in SH SIU driver with a simple work. The conversion is fairly straightforward. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20200903104749.21435-3-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/sh/siu.h | 2 +- sound/soc/sh/siu_pcm.c | 21 +++++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/sh/siu.h b/sound/soc/sh/siu.h index 63a508fdfe78..6201840f1bc0 100644 --- a/sound/soc/sh/siu.h +++ b/sound/soc/sh/siu.h @@ -96,7 +96,7 @@ struct siu_info { }; struct siu_stream { - struct tasklet_struct tasklet; + struct work_struct work; struct snd_pcm_substream *substream; snd_pcm_format_t format; size_t buf_bytes; diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c index 50fc7810723e..45c4320976ab 100644 --- a/sound/soc/sh/siu_pcm.c +++ b/sound/soc/sh/siu_pcm.c @@ -70,7 +70,7 @@ static int siu_pcm_stmwrite_start(struct siu_port *port_info) siu_stream->rw_flg = RWF_STM_WT; /* DMA transfer start */ - tasklet_schedule(&siu_stream->tasklet); + queue_work(system_highpri_wq, &siu_stream->work); return 0; } @@ -93,7 +93,7 @@ static void siu_dma_tx_complete(void *arg) siu_stream->cur_period * siu_stream->period_bytes, siu_stream->buf_bytes, siu_stream->cookie); - tasklet_schedule(&siu_stream->tasklet); + queue_work(system_highpri_wq, &siu_stream->work); /* Notify alsa: a period is done */ snd_pcm_period_elapsed(siu_stream->substream); @@ -198,9 +198,10 @@ static int siu_pcm_rd_set(struct siu_port *port_info, return 0; } -static void siu_io_tasklet(struct tasklet_struct *t) +static void siu_io_work(struct work_struct *work) { - struct siu_stream *siu_stream = from_tasklet(siu_stream, t, tasklet); + struct siu_stream *siu_stream = container_of(work, struct siu_stream, + work); struct snd_pcm_substream *substream = siu_stream->substream; struct device *dev = substream->pcm->card->dev; struct snd_pcm_runtime *rt = substream->runtime; @@ -253,7 +254,7 @@ static int siu_pcm_stmread_start(struct siu_port *port_info) /* during stmread flag set */ siu_stream->rw_flg = RWF_STM_RD; - tasklet_schedule(&siu_stream->tasklet); + queue_work(system_highpri_wq, &siu_stream->work); return 0; } @@ -519,9 +520,9 @@ static int siu_pcm_new(struct snd_soc_component *component, (*port_info)->pcm = pcm; - /* IO tasklets */ - tasklet_setup(&(*port_info)->playback.tasklet, siu_io_tasklet); - tasklet_setup(&(*port_info)->capture.tasklet, siu_io_tasklet); + /* IO works */ + INIT_WORK(&(*port_info)->playback.work, siu_io_work); + INIT_WORK(&(*port_info)->capture.work, siu_io_work); } dev_info(card->dev, "SuperH SIU driver initialized.\n"); @@ -534,8 +535,8 @@ static void siu_pcm_free(struct snd_soc_component *component, struct platform_device *pdev = to_platform_device(pcm->card->dev); struct siu_port *port_info = siu_ports[pdev->id]; - tasklet_kill(&port_info->capture.tasklet); - tasklet_kill(&port_info->playback.tasklet); + cancel_work_sync(&port_info->capture.work); + cancel_work_sync(&port_info->playback.work); siu_free_port(port_info); -- cgit From dd8c0c0b37f1692a1202ea2c6f9d43aa0485faac Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:47:49 +0200 Subject: ASoC: txx9: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ASoC TXx9 ACLC driver, a tasklet is still used for offloading the hardware reset function. It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in TXx9 ACLC driver with a simple work. The conversion is fairly straightforward. Signed-off-by: Takashi Iwai Link: https://lore.kernel.org/r/20200903104749.21435-4-tiwai@suse.de Signed-off-by: Mark Brown --- sound/soc/txx9/txx9aclc.c | 11 ++++++----- sound/soc/txx9/txx9aclc.h | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c index 939b33ec39f5..1d2d0d9b57b0 100644 --- a/sound/soc/txx9/txx9aclc.c +++ b/sound/soc/txx9/txx9aclc.c @@ -102,7 +102,7 @@ static void txx9aclc_dma_complete(void *arg) if (dmadata->frag_count >= 0) { dmadata->dmacount--; if (!WARN_ON(dmadata->dmacount < 0)) - tasklet_schedule(&dmadata->tasklet); + queue_work(system_highpri_wq, &dmadata->work); } spin_unlock_irqrestore(&dmadata->dma_lock, flags); } @@ -134,9 +134,10 @@ txx9aclc_dma_submit(struct txx9aclc_dmadata *dmadata, dma_addr_t buf_dma_addr) #define NR_DMA_CHAIN 2 -static void txx9aclc_dma_tasklet(struct tasklet_struct *t) +static void txx9aclc_dma_work(struct work_struct *work) { - struct txx9aclc_dmadata *dmadata = from_tasklet(dmadata, t, tasklet); + struct txx9aclc_dmadata *dmadata = + container_of(work, struct txx9aclc_dmadata, work); struct dma_chan *chan = dmadata->dma_chan; struct dma_async_tx_descriptor *desc; struct snd_pcm_substream *substream = dmadata->substream; @@ -208,7 +209,7 @@ static int txx9aclc_pcm_trigger(struct snd_soc_component *component, switch (cmd) { case SNDRV_PCM_TRIGGER_START: dmadata->frag_count = -1; - tasklet_schedule(&dmadata->tasklet); + queue_work(system_highpri_wq, &dmadata->work); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: @@ -352,7 +353,7 @@ static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev, "playback" : "capture"); return -EBUSY; } - tasklet_setup(&dmadata->tasklet, txx9aclc_dma_tasklet); + INIT_WORK(&dmadata->work, txx9aclc_dma_work); return 0; } diff --git a/sound/soc/txx9/txx9aclc.h b/sound/soc/txx9/txx9aclc.h index 7b3d57e8e546..37c691ba56ed 100644 --- a/sound/soc/txx9/txx9aclc.h +++ b/sound/soc/txx9/txx9aclc.h @@ -43,7 +43,7 @@ struct txx9aclc_dmadata { struct resource *dma_res; struct txx9dmac_slave dma_slave; struct dma_chan *dma_chan; - struct tasklet_struct tasklet; + struct work_struct work; spinlock_t dma_lock; int stream; /* SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE */ struct snd_pcm_substream *substream; -- cgit From 06ca24e98e6bcc17c32ebe4b2fc579e5bf9ff5b2 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Wed, 9 Sep 2020 17:53:48 +0300 Subject: ASoC: mchp-spdiftx: add driver for S/PDIF TX Controller The new SPDIF TX controller is a serial port compliant with the IEC- 60958 standard. It also supports programmable User Data and Channel Status fields. This IP is embedded in Microchip's sama7g5 SoC. Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20200909145348.367033-2-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/Kconfig | 12 + sound/soc/atmel/Makefile | 2 + sound/soc/atmel/mchp-spdiftx.c | 871 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 885 insertions(+) create mode 100644 sound/soc/atmel/mchp-spdiftx.c (limited to 'sound') diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 71f2d42188c4..93beb7d670a3 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -132,4 +132,16 @@ config SND_MCHP_SOC_I2S_MCC and supports a Time Division Multiplexed (TDM) interface with external multi-channel audio codecs. +config SND_MCHP_SOC_SPDIFTX + tristate "Microchip ASoC driver for boards using S/PDIF TX" + depends on OF && (ARCH_AT91 || COMPILE_TEST) + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M if you want to add support for Microchip S/PDIF TX ASoc + driver on the following Microchip platforms: + - sama7g5 + + This S/PDIF TX driver is compliant with IEC-60958 standard and + includes programable User Data and Channel Status fields. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index c7d2989791be..3fd89a0063df 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -5,6 +5,7 @@ snd-soc-atmel-pcm-dma-objs := atmel-pcm-dma.o snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o snd-soc-atmel-i2s-objs := atmel-i2s.o snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o +snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o # pdc and dma need to both be built-in if any user of # ssc is built-in. @@ -17,6 +18,7 @@ endif obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o +obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c new file mode 100644 index 000000000000..36c23eb3a5ad --- /dev/null +++ b/sound/soc/atmel/mchp-spdiftx.c @@ -0,0 +1,871 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for Microchip S/PDIF TX Controller +// +// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu + +#include +#include +#include +#include + +#include +#include +#include +#include + +/* + * ---- S/PDIF Transmitter Controller Register map ---- + */ +#define SPDIFTX_CR 0x00 /* Control Register */ +#define SPDIFTX_MR 0x04 /* Mode Register */ +#define SPDIFTX_CDR 0x0C /* Common Data Register */ + +#define SPDIFTX_IER 0x14 /* Interrupt Enable Register */ +#define SPDIFTX_IDR 0x18 /* Interrupt Disable Register */ +#define SPDIFTX_IMR 0x1C /* Interrupt Mask Register */ +#define SPDIFTX_ISR 0x20 /* Interrupt Status Register */ + +#define SPDIFTX_CH1UD(reg) (0x50 + (reg) * 4) /* User Data 1 Register x */ +#define SPDIFTX_CH1S(reg) (0x80 + (reg) * 4) /* Channel Status 1 Register x */ + +#define SPDIFTX_VERSION 0xF0 + +/* + * ---- Control Register (Write-only) ---- + */ +#define SPDIFTX_CR_SWRST BIT(0) /* Software Reset */ +#define SPDIFTX_CR_FCLR BIT(1) /* FIFO clear */ + +/* + * ---- Mode Register (Read/Write) ---- + */ +/* Transmit Enable */ +#define SPDIFTX_MR_TXEN_MASK GENMASK(0, 0) +#define SPDIFTX_MR_TXEN_DISABLE (0 << 0) +#define SPDIFTX_MR_TXEN_ENABLE (1 << 0) + +/* Multichannel Transfer */ +#define SPDIFTX_MR_MULTICH_MASK GENAMSK(1, 1) +#define SPDIFTX_MR_MULTICH_MONO (0 << 1) +#define SPDIFTX_MR_MULTICH_DUAL (1 << 1) + +/* Data Word Endian Mode */ +#define SPDIFTX_MR_ENDIAN_MASK GENMASK(2, 2) +#define SPDIFTX_MR_ENDIAN_LITTLE (0 << 2) +#define SPDIFTX_MR_ENDIAN_BIG (1 << 2) + +/* Data Justification */ +#define SPDIFTX_MR_JUSTIFY_MASK GENMASK(3, 3) +#define SPDIFTX_MR_JUSTIFY_LSB (0 << 3) +#define SPDIFTX_MR_JUSTIFY_MSB (1 << 3) + +/* Common Audio Register Transfer Mode */ +#define SPDIFTX_MR_CMODE_MASK GENMASK(5, 4) +#define SPDIFTX_MR_CMODE_INDEX_ACCESS (0 << 4) +#define SPDIFTX_MR_CMODE_TOGGLE_ACCESS (1 << 4) +#define SPDIFTX_MR_CMODE_INTERLVD_ACCESS (2 << 4) + +/* Valid Bits per Sample */ +#define SPDIFTX_MR_VBPS_MASK GENMASK(13, 8) +#define SPDIFTX_MR_VBPS(bps) (((bps) << 8) & SPDIFTX_MR_VBPS_MASK) + +/* Chunk Size */ +#define SPDIFTX_MR_CHUNK_MASK GENMASK(19, 16) +#define SPDIFTX_MR_CHUNK(size) (((size) << 16) & SPDIFTX_MR_CHUNK_MASK) + +/* Validity Bits for Channels 1 and 2 */ +#define SPDIFTX_MR_VALID1 BIT(24) +#define SPDIFTX_MR_VALID2 BIT(25) + +/* Disable Null Frame on underrrun */ +#define SPDIFTX_MR_DNFR_MASK GENMASK(27, 27) +#define SPDIFTX_MR_DNFR_INVALID (0 << 27) +#define SPDIFTX_MR_DNFR_VALID (1 << 27) + +/* Bytes per Sample */ +#define SPDIFTX_MR_BPS_MASK GENMASK(29, 28) +#define SPDIFTX_MR_BPS(bytes) \ + ((((bytes) - 1) << 28) & SPDIFTX_MR_BPS_MASK) + +/* + * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- + */ +#define SPDIFTX_IR_TXRDY BIT(0) +#define SPDIFTX_IR_TXEMPTY BIT(1) +#define SPDIFTX_IR_TXFULL BIT(2) +#define SPDIFTX_IR_TXCHUNK BIT(3) +#define SPDIFTX_IR_TXUDR BIT(4) +#define SPDIFTX_IR_TXOVR BIT(5) +#define SPDIFTX_IR_CSRDY BIT(6) +#define SPDIFTX_IR_UDRDY BIT(7) +#define SPDIFTX_IR_TXRDYCH(ch) BIT((ch) + 8) +#define SPDIFTX_IR_SECE BIT(10) +#define SPDIFTX_IR_TXUDRCH(ch) BIT((ch) + 11) +#define SPDIFTX_IR_BEND BIT(13) + +static bool mchp_spdiftx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFTX_MR: + case SPDIFTX_IMR: + case SPDIFTX_ISR: + case SPDIFTX_CH1UD(0): + case SPDIFTX_CH1UD(1): + case SPDIFTX_CH1UD(2): + case SPDIFTX_CH1UD(3): + case SPDIFTX_CH1UD(4): + case SPDIFTX_CH1UD(5): + case SPDIFTX_CH1S(0): + case SPDIFTX_CH1S(1): + case SPDIFTX_CH1S(2): + case SPDIFTX_CH1S(3): + case SPDIFTX_CH1S(4): + case SPDIFTX_CH1S(5): + return true; + default: + return false; + } +} + +static bool mchp_spdiftx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFTX_CR: + case SPDIFTX_MR: + case SPDIFTX_CDR: + case SPDIFTX_IER: + case SPDIFTX_IDR: + case SPDIFTX_CH1UD(0): + case SPDIFTX_CH1UD(1): + case SPDIFTX_CH1UD(2): + case SPDIFTX_CH1UD(3): + case SPDIFTX_CH1UD(4): + case SPDIFTX_CH1UD(5): + case SPDIFTX_CH1S(0): + case SPDIFTX_CH1S(1): + case SPDIFTX_CH1S(2): + case SPDIFTX_CH1S(3): + case SPDIFTX_CH1S(4): + case SPDIFTX_CH1S(5): + return true; + default: + return false; + } +} + +static bool mchp_spdiftx_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFTX_CDR: + case SPDIFTX_ISR: + return true; + default: + return false; + } +} + +static const struct regmap_config mchp_spdiftx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SPDIFTX_VERSION, + .readable_reg = mchp_spdiftx_readable_reg, + .writeable_reg = mchp_spdiftx_writeable_reg, + .precious_reg = mchp_spdiftx_precious_reg, +}; + +#define SPDIFTX_GCLK_RATIO 128 + +#define SPDIFTX_CS_BITS 192 +#define SPDIFTX_UD_BITS 192 + +struct mchp_spdiftx_mixer_control { + unsigned char ch_stat[SPDIFTX_CS_BITS / 8]; + unsigned char user_data[SPDIFTX_UD_BITS / 8]; + spinlock_t lock; /* exclusive access to control data */ +}; + +struct mchp_spdiftx_dev { + struct mchp_spdiftx_mixer_control control; + struct snd_dmaengine_dai_dma_data playback; + struct device *dev; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + unsigned int fmt; + const struct mchp_i2s_caps *caps; + int gclk_enabled:1; +}; + +static inline int mchp_spdiftx_is_running(struct mchp_spdiftx_dev *dev) +{ + u32 mr; + + regmap_read(dev->regmap, SPDIFTX_MR, &mr); + return !!(mr & SPDIFTX_MR_TXEN_ENABLE); +} + +static void mchp_spdiftx_channel_status_write(struct mchp_spdiftx_dev *dev) +{ + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + u32 val; + int i; + + for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat) / 4; i++) { + val = (ctrl->ch_stat[(i * 4) + 0] << 0) | + (ctrl->ch_stat[(i * 4) + 1] << 8) | + (ctrl->ch_stat[(i * 4) + 2] << 16) | + (ctrl->ch_stat[(i * 4) + 3] << 24); + + regmap_write(dev->regmap, SPDIFTX_CH1S(i), val); + } +} + +static void mchp_spdiftx_user_data_write(struct mchp_spdiftx_dev *dev) +{ + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + u32 val; + int i; + + for (i = 0; i < ARRAY_SIZE(ctrl->user_data) / 4; i++) { + val = (ctrl->user_data[(i * 4) + 0] << 0) | + (ctrl->user_data[(i * 4) + 1] << 8) | + (ctrl->user_data[(i * 4) + 2] << 16) | + (ctrl->user_data[(i * 4) + 3] << 24); + + regmap_write(dev->regmap, SPDIFTX_CH1UD(i), val); + } +} + +static irqreturn_t mchp_spdiftx_interrupt(int irq, void *dev_id) +{ + struct mchp_spdiftx_dev *dev = dev_id; + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + u32 sr, imr, pending, idr = 0; + + regmap_read(dev->regmap, SPDIFTX_ISR, &sr); + regmap_read(dev->regmap, SPDIFTX_IMR, &imr); + pending = sr & imr; + + if (!pending) + return IRQ_NONE; + + if (pending & SPDIFTX_IR_TXUDR) { + dev_warn(dev->dev, "underflow detected\n"); + idr |= SPDIFTX_IR_TXUDR; + } + + if (pending & SPDIFTX_IR_TXOVR) { + dev_warn(dev->dev, "overflow detected\n"); + idr |= SPDIFTX_IR_TXOVR; + } + + if (pending & SPDIFTX_IR_UDRDY) { + spin_lock(&ctrl->lock); + mchp_spdiftx_user_data_write(dev); + spin_unlock(&ctrl->lock); + idr |= SPDIFTX_IR_UDRDY; + } + + if (pending & SPDIFTX_IR_CSRDY) { + spin_lock(&ctrl->lock); + mchp_spdiftx_channel_status_write(dev); + spin_unlock(&ctrl->lock); + idr |= SPDIFTX_IR_CSRDY; + } + + regmap_write(dev->regmap, SPDIFTX_IDR, idr); + + return IRQ_HANDLED; +} + +static int mchp_spdiftx_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + + /* Software reset the IP */ + regmap_write(dev->regmap, SPDIFTX_CR, + SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); + + return 0; +} + +static void mchp_spdiftx_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + + /* Disable interrupts */ + regmap_write(dev->regmap, SPDIFTX_IDR, 0xffffffff); +} + +static int mchp_spdiftx_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + u32 mr; + int running; + int ret; + + /* do not start/stop while channel status or user data is updated */ + spin_lock(&ctrl->lock); + regmap_read(dev->regmap, SPDIFTX_MR, &mr); + running = !!(mr & SPDIFTX_MR_TXEN_ENABLE); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!running) { + mr &= ~SPDIFTX_MR_TXEN_MASK; + mr |= SPDIFTX_MR_TXEN_ENABLE; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (running) { + mr &= ~SPDIFTX_MR_TXEN_MASK; + mr |= SPDIFTX_MR_TXEN_DISABLE; + } + break; + default: + spin_unlock(&ctrl->lock); + return -EINVAL; + } + + ret = regmap_write(dev->regmap, SPDIFTX_MR, mr); + spin_unlock(&ctrl->lock); + if (ret) { + dev_err(dev->dev, "unable to disable TX: %d\n", ret); + return ret; + } + + return 0; +} + +static int mchp_spdiftx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + unsigned long flags; + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + u32 mr; + unsigned int bps = params_physical_width(params) / 8; + int ret; + + dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + __func__, params_rate(params), params_format(params), + params_width(params), params_channels(params)); + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + dev_err(dev->dev, "Capture is not supported\n"); + return -EINVAL; + } + + regmap_read(dev->regmap, SPDIFTX_MR, &mr); + + if (mr & SPDIFTX_MR_TXEN_ENABLE) { + dev_err(dev->dev, "PCM already running\n"); + return -EBUSY; + } + + /* Defaults: Toggle mode, justify to LSB, chunksize 1 */ + mr = SPDIFTX_MR_CMODE_TOGGLE_ACCESS | SPDIFTX_MR_JUSTIFY_LSB; + dev->playback.maxburst = 1; + switch (params_channels(params)) { + case 1: + mr |= SPDIFTX_MR_MULTICH_MONO; + break; + case 2: + mr |= SPDIFTX_MR_MULTICH_DUAL; + if (bps > 2) + dev->playback.maxburst = 2; + break; + default: + dev_err(dev->dev, "unsupported number of channels: %d\n", + params_channels(params)); + return -EINVAL; + } + mr |= SPDIFTX_MR_CHUNK(dev->playback.maxburst); + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + mr |= SPDIFTX_MR_VBPS(8); + break; + case SNDRV_PCM_FORMAT_S16_BE: + mr |= SPDIFTX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S16_LE: + mr |= SPDIFTX_MR_VBPS(16); + break; + case SNDRV_PCM_FORMAT_S18_3BE: + mr |= SPDIFTX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S18_3LE: + mr |= SPDIFTX_MR_VBPS(18); + break; + case SNDRV_PCM_FORMAT_S20_3BE: + mr |= SPDIFTX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S20_3LE: + mr |= SPDIFTX_MR_VBPS(20); + break; + case SNDRV_PCM_FORMAT_S24_3BE: + mr |= SPDIFTX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S24_3LE: + mr |= SPDIFTX_MR_VBPS(24); + break; + case SNDRV_PCM_FORMAT_S24_BE: + mr |= SPDIFTX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S24_LE: + mr |= SPDIFTX_MR_VBPS(24); + break; + case SNDRV_PCM_FORMAT_S32_BE: + mr |= SPDIFTX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S32_LE: + mr |= SPDIFTX_MR_VBPS(32); + break; + default: + dev_err(dev->dev, "unsupported PCM format: %d\n", + params_format(params)); + return -EINVAL; + } + + mr |= SPDIFTX_MR_BPS(bps); + + spin_lock_irqsave(&ctrl->lock, flags); + ctrl->ch_stat[3] &= ~IEC958_AES3_CON_FS; + switch (params_rate(params)) { + case 22050: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_22050; + break; + case 24000: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_24000; + break; + case 32000: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_32000; + break; + case 44100: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_44100; + break; + case 48000: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_48000; + break; + case 88200: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_88200; + break; + case 96000: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_96000; + break; + case 176400: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_176400; + break; + case 192000: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_192000; + break; + case 8000: + case 11025: + case 16000: + case 64000: + ctrl->ch_stat[3] |= IEC958_AES3_CON_FS_NOTID; + break; + default: + dev_err(dev->dev, "unsupported sample frequency: %u\n", + params_rate(params)); + spin_unlock_irqrestore(&ctrl->lock, flags); + return -EINVAL; + } + mchp_spdiftx_channel_status_write(dev); + spin_unlock_irqrestore(&ctrl->lock, flags); + mr |= SPDIFTX_MR_VALID1 | SPDIFTX_MR_VALID2; + + if (dev->gclk_enabled) { + clk_disable_unprepare(dev->gclk); + dev->gclk_enabled = 0; + } + ret = clk_set_rate(dev->gclk, params_rate(params) * + SPDIFTX_GCLK_RATIO); + if (ret) { + dev_err(dev->dev, + "unable to change gclk rate to: rate %u * ratio %u\n", + params_rate(params), SPDIFTX_GCLK_RATIO); + return ret; + } + ret = clk_prepare_enable(dev->gclk); + if (ret) { + dev_err(dev->dev, "unable to enable gclk: %d\n", ret); + return ret; + } + dev->gclk_enabled = 1; + dev_dbg(dev->dev, "%s(): GCLK set to %d\n", __func__, + params_rate(params) * SPDIFTX_GCLK_RATIO); + + /* Enable interrupts */ + regmap_write(dev->regmap, SPDIFTX_IER, + SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); + + regmap_write(dev->regmap, SPDIFTX_MR, mr); + + return 0; +} + +static int mchp_spdiftx_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + + regmap_write(dev->regmap, SPDIFTX_IDR, + SPDIFTX_IR_TXUDR | SPDIFTX_IR_TXOVR); + if (dev->gclk_enabled) { + clk_disable_unprepare(dev->gclk); + dev->gclk_enabled = 0; + } + + return regmap_write(dev->regmap, SPDIFTX_CR, + SPDIFTX_CR_SWRST | SPDIFTX_CR_FCLR); +} + +static const struct snd_soc_dai_ops mchp_spdiftx_dai_ops = { + .startup = mchp_spdiftx_dai_startup, + .shutdown = mchp_spdiftx_dai_shutdown, + .trigger = mchp_spdiftx_trigger, + .hw_params = mchp_spdiftx_hw_params, + .hw_free = mchp_spdiftx_hw_free, +}; + +#define MCHP_SPDIFTX_RATES SNDRV_PCM_RATE_8000_192000 + +#define MCHP_SPDIFTX_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_BE | \ + SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S18_3BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_BE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_S32_BE \ + ) + +static int mchp_spdiftx_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 mchp_spdiftx_cs_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + unsigned long flags; + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + + spin_lock_irqsave(&ctrl->lock, flags); + memcpy(uvalue->value.iec958.status, ctrl->ch_stat, + sizeof(ctrl->ch_stat)); + spin_unlock_irqrestore(&ctrl->lock, flags); + + return 0; +} + +static int mchp_spdiftx_cs_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + unsigned long flags; + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + int changed = 0; + int i; + + spin_lock_irqsave(&ctrl->lock, flags); + for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat); i++) { + if (ctrl->ch_stat[i] != uvalue->value.iec958.status[i]) + changed = 1; + ctrl->ch_stat[i] = uvalue->value.iec958.status[i]; + } + + if (changed) { + /* don't enable IP while we copy the channel status */ + if (mchp_spdiftx_is_running(dev)) { + /* + * if SPDIF is running, wait for interrupt to write + * channel status + */ + regmap_write(dev->regmap, SPDIFTX_IER, + SPDIFTX_IR_CSRDY); + } else { + mchp_spdiftx_channel_status_write(dev); + } + } + spin_unlock_irqrestore(&ctrl->lock, flags); + + return changed; +} + +static int mchp_spdiftx_cs_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + memset(uvalue->value.iec958.status, 0xff, + sizeof(uvalue->value.iec958.status)); + + return 0; +} + +static int mchp_spdiftx_subcode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + unsigned long flags; + + spin_lock_irqsave(&ctrl->lock, flags); + memcpy(uvalue->value.iec958.subcode, ctrl->user_data, + sizeof(ctrl->user_data)); + spin_unlock_irqrestore(&ctrl->lock, flags); + + return 0; +} + +static int mchp_spdiftx_subcode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + unsigned long flags; + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdiftx_mixer_control *ctrl = &dev->control; + int changed = 0; + int i; + + spin_lock_irqsave(&ctrl->lock, flags); + for (i = 0; i < ARRAY_SIZE(ctrl->user_data); i++) { + if (ctrl->user_data[i] != uvalue->value.iec958.subcode[i]) + changed = 1; + + ctrl->user_data[i] = uvalue->value.iec958.subcode[i]; + } + if (changed) { + if (mchp_spdiftx_is_running(dev)) { + /* + * if SPDIF is running, wait for interrupt to write + * user data + */ + regmap_write(dev->regmap, SPDIFTX_IER, + SPDIFTX_IR_UDRDY); + } else { + mchp_spdiftx_user_data_write(dev); + } + } + spin_unlock_irqrestore(&ctrl->lock, flags); + + return changed; +} + +static struct snd_kcontrol_new mchp_spdiftx_ctrls[] = { + /* Channel status controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdiftx_info, + .get = mchp_spdiftx_cs_get, + .put = mchp_spdiftx_cs_put, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdiftx_info, + .get = mchp_spdiftx_cs_mask, + }, + /* User bits controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Subcode Playback Default", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = mchp_spdiftx_info, + .get = mchp_spdiftx_subcode_get, + .put = mchp_spdiftx_subcode_put, + }, +}; + +static int mchp_spdiftx_dai_probe(struct snd_soc_dai *dai) +{ + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + int ret; + + snd_soc_dai_init_dma_data(dai, &dev->playback, NULL); + + ret = clk_prepare_enable(dev->pclk); + if (ret) { + dev_err(dev->dev, + "failed to enable the peripheral clock: %d\n", ret); + return ret; + } + + /* Add controls */ + snd_soc_add_dai_controls(dai, mchp_spdiftx_ctrls, + ARRAY_SIZE(mchp_spdiftx_ctrls)); + + return 0; +} + +static int mchp_spdiftx_dai_remove(struct snd_soc_dai *dai) +{ + struct mchp_spdiftx_dev *dev = snd_soc_dai_get_drvdata(dai); + + clk_disable_unprepare(dev->pclk); + + return 0; +} + +static struct snd_soc_dai_driver mchp_spdiftx_dai = { + .name = "mchp-spdiftx", + .probe = mchp_spdiftx_dai_probe, + .remove = mchp_spdiftx_dai_remove, + .playback = { + .stream_name = "S/PDIF TX Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MCHP_SPDIFTX_RATES, + .formats = MCHP_SPDIFTX_FORMATS, + }, + .ops = &mchp_spdiftx_dai_ops, +}; + +static const struct snd_soc_component_driver mchp_spdiftx_component = { + .name = "mchp-spdiftx", +}; + +static const struct of_device_id mchp_spdiftx_dt_ids[] = { + { + .compatible = "microchip,sama7g5-spdiftx", + }, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, mchp_spdiftx_dt_ids); +static int mchp_spdiftx_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const struct of_device_id *match; + struct mchp_spdiftx_dev *dev; + struct resource *mem; + struct regmap *regmap; + void __iomem *base; + struct mchp_spdiftx_mixer_control *ctrl; + int irq; + int err; + + /* Get memory for driver data. */ + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* Get hardware capabilities. */ + match = of_match_node(mchp_spdiftx_dt_ids, np); + if (match) + dev->caps = match->data; + + /* Map I/O registers. */ + base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, + &mchp_spdiftx_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Request IRQ */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_irq(&pdev->dev, irq, mchp_spdiftx_interrupt, 0, + dev_name(&pdev->dev), dev); + if (err) + return err; + + /* Get the peripheral clock */ + dev->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(dev->pclk)) { + err = PTR_ERR(dev->pclk); + dev_err(&pdev->dev, + "failed to get the peripheral clock: %d\n", err); + return err; + } + + /* Get the generic clock */ + dev->gclk = devm_clk_get(&pdev->dev, "gclk"); + if (IS_ERR(dev->gclk)) { + err = PTR_ERR(dev->gclk); + dev_err(&pdev->dev, + "failed to get the PMC generic clock: %d\n", err); + return err; + } + + ctrl = &dev->control; + spin_lock_init(&ctrl->lock); + + /* Init channel status */ + ctrl->ch_stat[0] = IEC958_AES0_CON_NOT_COPYRIGHT | + IEC958_AES0_CON_EMPHASIS_NONE; + + dev->dev = &pdev->dev; + dev->regmap = regmap; + platform_set_drvdata(pdev, dev); + + dev->playback.addr = (dma_addr_t)mem->start + SPDIFTX_CDR; + dev->playback.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + + err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (err) { + dev_err(&pdev->dev, "failed to register PMC: %d\n", err); + return err; + } + + err = devm_snd_soc_register_component(&pdev->dev, + &mchp_spdiftx_component, + &mchp_spdiftx_dai, 1); + if (err) { + dev_err(&pdev->dev, "failed to register component: %d\n", err); + return err; + } + + return 0; +} + +static struct platform_driver mchp_spdiftx_driver = { + .probe = mchp_spdiftx_probe, + .driver = { + .name = "mchp_spdiftx", + .of_match_table = of_match_ptr(mchp_spdiftx_dt_ids), + }, +}; + +module_platform_driver(mchp_spdiftx_driver); + +MODULE_AUTHOR("Codrin Ciubotariu "); +MODULE_DESCRIPTION("Microchip S/PDIF TX Controller Driver"); +MODULE_LICENSE("GPL v2"); -- cgit From 68f86a905e2c1d7a6d5d0bcc57f4e44ae204c171 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:21 +0200 Subject: ALSA: pcsp: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. This patch replaces the usage of tasklet in pcsp driver with a simple work. In pcsp driver, a global tasklet is used for offloading the period-elapse handling in the hrtimer callback (introduced in commit 96c7d478efad "ALSA: pcsp - Fix locking messes in snd-pcsp"). It can be achieved gracefully with a work queued in the high-prio system workqueue. This also changes tasklet_kill() with cancel_work_sync() in the sync_stop callback, which is anyway better to assure canceling the pending tasks. Link: https://lore.kernel.org/r/20200903104131.21097-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/drivers/pcsp/pcsp_lib.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/drivers/pcsp/pcsp_lib.c b/sound/drivers/pcsp/pcsp_lib.c index 4e79293d7f11..ed40d0f7432c 100644 --- a/sound/drivers/pcsp/pcsp_lib.c +++ b/sound/drivers/pcsp/pcsp_lib.c @@ -23,10 +23,10 @@ MODULE_PARM_DESC(nforce_wa, "Apply NForce chipset workaround " #define DMIX_WANTS_S16 1 /* - * Call snd_pcm_period_elapsed in a tasklet + * Call snd_pcm_period_elapsed in a work * This avoids spinlock messes and long-running irq contexts */ -static void pcsp_call_pcm_elapsed(unsigned long priv) +static void pcsp_call_pcm_elapsed(struct work_struct *work) { if (atomic_read(&pcsp_chip.timer_active)) { struct snd_pcm_substream *substream; @@ -36,7 +36,7 @@ static void pcsp_call_pcm_elapsed(unsigned long priv) } } -static DECLARE_TASKLET_OLD(pcsp_pcm_tasklet, pcsp_call_pcm_elapsed); +static DECLARE_WORK(pcsp_pcm_work, pcsp_call_pcm_elapsed); /* write the port and returns the next expire time in ns; * called at the trigger-start and in hrtimer callback @@ -119,11 +119,9 @@ static void pcsp_pointer_update(struct snd_pcsp *chip) if (periods_elapsed) { chip->period_ptr += periods_elapsed * period_bytes; chip->period_ptr %= buffer_bytes; + queue_work(system_highpri_wq, &pcsp_pcm_work); } spin_unlock_irqrestore(&chip->substream_lock, flags); - - if (periods_elapsed) - tasklet_schedule(&pcsp_pcm_tasklet); } enum hrtimer_restart pcsp_do_timer(struct hrtimer *handle) @@ -196,7 +194,7 @@ void pcsp_sync_stop(struct snd_pcsp *chip) pcsp_stop_playing(chip); local_irq_enable(); hrtimer_cancel(&chip->timer); - tasklet_kill(&pcsp_pcm_tasklet); + cancel_work_sync(&pcsp_pcm_work); } static int snd_pcsp_playback_close(struct snd_pcm_substream *substream) -- cgit From bf0835957f553aeddec896f3de386562536feee4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:22 +0200 Subject: ALSA: timer: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ALSA core timer API, the callbacks can be offlined to a tasklet when a flag is set in the timer backend. It can be achieved gracefully with a work queued in the high-prio system workqueue. This patch replaces the usage of tasklet in ALSA timer API with a simple work. Currently the tasklet feature is used only in the system timer and hrtimer backends, so both are patched to use the new flag name SNDRV_TIMER_HW_WORK, too. Link: https://lore.kernel.org/r/20200903104131.21097-3-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/hrtimer.c | 2 +- sound/core/timer.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c index c61ba52a530a..e97ff8cccb64 100644 --- a/sound/core/hrtimer.c +++ b/sound/core/hrtimer.c @@ -114,7 +114,7 @@ static int snd_hrtimer_stop(struct snd_timer *t) } static const struct snd_timer_hardware hrtimer_hw __initconst = { - .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET, + .flags = SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_WORK, .open = snd_hrtimer_open, .close = snd_hrtimer_close, .start = snd_hrtimer_start, diff --git a/sound/core/timer.c b/sound/core/timer.c index 227d8152d325..765ea66665a8 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -813,12 +813,12 @@ static void snd_timer_clear_callbacks(struct snd_timer *timer, } /* - * timer tasklet + * timer work * */ -static void snd_timer_tasklet(struct tasklet_struct *t) +static void snd_timer_work(struct work_struct *work) { - struct snd_timer *timer = from_tasklet(timer, t, task_queue); + struct snd_timer *timer = container_of(work, struct snd_timer, task_work); unsigned long flags; if (timer->card && timer->card->shutdown) { @@ -843,7 +843,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) unsigned long resolution; struct list_head *ack_list_head; unsigned long flags; - int use_tasklet = 0; + bool use_work = false; if (timer == NULL) return; @@ -884,7 +884,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) --timer->running; list_del_init(&ti->active_list); } - if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || + if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) || (ti->flags & SNDRV_TIMER_IFLG_FAST)) ack_list_head = &timer->ack_list_head; else @@ -919,11 +919,11 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) snd_timer_process_callbacks(timer, &timer->ack_list_head); /* do we have any slow callbacks? */ - use_tasklet = !list_empty(&timer->sack_list_head); + use_work = !list_empty(&timer->sack_list_head); spin_unlock_irqrestore(&timer->lock, flags); - if (use_tasklet) - tasklet_schedule(&timer->task_queue); + if (use_work) + queue_work(system_highpri_wq, &timer->task_work); } EXPORT_SYMBOL(snd_timer_interrupt); @@ -967,7 +967,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, INIT_LIST_HEAD(&timer->ack_list_head); INIT_LIST_HEAD(&timer->sack_list_head); spin_lock_init(&timer->lock); - tasklet_setup(&timer->task_queue, snd_timer_tasklet); + INIT_WORK(&timer->task_work, snd_timer_work); timer->max_instances = 1000; /* default limit per timer */ if (card != NULL) { timer->module = card->module; @@ -1200,7 +1200,7 @@ static int snd_timer_s_close(struct snd_timer *timer) static const struct snd_timer_hardware snd_timer_system = { - .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, + .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK, .resolution = 1000000000L / HZ, .ticks = 10000000L, .close = snd_timer_s_close, -- cgit From c7d9efdff68e00350e611227ad191ce347b88ea5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:23 +0200 Subject: ALSA: usb-audio: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In USB-audio driver, a tasklet is still used in MIDI interface code for handling the output byte stream. It can be achieved gracefully with a work queued in the high-prio system workqueue. This patch replaces the tasklet usage in USB-audio driver with a simple work. Link: https://lore.kernel.org/r/20200903104131.21097-4-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/midi.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/usb/midi.c b/sound/usb/midi.c index e8287a05e36b..c8213652470c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -142,7 +142,7 @@ struct snd_usb_midi_out_endpoint { unsigned int active_urbs; unsigned int drain_urbs; int max_transfer; /* size of urb buffer */ - struct tasklet_struct tasklet; + struct work_struct work; unsigned int next_urb; spinlock_t buffer_lock; @@ -344,9 +344,10 @@ static void snd_usbmidi_do_output(struct snd_usb_midi_out_endpoint *ep) spin_unlock_irqrestore(&ep->buffer_lock, flags); } -static void snd_usbmidi_out_tasklet(struct tasklet_struct *t) +static void snd_usbmidi_out_work(struct work_struct *work) { - struct snd_usb_midi_out_endpoint *ep = from_tasklet(ep, t, tasklet); + struct snd_usb_midi_out_endpoint *ep = + container_of(work, struct snd_usb_midi_out_endpoint, work); snd_usbmidi_do_output(ep); } @@ -1177,7 +1178,7 @@ static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, snd_rawmidi_proceed(substream); return; } - tasklet_schedule(&port->ep->tasklet); + queue_work(system_highpri_wq, &port->ep->work); } } @@ -1440,7 +1441,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi, } spin_lock_init(&ep->buffer_lock); - tasklet_setup(&ep->tasklet, snd_usbmidi_out_tasklet); + INIT_WORK(&ep->work, snd_usbmidi_out_work); init_waitqueue_head(&ep->drain_wait); for (i = 0; i < 0x10; ++i) @@ -1503,7 +1504,7 @@ void snd_usbmidi_disconnect(struct list_head *p) for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint *ep = &umidi->endpoints[i]; if (ep->out) - tasklet_kill(&ep->out->tasklet); + cancel_work_sync(&ep->out->work); if (ep->out) { for (j = 0; j < OUTPUT_URBS; ++j) usb_kill_urb(ep->out->urbs[j].urb); -- cgit From 45e4d67f8a53e713833292cc5fef3603294a5914 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:24 +0200 Subject: ALSA: ua101: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In UA101 driver, a tasklet is still used for handling the output URBs. It can be achieved gracefully with a work queued in the high-prio system workqueue, too. This patch replaces the tasklet usage in UA101 driver with a simple work. Link: https://lore.kernel.org/r/20200903104131.21097-5-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/usb/misc/ua101.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/usb/misc/ua101.c b/sound/usb/misc/ua101.c index 3b2dce1043f5..6b30155964ec 100644 --- a/sound/usb/misc/ua101.c +++ b/sound/usb/misc/ua101.c @@ -96,7 +96,7 @@ struct ua101 { u8 rate_feedback[MAX_QUEUE_LENGTH]; struct list_head ready_playback_urbs; - struct tasklet_struct playback_tasklet; + struct work_struct playback_work; wait_queue_head_t alsa_capture_wait; wait_queue_head_t rate_feedback_wait; wait_queue_head_t alsa_playback_wait; @@ -188,7 +188,7 @@ static void playback_urb_complete(struct urb *usb_urb) spin_lock_irqsave(&ua->lock, flags); list_add_tail(&urb->ready_list, &ua->ready_playback_urbs); if (ua->rate_feedback_count > 0) - tasklet_schedule(&ua->playback_tasklet); + queue_work(system_highpri_wq, &ua->playback_work); ua->playback.substream->runtime->delay -= urb->urb.iso_frame_desc[0].length / ua->playback.frame_bytes; @@ -247,9 +247,9 @@ static inline void add_with_wraparound(struct ua101 *ua, *value -= ua->playback.queue_length; } -static void playback_tasklet(struct tasklet_struct *t) +static void playback_work(struct work_struct *work) { - struct ua101 *ua = from_tasklet(ua, t, playback_tasklet); + struct ua101 *ua = container_of(work, struct ua101, playback_work); unsigned long flags; unsigned int frames; struct ua101_urb *urb; @@ -401,7 +401,7 @@ static void capture_urb_complete(struct urb *urb) } if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) && !list_empty(&ua->ready_playback_urbs)) - tasklet_schedule(&ua->playback_tasklet); + queue_work(system_highpri_wq, &ua->playback_work); } spin_unlock_irqrestore(&ua->lock, flags); @@ -532,7 +532,7 @@ static void stop_usb_playback(struct ua101 *ua) kill_stream_urbs(&ua->playback); - tasklet_kill(&ua->playback_tasklet); + cancel_work_sync(&ua->playback_work); disable_iso_interface(ua, INTF_PLAYBACK); } @@ -550,7 +550,7 @@ static int start_usb_playback(struct ua101 *ua) return 0; kill_stream_urbs(&ua->playback); - tasklet_kill(&ua->playback_tasklet); + cancel_work_sync(&ua->playback_work); err = enable_iso_interface(ua, INTF_PLAYBACK); if (err < 0) @@ -1218,7 +1218,7 @@ static int ua101_probe(struct usb_interface *interface, spin_lock_init(&ua->lock); mutex_init(&ua->mutex); INIT_LIST_HEAD(&ua->ready_playback_urbs); - tasklet_setup(&ua->playback_tasklet, playback_tasklet); + INIT_WORK(&ua->playback_work, playback_work); init_waitqueue_head(&ua->alsa_capture_wait); init_waitqueue_head(&ua->rate_feedback_wait); init_waitqueue_head(&ua->alsa_playback_wait); -- cgit From 6053a712472461c3800d4406bea0c8d5ce98df1a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:25 +0200 Subject: ALSA: aloop: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In aloop driver, a tasklet is still used for offloading the timer event task. It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in aloop driver with a simple work. Link: https://lore.kernel.org/r/20200903104131.21097-6-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/drivers/aloop.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) (limited to 'sound') diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 251eaf1152e2..c91356326699 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c @@ -110,7 +110,7 @@ struct loopback_cable { struct { int stream; struct snd_timer_id id; - struct tasklet_struct event_tasklet; + struct work_struct event_work; struct snd_timer_instance *instance; } snd_timer; }; @@ -309,8 +309,8 @@ static int loopback_snd_timer_close_cable(struct loopback_pcm *dpcm) */ snd_timer_close(cable->snd_timer.instance); - /* wait till drain tasklet has finished if requested */ - tasklet_kill(&cable->snd_timer.event_tasklet); + /* wait till drain work has finished if requested */ + cancel_work_sync(&cable->snd_timer.event_work); snd_timer_instance_free(cable->snd_timer.instance); memset(&cable->snd_timer, 0, sizeof(cable->snd_timer)); @@ -794,11 +794,11 @@ static void loopback_snd_timer_function(struct snd_timer_instance *timeri, resolution); } -static void loopback_snd_timer_tasklet(unsigned long arg) +static void loopback_snd_timer_work(struct work_struct *work) { - struct snd_timer_instance *timeri = (struct snd_timer_instance *)arg; - struct loopback_cable *cable = timeri->callback_data; + struct loopback_cable *cable; + cable = container_of(work, struct loopback_cable, snd_timer.event_work); loopback_snd_timer_period_elapsed(cable, SNDRV_TIMER_EVENT_MSTOP, 0); } @@ -828,9 +828,9 @@ static void loopback_snd_timer_event(struct snd_timer_instance *timeri, * state the streaming will be aborted by the usual timeout. It * should not be aborted here because may be the timer sound * card does only a recovery and the timer is back soon. - * This tasklet triggers loopback_snd_timer_tasklet() + * This work triggers loopback_snd_timer_work() */ - tasklet_schedule(&cable->snd_timer.event_tasklet); + schedule_work(&cable->snd_timer.event_work); } } @@ -1124,7 +1124,7 @@ static int loopback_snd_timer_open(struct loopback_pcm *dpcm) err = -ENOMEM; goto exit; } - /* The callback has to be called from another tasklet. If + /* The callback has to be called from another work. If * SNDRV_TIMER_IFLG_FAST is specified it will be called from the * snd_pcm_period_elapsed() call of the selected sound card. * snd_pcm_period_elapsed() helds snd_pcm_stream_lock_irqsave(). @@ -1137,9 +1137,8 @@ static int loopback_snd_timer_open(struct loopback_pcm *dpcm) timeri->callback_data = (void *)cable; timeri->ccallback = loopback_snd_timer_event; - /* initialise a tasklet used for draining */ - tasklet_init(&cable->snd_timer.event_tasklet, - loopback_snd_timer_tasklet, (unsigned long)timeri); + /* initialise a work used for draining */ + INIT_WORK(&cable->snd_timer.event_work, loopback_snd_timer_work); /* The mutex loopback->cable_lock is kept locked. * Therefore snd_timer_open() cannot be called a second time -- cgit From 4adab848ae752cc92c4de1403144002f369b079f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:26 +0200 Subject: ALSA: hdsp: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In HDSP driver, a tasklet is still used for offloading the MIDI I/O handling (optional via mixer switch). It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in HDSP driver with a simple work. The conversion is fairly straightforward. The only significant difference is that a superfluous tasklet_kill() call is removed from snd_hdap_midi_input_trigger(). Link: https://lore.kernel.org/r/20200903104131.21097-7-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdsp.c | 55 ++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c index dda56ecfd33b..cea53a878c36 100644 --- a/sound/pci/rme9652/hdsp.c +++ b/sound/pci/rme9652/hdsp.c @@ -447,8 +447,8 @@ struct hdsp { struct snd_pcm_substream *capture_substream; struct snd_pcm_substream *playback_substream; struct hdsp_midi midi[2]; - struct tasklet_struct midi_tasklet; - int use_midi_tasklet; + struct work_struct midi_work; + int use_midi_work; int precise_ptr; u32 control_register; /* cached value */ u32 control2_register; /* cached value */ @@ -1385,7 +1385,6 @@ static void snd_hdsp_midi_input_trigger(struct snd_rawmidi_substream *substream, } } else { hdsp->control_register &= ~ie; - tasklet_kill(&hdsp->midi_tasklet); } hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); @@ -2542,37 +2541,37 @@ static int snd_hdsp_put_precise_pointer(struct snd_kcontrol *kcontrol, struct sn return change; } -#define HDSP_USE_MIDI_TASKLET(xname, xindex) \ +#define HDSP_USE_MIDI_WORK(xname, xindex) \ { .iface = SNDRV_CTL_ELEM_IFACE_CARD, \ .name = xname, \ .index = xindex, \ - .info = snd_hdsp_info_use_midi_tasklet, \ - .get = snd_hdsp_get_use_midi_tasklet, \ - .put = snd_hdsp_put_use_midi_tasklet \ + .info = snd_hdsp_info_use_midi_work, \ + .get = snd_hdsp_get_use_midi_work, \ + .put = snd_hdsp_put_use_midi_work \ } -static int hdsp_set_use_midi_tasklet(struct hdsp *hdsp, int use_tasklet) +static int hdsp_set_use_midi_work(struct hdsp *hdsp, int use_work) { - if (use_tasklet) - hdsp->use_midi_tasklet = 1; + if (use_work) + hdsp->use_midi_work = 1; else - hdsp->use_midi_tasklet = 0; + hdsp->use_midi_work = 0; return 0; } -#define snd_hdsp_info_use_midi_tasklet snd_ctl_boolean_mono_info +#define snd_hdsp_info_use_midi_work snd_ctl_boolean_mono_info -static int snd_hdsp_get_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_hdsp_get_use_midi_work(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); spin_lock_irq(&hdsp->lock); - ucontrol->value.integer.value[0] = hdsp->use_midi_tasklet; + ucontrol->value.integer.value[0] = hdsp->use_midi_work; spin_unlock_irq(&hdsp->lock); return 0; } -static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) +static int snd_hdsp_put_use_midi_work(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hdsp *hdsp = snd_kcontrol_chip(kcontrol); int change; @@ -2582,8 +2581,8 @@ static int snd_hdsp_put_use_midi_tasklet(struct snd_kcontrol *kcontrol, struct s return -EBUSY; val = ucontrol->value.integer.value[0] & 1; spin_lock_irq(&hdsp->lock); - change = (int)val != hdsp->use_midi_tasklet; - hdsp_set_use_midi_tasklet(hdsp, val); + change = (int)val != hdsp->use_midi_work; + hdsp_set_use_midi_work(hdsp, val); spin_unlock_irq(&hdsp->lock); return change; } @@ -2950,7 +2949,7 @@ HDSP_SPDIF_SYNC_CHECK("SPDIF Lock Status", 0), HDSP_ADATSYNC_SYNC_CHECK("ADAT Sync Lock Status", 0), HDSP_TOGGLE_SETTING("Line Out", HDSP_LineOut), HDSP_PRECISE_POINTER("Precise Pointer", 0), -HDSP_USE_MIDI_TASKLET("Use Midi Tasklet", 0), +HDSP_USE_MIDI_WORK("Use Midi Tasklet", 0), }; @@ -3370,7 +3369,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) snd_iprintf(buffer, "MIDI1 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn0)); snd_iprintf(buffer, "MIDI2 Output status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusOut1)); snd_iprintf(buffer, "MIDI2 Input status: 0x%x\n", hdsp_read(hdsp, HDSP_midiStatusIn1)); - snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_tasklet ? "on" : "off"); + snd_iprintf(buffer, "Use Midi Tasklet: %s\n", hdsp->use_midi_work ? "on" : "off"); snd_iprintf(buffer, "\n"); @@ -3791,9 +3790,9 @@ static int snd_hdsp_set_defaults(struct hdsp *hdsp) return 0; } -static void hdsp_midi_tasklet(struct tasklet_struct *t) +static void hdsp_midi_work(struct work_struct *work) { - struct hdsp *hdsp = from_tasklet(hdsp, t, midi_tasklet); + struct hdsp *hdsp = container_of(work, struct hdsp, midi_work); if (hdsp->midi[0].pending) snd_hdsp_midi_input_read (&hdsp->midi[0]); @@ -3838,7 +3837,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id) } if (midi0 && midi0status) { - if (hdsp->use_midi_tasklet) { + if (hdsp->use_midi_work) { /* we disable interrupts for this input until processing is done */ hdsp->control_register &= ~HDSP_Midi0InterruptEnable; hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); @@ -3849,7 +3848,7 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id) } } if (hdsp->io_type != Multiface && hdsp->io_type != RPM && hdsp->io_type != H9632 && midi1 && midi1status) { - if (hdsp->use_midi_tasklet) { + if (hdsp->use_midi_work) { /* we disable interrupts for this input until processing is done */ hdsp->control_register &= ~HDSP_Midi1InterruptEnable; hdsp_write(hdsp, HDSP_controlRegister, hdsp->control_register); @@ -3859,8 +3858,8 @@ static irqreturn_t snd_hdsp_interrupt(int irq, void *dev_id) snd_hdsp_midi_input_read (&hdsp->midi[1]); } } - if (hdsp->use_midi_tasklet && schedule) - tasklet_schedule(&hdsp->midi_tasklet); + if (hdsp->use_midi_work && schedule) + queue_work(system_highpri_wq, &hdsp->midi_work); return IRQ_HANDLED; } @@ -5182,7 +5181,7 @@ static int snd_hdsp_create(struct snd_card *card, spin_lock_init(&hdsp->lock); - tasklet_setup(&hdsp->midi_tasklet, hdsp_midi_tasklet); + INIT_WORK(&hdsp->midi_work, hdsp_midi_work); pci_read_config_word(hdsp->pci, PCI_CLASS_REVISION, &hdsp->firmware_rev); hdsp->firmware_rev &= 0xff; @@ -5235,7 +5234,7 @@ static int snd_hdsp_create(struct snd_card *card, hdsp->irq = pci->irq; card->sync_irq = hdsp->irq; hdsp->precise_ptr = 0; - hdsp->use_midi_tasklet = 1; + hdsp->use_midi_work = 1; hdsp->dds_value = 0; if ((err = snd_hdsp_initialize_memory(hdsp)) < 0) @@ -5305,7 +5304,7 @@ static int snd_hdsp_free(struct hdsp *hdsp) { if (hdsp->port) { /* stop the audio, and cancel all interrupts */ - tasklet_kill(&hdsp->midi_tasklet); + cancel_work_sync(&hdsp->midi_work); hdsp->control_register &= ~(HDSP_Start|HDSP_AudioInterruptEnable|HDSP_Midi0InterruptEnable|HDSP_Midi1InterruptEnable); hdsp_write (hdsp, HDSP_controlRegister, hdsp->control_register); } -- cgit From a2e527c5a3ebcf248b51705ef44f6700403fd8e3 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:27 +0200 Subject: ALSA: hdspm: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In HDSP-MADI driver, a tasklet is still used for offloading the MIDI I/O handling (optional via mixer switch). It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in HDSP-MADI driver with a simple work. The conversion is fairly straightforward. The only significant difference is that the work initialization is moved to the right place in snd_hdspm_create() and cancel_work_sync() is always called in snd_hdspm_free() to assure killing the pending works. Link: https://lore.kernel.org/r/20200903104131.21097-8-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdspm.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index 572350aaf18d..e532312a5e1c 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -997,7 +997,7 @@ struct hdspm { u32 settings_register; /* cached value for AIO / RayDat (sync reference, master/slave) */ struct hdspm_midi midi[4]; - struct tasklet_struct midi_tasklet; + struct work_struct midi_work; size_t period_bytes; unsigned char ss_in_channels; @@ -2169,9 +2169,9 @@ static int snd_hdspm_create_midi(struct snd_card *card, } -static void hdspm_midi_tasklet(struct tasklet_struct *t) +static void hdspm_midi_work(struct work_struct *work) { - struct hdspm *hdspm = from_tasklet(hdspm, t, midi_tasklet); + struct hdspm *hdspm = container_of(work, struct hdspm, midi_work); int i = 0; while (i < hdspm->midiPorts) { @@ -5449,7 +5449,7 @@ static irqreturn_t snd_hdspm_interrupt(int irq, void *dev_id) } if (schedule) - tasklet_hi_schedule(&hdspm->midi_tasklet); + queue_work(system_highpri_wq, &hdspm->midi_work); } return IRQ_HANDLED; @@ -6538,6 +6538,7 @@ static int snd_hdspm_create(struct snd_card *card, hdspm->card = card; spin_lock_init(&hdspm->lock); + INIT_WORK(&hdspm->midi_work, hdspm_midi_work); pci_read_config_word(hdspm->pci, PCI_CLASS_REVISION, &hdspm->firmware_rev); @@ -6836,9 +6837,6 @@ static int snd_hdspm_create(struct snd_card *card, } - tasklet_setup(&hdspm->midi_tasklet, hdspm_midi_tasklet); - - if (hdspm->io_type != MADIface) { hdspm->serial = (hdspm_read(hdspm, HDSPM_midiStatusIn0)>>8) & 0xFFFFFF; @@ -6873,6 +6871,7 @@ static int snd_hdspm_free(struct hdspm * hdspm) { if (hdspm->port) { + cancel_work_sync(&hdspm->midi_work); /* stop th audio, and cancel all interrupts */ hdspm->control_register &= -- cgit From 2ac55daffee5af04b13f96e6acef63a8e4bfff85 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:28 +0200 Subject: ALSA: riptide: Replace tasklet with threaded irq The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In Riptide driver, a tasklet is still used for offloading the PCM IRQ handling. It can be achieved gracefully with a threaded IRQ, too. This patch replaces the tasklet usage in riptide driver with a threaded IRQ. Link: https://lore.kernel.org/r/20200903104131.21097-9-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/riptide/riptide.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c index 098c69b3b7aa..fcc2073c5025 100644 --- a/sound/pci/riptide/riptide.c +++ b/sound/pci/riptide/riptide.c @@ -445,7 +445,6 @@ struct snd_riptide { union firmware_version firmware; spinlock_t lock; - struct tasklet_struct riptide_tq; struct snd_info_entry *proc_entry; unsigned long received_irqs; @@ -1070,9 +1069,9 @@ getmixer(struct cmdif *cif, short num, unsigned short *rval, return 0; } -static void riptide_handleirq(struct tasklet_struct *t) +static irqreturn_t riptide_handleirq(int irq, void *dev_id) { - struct snd_riptide *chip = from_tasklet(chip, t, riptide_tq); + struct snd_riptide *chip = dev_id; struct cmdif *cif = chip->cif; struct snd_pcm_substream *substream[PLAYBACK_SUBSTREAMS + 1]; struct snd_pcm_runtime *runtime; @@ -1083,7 +1082,7 @@ static void riptide_handleirq(struct tasklet_struct *t) unsigned int flag; if (!cif) - return; + return IRQ_HANDLED; for (i = 0; i < PLAYBACK_SUBSTREAMS; i++) substream[i] = chip->playback_substream[i]; @@ -1134,6 +1133,8 @@ static void riptide_handleirq(struct tasklet_struct *t) } } } + + return IRQ_HANDLED; } #ifdef CONFIG_PM_SLEEP @@ -1699,13 +1700,14 @@ snd_riptide_interrupt(int irq, void *dev_id) { struct snd_riptide *chip = dev_id; struct cmdif *cif = chip->cif; + irqreturn_t ret = IRQ_HANDLED; if (cif) { chip->received_irqs++; if (IS_EOBIRQ(cif->hwport) || IS_EOSIRQ(cif->hwport) || IS_EOCIRQ(cif->hwport)) { chip->handled_irqs++; - tasklet_schedule(&chip->riptide_tq); + ret = IRQ_WAKE_THREAD; } if (chip->rmidi && IS_MPUIRQ(cif->hwport)) { chip->handled_irqs++; @@ -1714,7 +1716,7 @@ snd_riptide_interrupt(int irq, void *dev_id) } SET_AIACK(cif->hwport); } - return IRQ_HANDLED; + return ret; } static void @@ -1843,7 +1845,6 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci, chip->received_irqs = 0; chip->handled_irqs = 0; chip->cif = NULL; - tasklet_setup(&chip->riptide_tq, riptide_handleirq); if ((chip->res_port = request_region(chip->port, 64, "RIPTIDE")) == NULL) { @@ -1856,8 +1857,9 @@ snd_riptide_create(struct snd_card *card, struct pci_dev *pci, hwport = (struct riptideport *)chip->port; UNSET_AIE(hwport); - if (request_irq(pci->irq, snd_riptide_interrupt, IRQF_SHARED, - KBUILD_MODNAME, chip)) { + if (request_threaded_irq(pci->irq, snd_riptide_interrupt, + riptide_handleirq, IRQF_SHARED, + KBUILD_MODNAME, chip)) { snd_printk(KERN_ERR "Riptide: unable to grab IRQ %d\n", pci->irq); snd_riptide_free(chip); -- cgit From ce4f25759372bbf6c37b92712b00c1b1b9b4b48e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:29 +0200 Subject: ALSA: asihpi: Replace tasklet with threaded irq The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In ASIHPI driver, a tasklet is still used for offloading the PCM IRQ handling. It can be achieved gracefully with a threaded IRQ, too. This patch replaces the tasklet usage in asihpi driver with a threaded IRQ. It also simplified some call patterns. Link: https://lore.kernel.org/r/20200903104131.21097-10-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 28 +++------------------------- sound/pci/asihpi/hpioctl.c | 16 +++++++++++++--- 2 files changed, 16 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 35e76480306e..46d8166ceaeb 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -117,7 +117,6 @@ struct snd_card_asihpi { * snd_card_asihpi_timer_function(). */ struct snd_card_asihpi_pcm *llmode_streampriv; - struct tasklet_struct t; void (*pcm_start)(struct snd_pcm_substream *substream); void (*pcm_stop)(struct snd_pcm_substream *substream); @@ -547,9 +546,7 @@ static void snd_card_asihpi_pcm_int_start(struct snd_pcm_substream *substream) card = snd_pcm_substream_chip(substream); WARN_ON(in_interrupt()); - tasklet_disable(&card->t); card->llmode_streampriv = dpcm; - tasklet_enable(&card->t); hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, HPI_ADAPTER_PROPERTY_IRQ_RATE, @@ -565,13 +562,7 @@ static void snd_card_asihpi_pcm_int_stop(struct snd_pcm_substream *substream) hpi_handle_error(hpi_adapter_set_property(card->hpi->adapter->index, HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); - if (in_interrupt()) - card->llmode_streampriv = NULL; - else { - tasklet_disable(&card->t); - card->llmode_streampriv = NULL; - tasklet_enable(&card->t); - } + card->llmode_streampriv = NULL; } static int snd_card_asihpi_trigger(struct snd_pcm_substream *substream, @@ -921,10 +912,9 @@ static void snd_card_asihpi_timer_function(struct timer_list *t) add_timer(&dpcm->timer); } -static void snd_card_asihpi_int_task(struct tasklet_struct *t) +static void snd_card_asihpi_isr(struct hpi_adapter *a) { - struct snd_card_asihpi *asihpi = from_tasklet(asihpi, t, t); - struct hpi_adapter *a = asihpi->hpi; + struct snd_card_asihpi *asihpi; WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; @@ -933,15 +923,6 @@ static void snd_card_asihpi_int_task(struct tasklet_struct *t) &asihpi->llmode_streampriv->timer); } -static void snd_card_asihpi_isr(struct hpi_adapter *a) -{ - struct snd_card_asihpi *asihpi; - - WARN_ON(!a || !a->snd_card || !a->snd_card->private_data); - asihpi = (struct snd_card_asihpi *)a->snd_card->private_data; - tasklet_schedule(&asihpi->t); -} - /***************************** PLAYBACK OPS ****************/ static int snd_card_asihpi_playback_prepare(struct snd_pcm_substream * substream) @@ -2871,7 +2852,6 @@ static int snd_asihpi_probe(struct pci_dev *pci_dev, if (hpi->interrupt_mode) { asihpi->pcm_start = snd_card_asihpi_pcm_int_start; asihpi->pcm_stop = snd_card_asihpi_pcm_int_stop; - tasklet_setup(&asihpi->t, snd_card_asihpi_int_task); hpi->interrupt_callback = snd_card_asihpi_isr; } else { asihpi->pcm_start = snd_card_asihpi_pcm_timer_start; @@ -2960,14 +2940,12 @@ __nodev: static void snd_asihpi_remove(struct pci_dev *pci_dev) { struct hpi_adapter *hpi = pci_get_drvdata(pci_dev); - struct snd_card_asihpi *asihpi = hpi->snd_card->private_data; /* Stop interrupts */ if (hpi->interrupt_mode) { hpi->interrupt_callback = NULL; hpi_handle_error(hpi_adapter_set_property(hpi->adapter->index, HPI_ADAPTER_PROPERTY_IRQ_RATE, 0, 0)); - tasklet_kill(&asihpi->t); } snd_card_free(hpi->snd_card); diff --git a/sound/pci/asihpi/hpioctl.c b/sound/pci/asihpi/hpioctl.c index 496dcde9715d..6cc2b6964bb5 100644 --- a/sound/pci/asihpi/hpioctl.c +++ b/sound/pci/asihpi/hpioctl.c @@ -329,11 +329,20 @@ static irqreturn_t asihpi_isr(int irq, void *dev_id) asihpi_irq_count, a->adapter->type, a->adapter->index); */ if (a->interrupt_callback) - a->interrupt_callback(a); + return IRQ_WAKE_THREAD; return IRQ_HANDLED; } +static irqreturn_t asihpi_isr_thread(int irq, void *dev_id) +{ + struct hpi_adapter *a = dev_id; + + if (a->interrupt_callback) + a->interrupt_callback(a); + return IRQ_HANDLED; +} + int asihpi_adapter_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { @@ -478,8 +487,9 @@ int asihpi_adapter_probe(struct pci_dev *pci_dev, } /* Note: request_irq calls asihpi_isr here */ - if (request_irq(pci_dev->irq, asihpi_isr, IRQF_SHARED, - "asihpi", &adapters[adapter_index])) { + if (request_threaded_irq(pci_dev->irq, asihpi_isr, + asihpi_isr_thread, IRQF_SHARED, + "asihpi", &adapters[adapter_index])) { dev_err(&pci_dev->dev, "request_irq(%d) failed\n", pci_dev->irq); goto err; -- cgit From f2a852d36711bd7c7c717abc66c07751f3e1d482 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 3 Sep 2020 12:41:31 +0200 Subject: ALSA: mixart: Correct comment wrt obsoleted tasklet usage The miXart driver has been already converted to use the threaded IRQ instead of tasklet while there is a remaining comment still mentioning a tasklet. Update the comment appropriately. Fixes: 8d3a8b5cb57d ("ALSA: mixart: Use nonatomic PCM ops") Link: https://lore.kernel.org/r/20200903104131.21097-12-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/pci/mixart/mixart.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/mixart/mixart.h b/sound/pci/mixart/mixart.h index 42111562e9bc..cbed6d9a9f2e 100644 --- a/sound/pci/mixart/mixart.h +++ b/sound/pci/mixart/mixart.h @@ -69,7 +69,7 @@ struct mixart_mgr { u32 msg_fifo[MSG_FIFO_SIZE]; int msg_fifo_readptr; int msg_fifo_writeptr; - atomic_t msg_processed; /* number of messages to be processed in tasklet */ + atomic_t msg_processed; /* number of messages to be processed in irq thread */ struct mutex lock; /* interrupt lock */ struct mutex msg_lock; /* mailbox lock */ -- cgit From 175860c50a800fb46f5f3cb44ed3db0344c0fcc5 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Wed, 9 Sep 2020 21:49:27 +0800 Subject: ALSA: pci/asihpi: Remove unused function hpi_stream_group_get_map() There is no caller in tree, so can remove it. Signed-off-by: YueHaibing Link: https://lore.kernel.org/r/20200909134927.33964-1-yuehaibing@huawei.com Signed-off-by: Takashi Iwai --- sound/pci/asihpi/asihpi.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'sound') diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c index 46d8166ceaeb..5e1f9f10051b 100644 --- a/sound/pci/asihpi/asihpi.c +++ b/sound/pci/asihpi/asihpi.c @@ -257,15 +257,6 @@ static inline u16 hpi_stream_group_reset(u32 h_stream) return hpi_instream_group_reset(h_stream); } -static inline u16 hpi_stream_group_get_map( - u32 h_stream, u32 *mo, u32 *mi) -{ - if (hpi_handle_object(h_stream) == HPI_OBJ_OSTREAM) - return hpi_outstream_group_get_map(h_stream, mo, mi); - else - return hpi_instream_group_get_map(h_stream, mo, mi); -} - static u16 handle_error(u16 err, int line, char *filename) { if (err) -- cgit From c07152d46b3b056ff182a0b5beb323fc2e6788fe Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Thu, 10 Sep 2020 12:29:48 +0800 Subject: ASoC: rt1015p: add codec driver Adds rt1015p codec driver. Rt1015p is a rt1015 variant. - It doesn't support I2C. - It only supports S24, 48kHz, 64FS. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20200910042949.3795682-2-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 7 +++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/rt1015p.c | 148 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 sound/soc/codecs/rt1015p.c (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index e6c71ca0cd9b..1cf686eb6a51 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -155,6 +155,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT298 imply SND_SOC_RT1011 imply SND_SOC_RT1015 + imply SND_SOC_RT1015P imply SND_SOC_RT1305 imply SND_SOC_RT1308 imply SND_SOC_RT5514 @@ -1032,6 +1033,7 @@ config SND_SOC_RL6231 default y if SND_SOC_RT5682=y default y if SND_SOC_RT1011=y default y if SND_SOC_RT1015=y + default y if SND_SOC_RT1015P=y default y if SND_SOC_RT1305=y default y if SND_SOC_RT1308=y default m if SND_SOC_RT5514=m @@ -1049,6 +1051,7 @@ config SND_SOC_RL6231 default m if SND_SOC_RT5682=m default m if SND_SOC_RT1011=m default m if SND_SOC_RT1015=m + default m if SND_SOC_RT1015P=m default m if SND_SOC_RT1305=m default m if SND_SOC_RT1308=m @@ -1081,6 +1084,10 @@ config SND_SOC_RT1015 tristate depends on I2C +config SND_SOC_RT1015P + tristate + depends on GPIOLIB + config SND_SOC_RT1305 tristate depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 9a3f58c1069a..2e5a79b55102 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -159,6 +159,7 @@ snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt1011-objs := rt1011.o snd-soc-rt1015-objs := rt1015.o +snd-soc-rt1015p-objs := rt1015p.o snd-soc-rt1305-objs := rt1305.o snd-soc-rt1308-objs := rt1308.o snd-soc-rt1308-sdw-objs := rt1308-sdw.o @@ -465,6 +466,7 @@ obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o +obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o obj-$(CONFIG_SND_SOC_RT1308_SDW) += snd-soc-rt1308-sdw.o diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c new file mode 100644 index 000000000000..59bb60682270 --- /dev/null +++ b/sound/soc/codecs/rt1015p.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt1015p.c -- RT1015P ALSA SoC audio amplifier driver +// +// Copyright 2020 The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rt1015p_priv { + struct gpio_desc *sdb; + int sdb_switch; +}; + +static int rt1015p_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt1015p_priv *rt1015p = + snd_soc_component_get_drvdata(component); + + if (!rt1015p->sdb) + return 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (rt1015p->sdb_switch) { + gpiod_set_value(rt1015p->sdb, 1); + dev_dbg(component->dev, "set sdb to 1"); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + gpiod_set_value(rt1015p->sdb, 0); + dev_dbg(component->dev, "set sdb to 0"); + break; + } + + return 0; +} + +static int rt1015p_sdb_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1015p_priv *rt1015p = + snd_soc_component_get_drvdata(component); + + if (event & SND_SOC_DAPM_POST_PMU) + rt1015p->sdb_switch = 1; + else if (event & SND_SOC_DAPM_POST_PMD) + rt1015p->sdb_switch = 0; + + return 0; +} + +static const struct snd_soc_dapm_widget rt1015p_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("Speaker"), + SND_SOC_DAPM_OUT_DRV_E("SDB", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1015p_sdb_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route rt1015p_dapm_routes[] = { + {"SDB", NULL, "HiFi Playback"}, + {"Speaker", NULL, "SDB"}, +}; + +static const struct snd_soc_component_driver rt1015p_component_driver = { + .dapm_widgets = rt1015p_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt1015p_dapm_widgets), + .dapm_routes = rt1015p_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(rt1015p_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct snd_soc_dai_ops rt1015p_dai_ops = { + .trigger = rt1015p_daiops_trigger, +}; + +static struct snd_soc_dai_driver rt1015p_dai_driver = { + .name = "HiFi", + .playback = { + .stream_name = "HiFi Playback", + .formats = SNDRV_PCM_FMTBIT_S24, + .rates = SNDRV_PCM_RATE_48000, + .channels_min = 1, + .channels_max = 2, + }, + .ops = &rt1015p_dai_ops, +}; + +static int rt1015p_platform_probe(struct platform_device *pdev) +{ + struct rt1015p_priv *rt1015p; + + rt1015p = devm_kzalloc(&pdev->dev, sizeof(*rt1015p), GFP_KERNEL); + if (!rt1015p) + return -ENOMEM; + + rt1015p->sdb = devm_gpiod_get_optional(&pdev->dev, + "sdb", GPIOD_OUT_LOW); + if (IS_ERR(rt1015p->sdb)) + return PTR_ERR(rt1015p->sdb); + + dev_set_drvdata(&pdev->dev, rt1015p); + + return devm_snd_soc_register_component(&pdev->dev, + &rt1015p_component_driver, + &rt1015p_dai_driver, 1); +} + +#ifdef CONFIG_OF +static const struct of_device_id rt1015p_device_id[] = { + { .compatible = "realtek,rt1015p" }, + {} +}; +MODULE_DEVICE_TABLE(of, rt1015p_device_id); +#endif + +static struct platform_driver rt1015p_platform_driver = { + .driver = { + .name = "rt1015p", + .of_match_table = of_match_ptr(rt1015p_device_id), + }, + .probe = rt1015p_platform_probe, +}; +module_platform_driver(rt1015p_platform_driver); + +MODULE_DESCRIPTION("ASoC RT1015P driver"); +MODULE_LICENSE("GPL v2"); -- cgit From 150b2e86c54ad09c26e60f32925aeaf1fca1a5d3 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:25 +0100 Subject: ASoC: q6dsp: q6afe: add support to Codec DMA ports New LPASS supports various codec macros, DSP firmware already has support to those ports. Add corresponding configuration support to those ports in adsp drivers. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 127 ++++++++++++++++++++++++++++++++++++++++++- sound/soc/qcom/qdsp6/q6afe.h | 14 ++++- 2 files changed, 139 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index e0945f7a58c8..8ceefb431bcb 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -42,6 +42,7 @@ #define AFE_PARAM_ID_I2S_CONFIG 0x0001020D #define AFE_PARAM_ID_TDM_CONFIG 0x0001029D #define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 +#define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8 /* I2S config specific */ #define AFE_API_VERSION_I2S_CONFIG 0x1 @@ -299,12 +300,58 @@ #define AFE_PORT_ID_QUINARY_TDM_TX_7 \ (AFE_PORT_ID_QUINARY_TDM_TX + 0x0E) +/* AFE WSA Codec DMA Rx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_0 0xB000 +/* AFE WSA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_0 0xB001 +/* AFE WSA Codec DMA Rx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_RX_1 0xB002 +/* AFE WSA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_1 0xB003 +/* AFE WSA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_WSA_CODEC_DMA_TX_2 0xB005 +/* AFE VA Codec DMA Tx port 0 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_0 0xB021 +/* AFE VA Codec DMA Tx port 1 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_1 0xB023 +/* AFE VA Codec DMA Tx port 2 */ +#define AFE_PORT_ID_VA_CODEC_DMA_TX_2 0xB025 +/* AFE Rx Codec DMA Rx port 0 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_0 0xB030 +/* AFE Tx Codec DMA Tx port 0 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_0 0xB031 +/* AFE Rx Codec DMA Rx port 1 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_1 0xB032 +/* AFE Tx Codec DMA Tx port 1 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_1 0xB033 +/* AFE Rx Codec DMA Rx port 2 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_2 0xB034 +/* AFE Tx Codec DMA Tx port 2 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_2 0xB035 +/* AFE Rx Codec DMA Rx port 3 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_3 0xB036 +/* AFE Tx Codec DMA Tx port 3 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_3 0xB037 +/* AFE Rx Codec DMA Rx port 4 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_4 0xB038 +/* AFE Tx Codec DMA Tx port 4 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_4 0xB039 +/* AFE Rx Codec DMA Rx port 5 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_5 0xB03A +/* AFE Tx Codec DMA Tx port 5 */ +#define AFE_PORT_ID_TX_CODEC_DMA_TX_5 0xB03B +/* AFE Rx Codec DMA Rx port 6 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_6 0xB03C +/* AFE Rx Codec DMA Rx port 7 */ +#define AFE_PORT_ID_RX_CODEC_DMA_RX_7 0xB03E + #define Q6AFE_LPASS_MODE_CLK1_VALID 1 #define Q6AFE_LPASS_MODE_CLK2_VALID 2 #define Q6AFE_LPASS_CLK_SRC_INTERNAL 1 #define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0 #define AFE_API_VERSION_TDM_CONFIG 1 #define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1 +#define AFE_API_VERSION_CODEC_DMA_CONFIG 1 #define TIMEOUT_MS 1000 #define AFE_CMD_RESP_AVAIL 0 @@ -448,11 +495,21 @@ struct afe_param_id_tdm_cfg { u32 slot_mask; } __packed; +struct afe_param_id_cdc_dma_cfg { + u32 cdc_dma_cfg_minor_version; + u32 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u16 active_channels_mask; +} __packed; + union afe_port_config { struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch; struct afe_param_id_slimbus_cfg slim_cfg; struct afe_param_id_i2s_cfg i2s_cfg; struct afe_param_id_tdm_cfg tdm_cfg; + struct afe_param_id_cdc_dma_cfg dma_cfg; } __packed; @@ -707,6 +764,50 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = { QUINARY_TDM_TX_7, 0, 1}, [DISPLAY_PORT_RX] = { AFE_PORT_ID_HDMI_OVER_DP_RX, DISPLAY_PORT_RX, 1, 1}, + [WSA_CODEC_DMA_RX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_0, + WSA_CODEC_DMA_RX_0, 1, 1}, + [WSA_CODEC_DMA_TX_0] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_0, + WSA_CODEC_DMA_TX_0, 0, 1}, + [WSA_CODEC_DMA_RX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_RX_1, + WSA_CODEC_DMA_RX_1, 1, 1}, + [WSA_CODEC_DMA_TX_1] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_1, + WSA_CODEC_DMA_TX_1, 0, 1}, + [WSA_CODEC_DMA_TX_2] = { AFE_PORT_ID_WSA_CODEC_DMA_TX_2, + WSA_CODEC_DMA_TX_2, 0, 1}, + [VA_CODEC_DMA_TX_0] = { AFE_PORT_ID_VA_CODEC_DMA_TX_0, + VA_CODEC_DMA_TX_0, 0, 1}, + [VA_CODEC_DMA_TX_1] = { AFE_PORT_ID_VA_CODEC_DMA_TX_1, + VA_CODEC_DMA_TX_1, 0, 1}, + [VA_CODEC_DMA_TX_2] = { AFE_PORT_ID_VA_CODEC_DMA_TX_2, + VA_CODEC_DMA_TX_2, 0, 1}, + [RX_CODEC_DMA_RX_0] = { AFE_PORT_ID_RX_CODEC_DMA_RX_0, + RX_CODEC_DMA_RX_0, 1, 1}, + [TX_CODEC_DMA_TX_0] = { AFE_PORT_ID_TX_CODEC_DMA_TX_0, + TX_CODEC_DMA_TX_0, 0, 1}, + [RX_CODEC_DMA_RX_1] = { AFE_PORT_ID_RX_CODEC_DMA_RX_1, + RX_CODEC_DMA_RX_1, 1, 1}, + [TX_CODEC_DMA_TX_1] = { AFE_PORT_ID_TX_CODEC_DMA_TX_1, + TX_CODEC_DMA_TX_1, 0, 1}, + [RX_CODEC_DMA_RX_2] = { AFE_PORT_ID_RX_CODEC_DMA_RX_2, + RX_CODEC_DMA_RX_2, 1, 1}, + [TX_CODEC_DMA_TX_2] = { AFE_PORT_ID_TX_CODEC_DMA_TX_2, + TX_CODEC_DMA_TX_2, 0, 1}, + [RX_CODEC_DMA_RX_3] = { AFE_PORT_ID_RX_CODEC_DMA_RX_3, + RX_CODEC_DMA_RX_3, 1, 1}, + [TX_CODEC_DMA_TX_3] = { AFE_PORT_ID_TX_CODEC_DMA_TX_3, + TX_CODEC_DMA_TX_3, 0, 1}, + [RX_CODEC_DMA_RX_4] = { AFE_PORT_ID_RX_CODEC_DMA_RX_4, + RX_CODEC_DMA_RX_4, 1, 1}, + [TX_CODEC_DMA_TX_4] = { AFE_PORT_ID_TX_CODEC_DMA_TX_4, + TX_CODEC_DMA_TX_4, 0, 1}, + [RX_CODEC_DMA_RX_5] = { AFE_PORT_ID_RX_CODEC_DMA_RX_5, + RX_CODEC_DMA_RX_5, 1, 1}, + [TX_CODEC_DMA_TX_5] = { AFE_PORT_ID_TX_CODEC_DMA_TX_5, + TX_CODEC_DMA_TX_5, 0, 1}, + [RX_CODEC_DMA_RX_6] = { AFE_PORT_ID_RX_CODEC_DMA_RX_6, + RX_CODEC_DMA_RX_6, 1, 1}, + [RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7, + RX_CODEC_DMA_RX_7, 1, 1}, }; static void q6afe_port_free(struct kref *ref) @@ -1288,6 +1389,28 @@ int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg) } EXPORT_SYMBOL_GPL(q6afe_i2s_port_prepare); +/** + * q6afe_dam_port_prepare() - Prepare dma afe port. + * + * @port: Instance of afe port + * @cfg: DMA configuration for the afe port + * + */ +void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, + struct q6afe_cdc_dma_cfg *cfg) +{ + union afe_port_config *pcfg = &port->port_cfg; + struct afe_param_id_cdc_dma_cfg *dma_cfg = &pcfg->dma_cfg; + + dma_cfg->cdc_dma_cfg_minor_version = AFE_API_VERSION_CODEC_DMA_CONFIG; + dma_cfg->sample_rate = cfg->sample_rate; + dma_cfg->bit_width = cfg->bit_width; + dma_cfg->data_format = cfg->data_format; + dma_cfg->num_channels = cfg->num_channels; + if (!cfg->active_channels_mask) + dma_cfg->active_channels_mask = (1 << cfg->num_channels) - 1; +} +EXPORT_SYMBOL_GPL(q6afe_cdc_dma_port_prepare); /** * q6afe_port_start() - Start a afe port * @@ -1420,7 +1543,9 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id) case AFE_PORT_ID_PRIMARY_TDM_RX ... AFE_PORT_ID_QUINARY_TDM_TX_7: cfg_type = AFE_PARAM_ID_TDM_CONFIG; break; - + case AFE_PORT_ID_WSA_CODEC_DMA_RX_0 ... AFE_PORT_ID_RX_CODEC_DMA_RX_7: + cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG; + break; default: dev_err(dev, "Invalid port id 0x%x\n", port_id); return ERR_PTR(-EINVAL); diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index c7ed5422baff..1f7cbed9335d 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -5,7 +5,7 @@ #include -#define AFE_PORT_MAX 105 +#define AFE_PORT_MAX 127 #define MSM_AFE_PORT_TYPE_RX 0 #define MSM_AFE_PORT_TYPE_TX 1 @@ -184,11 +184,21 @@ struct q6afe_tdm_cfg { u16 ch_mapping[AFE_MAX_CHAN_COUNT]; }; +struct q6afe_cdc_dma_cfg { + u16 sample_rate; + u16 bit_width; + u16 data_format; + u16 num_channels; + u16 active_channels_mask; +}; + + struct q6afe_port_config { struct q6afe_hdmi_cfg hdmi; struct q6afe_slim_cfg slim; struct q6afe_i2s_cfg i2s_cfg; struct q6afe_tdm_cfg tdm; + struct q6afe_cdc_dma_cfg dma_cfg; }; struct q6afe_port; @@ -204,6 +214,8 @@ void q6afe_slim_port_prepare(struct q6afe_port *port, struct q6afe_slim_cfg *cfg); int q6afe_i2s_port_prepare(struct q6afe_port *port, struct q6afe_i2s_cfg *cfg); void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg); +void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, + struct q6afe_cdc_dma_cfg *cfg); int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, -- cgit From 825492cb518bcf654e9205b3c723585191314d1a Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:26 +0100 Subject: ASoC: q6dsp: q6routing: add support to Codec DMA ports Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6routing.c | 121 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 120 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index 25d23e0266c7..b12539fae6ed 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -113,7 +113,19 @@ { mix_name, "QUIN_TDM_TX_4", "QUIN_TDM_TX_4"}, \ { mix_name, "QUIN_TDM_TX_5", "QUIN_TDM_TX_5"}, \ { mix_name, "QUIN_TDM_TX_6", "QUIN_TDM_TX_6"}, \ - { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"} + { mix_name, "QUIN_TDM_TX_7", "QUIN_TDM_TX_7"}, \ + { mix_name, "WSA_CODEC_DMA_TX_0", "WSA_CODEC_DMA_TX_0"}, \ + { mix_name, "WSA_CODEC_DMA_TX_1", "WSA_CODEC_DMA_TX_1"}, \ + { mix_name, "WSA_CODEC_DMA_TX_2", "WSA_CODEC_DMA_TX_2"}, \ + { mix_name, "VA_CODEC_DMA_TX_0", "VA_CODEC_DMA_TX_0"}, \ + { mix_name, "VA_CODEC_DMA_TX_1", "VA_CODEC_DMA_TX_1"}, \ + { mix_name, "VA_CODEC_DMA_TX_2", "VA_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_0", "TX_CODEC_DMA_TX_0"}, \ + { mix_name, "TX_CODEC_DMA_TX_1", "TX_CODEC_DMA_TX_1"}, \ + { mix_name, "TX_CODEC_DMA_TX_2", "TX_CODEC_DMA_TX_2"}, \ + { mix_name, "TX_CODEC_DMA_TX_3", "TX_CODEC_DMA_TX_3"}, \ + { mix_name, "TX_CODEC_DMA_TX_4", "TX_CODEC_DMA_TX_4"}, \ + { mix_name, "TX_CODEC_DMA_TX_5", "TX_CODEC_DMA_TX_5"} #define Q6ROUTING_TX_MIXERS(id) \ SOC_SINGLE_EXT("PRI_MI2S_TX", PRIMARY_MI2S_TX, \ @@ -267,6 +279,42 @@ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), \ SOC_SINGLE_EXT("QUIN_TDM_TX_7", QUINARY_TDM_TX_7, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_0", WSA_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_1", WSA_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("WSA_CODEC_DMA_TX_2", WSA_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_0", VA_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_1", VA_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("VA_CODEC_DMA_TX_2", VA_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_0", TX_CODEC_DMA_TX_0, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_1", TX_CODEC_DMA_TX_1, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_2", TX_CODEC_DMA_TX_2, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_3", TX_CODEC_DMA_TX_3, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_4", TX_CODEC_DMA_TX_4, \ + id, 1, 0, msm_routing_get_audio_mixer, \ + msm_routing_put_audio_mixer), \ + SOC_SINGLE_EXT("TX_CODEC_DMA_TX_5", TX_CODEC_DMA_TX_5, \ id, 1, 0, msm_routing_get_audio_mixer, \ msm_routing_put_audio_mixer), @@ -609,6 +657,36 @@ static const struct snd_kcontrol_new quin_tdm_rx_6_mixer_controls[] = { static const struct snd_kcontrol_new quin_tdm_rx_7_mixer_controls[] = { Q6ROUTING_RX_MIXERS(QUINARY_TDM_RX_7) }; +static const struct snd_kcontrol_new wsa_codec_dma_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_0) }; + +static const struct snd_kcontrol_new wsa_codec_dma_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(WSA_CODEC_DMA_RX_1) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_0_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_0) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_1_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_1) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_2_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_2) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_3_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_3) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_4_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_4) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_5_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_5) }; + +static const struct snd_kcontrol_new rxcodec_dma_rx_6_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_6) }; + +static const struct snd_kcontrol_new rx_codec_dma_rx_7_mixer_controls[] = { + Q6ROUTING_RX_MIXERS(RX_CODEC_DMA_RX_7) }; + static const struct snd_kcontrol_new mmul1_mixer_controls[] = { Q6ROUTING_TX_MIXERS(MSM_FRONTEND_DAI_MULTIMEDIA1) }; @@ -819,6 +897,37 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = { SND_SOC_DAPM_MIXER("QUIN_TDM_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, quin_tdm_rx_7_mixer_controls, ARRAY_SIZE(quin_tdm_rx_7_mixer_controls)), + + SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_codec_dma_rx_0_mixer_controls, + ARRAY_SIZE(wsa_codec_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("WSA_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + wsa_codec_dma_rx_1_mixer_controls, + ARRAY_SIZE(wsa_codec_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_0_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_0_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_1_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_1_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_2_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_2_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_3_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_3_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_4 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_4_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_4_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_5 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_5_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_5_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_6 Audio Mixer", SND_SOC_NOPM, 0, 0, + rxcodec_dma_rx_6_mixer_controls, + ARRAY_SIZE(rxcodec_dma_rx_6_mixer_controls)), + SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0, + rx_codec_dma_rx_7_mixer_controls, + ARRAY_SIZE(rx_codec_dma_rx_7_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0, mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)), SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0, @@ -901,6 +1010,16 @@ static const struct snd_soc_dapm_route intercon[] = { Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_5 Audio Mixer", "QUIN_TDM_RX_5"), Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_6 Audio Mixer", "QUIN_TDM_RX_6"), Q6ROUTING_RX_DAPM_ROUTE("QUIN_TDM_RX_7 Audio Mixer", "QUIN_TDM_RX_7"), + Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_0 Audio Mixer", "WSA_CODEC_DMA_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("WSA_CODEC_DMA_RX_1 Audio Mixer", "WSA_CODEC_DMA_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_0 Audio Mixer", "RX_CODEC_DMA_RX_0"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_1 Audio Mixer", "RX_CODEC_DMA_RX_1"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_2 Audio Mixer", "RX_CODEC_DMA_RX_2"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_3 Audio Mixer", "RX_CODEC_DMA_RX_3"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_4 Audio Mixer", "RX_CODEC_DMA_RX_4"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_5 Audio Mixer", "RX_CODEC_DMA_RX_5"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"), + Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"), Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"), -- cgit From 342a4f8ca12b1cac812151b05f8a837eebc6885c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:27 +0100 Subject: ASoC: q6dsp: q6afe: prepare afe_apr_send_pkt to take response opcode Update afe_apr_send_pkt() to take response opcode that it should wait for. This is helpful in cases where we expect response other than the actual command opcode. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-4-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 8ceefb431bcb..f934c69f0a14 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -902,10 +902,9 @@ int q6afe_get_port_id(int index) EXPORT_SYMBOL_GPL(q6afe_get_port_id); static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, - struct q6afe_port *port) + struct q6afe_port *port, uint32_t rsp_opcode) { wait_queue_head_t *wait = &port->wait; - struct apr_hdr *hdr = &pkt->hdr; int ret; mutex_lock(&afe->lock); @@ -919,7 +918,7 @@ static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, goto err; } - ret = wait_event_timeout(*wait, (port->result.opcode == hdr->opcode), + ret = wait_event_timeout(*wait, (port->result.opcode == rsp_opcode), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { ret = -ETIMEDOUT; @@ -976,7 +975,7 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, pdata->param_id = param_id; pdata->param_size = psize; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_SVC_CMD_SET_PARAM); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); @@ -1025,7 +1024,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, pdata->param_id = param_id; pdata->param_size = psize; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_SET_PARAM_V2); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); @@ -1155,7 +1154,7 @@ int q6afe_port_stop(struct q6afe_port *port) stop->port_id = port_id; stop->reserved = 0; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_DEVICE_STOP); if (ret) dev_err(afe->dev, "AFE close failed %d\n", ret); @@ -1467,7 +1466,7 @@ int q6afe_port_start(struct q6afe_port *port) start->port_id = port_id; - ret = afe_apr_send_pkt(afe, pkt, port); + ret = afe_apr_send_pkt(afe, pkt, port, AFE_PORT_CMD_DEVICE_START); if (ret) dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", port_id, ret); -- cgit From 181202d021f51d4c0442e71adc34e9629a35a6d8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:28 +0100 Subject: ASoC: q6dsp: q6afe: add global q6afe waitqueue In some cases like clocks q6afe would have to process commands without an associated q6afe port, in such cases waitqueue is required at global level to wait for the command to finish. This patch also adds the command result to go with this waitqueue. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-5-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index f934c69f0a14..2a8e3c3acb10 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -362,6 +362,8 @@ struct q6afe { struct device *dev; struct q6core_svc_api_info ainfo; struct mutex lock; + struct aprv2_ibasic_rsp_result_t result; + wait_queue_head_t wait; struct list_head port_list; spinlock_t port_list_lock; }; @@ -905,11 +907,20 @@ static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, struct q6afe_port *port, uint32_t rsp_opcode) { wait_queue_head_t *wait = &port->wait; + struct aprv2_ibasic_rsp_result_t *result; int ret; mutex_lock(&afe->lock); - port->result.opcode = 0; - port->result.status = 0; + if (port) { + wait = &port->wait; + result = &port->result; + } else { + result = &afe->result; + wait = &afe->wait; + } + + result->opcode = 0; + result->status = 0; ret = apr_send_pkt(afe->apr, pkt); if (ret < 0) { @@ -918,13 +929,13 @@ static int afe_apr_send_pkt(struct q6afe *afe, struct apr_pkt *pkt, goto err; } - ret = wait_event_timeout(*wait, (port->result.opcode == rsp_opcode), + ret = wait_event_timeout(*wait, (result->opcode == rsp_opcode), msecs_to_jiffies(TIMEOUT_MS)); if (!ret) { ret = -ETIMEDOUT; - } else if (port->result.status > 0) { + } else if (result->status > 0) { dev_err(afe->dev, "DSP returned error[%x]\n", - port->result.status); + result->status); ret = -EINVAL; } else { ret = 0; @@ -1594,6 +1605,7 @@ static int q6afe_probe(struct apr_device *adev) q6core_get_svc_api_info(adev->svc_id, &afe->ainfo); afe->apr = adev; mutex_init(&afe->lock); + init_waitqueue_head(&afe->wait); afe->dev = dev; INIT_LIST_HEAD(&afe->port_list); spin_lock_init(&afe->port_list_lock); -- cgit From 55e07531d922540c656c7fc2e21d76e1b751f279 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:29 +0100 Subject: ASoC: q6dsp: q6afe: add lpass hw voting support Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-6-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 99 ++++++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6afe.h | 8 ++++ 2 files changed, 107 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 2a8e3c3acb10..51c94dd9998d 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -43,6 +43,9 @@ #define AFE_PARAM_ID_TDM_CONFIG 0x0001029D #define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297 #define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8 +#define AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f4 +#define AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f5 +#define AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST 0x000100f6 /* I2S config specific */ #define AFE_API_VERSION_I2S_CONFIG 0x1 @@ -545,6 +548,18 @@ struct q6afe_port { struct list_head node; }; +struct afe_cmd_remote_lpass_core_hw_vote_request { + uint32_t hw_block_id; + char client_name[8]; +} __packed; + +struct afe_cmd_remote_lpass_core_hw_devote_request { + uint32_t hw_block_id; + uint32_t client_handle; +} __packed; + + + struct afe_port_map { int port_id; int token; @@ -880,6 +895,11 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) } } break; + case AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST: + afe->result.opcode = hdr->opcode; + afe->result.status = res->status; + wake_up(&afe->wait); + break; default: break; } @@ -1593,6 +1613,85 @@ void q6afe_port_put(struct q6afe_port *port) } EXPORT_SYMBOL_GPL(q6afe_port_put); +int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + uint32_t client_handle) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_cmd_remote_lpass_core_hw_devote_request *vote_cfg; + struct apr_pkt *pkt; + int ret = 0; + int pkt_size; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + vote_cfg = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = hw_block_id; + pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST; + vote_cfg->hw_block_id = hw_block_id; + vote_cfg->client_handle = client_handle; + + ret = apr_send_pkt(afe->apr, pkt); + if (ret < 0) + dev_err(afe->dev, "AFE failed to unvote (%d)\n", hw_block_id); + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL(q6afe_unvote_lpass_core_hw); + +int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + char *client_name, uint32_t *client_handle) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_cmd_remote_lpass_core_hw_vote_request *vote_cfg; + struct apr_pkt *pkt; + int ret = 0; + int pkt_size; + void *p; + + pkt_size = APR_HDR_SIZE + sizeof(*vote_cfg); + p = kzalloc(pkt_size, GFP_KERNEL); + if (!p) + return -ENOMEM; + + pkt = p; + vote_cfg = p + APR_HDR_SIZE; + + pkt->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, + APR_HDR_LEN(APR_HDR_SIZE), + APR_PKT_VER); + pkt->hdr.pkt_size = pkt_size; + pkt->hdr.src_port = 0; + pkt->hdr.dest_port = 0; + pkt->hdr.token = hw_block_id; + pkt->hdr.opcode = AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST; + vote_cfg->hw_block_id = hw_block_id; + strlcpy(vote_cfg->client_name, client_name, + sizeof(vote_cfg->client_name)); + + ret = afe_apr_send_pkt(afe, pkt, NULL, + AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST); + if (ret) + dev_err(afe->dev, "AFE failed to vote (%d)\n", hw_block_id); + + + kfree(pkt); + return ret; +} +EXPORT_SYMBOL(q6afe_vote_lpass_core_hw); + static int q6afe_probe(struct apr_device *adev) { struct q6afe *afe; diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index 1f7cbed9335d..93592670ef0b 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -133,6 +133,10 @@ /* Clock ID for INT MCLK1 */ #define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306 +#define Q6AFE_LPASS_CORE_AVTIMER_BLOCK 0x2 +#define Q6AFE_LPASS_CORE_HW_MACRO_BLOCK 0x3 +#define Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK 0x4 + /* Clock attribute for invalid use (reserved for internal usage) */ #define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID 0x0 /* Clock attribute for no couple case */ @@ -220,4 +224,8 @@ void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir); +int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + char *client_name, uint32_t *client_handle); +int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, + uint32_t client_handle); #endif /* __Q6AFE_H__ */ -- cgit From 84ab3b9f19f6ff0bb5df6c6deea75ab4c1d2aff8 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:30 +0100 Subject: ASoC: q6dsp: q6afe: update q6afe_set_param to support global clocks Previously there was no case where we need to set clock or send commands that are not associated with q6afe ports, now we have cases like clock voting and clock consumers like codecs that needed these clocks. update q6afe_set_param() to support such cases, including token passing. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-7-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 51c94dd9998d..9ed5537ee58e 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -967,14 +967,13 @@ err: return ret; } -static int q6afe_port_set_param(struct q6afe_port *port, void *data, - int param_id, int module_id, int psize) +static int q6afe_set_param(struct q6afe *afe, struct q6afe_port *port, + void *data, int param_id, int module_id, int psize, + int token) { struct afe_svc_cmd_set_param *param; struct afe_port_param_data_v2 *pdata; - struct q6afe *afe = port->afe; struct apr_pkt *pkt; - u16 port_id = port->id; int ret, pkt_size; void *p, *pl; @@ -995,7 +994,7 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, pkt->hdr.pkt_size = pkt_size; pkt->hdr.src_port = 0; pkt->hdr.dest_port = 0; - pkt->hdr.token = port->token; + pkt->hdr.token = token; pkt->hdr.opcode = AFE_SVC_CMD_SET_PARAM; param->payload_size = sizeof(*pdata) + psize; @@ -1008,13 +1007,19 @@ static int q6afe_port_set_param(struct q6afe_port *port, void *data, ret = afe_apr_send_pkt(afe, pkt, port, AFE_SVC_CMD_SET_PARAM); if (ret) - dev_err(afe->dev, "AFE enable for port 0x%x failed %d\n", - port_id, ret); + dev_err(afe->dev, "AFE set params failed %d\n", ret); kfree(pkt); return ret; } +static int q6afe_port_set_param(struct q6afe_port *port, void *data, + int param_id, int module_id, int psize) +{ + return q6afe_set_param(port->afe, port, data, param_id, module_id, + psize, port->token); +} + static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, int param_id, int module_id, int psize) { @@ -1064,7 +1069,7 @@ static int q6afe_port_set_param_v2(struct q6afe_port *port, void *data, return ret; } -static int q6afe_set_lpass_clock(struct q6afe_port *port, +static int q6afe_port_set_lpass_clock(struct q6afe_port *port, struct afe_clk_cfg *cfg) { return q6afe_port_set_param_v2(port, cfg, @@ -1111,7 +1116,7 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, ccfg.clk_src = clk_src; ccfg.clk_root = clk_root; ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK1_VALID; - ret = q6afe_set_lpass_clock(port, &ccfg); + ret = q6afe_port_set_lpass_clock(port, &ccfg); break; case LPAIF_OSR_CLK: @@ -1120,7 +1125,7 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, ccfg.clk_src = clk_src; ccfg.clk_root = clk_root; ccfg.clk_set_mode = Q6AFE_LPASS_MODE_CLK2_VALID; - ret = q6afe_set_lpass_clock(port, &ccfg); + ret = q6afe_port_set_lpass_clock(port, &ccfg); break; case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: -- cgit From 0c3e35fc1ebe22a5254ba3bff2599a2c49b00abe Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:31 +0100 Subject: ASoC: q6dsp: q6afe: add codec lpass clocks LPASS now has integrated codec control whose clocks are controlled by Q6DSP. This patch adds support to those clocks. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-8-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe.c | 24 ++++++++++++++++++++++++ sound/soc/qcom/qdsp6/q6afe.h | 11 +++++++++++ 2 files changed, 35 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 9ed5537ee58e..688878a002a4 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -359,6 +359,7 @@ #define TIMEOUT_MS 1000 #define AFE_CMD_RESP_AVAIL 0 #define AFE_CMD_RESP_NONE 1 +#define AFE_CLK_TOKEN 1024 struct q6afe { struct apr_device *apr; @@ -887,6 +888,9 @@ static int q6afe_callback(struct apr_device *adev, struct apr_resp_pkt *data) port->result = *res; wake_up(&port->wait); kref_put(&port->refcount, q6afe_port_free); + } else if (hdr->token == AFE_CLK_TOKEN) { + afe->result = *res; + wake_up(&afe->wait); } break; default: @@ -1094,6 +1098,25 @@ static int q6afe_set_digital_codec_core_clock(struct q6afe_port *port, sizeof(*cfg)); } +int q6afe_set_lpass_clock(struct device *dev, int clk_id, int attri, + int clk_root, unsigned int freq) +{ + struct q6afe *afe = dev_get_drvdata(dev->parent); + struct afe_clk_set cset = {0,}; + + cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; + cset.clk_id = clk_id; + cset.clk_freq_in_hz = freq; + cset.clk_attri = attri; + cset.clk_root = clk_root; + cset.enable = !!freq; + + return q6afe_set_param(afe, NULL, &cset, AFE_PARAM_ID_CLOCK_SET, + AFE_MODULE_CLOCK_SET, sizeof(cset), + AFE_CLK_TOKEN); +} +EXPORT_SYMBOL_GPL(q6afe_set_lpass_clock); + int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir) @@ -1130,6 +1153,7 @@ int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: case Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT ... Q6AFE_LPASS_CLK_ID_QUIN_TDM_EBIT: + case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK: cset.clk_set_minor_version = AFE_API_VERSION_CLOCK_SET; cset.clk_id = clk_id; cset.clk_freq_in_hz = freq; diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h index 93592670ef0b..22e10269aa10 100644 --- a/sound/soc/qcom/qdsp6/q6afe.h +++ b/sound/soc/qcom/qdsp6/q6afe.h @@ -133,6 +133,15 @@ /* Clock ID for INT MCLK1 */ #define Q6AFE_LPASS_CLK_ID_INT_MCLK_1 0x306 +#define Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK 0x309 +#define Q6AFE_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x30a +#define Q6AFE_LPASS_CLK_ID_TX_CORE_MCLK 0x30c +#define Q6AFE_LPASS_CLK_ID_TX_CORE_NPL_MCLK 0x30d +#define Q6AFE_LPASS_CLK_ID_RX_CORE_MCLK 0x30e +#define Q6AFE_LPASS_CLK_ID_RX_CORE_NPL_MCLK 0x30f +#define Q6AFE_LPASS_CLK_ID_VA_CORE_MCLK 0x30b +#define Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK 0x310 + #define Q6AFE_LPASS_CORE_AVTIMER_BLOCK 0x2 #define Q6AFE_LPASS_CORE_HW_MACRO_BLOCK 0x3 #define Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK 0x4 @@ -224,6 +233,8 @@ void q6afe_cdc_dma_port_prepare(struct q6afe_port *port, int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id, int clk_src, int clk_root, unsigned int freq, int dir); +int q6afe_set_lpass_clock(struct device *dev, int clk_id, int clk_src, + int clk_root, unsigned int freq); int q6afe_vote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, char *client_name, uint32_t *client_handle); int q6afe_unvote_lpass_core_hw(struct device *dev, uint32_t hw_block_id, -- cgit From 1fdbcfa9fdee6f9cc00129f0f5ed0ff29cfef646 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 11:17:32 +0100 Subject: ASoC: q6dsp: q6afe-dai: add support to Codec DMA ports Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910101732.23484-9-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe-dai.c | 229 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index 0168af849272..d58b86a98114 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -55,6 +55,48 @@ .remove = msm_dai_q6_dai_remove, \ } +#define Q6AFE_CDC_DMA_RX_DAI(did) { \ + .playback = { \ + .stream_name = #did" Playback", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6dma_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + +#define Q6AFE_CDC_DMA_TX_DAI(did) { \ + .capture = { \ + .stream_name = #did" Capture", \ + .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_176400, \ + .formats = SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE, \ + .channels_min = 1, \ + .channels_max = 8, \ + .rate_min = 8000, \ + .rate_max = 176400, \ + }, \ + .name = #did, \ + .ops = &q6dma_ops, \ + .id = did, \ + .probe = msm_dai_q6_dai_probe, \ + .remove = msm_dai_q6_dai_remove, \ + } + struct q6afe_dai_priv_data { uint32_t sd_line_mask; uint32_t sync_mode; @@ -307,6 +349,90 @@ static int q6tdm_hw_params(struct snd_pcm_substream *substream, return 0; } + +static int q6dma_set_channel_map(struct snd_soc_dai *dai, + unsigned int tx_num, unsigned int *tx_ch_mask, + unsigned int rx_num, unsigned int *rx_ch_mask) +{ + + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg; + int ch_mask; + int rc = 0; + + switch (dai->id) { + case WSA_CODEC_DMA_TX_0: + case WSA_CODEC_DMA_TX_1: + case WSA_CODEC_DMA_TX_2: + case VA_CODEC_DMA_TX_0: + case VA_CODEC_DMA_TX_1: + case VA_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_0: + case TX_CODEC_DMA_TX_1: + case TX_CODEC_DMA_TX_2: + case TX_CODEC_DMA_TX_3: + case TX_CODEC_DMA_TX_4: + case TX_CODEC_DMA_TX_5: + if (!tx_ch_mask) { + dev_err(dai->dev, "tx slot not found\n"); + return -EINVAL; + } + + if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid tx num %d\n", + tx_num); + return -EINVAL; + } + ch_mask = *tx_ch_mask; + + break; + case WSA_CODEC_DMA_RX_0: + case WSA_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_0: + case RX_CODEC_DMA_RX_1: + case RX_CODEC_DMA_RX_2: + case RX_CODEC_DMA_RX_3: + case RX_CODEC_DMA_RX_4: + case RX_CODEC_DMA_RX_5: + case RX_CODEC_DMA_RX_6: + case RX_CODEC_DMA_RX_7: + /* rx */ + if (!rx_ch_mask) { + dev_err(dai->dev, "rx slot not found\n"); + return -EINVAL; + } + if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) { + dev_err(dai->dev, "invalid rx num %d\n", + rx_num); + return -EINVAL; + } + ch_mask = *rx_ch_mask; + + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; + } + + cfg->active_channels_mask = ch_mask; + + return rc; +} + +static int q6dma_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct q6afe_cdc_dma_cfg *cfg = &dai_data->port_config[dai->id].dma_cfg; + + cfg->bit_width = params_width(params); + cfg->sample_rate = params_rate(params); + cfg->num_channels = params_channels(params); + + return 0; +} static void q6afe_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { @@ -362,6 +488,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream, q6afe_tdm_port_prepare(dai_data->port[dai->id], &dai_data->port_config[dai->id].tdm); break; + case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7: + q6afe_cdc_dma_port_prepare(dai_data->port[dai->id], + &dai_data->port_config[dai->id].dma_cfg); + break; default: return -EINVAL; } @@ -430,6 +560,7 @@ static int q6afe_mi2s_set_sysclk(struct snd_soc_dai *dai, freq, dir); case Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT ... Q6AFE_LPASS_CLK_ID_QUI_MI2S_OSR: case Q6AFE_LPASS_CLK_ID_MCLK_1 ... Q6AFE_LPASS_CLK_ID_INT_MCLK_1: + case Q6AFE_LPASS_CLK_ID_WSA_CORE_MCLK ... Q6AFE_LPASS_CLK_ID_VA_CORE_2X_MCLK: return q6afe_port_set_sysclk(port, clk_id, Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO, Q6AFE_LPASS_CLK_ROOT_DEFAULT, @@ -562,6 +693,29 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = { {"PRI_MI2S_TX", NULL, "Primary MI2S Capture"}, {"SEC_MI2S_TX", NULL, "Secondary MI2S Capture"}, {"QUAT_MI2S_TX", NULL, "Quaternary MI2S Capture"}, + + {"WSA_CODEC_DMA_RX_0 Playback", NULL, "WSA_CODEC_DMA_RX_0"}, + {"WSA_CODEC_DMA_TX_0", NULL, "WSA_CODEC_DMA_TX_0 Capture"}, + {"WSA_CODEC_DMA_RX_1 Playback", NULL, "WSA_CODEC_DMA_RX_1"}, + {"WSA_CODEC_DMA_TX_1", NULL, "WSA_CODEC_DMA_TX_1 Capture"}, + {"WSA_CODEC_DMA_TX_2", NULL, "WSA_CODEC_DMA_TX_2 Capture"}, + {"VA_CODEC_DMA_TX_0", NULL, "VA_CODEC_DMA_TX_0 Capture"}, + {"VA_CODEC_DMA_TX_1", NULL, "VA_CODEC_DMA_TX_1 Capture"}, + {"VA_CODEC_DMA_TX_2", NULL, "VA_CODEC_DMA_TX_2 Capture"}, + {"RX_CODEC_DMA_RX_0 Playback", NULL, "RX_CODEC_DMA_RX_0"}, + {"TX_CODEC_DMA_TX_0", NULL, "TX_CODEC_DMA_TX_0 Capture"}, + {"RX_CODEC_DMA_RX_1 Playback", NULL, "RX_CODEC_DMA_RX_1"}, + {"TX_CODEC_DMA_TX_1", NULL, "TX_CODEC_DMA_TX_1 Capture"}, + {"RX_CODEC_DMA_RX_2 Playback", NULL, "RX_CODEC_DMA_RX_2"}, + {"TX_CODEC_DMA_TX_2", NULL, "TX_CODEC_DMA_TX_2 Capture"}, + {"RX_CODEC_DMA_RX_3 Playback", NULL, "RX_CODEC_DMA_RX_3"}, + {"TX_CODEC_DMA_TX_3", NULL, "TX_CODEC_DMA_TX_3 Capture"}, + {"RX_CODEC_DMA_RX_4 Playback", NULL, "RX_CODEC_DMA_RX_4"}, + {"TX_CODEC_DMA_TX_4", NULL, "TX_CODEC_DMA_TX_4 Capture"}, + {"RX_CODEC_DMA_RX_5 Playback", NULL, "RX_CODEC_DMA_RX_5"}, + {"TX_CODEC_DMA_TX_5", NULL, "TX_CODEC_DMA_TX_5 Capture"}, + {"RX_CODEC_DMA_RX_6 Playback", NULL, "RX_CODEC_DMA_RX_6"}, + {"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"}, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -594,6 +748,14 @@ static const struct snd_soc_dai_ops q6tdm_ops = { .hw_params = q6tdm_hw_params, }; +static const struct snd_soc_dai_ops q6dma_ops = { + .prepare = q6afe_dai_prepare, + .shutdown = q6afe_dai_shutdown, + .set_sysclk = q6afe_mi2s_set_sysclk, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, +}; + static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai) { struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -1128,6 +1290,28 @@ static struct snd_soc_dai_driver q6afe_dais[] = { .probe = msm_dai_q6_dai_probe, .remove = msm_dai_q6_dai_remove, }, + Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_0), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_RX_DAI(WSA_CODEC_DMA_RX_1), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_TX_DAI(WSA_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_TX_DAI(VA_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_0), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_0), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_1), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_1), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_2), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_2), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_3), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_3), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_4), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_4), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_5), + Q6AFE_CDC_DMA_TX_DAI(TX_CODEC_DMA_TX_5), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_6), + Q6AFE_CDC_DMA_RX_DAI(RX_CODEC_DMA_RX_7), }; static int q6afe_of_xlate_dai_name(struct snd_soc_component *component, @@ -1350,6 +1534,51 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = { SND_SOC_DAPM_AIF_OUT("QUIN_TDM_TX_7", NULL, 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT_RX", "NULL", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("WSA_CODEC_DMA_RX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("WSA_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("VA_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_0", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_1", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_2", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_3", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_3", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_4", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_4", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_5", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("TX_CODEC_DMA_TX_5", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_6", "NULL", + 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_7", "NULL", + 0, SND_SOC_NOPM, 0, 0), }; static const struct snd_soc_component_driver q6afe_dai_component = { -- cgit From 7e5bfdddd8772011a2d38cf6be821d616db6cf8c Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Fri, 11 Sep 2020 10:48:33 +0800 Subject: ASoC: mediatek: mt8183-da7219: support machine driver with rt1015p Supports machine driver with rt1015p ("mt8183_da7219_rt1015p"). Embeds in the existing mt8183-da7219-max98357.c because they share most of the code. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20200911024833.1673961-3-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/Kconfig | 1 + sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 40 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) (limited to 'sound') diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig index f7bc007bbdec..76e055d1dfb2 100644 --- a/sound/soc/mediatek/Kconfig +++ b/sound/soc/mediatek/Kconfig @@ -140,6 +140,7 @@ config SND_SOC_MT8183_DA7219_MAX98357A select SND_SOC_MT6358 select SND_SOC_MAX98357A select SND_SOC_RT1015 + select SND_SOC_RT1015P select SND_SOC_DA7219 select SND_SOC_BT_SCO select SND_SOC_HDMI_CODEC diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 06d0a4f80fc1..68fe23b96b14 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -348,6 +348,12 @@ SND_SOC_DAILINK_DEFS(i2s3_rt1015, COMP_CODEC(DA7219_DEV_NAME, DA7219_CODEC_DAI)), DAILINK_COMP_ARRAY(COMP_EMPTY())); +SND_SOC_DAILINK_DEFS(i2s3_rt1015p, + DAILINK_COMP_ARRAY(COMP_CPU("I2S3")), + DAILINK_COMP_ARRAY(COMP_CODEC("rt1015p", "HiFi"), + COMP_CODEC(DA7219_DEV_NAME, DA7219_CODEC_DAI)), + DAILINK_COMP_ARRAY(COMP_EMPTY())); + SND_SOC_DAILINK_DEFS(i2s5, DAILINK_COMP_ARRAY(COMP_CPU("I2S5")), DAILINK_COMP_ARRAY(COMP_CODEC("bt-sco", "bt-sco-pcm")), @@ -641,6 +647,23 @@ static struct snd_soc_card mt8183_da7219_rt1015_card = { .num_configs = ARRAY_SIZE(mt8183_da7219_rt1015_codec_conf), }; +static struct snd_soc_card mt8183_da7219_rt1015p_card = { + .name = "mt8183_da7219_rt1015p", + .owner = THIS_MODULE, + .controls = mt8183_da7219_max98357_snd_controls, + .num_controls = ARRAY_SIZE(mt8183_da7219_max98357_snd_controls), + .dapm_widgets = mt8183_da7219_max98357_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(mt8183_da7219_max98357_dapm_widgets), + .dapm_routes = mt8183_da7219_max98357_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(mt8183_da7219_max98357_dapm_routes), + .dai_link = mt8183_da7219_dai_links, + .num_links = ARRAY_SIZE(mt8183_da7219_dai_links), + .aux_dev = &mt8183_da7219_max98357_headset_dev, + .num_aux_devs = 1, + .codec_conf = mt6358_codec_conf, + .num_configs = ARRAY_SIZE(mt6358_codec_conf), +}; + static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card; @@ -696,6 +719,19 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) dai_link->platforms = i2s3_rt1015_platforms; dai_link->num_platforms = ARRAY_SIZE(i2s3_rt1015_platforms); + } else if (card == &mt8183_da7219_rt1015p_card) { + dai_link->be_hw_params_fixup = + mt8183_rt1015_i2s_hw_params_fixup; + dai_link->ops = &mt8183_da7219_i2s_ops; + dai_link->cpus = i2s3_rt1015p_cpus; + dai_link->num_cpus = + ARRAY_SIZE(i2s3_rt1015p_cpus); + dai_link->codecs = i2s3_rt1015p_codecs; + dai_link->num_codecs = + ARRAY_SIZE(i2s3_rt1015p_codecs); + dai_link->platforms = i2s3_rt1015p_platforms; + dai_link->num_platforms = + ARRAY_SIZE(i2s3_rt1015p_platforms); } } @@ -742,6 +778,10 @@ static const struct of_device_id mt8183_da7219_max98357_dt_match[] = { .compatible = "mediatek,mt8183_da7219_rt1015", .data = &mt8183_da7219_rt1015_card, }, + { + .compatible = "mediatek,mt8183_da7219_rt1015p", + .data = &mt8183_da7219_rt1015p_card, + }, {} }; #endif -- cgit From 18c140f4a2de8fa674d52fe522a47133bc124f81 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 10 Sep 2020 15:41:10 +0300 Subject: ASoC: ti: j721e-evm: Add support for j7200-cpb audio When j7200 SOM is attached to the CPB we only have parent clock for 48KHz family and the rate of the parent clock is 2359296000Hz. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200910124110.19361-3-peter.ujfalusi@ti.com Signed-off-by: Mark Brown --- sound/soc/ti/j721e-evm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index cb074af47a7d..29b73303f3fc 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -525,6 +525,14 @@ static const struct j721e_audio_match_data j721e_cpb_ivi_data = { }, }; +static const struct j721e_audio_match_data j7200_cpb_data = { + .board_type = J721E_BOARD_CPB, + .num_links = 2, /* CPB pcm3168a */ + .pll_rates = { + [J721E_CLK_PARENT_48000] = 2359296000u, /* PLL4 */ + }, +}; + static const struct of_device_id j721e_audio_of_match[] = { { .compatible = "ti,j721e-cpb-audio", @@ -532,6 +540,9 @@ static const struct of_device_id j721e_audio_of_match[] = { }, { .compatible = "ti,j721e-cpb-ivi-audio", .data = &j721e_cpb_ivi_data, + }, { + .compatible = "ti,j7200-cpb-audio", + .data = &j7200_cpb_data, }, { }, }; -- cgit From 64b969177c744a76142f45823dd9d94c38f79cd0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 10 Sep 2020 19:41:22 +0300 Subject: ASoC: SOF: rename cores_mask to host_managed_cores_mask Rename the cores_mask in struct sof_intel_dsp_desc to host_managed_cores_mask to be more indicative of the fact that only these cores can be powered up/down by the host. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Keyon Jie Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200910164125.2033062-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 2 +- sound/soc/sof/intel/bdw.c | 2 +- sound/soc/sof/intel/byt.c | 6 +++--- sound/soc/sof/intel/cnl.c | 8 ++++---- sound/soc/sof/intel/hda-dsp.c | 2 +- sound/soc/sof/intel/hda-loader.c | 6 +++--- sound/soc/sof/intel/hda.c | 2 +- sound/soc/sof/intel/shim.h | 2 +- sound/soc/sof/intel/tgl.c | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 9e29d4fd393a..25d3f5775aac 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -129,7 +129,7 @@ const struct sof_intel_dsp_desc apl_chip_info = { /* Apollolake */ .cores_num = 2, .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), .ipc_req = HDA_DSP_REG_HIPCI, .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, .ipc_ack = HDA_DSP_REG_HIPCIE, diff --git a/sound/soc/sof/intel/bdw.c b/sound/soc/sof/intel/bdw.c index 99fd0bd7276e..50a4a73e6b9f 100644 --- a/sound/soc/sof/intel/bdw.c +++ b/sound/soc/sof/intel/bdw.c @@ -655,7 +655,7 @@ EXPORT_SYMBOL_NS(sof_bdw_ops, SND_SOC_SOF_BROADWELL); const struct sof_intel_dsp_desc bdw_chip_info = { .cores_num = 1, - .cores_mask = 1, + .host_managed_cores_mask = 1, }; EXPORT_SYMBOL_NS(bdw_chip_info, SND_SOC_SOF_BROADWELL); diff --git a/sound/soc/sof/intel/byt.c b/sound/soc/sof/intel/byt.c index 49f67f1b94e0..186736ee5fc2 100644 --- a/sound/soc/sof/intel/byt.c +++ b/sound/soc/sof/intel/byt.c @@ -651,7 +651,7 @@ EXPORT_SYMBOL_NS(sof_tng_ops, SND_SOC_SOF_MERRIFIELD); const struct sof_intel_dsp_desc tng_chip_info = { .cores_num = 1, - .cores_mask = 1, + .host_managed_cores_mask = 1, }; EXPORT_SYMBOL_NS(tng_chip_info, SND_SOC_SOF_MERRIFIELD); @@ -896,7 +896,7 @@ EXPORT_SYMBOL_NS(sof_byt_ops, SND_SOC_SOF_BAYTRAIL); const struct sof_intel_dsp_desc byt_chip_info = { .cores_num = 1, - .cores_mask = 1, + .host_managed_cores_mask = 1, }; EXPORT_SYMBOL_NS(byt_chip_info, SND_SOC_SOF_BAYTRAIL); @@ -976,7 +976,7 @@ EXPORT_SYMBOL_NS(sof_cht_ops, SND_SOC_SOF_BAYTRAIL); const struct sof_intel_dsp_desc cht_chip_info = { .cores_num = 1, - .cores_mask = 1, + .host_managed_cores_mask = 1, }; EXPORT_SYMBOL_NS(cht_chip_info, SND_SOC_SOF_BAYTRAIL); diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 70f14b2aa954..51e336d7348f 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -334,7 +334,7 @@ const struct sof_intel_dsp_desc cnl_chip_info = { /* Cannonlake */ .cores_num = 4, .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0) | + .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1) | HDA_DSP_CORE_MASK(2) | HDA_DSP_CORE_MASK(3), @@ -353,7 +353,7 @@ const struct sof_intel_dsp_desc icl_chip_info = { /* Icelake */ .cores_num = 4, .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0) | + .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1) | HDA_DSP_CORE_MASK(2) | HDA_DSP_CORE_MASK(3), @@ -372,7 +372,7 @@ const struct sof_intel_dsp_desc ehl_chip_info = { /* Elkhartlake */ .cores_num = 4, .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0), + .host_managed_cores_mask = HDA_DSP_CORE_MASK(0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, @@ -388,7 +388,7 @@ const struct sof_intel_dsp_desc jsl_chip_info = { /* Jasperlake */ .cores_num = 2, .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0) | + .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index ed4d65a29d3a..18d726669c6f 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -610,7 +610,7 @@ static int hda_suspend(struct snd_sof_dev *sdev, bool runtime_suspend) #endif /* power down DSP */ - ret = hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + ret = hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); if (ret < 0) { dev_err(sdev->dev, "error: failed to power down core during suspend\n"); diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 70727495fdbf..713ebe8d7311 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -91,7 +91,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) int i; /* step 1: power up corex */ - ret = hda_dsp_core_power_up(sdev, chip->cores_mask); + ret = hda_dsp_core_power_up(sdev, chip->host_managed_cores_mask); if (ret < 0) { if (iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); @@ -147,7 +147,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) /* step 5: power down corex */ ret = hda_dsp_core_power_down(sdev, - chip->cores_mask & ~(HDA_DSP_CORE_MASK(0))); + chip->host_managed_cores_mask & ~(HDA_DSP_CORE_MASK(0))); if (ret < 0) { if (iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -176,7 +176,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) err: hda_dsp_dump(sdev, SOF_DBG_REGS | SOF_DBG_PCI | SOF_DBG_MBOX); - hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); return ret; } diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index de8e85920402..882527119c70 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -928,7 +928,7 @@ int hda_dsp_remove(struct snd_sof_dev *sdev) /* disable cores */ if (chip) - hda_dsp_core_reset_power_down(sdev, chip->cores_mask); + hda_dsp_core_reset_power_down(sdev, chip->host_managed_cores_mask); /* disable DSP */ snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, SOF_HDA_REG_PP_PPCTL, diff --git a/sound/soc/sof/intel/shim.h b/sound/soc/sof/intel/shim.h index 6fe8b004b50e..1e0afb5c8720 100644 --- a/sound/soc/sof/intel/shim.h +++ b/sound/soc/sof/intel/shim.h @@ -154,7 +154,7 @@ /* DSP hardware descriptor */ struct sof_intel_dsp_desc { int cores_num; - int cores_mask; + int host_managed_cores_mask; int init_core_mask; /* cores available after fw boot */ int ipc_req; int ipc_req_mask; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index d0e84b7747a0..8f3fe82a22bc 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -124,7 +124,7 @@ const struct sof_intel_dsp_desc tgl_chip_info = { /* Tigerlake */ .cores_num = 4, .init_core_mask = 1, - .cores_mask = HDA_DSP_CORE_MASK(0), + .host_managed_cores_mask = HDA_DSP_CORE_MASK(0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, -- cgit From 914fab3b43633d03eb40e3216cc2857864589c60 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 10 Sep 2020 19:41:23 +0300 Subject: ASoC: SOF: Intel: hda: modify core_power_up/down op Modify the core_power_up/down ops for HDA platforms to restrict the core_mask to the ones allowed by chip->cores_mask. This is needed because on some HDA platforms not all cores can be powered up/down by the host and this must be handled internally in the FW. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Keyon Jie Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200910164125.2033062-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-dsp.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 18d726669c6f..18ff1c2f5376 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -239,10 +239,15 @@ bool hda_dsp_core_is_enabled(struct snd_sof_dev *sdev, int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; int ret; - /* return if core is already enabled */ - if (hda_dsp_core_is_enabled(sdev, core_mask)) + /* restrict core_mask to host managed cores mask */ + core_mask &= chip->host_managed_cores_mask; + + /* return if core_mask is not valid or cores are already enabled */ + if (!core_mask || hda_dsp_core_is_enabled(sdev, core_mask)) return 0; /* power up */ @@ -259,8 +264,17 @@ int hda_dsp_enable_core(struct snd_sof_dev *sdev, unsigned int core_mask) int hda_dsp_core_reset_power_down(struct snd_sof_dev *sdev, unsigned int core_mask) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + const struct sof_intel_dsp_desc *chip = hda->desc; int ret; + /* restrict core_mask to host managed cores mask */ + core_mask &= chip->host_managed_cores_mask; + + /* return if core_mask is not valid */ + if (!core_mask) + return 0; + /* place core in reset prior to power down */ ret = hda_dsp_core_stall_reset(sdev, core_mask); if (ret < 0) { -- cgit From fde106552845b7c369c2385b27062b1c2130a4dd Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 10 Sep 2020 19:41:24 +0300 Subject: ASoC: SOF: Intel: remove the HDA_DSP_CORE_MASK() macro Remove the HDA_DSP_CORE_MASK() macro and use BIT() and GENMASK() macros directly for more clarity. Signed-off-by: Ranjani Sridharan Reviewed-by: Rander Wang Reviewed-by: Guennadi Liakhovetski Reviewed-by: Keyon Jie Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200910164125.2033062-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/apl.c | 2 +- sound/soc/sof/intel/cnl.c | 15 ++++----------- sound/soc/sof/intel/hda-loader.c | 5 ++--- sound/soc/sof/intel/hda.h | 3 --- sound/soc/sof/intel/tgl.c | 2 +- 5 files changed, 8 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c index 25d3f5775aac..4eeade2e77f7 100644 --- a/sound/soc/sof/intel/apl.c +++ b/sound/soc/sof/intel/apl.c @@ -129,7 +129,7 @@ const struct sof_intel_dsp_desc apl_chip_info = { /* Apollolake */ .cores_num = 2, .init_core_mask = 1, - .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | HDA_DSP_CORE_MASK(1), + .host_managed_cores_mask = GENMASK(1, 0), .ipc_req = HDA_DSP_REG_HIPCI, .ipc_req_mask = HDA_DSP_REG_HIPCI_BUSY, .ipc_ack = HDA_DSP_REG_HIPCIE, diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c index 51e336d7348f..a5d3258104c0 100644 --- a/sound/soc/sof/intel/cnl.c +++ b/sound/soc/sof/intel/cnl.c @@ -334,10 +334,7 @@ const struct sof_intel_dsp_desc cnl_chip_info = { /* Cannonlake */ .cores_num = 4, .init_core_mask = 1, - .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | - HDA_DSP_CORE_MASK(1) | - HDA_DSP_CORE_MASK(2) | - HDA_DSP_CORE_MASK(3), + .host_managed_cores_mask = GENMASK(3, 0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, @@ -353,10 +350,7 @@ const struct sof_intel_dsp_desc icl_chip_info = { /* Icelake */ .cores_num = 4, .init_core_mask = 1, - .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | - HDA_DSP_CORE_MASK(1) | - HDA_DSP_CORE_MASK(2) | - HDA_DSP_CORE_MASK(3), + .host_managed_cores_mask = GENMASK(3, 0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, @@ -372,7 +366,7 @@ const struct sof_intel_dsp_desc ehl_chip_info = { /* Elkhartlake */ .cores_num = 4, .init_core_mask = 1, - .host_managed_cores_mask = HDA_DSP_CORE_MASK(0), + .host_managed_cores_mask = BIT(0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, @@ -388,8 +382,7 @@ const struct sof_intel_dsp_desc jsl_chip_info = { /* Jasperlake */ .cores_num = 2, .init_core_mask = 1, - .host_managed_cores_mask = HDA_DSP_CORE_MASK(0) | - HDA_DSP_CORE_MASK(1), + .host_managed_cores_mask = GENMASK(1, 0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 713ebe8d7311..5515c75e53e4 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -114,7 +114,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) ((stream_tag - 1) << 9))); /* step 3: unset core 0 reset state & unstall/run core 0 */ - ret = hda_dsp_core_run(sdev, HDA_DSP_CORE_MASK(0)); + ret = hda_dsp_core_run(sdev, BIT(0)); if (ret < 0) { if (iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, @@ -146,8 +146,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) chip->ipc_ack_mask); /* step 5: power down corex */ - ret = hda_dsp_core_power_down(sdev, - chip->host_managed_cores_mask & ~(HDA_DSP_CORE_MASK(0))); + ret = hda_dsp_core_power_down(sdev, chip->host_managed_cores_mask & ~(BIT(0))); if (ret < 0) { if (iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 5ee2f8354051..f0f8f95c082b 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -305,9 +305,6 @@ #define HDA_DSP_ADSPCS_CPA_SHIFT 24 #define HDA_DSP_ADSPCS_CPA_MASK(cm) ((cm) << HDA_DSP_ADSPCS_CPA_SHIFT) -/* Mask for a given core index, c = 0.. number of supported cores - 1 */ -#define HDA_DSP_CORE_MASK(c) BIT(c) - /* * Mask for a given number of cores * nc = number of supported cores diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 8f3fe82a22bc..f8d04fd66ceb 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -124,7 +124,7 @@ const struct sof_intel_dsp_desc tgl_chip_info = { /* Tigerlake */ .cores_num = 4, .init_core_mask = 1, - .host_managed_cores_mask = HDA_DSP_CORE_MASK(0), + .host_managed_cores_mask = BIT(0), .ipc_req = CNL_DSP_REG_HIPCIDR, .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, .ipc_ack = CNL_DSP_REG_HIPCIDA, -- cgit From 52e4d0ae6255446efaaaa2a7a1da7cc1640f78b5 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 10 Sep 2020 19:41:25 +0300 Subject: ASoC: SOF: Intel: hda-loader: s/master/primary Use inclusive language for DSP cores. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Reviewed-by: Jaska Uimonen Reviewed-by: Seppo Ingalsuo Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200910164125.2033062-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 5515c75e53e4..dfbfc89ffe70 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -426,7 +426,7 @@ cleanup: } /* - * return master core id if both fw copy + * return primary core id if both fw copy * and stream clean up are successful */ if (!ret) -- cgit From 2a4b91a26403fa3e7b07271700c3ca7103664bba Mon Sep 17 00:00:00 2001 From: Sathyanarayana Nujella Date: Thu, 10 Sep 2020 19:27:05 +0300 Subject: ASoC: SOF: Intel: Use DMI oem string search for tgl_max98373_rt5682 DMI product name is used to support system variants based out of tgl_max98373_rt5682 in current implementation. Replace this DMI search with DMI_OEM_STRING. Coreboot(BIOS used in these systems) is setting the needed DMI_OEM_STRING field to uniquely identify these systems. Signed-off-by: Sathyanarayana Nujella Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200910162705.2026036-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_rt5682.c | 2 +- sound/soc/sof/sof-pci-dev.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 9a6f10ede427..ddbb9fe7cc06 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -123,7 +123,7 @@ static const struct dmi_system_id sof_rt5682_quirk_table[] = { .callback = sof_rt5682_quirk_cb, .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Terrador"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98373_ALC5682I_I2S_UP4"), }, .driver_data = (void *)(SOF_RT5682_MCLK_EN | SOF_RT5682_SSP_CODEC(0) | diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index ec62d9337d68..3ec380945466 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -50,7 +50,7 @@ static const struct dmi_system_id sof_tplg_table[] = { .callback = sof_tplg_cb, .matches = { DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Volteer"), - DMI_MATCH(DMI_PRODUCT_NAME, "Terrador"), + DMI_MATCH(DMI_OEM_STRING, "AUDIO-MAX98373_ALC5682I_I2S_UP4"), }, .driver_data = "sof-tgl-rt5682-ssp0-max98373-ssp2.tplg", }, -- cgit From 375e2c352582442783178e6a33c279d6bc9354a2 Mon Sep 17 00:00:00 2001 From: Tuo Li Date: Mon, 7 Sep 2020 21:09:37 +0800 Subject: ALSA: rockchip_i2s: fix a possible divide-by-zero bug in rockchip_i2s_hw_params() The variable bclk_rate is checked in: if (bclk_rate && mclk_rate % bclk_rate) This indicates that bclk_rate can be zero. If so, a divide-by-zero bug will occur: div_bclk = mclk_rate / bclk_rate; To fix this possible bug, the function returns -EINVAL when bclk_rate is zero. Signed-off-by: Tuo Li Link: https://lore.kernel.org/r/TY2PR04MB4029799E60A5BCAAD5B7B5BBB8280@TY2PR04MB4029.apcprd04.prod.outlook.com Signed-off-by: Mark Brown --- sound/soc/rockchip/rockchip_i2s.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c index d1438753edb4..593299675b8c 100644 --- a/sound/soc/rockchip/rockchip_i2s.c +++ b/sound/soc/rockchip/rockchip_i2s.c @@ -279,7 +279,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream, if (i2s->is_master_mode) { mclk_rate = clk_get_rate(i2s->mclk); bclk_rate = 2 * 32 * params_rate(params); - if (bclk_rate && mclk_rate % bclk_rate) + if (bclk_rate == 0 || mclk_rate % bclk_rate) return -EINVAL; div_bclk = mclk_rate / bclk_rate; -- cgit From 4e723e7565c4031568fb9db18253cfbf6442831d Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Fri, 11 Sep 2020 10:15:07 +0200 Subject: ASoC: stm32: sai: add pm_runtime support Enable support of pm_runtime on STM32 SAI driver to allow SAI power state monitoring. pm_runtime_put_autosuspend() is called from ASoC framework on pcm device close. The pmdown_time delay is available in runtime context, and may be set in SAI driver to take into account shutdown delay on playback. However, this shutdown delay is already handled in the DAPMs of the audio codec linked to SAI CPU DAI. So, the choice is made, not to support this delay on CPU DAI side. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20200911081507.7276-1-olivier.moysan@st.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_sai_sub.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c index 3fb9513cedb2..3aa1cf262402 100644 --- a/sound/soc/stm/stm32_sai_sub.c +++ b/sound/soc/stm/stm32_sai_sub.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -1559,10 +1560,14 @@ static int stm32_sai_sub_probe(struct platform_device *pdev) ret = snd_soc_register_component(&pdev->dev, &stm32_component, &sai->cpu_dai_drv, 1); - if (ret) + if (ret) { snd_dmaengine_pcm_unregister(&pdev->dev); + return ret; + } - return ret; + pm_runtime_enable(&pdev->dev); + + return 0; } static int stm32_sai_sub_remove(struct platform_device *pdev) @@ -1572,6 +1577,7 @@ static int stm32_sai_sub_remove(struct platform_device *pdev) clk_unprepare(sai->pdata->pclk); snd_dmaengine_pcm_unregister(&pdev->dev); snd_soc_unregister_component(&pdev->dev); + pm_runtime_disable(&pdev->dev); return 0; } -- cgit From 2b3d2987d800cc6dd8f6459971bc332354bd6a31 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Sep 2020 18:36:59 +0200 Subject: ALSA: firewire: Replace tasklet with work The tasklet is an old API that should be deprecated, usually can be converted to another decent API. In FireWire driver, a tasklet is still used for offloading the AMDTP PCM stream handling. It can be achieved gracefully with a work queued, too. This patch replaces the tasklet usage in firewire-lib driver with a simple work. The conversion is fairly straightforward but for the in_interrupt() checks that are replaced with the check using the current_work(). Note that in_interrupt() in amdtp_packet tracepoint is still kept as is. This is the place that is probed by both softirq of 1394 OHCI and a user task of a PCM application, and the work handling is already filtered in amdtp_domain_stream_pcm_pointer(). Tested-by: Takashi Sakamoto Acked-by: Takashi Sakamoto Link: https://lore.kernel.org/r/20200909163659.21708-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/firewire/amdtp-stream.c | 25 +++++++++++++------------ sound/firewire/amdtp-stream.h | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c index ee1c428b1fd3..4e2f2bb7879f 100644 --- a/sound/firewire/amdtp-stream.c +++ b/sound/firewire/amdtp-stream.c @@ -64,7 +64,7 @@ #define IT_PKT_HEADER_SIZE_CIP 8 // For 2 CIP header. #define IT_PKT_HEADER_SIZE_NO_CIP 0 // Nothing. -static void pcm_period_tasklet(struct tasklet_struct *t); +static void pcm_period_work(struct work_struct *work); /** * amdtp_stream_init - initialize an AMDTP stream structure @@ -94,7 +94,7 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit, s->flags = flags; s->context = ERR_PTR(-1); mutex_init(&s->mutex); - tasklet_setup(&s->period_tasklet, pcm_period_tasklet); + INIT_WORK(&s->period_work, pcm_period_work); s->packet_index = 0; init_waitqueue_head(&s->callback_wait); @@ -203,7 +203,7 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, // Linux driver for 1394 OHCI controller voluntarily flushes isoc // context when total size of accumulated context header reaches - // PAGE_SIZE. This kicks tasklet for the isoc context and brings + // PAGE_SIZE. This kicks work for the isoc context and brings // callback in the middle of scheduled interrupts. // Although AMDTP streams in the same domain use the same events per // IRQ, use the largest size of context header between IT/IR contexts. @@ -333,7 +333,7 @@ EXPORT_SYMBOL(amdtp_stream_get_max_payload); */ void amdtp_stream_pcm_prepare(struct amdtp_stream *s) { - tasklet_kill(&s->period_tasklet); + cancel_work_sync(&s->period_work); s->pcm_buffer_pointer = 0; s->pcm_period_pointer = 0; } @@ -437,13 +437,14 @@ static void update_pcm_pointers(struct amdtp_stream *s, s->pcm_period_pointer += frames; if (s->pcm_period_pointer >= pcm->runtime->period_size) { s->pcm_period_pointer -= pcm->runtime->period_size; - tasklet_hi_schedule(&s->period_tasklet); + queue_work(system_highpri_wq, &s->period_work); } } -static void pcm_period_tasklet(struct tasklet_struct *t) +static void pcm_period_work(struct work_struct *work) { - struct amdtp_stream *s = from_tasklet(s, t, period_tasklet); + struct amdtp_stream *s = container_of(work, struct amdtp_stream, + period_work); struct snd_pcm_substream *pcm = READ_ONCE(s->pcm); if (pcm) @@ -794,7 +795,7 @@ static void generate_pkt_descs(struct amdtp_stream *s, struct pkt_desc *descs, static inline void cancel_stream(struct amdtp_stream *s) { s->packet_index = -1; - if (in_interrupt()) + if (current_work() == &s->period_work) amdtp_stream_pcm_abort(s); WRITE_ONCE(s->pcm_buffer_pointer, SNDRV_PCM_POS_XRUN); } @@ -1184,7 +1185,7 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, if (irq_target && amdtp_stream_running(irq_target)) { // This function is called in software IRQ context of - // period_tasklet or process context. + // period_work or process context. // // When the software IRQ context was scheduled by software IRQ // context of IT contexts, queued packets were already handled. @@ -1195,9 +1196,9 @@ unsigned long amdtp_domain_stream_pcm_pointer(struct amdtp_domain *d, // immediately to keep better granularity of PCM pointer. // // Later, the process context will sometimes schedules software - // IRQ context of the period_tasklet. Then, no need to flush the + // IRQ context of the period_work. Then, no need to flush the // queue by the same reason as described in the above - if (!in_interrupt()) { + if (current_work() != &s->period_work) { // Queued packet should be processed without any kernel // preemption to keep latency against bus cycle. preempt_disable(); @@ -1263,7 +1264,7 @@ static void amdtp_stream_stop(struct amdtp_stream *s) return; } - tasklet_kill(&s->period_tasklet); + cancel_work_sync(&s->period_work); fw_iso_context_stop(s->context); fw_iso_context_destroy(s->context); s->context = ERR_PTR(-1); diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h index 703b710aaf7f..2ceb57d1d58e 100644 --- a/sound/firewire/amdtp-stream.h +++ b/sound/firewire/amdtp-stream.h @@ -163,7 +163,7 @@ struct amdtp_stream { /* For a PCM substream processing. */ struct snd_pcm_substream *pcm; - struct tasklet_struct period_tasklet; + struct work_struct period_work; snd_pcm_uframes_t pcm_buffer_pointer; unsigned int pcm_period_pointer; -- cgit From da145172b236b1ac322fa81f6250aa59074eba68 Mon Sep 17 00:00:00 2001 From: "derek.fang" Date: Mon, 14 Sep 2020 16:57:18 +0800 Subject: ASoC: rt1015: Fix DC calibration on bypass boost mode Fix the DC calibration unsuccessful issue on rt1015 bypass boost mode. Signed-off-by: derek.fang Link: https://lore.kernel.org/r/1600073839-6762-1-git-send-email-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1015.c | 48 +++++++++++++++++++++++++++++++++-------------- sound/soc/codecs/rt1015.h | 1 + 2 files changed, 35 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 548f68649064..ba1b6b3822a5 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -484,6 +484,33 @@ static int rt1015_bypass_boost_get(struct snd_kcontrol *kcontrol, return 0; } +static void rt1015_calibrate(struct rt1015_priv *rt1015) +{ + struct snd_soc_component *component = rt1015->component; + struct regmap *regmap = rt1015->regmap; + + snd_soc_dapm_mutex_lock(&component->dapm); + regcache_cache_bypass(regmap, true); + + regmap_write(regmap, RT1015_PWR1, 0xd7df); + regmap_write(regmap, RT1015_PWR4, 0x00b2); + regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2008); + regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140); + regmap_write(regmap, RT1015_GAT_BOOST, 0x0efe); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000d); + regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000e); + regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5a00); + regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5a01); + regmap_write(regmap, RT1015_DC_CALIB_CLSD1, 0x5a05); + msleep(500); + regmap_write(regmap, RT1015_PWR1, 0x0); + + regcache_cache_bypass(regmap, false); + regcache_mark_dirty(regmap); + regcache_sync(regmap); + snd_soc_dapm_mutex_unlock(&component->dapm); +} + static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -494,20 +521,12 @@ static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol, if (!rt1015->dac_is_used) { rt1015->bypass_boost = ucontrol->value.integer.value[0]; - if (rt1015->bypass_boost == RT1015_Bypass_Boost) { - snd_soc_component_write(component, - RT1015_PWR4, 0x00b2); - snd_soc_component_write(component, - RT1015_CLSD_INTERNAL8, 0x2008); - snd_soc_component_write(component, - RT1015_CLSD_INTERNAL9, 0x0140); - snd_soc_component_write(component, - RT1015_GAT_BOOST, 0x0efe); - snd_soc_component_write(component, - RT1015_PWR_STATE_CTRL, 0x000d); - msleep(500); - snd_soc_component_write(component, - RT1015_PWR_STATE_CTRL, 0x000e); + if (rt1015->bypass_boost == RT1015_Bypass_Boost && + !rt1015->cali_done) { + rt1015_calibrate(rt1015); + rt1015->cali_done = 1; + + regmap_write(rt1015->regmap, RT1015_MONO_DYNA_CTRL, 0x0010); } } else dev_err(component->dev, "DAC is being used!\n"); @@ -888,6 +907,7 @@ static int rt1015_probe(struct snd_soc_component *component) rt1015->component = component; rt1015->bclk_ratio = 0; + rt1015->cali_done = 0; snd_soc_component_write(component, RT1015_BAT_RPO_STEP1, 0x061c); return 0; diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h index 7bd159e8f958..4d11f5865725 100644 --- a/sound/soc/codecs/rt1015.h +++ b/sound/soc/codecs/rt1015.h @@ -389,6 +389,7 @@ struct rt1015_priv { int bypass_boost; int amp_ver; int dac_is_used; + int cali_done; }; #endif /* __RT1015_H__ */ -- cgit From 8d9a14fc7371a18d54226bf2542f77b11e5a7101 Mon Sep 17 00:00:00 2001 From: "derek.fang" Date: Mon, 14 Sep 2020 16:57:19 +0800 Subject: ASoC: rt1015: Fix the failure to flush DAC data before playback Fix the failure to flush DAC data before playback. Signed-off-by: derek.fang Link: https://lore.kernel.org/r/1600073839-6762-2-git-send-email-derek.fang@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt1015.c | 63 +++++++++++++++++++++++++++++++++++++++++------ sound/soc/codecs/rt1015.h | 7 ++++++ 2 files changed, 63 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index ba1b6b3822a5..25fe2ddedd54 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -534,6 +534,32 @@ static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol, return 0; } +static void rt1015_flush_work(struct work_struct *work) +{ + struct rt1015_priv *rt1015 = container_of(work, struct rt1015_priv, + flush_work.work); + struct snd_soc_component *component = rt1015->component; + unsigned int val, i = 0, count = 20; + + while (i < count) { + usleep_range(1000, 1500); + dev_dbg(component->dev, "Flush DAC (retry:%u)\n", i); + regmap_read(rt1015->regmap, RT1015_CLK_DET, &val); + if (val & 0x800) + break; + i++; + } + + regmap_write(rt1015->regmap, RT1015_SYS_RST1, 0x0597); + regmap_write(rt1015->regmap, RT1015_SYS_RST1, 0x05f7); + regmap_write(rt1015->regmap, RT1015_MAN_I2C, 0x0028); + + if (val & 0x800) + dev_dbg(component->dev, "Flush DAC completed.\n"); + else + dev_warn(component->dev, "Fail to flush DAC data.\n"); +} + static const struct snd_kcontrol_new rt1015_snd_controls[] = { SOC_SINGLE_TLV("DAC Playback Volume", RT1015_DAC1, RT1015_DAC_VOL_SFT, 127, 0, dac_vol_tlv), @@ -587,12 +613,7 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, break; case SND_SOC_DAPM_POST_PMU: - if (rt1015->bypass_boost == RT1015_Bypass_Boost) { - regmap_write(rt1015->regmap, RT1015_MAN_I2C, 0x00a8); - regmap_write(rt1015->regmap, RT1015_SYS_RST1, 0x0597); - regmap_write(rt1015->regmap, RT1015_SYS_RST1, 0x05f7); - regmap_write(rt1015->regmap, RT1015_MAN_I2C, 0x0028); - } + regmap_write(rt1015->regmap, RT1015_MAN_I2C, 0x00a8); break; case SND_SOC_DAPM_POST_PMD: @@ -608,6 +629,8 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, RT1015_SYS_RST1, 0x05f5); } rt1015->dac_is_used = 0; + + cancel_delayed_work_sync(&rt1015->flush_work); break; default: @@ -616,6 +639,24 @@ static int r1015_dac_event(struct snd_soc_dapm_widget *w, return 0; } +static int rt1015_amp_drv_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (rt1015->hw_config == RT1015_HW_28) + schedule_delayed_work(&rt1015->flush_work, msecs_to_jiffies(10)); + break; + default: + break; + } + return 0; +} + static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("LDO2", RT1015_PWR1, RT1015_PWR_LDO2_BIT, 0, NULL, 0), @@ -649,6 +690,8 @@ static const struct snd_soc_dapm_widget rt1015_dapm_widgets[] = { r1015_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), + SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, + rt1015_amp_drv_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_OUTPUT("SPO"), }; @@ -667,7 +710,8 @@ static const struct snd_soc_dapm_route rt1015_dapm_routes[] = { { "DAC", NULL, "MIXERV" }, { "DAC", NULL, "SUMV" }, { "DAC", NULL, "VREFLV" }, - { "SPO", NULL, "DAC" }, + { "Amp Drv", NULL, "DAC" }, + { "SPO", NULL, "Amp Drv" }, }; static int rt1015_hw_params(struct snd_pcm_substream *substream, @@ -910,6 +954,8 @@ static int rt1015_probe(struct snd_soc_component *component) rt1015->cali_done = 0; snd_soc_component_write(component, RT1015_BAT_RPO_STEP1, 0x061c); + INIT_DELAYED_WORK(&rt1015->flush_work, rt1015_flush_work); + return 0; } @@ -917,6 +963,7 @@ static void rt1015_remove(struct snd_soc_component *component) { struct rt1015_priv *rt1015 = snd_soc_component_get_drvdata(component); + cancel_delayed_work_sync(&rt1015->flush_work); regmap_write(rt1015->regmap, RT1015_RESET, 0); } @@ -1042,6 +1089,8 @@ static int rt1015_i2c_probe(struct i2c_client *i2c, return ret; } + rt1015->hw_config = (i2c->addr == 0x29) ? RT1015_HW_29 : RT1015_HW_28; + regmap_read(rt1015->regmap, RT1015_DEVICE_ID, &val); if ((val != RT1015_DEVICE_ID_VAL) && (val != RT1015_DEVICE_ID_VAL2)) { dev_err(&i2c->dev, diff --git a/sound/soc/codecs/rt1015.h b/sound/soc/codecs/rt1015.h index 4d11f5865725..d3fdd30aca6d 100644 --- a/sound/soc/codecs/rt1015.h +++ b/sound/soc/codecs/rt1015.h @@ -373,6 +373,11 @@ enum { RT1015_Bypass_Boost, }; +enum { + RT1015_HW_28 = 0, + RT1015_HW_29, +}; + struct rt1015_priv { struct snd_soc_component *component; struct regmap *regmap; @@ -390,6 +395,8 @@ struct rt1015_priv { int amp_ver; int dac_is_used; int cali_done; + int hw_config; + struct delayed_work flush_work; }; #endif /* __RT1015_H__ */ -- cgit From 944c517b8c838832a166f1c89afbf8724f4a6b49 Mon Sep 17 00:00:00 2001 From: Viorel Suman Date: Mon, 14 Sep 2020 20:24:34 +0300 Subject: ASoC: fsl_audmix: make clock and output src write only "alsactl -f state.conf store/restore" sequence fails because setting "mixing clock source" and "output source" requires active TDM clock being started for configuration propagation. Make these two controls write only so that their values are not stored at "alsactl store". Signed-off-by: Viorel Suman Acked-by: Nicolin Chen Link: https://lore.kernel.org/r/1600104274-13110-1-git-send-email-viorel.suman@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_audmix.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_audmix.c b/sound/soc/fsl/fsl_audmix.c index a447bafa00d2..7ad5925772e8 100644 --- a/sound/soc/fsl/fsl_audmix.c +++ b/sound/soc/fsl/fsl_audmix.c @@ -199,10 +199,18 @@ static int fsl_audmix_put_out_src(struct snd_kcontrol *kcontrol, static const struct snd_kcontrol_new fsl_audmix_snd_controls[] = { /* FSL_AUDMIX_CTR controls */ - SOC_ENUM_EXT("Mixing Clock Source", fsl_audmix_enum[0], - snd_soc_get_enum_double, fsl_audmix_put_mix_clk_src), - SOC_ENUM_EXT("Output Source", fsl_audmix_enum[1], - snd_soc_get_enum_double, fsl_audmix_put_out_src), + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mixing Clock Source", + .info = snd_soc_info_enum_double, + .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .put = fsl_audmix_put_mix_clk_src, + .private_value = (unsigned long)&fsl_audmix_enum[0] }, + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Output Source", + .info = snd_soc_info_enum_double, + .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .put = fsl_audmix_put_out_src, + .private_value = (unsigned long)&fsl_audmix_enum[1] }, SOC_ENUM("Output Width", fsl_audmix_enum[2]), SOC_ENUM("Frame Rate Diff Error", fsl_audmix_enum[3]), SOC_ENUM("Clock Freq Diff Error", fsl_audmix_enum[4]), -- cgit From 6788fc1a66a0c1d1cec7a0f84f94b517eae8611c Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 Sep 2020 13:39:12 +0300 Subject: ASoC: topology: disable size checks for bytes_ext controls if needed When CONFIG_SND_CTL_VALIDATION is set, accesses to extended bytes control generate spurious error messages when the size exceeds 512 bytes, such as [ 11.224223] sof_sdw sof_sdw: control 2:0:0:EQIIR5.0 eqiir_coef_5:0: invalid count 1024 In addition the error check returns -EINVAL which has the nasty side effect of preventing applications accessing controls from working, e.g. root@plb:~# alsamixer cannot load mixer controls: Invalid argument It's agreed that the control interface has been abused since 2014, but forcing a check should not prevent existing solutions from working. This patch skips the checks conditionally if CONFIG_SND_CTL_VALIDATION is set and the byte array provided by topology is > 512. This preserves the checks for all other cases. Fixes: 1a3232d2f61d2 ('ASoC: topology: Add support for TLV bytes controls') BugLink: https://github.com/thesofproject/linux/issues/2430 Reported-by: Takashi Iwai Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Bard Liao Reviewed-by: Jaska Uimonen Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917103912.2565907-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/soc-topology.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c index cee998671318..e5ff9a561951 100644 --- a/sound/soc/soc-topology.c +++ b/sound/soc/soc-topology.c @@ -592,6 +592,17 @@ static int soc_tplg_kcontrol_bind_io(struct snd_soc_tplg_ctl_hdr *hdr, k->info = snd_soc_bytes_info_ext; k->tlv.c = snd_soc_bytes_tlv_callback; + /* + * When a topology-based implementation abuses the + * control interface and uses bytes_ext controls of + * more than 512 bytes, we need to disable the size + * checks, otherwise accesses to such controls will + * return an -EINVAL error and prevent the card from + * being configured. + */ + if (IS_ENABLED(CONFIG_SND_CTL_VALIDATION) && sbe->max > 512) + k->access |= SNDRV_CTL_ELEM_ACCESS_SKIP_CHECK; + ext_ops = tplg->bytes_ext_ops; num_ops = tplg->bytes_ext_ops_count; for (i = 0; i < num_ops; i++) { -- cgit From 73154aca4a03a2ab4833fd36683feb884af06d4b Mon Sep 17 00:00:00 2001 From: Camel Guo Date: Tue, 8 Sep 2020 11:04:17 +0200 Subject: ASoC: tlv320adcx140: Fix digital gain range According to its datasheet, the digital gain should be -100 dB when CHx_DVOL is 1 and 27 dB when CHx_DVOL is 255. But with the current dig_vol_tlv, "Digital CHx Out Volume" shows 27.5 dB if CHx_DVOL is 255 and -95.5 dB if CHx_DVOL is 1. This commit fixes this bug. Fixes: 689c7655b50c ("ASoC: tlv320adcx140: Add the tlv320adcx140 codec driver family") Signed-off-by: Camel Guo Link: https://lore.kernel.org/r/20200908090417.16695-1-camel.guo@axis.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 8efe20605f9b..c7c782d279d0 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -161,7 +161,7 @@ static const struct regmap_config adcx140_i2c_regmap = { }; /* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */ -static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10000, 50, 0); +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10050, 50, 0); /* ADC gain. From 0 to 42 dB in 1 dB steps */ static DECLARE_TLV_DB_SCALE(adc_tlv, 0, 100, 0); -- cgit From df16e2210454ca0b8a59caf364dd287fbb76a804 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 15 Sep 2020 14:06:01 -0500 Subject: ASoC: tlv320adcx140: Idle the device while writing registers It was observed that if the device was active and register writes were performed there were some unwanted behaviors particularly when writing the word length and some filter options. So when writing to the device the device should be placed in sleep mode and then exit sleep mode once the register update is complete. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200915190606.1744-1-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 43 ++++++++++++++++++++++++++++++---------- sound/soc/codecs/tlv320adcx140.h | 2 ++ 2 files changed, 34 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index c7c782d279d0..5e14b9f8c84f 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -30,7 +30,7 @@ struct adcx140_priv { struct regmap *regmap; struct device *dev; - int micbias_vg; + bool micbias_vg; unsigned int dai_fmt; unsigned int tdm_delay; @@ -614,11 +614,26 @@ static int adcx140_reset(struct adcx140_priv *adcx140) return ret; } +static void adcx140_pwr_ctrl(struct adcx140_priv *adcx140, bool power_state) +{ + int pwr_ctrl = 0; + + if (power_state) + pwr_ctrl = ADCX140_PWR_CFG_ADC_PDZ | ADCX140_PWR_CFG_PLL_PDZ; + + if (adcx140->micbias_vg && power_state) + pwr_ctrl |= ADCX140_PWR_CFG_BIAS_PDZ; + + regmap_update_bits(adcx140->regmap, ADCX140_PWR_CFG, + ADCX140_PWR_CTRL_MSK, pwr_ctrl); +} + static int adcx140_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; + struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); u8 data = 0; switch (params_width(params)) { @@ -640,9 +655,13 @@ static int adcx140_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + adcx140_pwr_ctrl(adcx140, false); + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, ADCX140_WORD_LEN_MSK, data); + adcx140_pwr_ctrl(adcx140, true); + return 0; } @@ -709,6 +728,8 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; + adcx140_pwr_ctrl(adcx140, false); + snd_soc_component_update_bits(component, ADCX140_ASI_CFG0, ADCX140_FSYNCINV_BIT | ADCX140_BCLKINV_BIT | @@ -721,6 +742,7 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, snd_soc_component_update_bits(component, ADCX140_ASI_CFG1, ADCX140_TX_OFFSET_MASK, offset); + adcx140_pwr_ctrl(adcx140, true); return 0; } @@ -818,12 +840,11 @@ static int adcx140_codec_probe(struct snd_soc_component *component) ret = device_property_read_u32(adcx140->dev, "ti,mic-bias-source", &bias_source); - if (ret) + if (ret || bias_source > ADCX140_MIC_BIAS_VAL_AVDD) { bias_source = ADCX140_MIC_BIAS_VAL_VREF; - - if (bias_source > ADCX140_MIC_BIAS_VAL_AVDD) { - dev_err(adcx140->dev, "Mic Bias source value is invalid\n"); - return -EINVAL; + adcx140->micbias_vg = false; + } else { + adcx140->micbias_vg = true; } ret = device_property_read_u32(adcx140->dev, "ti,vref-source", @@ -906,6 +927,8 @@ static int adcx140_codec_probe(struct snd_soc_component *component) ADCX140_MIC_BIAS_VREF_MSK, bias_cfg); if (ret) dev_err(adcx140->dev, "setting MIC bias failed %d\n", ret); + + adcx140_pwr_ctrl(adcx140, true); out: return ret; } @@ -914,21 +937,19 @@ static int adcx140_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); - int pwr_cfg = 0; switch (level) { case SND_SOC_BIAS_ON: case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_STANDBY: - pwr_cfg = ADCX140_PWR_CFG_BIAS_PDZ | ADCX140_PWR_CFG_PLL_PDZ | - ADCX140_PWR_CFG_ADC_PDZ; + adcx140_pwr_ctrl(adcx140, true); break; case SND_SOC_BIAS_OFF: - pwr_cfg = 0x0; + adcx140_pwr_ctrl(adcx140, false); break; } - return regmap_write(adcx140->regmap, ADCX140_PWR_CFG, pwr_cfg); + return 0; } static const struct snd_soc_component_driver soc_codec_driver_adcx140 = { diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h index eedbc1d7221f..94c6d1fd2977 100644 --- a/sound/soc/codecs/tlv320adcx140.h +++ b/sound/soc/codecs/tlv320adcx140.h @@ -123,6 +123,7 @@ #define ADCX140_MIC_BIAS_VREF_1375V 2 #define ADCX140_MIC_BIAS_VREF_MSK GENMASK(1, 0) +#define ADCX140_PWR_CTRL_MSK GENMASK(7, 5) #define ADCX140_PWR_CFG_BIAS_PDZ BIT(7) #define ADCX140_PWR_CFG_ADC_PDZ BIT(6) #define ADCX140_PWR_CFG_PLL_PDZ BIT(5) @@ -145,4 +146,5 @@ #define ADCX140_GPO_CFG_MAX 4 #define ADCX140_GPO_DRV_MAX 5 + #endif /* _TLV320ADCX140_ */ -- cgit From 244ac15de75ca62ed7a09c7291b67aeead9e12ac Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 15 Sep 2020 14:06:02 -0500 Subject: ASoC: tlv320adcx140: Fix BCLK inversion for DSP modes Fix the BCLK inversion for DSP modes This is how it is defined by ASoC: * BCLK: * - "normal" polarity means signal is available at rising edge of BCLK * - "inverted" polarity means signal is available at falling edge of BCLK The adcx140 defines the BCLK edge based on coding type. The PCM (DSP_A/B) should drive on rising and sample on falling edge, so from ASoC pov, it is IB_NF. But from the codec pov if it is configured in DSP mode, then the BCLK should not be inverted, defaults to the coding standard. For i2s, it is NB_NF from ASoC pov (drive on falling, sample on rising). >From the codec's pov BCLK should not invert either, as this is the default for the coding. So, inversion must take the format into account: IB_NF + DSP_A/B == the codec bclk inversion should be disabled NB_NF + DSP_A/B == the codec bclk inversion should be enabled NB_NF + I2S == the codec bclk inversion should be disabled Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200915190606.1744-2-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 44 +++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 5e14b9f8c84f..936261e6c6d8 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -673,7 +673,7 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, u8 iface_reg1 = 0; u8 iface_reg2 = 0; int offset = 0; - int width = adcx140->slot_width; + bool inverted_bclk = false; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { @@ -689,24 +689,6 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } - /* signal polarity */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_NB_IF: - iface_reg1 |= ADCX140_FSYNCINV_BIT; - break; - case SND_SOC_DAIFMT_IB_IF: - iface_reg1 |= ADCX140_BCLKINV_BIT | ADCX140_FSYNCINV_BIT; - break; - case SND_SOC_DAIFMT_IB_NF: - iface_reg1 |= ADCX140_BCLKINV_BIT; - break; - case SND_SOC_DAIFMT_NB_NF: - break; - default: - dev_err(component->dev, "Invalid DAI clock signal polarity\n"); - return -EINVAL; - } - /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: @@ -716,16 +698,36 @@ static int adcx140_set_dai_fmt(struct snd_soc_dai *codec_dai, iface_reg1 |= ADCX140_LEFT_JUST_BIT; break; case SND_SOC_DAIFMT_DSP_A: - offset += (adcx140->tdm_delay * width + 1); + offset = 1; + inverted_bclk = true; break; case SND_SOC_DAIFMT_DSP_B: - offset += adcx140->tdm_delay * width; + inverted_bclk = true; break; default: dev_err(component->dev, "Invalid DAI interface format\n"); return -EINVAL; } + /* signal polarity */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + inverted_bclk = !inverted_bclk; + break; + case SND_SOC_DAIFMT_NB_IF: + iface_reg1 |= ADCX140_FSYNCINV_BIT; + break; + case SND_SOC_DAIFMT_NB_NF: + break; + default: + dev_err(component->dev, "Invalid DAI clock signal polarity\n"); + return -EINVAL; + } + + if (inverted_bclk) + iface_reg1 |= ADCX140_BCLKINV_BIT; + adcx140->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; adcx140_pwr_ctrl(adcx140, false); -- cgit From 18ebffe4d043bf5f3a9b669d8d91f855bde8f6b7 Mon Sep 17 00:00:00 2001 From: Iulian Olaru Date: Thu, 17 Sep 2020 13:56:26 +0300 Subject: ASoC: SOF: imx: Add debug support for imx platforms This patch adds debug support for imx platforms. This is important in order to gather information about the state of the DSP in case of an oops and the reason for the oops. This is done by checking if a message with a panic code has been placed in the debug box, in the imx8_dsp_handle_request function from sof/imx. If positive, the function imx8_dump, added in common, will be called. The first step is to gather information about the registers, filename, line number and stack by calling the imx8_get_registers, added in common. Then the information will be printed to the console by calling the get_status function. Signed-off-by: Iulian Olaru Reviewed-by: Guennadi Liakhovetski Reviewed-by: Daniel Baluta Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/Kconfig | 8 +++++ sound/soc/sof/imx/Makefile | 3 ++ sound/soc/sof/imx/imx-common.c | 72 ++++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/imx/imx-common.h | 16 ++++++++++ sound/soc/sof/imx/imx8.c | 23 +++++++++++++- sound/soc/sof/imx/imx8m.c | 17 +++++++++- 6 files changed, 137 insertions(+), 2 deletions(-) create mode 100644 sound/soc/sof/imx/imx-common.c create mode 100644 sound/soc/sof/imx/imx-common.h (limited to 'sound') diff --git a/sound/soc/sof/imx/Kconfig b/sound/soc/sof/imx/Kconfig index 23bfd79d09c3..48f998a19ddb 100644 --- a/sound/soc/sof/imx/Kconfig +++ b/sound/soc/sof/imx/Kconfig @@ -19,6 +19,12 @@ config SND_SOC_SOF_IMX_OF This option is not user-selectable but automagically handled by 'select' statements at a higher level +config SND_SOC_SOF_IMX_COMMON + tristate + help + This option is not user-selectable but automagically handled by + 'select' statements at a higher level. + config SND_SOC_SOF_IMX8_SUPPORT bool "SOF support for i.MX8" depends on IMX_SCU=y || IMX_SCU=SND_SOC_SOF_IMX_OF @@ -30,6 +36,7 @@ config SND_SOC_SOF_IMX8_SUPPORT config SND_SOC_SOF_IMX8 tristate + select SND_SOC_SOF_IMX_COMMON select SND_SOC_SOF_XTENSA help This option is not user-selectable but automagically handled by @@ -45,6 +52,7 @@ config SND_SOC_SOF_IMX8M_SUPPORT config SND_SOC_SOF_IMX8M tristate + select SND_SOC_SOF_IMX_COMMON select SND_SOC_SOF_XTENSA help This option is not user-selectable but automagically handled by diff --git a/sound/soc/sof/imx/Makefile b/sound/soc/sof/imx/Makefile index 2b933b02bbac..dba93c3466ec 100644 --- a/sound/soc/sof/imx/Makefile +++ b/sound/soc/sof/imx/Makefile @@ -2,5 +2,8 @@ snd-sof-imx8-objs := imx8.o snd-sof-imx8m-objs := imx8m.o +snd-sof-imx-common-objs := imx-common.o + obj-$(CONFIG_SND_SOC_SOF_IMX8) += snd-sof-imx8.o obj-$(CONFIG_SND_SOC_SOF_IMX8M) += snd-sof-imx8m.o +obj-$(CONFIG_SND_SOC_SOF_IMX_COMMON) += imx-common.o diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c new file mode 100644 index 000000000000..63d8a5d7bc44 --- /dev/null +++ b/sound/soc/sof/imx/imx-common.c @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// Copyright 2020 NXP +// +// Common helpers for the audio DSP on i.MX8 + +#include +#include "../ops.h" + +#include "imx-common.h" + +/** + * imx8_get_registers() - This function is called in case of DSP oops + * in order to gather information about the registers, filename and + * linenumber and stack. + * @sdev: SOF device + * @xoops: Stores information about registers. + * @panic_info: Stores information about filename and line number. + * @stack: Stores the stack dump. + * @stack_words: Size of the stack dump. + */ +void imx8_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, + u32 *stack, size_t stack_words) +{ + u32 offset = sdev->dsp_oops_offset; + + /* first read registers */ + sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops)); + + /* then get panic info */ + if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) { + dev_err(sdev->dev, "invalid header size 0x%x. FW oops is bogus\n", + xoops->arch_hdr.totalsize); + return; + } + offset += xoops->arch_hdr.totalsize; + sof_mailbox_read(sdev, offset, panic_info, sizeof(*panic_info)); + + /* then get the stack */ + offset += sizeof(*panic_info); + sof_mailbox_read(sdev, offset, stack, stack_words * sizeof(u32)); +} + +/** + * imx8_dump() - This function is called when a panic message is + * received from the firmware. + */ +void imx8_dump(struct snd_sof_dev *sdev, u32 flags) +{ + struct sof_ipc_dsp_oops_xtensa xoops; + struct sof_ipc_panic_info panic_info; + u32 stack[IMX8_STACK_DUMP_SIZE]; + u32 status; + + /* Get information about the panic status from the debug box area. + * Compute the trace point based on the status. + */ + sof_mailbox_read(sdev, sdev->debug_box.offset + 0x4, &status, 4); + + /* Get information about the registers, the filename and line + * number and the stack. + */ + imx8_get_registers(sdev, &xoops, &panic_info, stack, + IMX8_STACK_DUMP_SIZE); + + /* Print the information to the console */ + snd_sof_get_status(sdev, status, status, &xoops, &panic_info, stack, + IMX8_STACK_DUMP_SIZE); +} +EXPORT_SYMBOL(imx8_dump); diff --git a/sound/soc/sof/imx/imx-common.h b/sound/soc/sof/imx/imx-common.h new file mode 100644 index 000000000000..1cc7d6704182 --- /dev/null +++ b/sound/soc/sof/imx/imx-common.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ + +#ifndef __IMX_COMMON_H__ +#define __IMX_COMMON_H__ + +#define EXCEPT_MAX_HDR_SIZE 0x400 +#define IMX8_STACK_DUMP_SIZE 32 + +void imx8_get_registers(struct snd_sof_dev *sdev, + struct sof_ipc_dsp_oops_xtensa *xoops, + struct sof_ipc_panic_info *panic_info, + u32 *stack, size_t stack_words); + +void imx8_dump(struct snd_sof_dev *sdev, u32 flags); + +#endif diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c index 3b9ffc760cb5..4e7dccadd7d0 100644 --- a/sound/soc/sof/imx/imx8.c +++ b/sound/soc/sof/imx/imx8.c @@ -21,6 +21,7 @@ #include #include #include "../ops.h" +#include "imx-common.h" /* DSP memories */ #define IRAM_OFFSET 0x10000 @@ -115,8 +116,16 @@ static void imx8_dsp_handle_reply(struct imx_dsp_ipc *ipc) static void imx8_dsp_handle_request(struct imx_dsp_ipc *ipc) { struct imx8_priv *priv = imx_dsp_get_data(ipc); + u32 p; /* panic code */ - snd_sof_ipc_msgs_rx(priv->sdev); + /* Read the message from the debug box. */ + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); + + /* Check to see if the message is a panic code (0x0dead***) */ + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) + snd_sof_dsp_panic(priv->sdev, p); + else + snd_sof_ipc_msgs_rx(priv->sdev); } static struct imx_dsp_ops dsp_ops = { @@ -409,6 +418,9 @@ struct snd_sof_dsp_ops sof_imx8_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Module IO */ + .read64 = sof_io_read64, + /* ipc */ .send_msg = imx8_send_msg, .fw_ready = sof_fw_ready, @@ -424,6 +436,9 @@ struct snd_sof_dsp_ops sof_imx8_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* Debug information */ + .dbg_dump = imx8_dump, + /* Firmware ops */ .arch_ops = &sof_xtensa_arch_ops, @@ -452,6 +467,9 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Module IO */ + .read64 = sof_io_read64, + /* ipc */ .send_msg = imx8_send_msg, .fw_ready = sof_fw_ready, @@ -467,6 +485,9 @@ struct snd_sof_dsp_ops sof_imx8x_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* Debug information */ + .dbg_dump = imx8_dump, + /* Firmware ops */ .arch_ops = &sof_xtensa_arch_ops, diff --git a/sound/soc/sof/imx/imx8m.c b/sound/soc/sof/imx/imx8m.c index ca23ac99a63d..cb822d953767 100644 --- a/sound/soc/sof/imx/imx8m.c +++ b/sound/soc/sof/imx/imx8m.c @@ -17,6 +17,7 @@ #include #include "../ops.h" +#include "imx-common.h" #define MBOX_OFFSET 0x800000 #define MBOX_SIZE 0x1000 @@ -88,8 +89,16 @@ static void imx8m_dsp_handle_reply(struct imx_dsp_ipc *ipc) static void imx8m_dsp_handle_request(struct imx_dsp_ipc *ipc) { struct imx8m_priv *priv = imx_dsp_get_data(ipc); + u32 p; /* Panic code */ - snd_sof_ipc_msgs_rx(priv->sdev); + /* Read the message from the debug box. */ + sof_mailbox_read(priv->sdev, priv->sdev->debug_box.offset + 4, &p, sizeof(p)); + + /* Check to see if the message is a panic code (0x0dead***) */ + if ((p & SOF_IPC_PANIC_MAGIC_MASK) == SOF_IPC_PANIC_MAGIC) + snd_sof_dsp_panic(priv->sdev, p); + else + snd_sof_ipc_msgs_rx(priv->sdev); } static struct imx_dsp_ops imx8m_dsp_ops = { @@ -262,6 +271,9 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { .block_read = sof_block_read, .block_write = sof_block_write, + /* Module IO */ + .read64 = sof_io_read64, + /* ipc */ .send_msg = imx8m_send_msg, .fw_ready = sof_fw_ready, @@ -277,6 +289,9 @@ struct snd_sof_dsp_ops sof_imx8m_ops = { /* firmware loading */ .load_firmware = snd_sof_load_firmware_memcpy, + /* Debug information */ + .dbg_dump = imx8_dump, + /* Firmware ops */ .arch_ops = &sof_xtensa_arch_ops, -- cgit From 7db6db9d1a4a7864cd2557e983e06f3adf788c6a Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 Sep 2020 13:56:28 +0300 Subject: ASoC: SOF: debug: update test for pm_runtime_get_sync() We need to avoid reporting an error for -EACCESS when pm_runtime is not enabled. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/debug.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c index 8e15f105d1d5..9419a99bab53 100644 --- a/sound/soc/sof/debug.c +++ b/sound/soc/sof/debug.c @@ -405,7 +405,7 @@ static ssize_t sof_dfsentry_write(struct file *file, const char __user *buffer, } ret = pm_runtime_get_sync(sdev->dev); - if (ret < 0) { + if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(sdev->dev, "error: debugfs write failed to resume %d\n", ret); -- cgit From 99ceec5ca0cb29e3b1d556d108ddc54654377792 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 Sep 2020 13:56:29 +0300 Subject: ASoC: SOF: control: update test for pm_runtime_get_sync() We need to avoid reporting an error for -EACCESS when pm_runtime is not enabled. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index d5e2966cafac..5419c93badd2 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -367,7 +367,7 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ int err; ret = pm_runtime_get_sync(scomp->dev); - if (ret < 0) { + if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); pm_runtime_put_noidle(scomp->dev); return ret; -- cgit From b9f8e1387cf0c9911b26c9d1fca84605d923de66 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Sep 2020 13:56:30 +0300 Subject: ASoC: SOF: (cosmetic) remove redundant "ret" variable uses In some cases no "ret" variable is even needed, those functions always return 0 anyway, in other cases "ret" initialisation is redundant. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-6-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 22 +++++++--------------- sound/soc/sof/topology.c | 24 +++++++++++------------- 2 files changed, 18 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 5419c93badd2..63ead9ebc4c6 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -221,7 +221,6 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; size_t size; - int ret = 0; if (be->max > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, @@ -235,15 +234,13 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, dev_err_ratelimited(scomp->dev, "error: DSP sent %zu bytes max is %d\n", size, be->max); - ret = -EINVAL; - goto out; + return -EINVAL; } /* copy back to kcontrol */ memcpy(ucontrol->value.bytes.data, data, size); -out: - return ret; + return 0; } int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, @@ -424,7 +421,6 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; int data_size; - int ret = 0; /* * Decrement the limit by ext bytes header size to @@ -443,20 +439,16 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, if (data_size > be->max) { dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %d.\n", data_size, be->max); - ret = -EINVAL; - goto out; + return -EINVAL; } header.numid = scontrol->cmd; header.length = data_size; - if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) { - ret = -EFAULT; - goto out; - } + if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) + return -EFAULT; if (copy_to_user(tlvd->tlv, cdata->data, data_size)) - ret = -EFAULT; + return -EFAULT; -out: - return ret; + return 0; } diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index d5efac3af5c2..fa85a22b5880 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -63,7 +63,7 @@ static int ipc_pcm_params(struct snd_sof_widget *swidget, int dir) struct sof_ipc_pcm_params pcm; struct snd_pcm_hw_params *params; struct snd_sof_pcm *spcm; - int ret = 0; + int ret; memset(&pcm, 0, sizeof(pcm)); @@ -121,7 +121,7 @@ static int ipc_trigger(struct snd_sof_widget *swidget, int cmd) struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct sof_ipc_stream stream; struct sof_ipc_reply reply; - int ret = 0; + int ret; /* set IPC stream params */ stream.hdr.size = sizeof(stream); @@ -1033,7 +1033,7 @@ static int sof_control_load_volume(struct snd_soc_component *scomp, struct sof_ipc_ctrl_data *cdata; int tlv[TLV_ITEMS]; unsigned int i; - int ret = 0; + int ret; /* validate topology data */ if (le32_to_cpu(mc->num_channels) > SND_SOC_TPLG_MAX_CHAN) { @@ -1098,7 +1098,7 @@ skip: dev_dbg(scomp->dev, "tplg: load kcontrol index %d chans %d\n", scontrol->comp_id, scontrol->num_channels); - return ret; + return 0; out_free_table: if (le32_to_cpu(mc->max) > 1) @@ -1151,7 +1151,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; int max_size = sbe->max; - int ret = 0; + int ret; /* init the get/put bytes data */ scontrol->size = sizeof(struct sof_ipc_ctrl_data) + @@ -1204,7 +1204,7 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, } } - return ret; + return 0; out_free: kfree(scontrol->control_data); @@ -1223,7 +1223,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_dobj *dobj; struct snd_sof_control *scontrol; - int ret = -EINVAL; + int ret; dev_dbg(scomp->dev, "tplg: load control type %d name : %s\n", hdr->type, hdr->name); @@ -1276,7 +1276,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, dobj->private = scontrol; list_add(&scontrol->list, &sdev->kcontrol_list); - return ret; + return 0; } static int sof_control_unload(struct snd_soc_component *scomp, @@ -2659,7 +2659,7 @@ static int sof_dai_load(struct snd_soc_component *scomp, int index, struct snd_soc_tplg_private *private = &pcm->priv; struct snd_sof_pcm *spcm; int stream; - int ret = 0; + int ret; /* nothing to do for BEs atm */ if (!pcm) @@ -3350,7 +3350,6 @@ static int sof_link_hda_unload(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) { struct snd_soc_dai *dai; - int ret = 0; dai = snd_soc_find_dai(link->cpus); if (!dai) { @@ -3359,7 +3358,7 @@ static int sof_link_hda_unload(struct snd_sof_dev *sdev, return -EINVAL; } - return ret; + return 0; } static int sof_link_unload(struct snd_soc_component *scomp, @@ -3492,7 +3491,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, sink_swidget->id != snd_soc_dapm_buffer) { dev_dbg(scomp->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", route->source, route->sink); - ret = 0; goto err; } else { ret = sof_ipc_tx_message(sdev->ipc, @@ -3526,7 +3524,7 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, /* add route to route list */ list_add(&sroute->list, &sdev->route_list); - return ret; + return 0; } err: -- cgit From db69bcf915a37d7b8e54acf5f67d09d8159eb616 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Sep 2020 13:56:31 +0300 Subject: ASoC: SOF: remove several superfluous type-casts No need to type-cast assignments between void and other pointers in C. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Bard Liao Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-7-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-audio.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index cd506a4bfc4b..afe7e503bf66 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -485,13 +485,13 @@ EXPORT_SYMBOL(sof_machine_check); int sof_machine_register(struct snd_sof_dev *sdev, void *pdata) { - struct snd_sof_pdata *plat_data = (struct snd_sof_pdata *)pdata; + struct snd_sof_pdata *plat_data = pdata; const char *drv_name; const void *mach; int size; drv_name = plat_data->machine->drv_name; - mach = (const void *)plat_data->machine; + mach = plat_data->machine; size = sizeof(*plat_data->machine); /* register machine driver, pass machine info as pdata */ @@ -510,7 +510,7 @@ EXPORT_SYMBOL(sof_machine_register); void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata) { - struct snd_sof_pdata *plat_data = (struct snd_sof_pdata *)pdata; + struct snd_sof_pdata *plat_data = pdata; if (!IS_ERR_OR_NULL(plat_data->pdev_mach)) platform_device_unregister(plat_data->pdev_mach); -- cgit From 0e4ea878708be903566ad93d4972ad3dd4c1c30e Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 17 Sep 2020 13:56:32 +0300 Subject: ASoC: SOF: fix range checks On multiple locations checks are performed of untrusted values after adding a constant to them. This is wrong, because the addition might overflow and the result can then pass the check, although the original value is invalid. Fix multiple such issues by checking the actual value and not a sum of it and a constant. Signed-off-by: Guennadi Liakhovetski Reviewed-by: Ranjani Sridharan Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-8-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 38 ++++++++++++++++++++++---------------- sound/soc/sof/topology.c | 20 +++++++++++++------- 2 files changed, 35 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 63ead9ebc4c6..58f8c998e6af 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -229,14 +229,16 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, return -EINVAL; } - size = data->size + sizeof(*data); - if (size > be->max) { + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > be->max - sizeof(*data)) { dev_err_ratelimited(scomp->dev, - "error: DSP sent %zu bytes max is %d\n", - size, be->max); + "error: %u bytes of control data is invalid, max is %zu\n", + data->size, be->max - sizeof(*data)); return -EINVAL; } + size = data->size + sizeof(*data); + /* copy back to kcontrol */ memcpy(ucontrol->value.bytes.data, data, size); @@ -252,7 +254,7 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_soc_component *scomp = scontrol->scomp; struct sof_ipc_ctrl_data *cdata = scontrol->control_data; struct sof_abi_hdr *data = cdata->data; - size_t size = data->size + sizeof(*data); + size_t size; if (be->max > sizeof(ucontrol->value.bytes.data)) { dev_err_ratelimited(scomp->dev, @@ -261,13 +263,16 @@ int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - if (size > be->max) { + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > be->max - sizeof(*data)) { dev_err_ratelimited(scomp->dev, - "error: size too big %zu bytes max is %d\n", - size, be->max); + "error: data size too big %u bytes max is %zu\n", + data->size, be->max - sizeof(*data)); return -EINVAL; } + size = data->size + sizeof(*data); + /* copy from kcontrol */ memcpy(data, ucontrol->value.bytes.data, size); @@ -334,7 +339,8 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, return -EINVAL; } - if (cdata->data->size + sizeof(const struct sof_abi_hdr) > be->max) { + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); return -EINVAL; } @@ -420,7 +426,7 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, struct snd_ctl_tlv header; struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; - int data_size; + size_t data_size; /* * Decrement the limit by ext bytes header size to @@ -432,16 +438,16 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, cdata->data->magic = SOF_ABI_MAGIC; cdata->data->abi = SOF_ABI_VERSION; - /* Prevent read of other kernel data or possibly corrupt response */ - data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); - /* check data size doesn't exceed max coming from topology */ - if (data_size > be->max) { - dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %d.\n", - data_size, be->max); + if (cdata->data->size > be->max - sizeof(const struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", + cdata->data->size, + be->max - sizeof(const struct sof_abi_hdr)); return -EINVAL; } + data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + header.numid = scontrol->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index fa85a22b5880..eaa1122d5a68 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1150,20 +1150,26 @@ static int sof_control_load_bytes(struct snd_soc_component *scomp, struct snd_soc_tplg_bytes_control *control = container_of(hdr, struct snd_soc_tplg_bytes_control, hdr); struct soc_bytes_ext *sbe = (struct soc_bytes_ext *)kc->private_value; - int max_size = sbe->max; + size_t max_size = sbe->max; + size_t priv_size = le32_to_cpu(control->priv.size); int ret; - /* init the get/put bytes data */ - scontrol->size = sizeof(struct sof_ipc_ctrl_data) + - le32_to_cpu(control->priv.size); + if (max_size < sizeof(struct sof_ipc_ctrl_data) || + max_size < sizeof(struct sof_abi_hdr)) { + ret = -EINVAL; + goto out; + } - if (scontrol->size > max_size) { - dev_err(scomp->dev, "err: bytes data size %d exceeds max %d.\n", - scontrol->size, max_size); + /* init the get/put bytes data */ + if (priv_size > max_size - sizeof(struct sof_ipc_ctrl_data)) { + dev_err(scomp->dev, "err: bytes data size %zu exceeds max %zu.\n", + priv_size, max_size - sizeof(struct sof_ipc_ctrl_data)); ret = -EINVAL; goto out; } + scontrol->size = sizeof(struct sof_ipc_ctrl_data) + priv_size; + scontrol->control_data = kzalloc(max_size, GFP_KERNEL); cdata = scontrol->control_data; if (!scontrol->control_data) { -- cgit From 776100a4ce6da8f7fa451509e46852d69c2162a8 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Thu, 17 Sep 2020 13:56:33 +0300 Subject: ASoC: SOF: Intel: hda: reduce verbosity of boot error logs Previous commits reduced the verbosity of errors during boot iterations, but there are still a couple remaining which generate false positives. Errors should only be logged when after last attempt to download firmware failed. Duplicating logs and assigning them different levels based on the iteration number isn't really elegant, use macro as suggested by Guennadi. Suggested-by: Guennadi Liakhovetski Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917105633.2579047-9-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda-loader.c | 16 +++++++++------- sound/soc/sof/intel/hda.c | 12 +++++++++--- sound/soc/sof/intel/hda.h | 2 ++ sound/soc/sof/sof-priv.h | 8 ++++++++ 4 files changed, 28 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index dfbfc89ffe70..2707a16c6a4d 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -82,7 +82,7 @@ error: * status on core 1, so power up core 1 also momentarily, keep it in * reset/stall and then turn it off */ -static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) +static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; const struct sof_intel_dsp_desc *chip = hda->desc; @@ -93,7 +93,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) /* step 1: power up corex */ ret = hda_dsp_core_power_up(sdev, chip->host_managed_cores_mask); if (ret < 0) { - if (iteration == HDA_FW_BOOT_ATTEMPTS) + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core 0/1 power up failed\n"); goto err; } @@ -116,7 +116,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) /* step 3: unset core 0 reset state & unstall/run core 0 */ ret = hda_dsp_core_run(sdev, BIT(0)); if (ret < 0) { - if (iteration == HDA_FW_BOOT_ATTEMPTS) + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core start failed %d\n", ret); ret = -EIO; @@ -132,7 +132,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) HDA_DSP_INIT_TIMEOUT_US); if (ret < 0) { - if (iteration == HDA_FW_BOOT_ATTEMPTS) + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: %s: timeout for HIPCIE done\n", __func__); @@ -148,7 +148,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) /* step 5: power down corex */ ret = hda_dsp_core_power_down(sdev, chip->host_managed_cores_mask & ~(BIT(0))); if (ret < 0) { - if (iteration == HDA_FW_BOOT_ATTEMPTS) + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: dsp core x power down failed\n"); goto err; @@ -168,7 +168,7 @@ static int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, int iteration) if (!ret) return 0; - if (iteration == HDA_FW_BOOT_ATTEMPTS) + if (hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS) dev_err(sdev->dev, "error: %s: timeout HDA_DSP_SRAM_REG_ROM_STATUS read\n", __func__); @@ -328,6 +328,7 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct snd_sof_pdata *plat_data = sdev->pdata; const struct sof_dev_desc *desc = plat_data->desc; const struct sof_intel_dsp_desc *chip_info; @@ -364,7 +365,8 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev) dev_dbg(sdev->dev, "Attempting iteration %d of Core En/ROM load...\n", i); - ret = cl_dsp_init(sdev, stream->hstream.stream_tag, i + 1); + hda->boot_iteration = i + 1; + ret = cl_dsp_init(sdev, stream->hstream.stream_tag); /* don't retry anymore if successful */ if (!ret) diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 882527119c70..bb4128a72a42 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -418,6 +418,7 @@ void hda_dsp_dump_skl(struct snd_sof_dev *sdev, u32 flags) /* dump the first 8 dwords representing the extended ROM status */ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; char msg[128]; int len = 0; u32 value; @@ -428,11 +429,14 @@ static void hda_dsp_dump_ext_rom_status(struct snd_sof_dev *sdev) len += snprintf(msg + len, sizeof(msg) - len, " 0x%x", value); } - dev_err(sdev->dev, "error: extended rom status:%s", msg); + sof_dev_dbg_or_err(sdev->dev, hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS, + "extended rom status: %s", msg); + } void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) { + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct sof_ipc_dsp_oops_xtensa xoops; struct sof_ipc_panic_info panic_info; u32 stack[HDA_DSP_STACK_DUMP_SIZE]; @@ -452,8 +456,10 @@ void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags) snd_sof_get_status(sdev, status, panic, &xoops, &panic_info, stack, HDA_DSP_STACK_DUMP_SIZE); } else { - dev_err(sdev->dev, "error: status = 0x%8.8x panic = 0x%8.8x\n", - status, panic); + sof_dev_dbg_or_err(sdev->dev, hda->boot_iteration == HDA_FW_BOOT_ATTEMPTS, + "status = 0x%8.8x panic = 0x%8.8x\n", + status, panic); + hda_dsp_dump_ext_rom_status(sdev); hda_dsp_get_status(sdev); } diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index f0f8f95c082b..badb308aed81 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -274,6 +274,7 @@ #define BXT_D0I3_DELAY 5000 #define FW_CL_STREAM_NUMBER 0x1 +#define HDA_FW_BOOT_ATTEMPTS 3 /* ADSPCS - Audio DSP Control & Status */ @@ -416,6 +417,7 @@ enum sof_hda_D0_substate { /* represents DSP HDA controller frontend - i.e. host facing control */ struct sof_intel_hda_dev { + int boot_iteration; struct hda_bus hbus; diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 1c51d99f0459..0aed2a7ab858 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -578,4 +578,12 @@ int intel_pcm_close(struct snd_sof_dev *sdev, int sof_machine_check(struct snd_sof_dev *sdev); +#define sof_dev_dbg_or_err(dev, is_err, fmt, ...) \ + do { \ + if (is_err) \ + dev_err(dev, "error: " fmt, __VA_ARGS__); \ + else \ + dev_dbg(dev, fmt, __VA_ARGS__); \ + } while (0) + #endif -- cgit From 520a1c396d1966b64884d8e0176a580150d5a09e Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 10 Sep 2020 14:57:08 +0100 Subject: ASoC: q6afe-clocks: add q6afe clock controller q6afe already exposes lpass clocks, however this was not presented as proper clock controller driver. This patch basically adds clock controller support for q6afe clocks. This is useful for other drivers like lpass digital codec or lpass lowpower island drivers to request or vote for these clocks. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200910135708.14842-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 4 + sound/soc/qcom/qdsp6/Makefile | 1 + sound/soc/qcom/qdsp6/q6afe-clocks.c | 270 ++++++++++++++++++++++++++++++++++++ 3 files changed, 275 insertions(+) create mode 100644 sound/soc/qcom/qdsp6/q6afe-clocks.c (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a607ace8b089..a7ef62685b41 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -63,6 +63,9 @@ config SND_SOC_QDSP6_AFE config SND_SOC_QDSP6_AFE_DAI tristate +config SND_SOC_QDSP6_AFE_CLOCKS + tristate + config SND_SOC_QDSP6_ADM tristate @@ -83,6 +86,7 @@ config SND_SOC_QDSP6 select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_AFE select SND_SOC_QDSP6_AFE_DAI + select SND_SOC_QDSP6_AFE_CLOCKS select SND_SOC_QDSP6_ADM select SND_SOC_QDSP6_ROUTING select SND_SOC_QDSP6_ASM diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile index 7e91e96f7ad5..3c1dd9f32f1d 100644 --- a/sound/soc/qcom/qdsp6/Makefile +++ b/sound/soc/qcom/qdsp6/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_SND_SOC_QDSP6_COMMON) += q6dsp-common.o obj-$(CONFIG_SND_SOC_QDSP6_CORE) += q6core.o obj-$(CONFIG_SND_SOC_QDSP6_AFE) += q6afe.o obj-$(CONFIG_SND_SOC_QDSP6_AFE_DAI) += q6afe-dai.o +obj-$(CONFIG_SND_SOC_QDSP6_AFE_CLOCKS) += q6afe-clocks.o obj-$(CONFIG_SND_SOC_QDSP6_ADM) += q6adm.o obj-$(CONFIG_SND_SOC_QDSP6_ROUTING) += q6routing.o obj-$(CONFIG_SND_SOC_QDSP6_ASM) += q6asm.o diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c new file mode 100644 index 000000000000..2967f4546af5 --- /dev/null +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: GPL-1.0 +// Copyright (c) 2020, Linaro Limited + +#include +#include +#include +#include +#include +#include +#include +#include +#include "q6afe.h" + +#define Q6AFE_CLK(id) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = Q6AFE_##id, \ + .name = #id, \ + .attributes = LPASS_CLK_ATTRIBUTE_COUPLE_NO, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_q6afe_ops, \ + .name = #id, \ + }, \ + } + +#define Q6AFE_VOTE_CLK(id, blkid, n) &(struct q6afe_clk) { \ + .clk_id = id, \ + .afe_clk_id = blkid, \ + .name = #n, \ + .hw.init = &(struct clk_init_data) { \ + .ops = &clk_vote_q6afe_ops, \ + .name = #id, \ + }, \ + } + +struct q6afe_clk { + struct device *dev; + int clk_id; + int afe_clk_id; + char *name; + int attributes; + int rate; + uint32_t handle; + struct clk_hw hw; +}; + +#define to_q6afe_clk(_hw) container_of(_hw, struct q6afe_clk, hw) + +struct q6afe_cc { + struct device *dev; + struct q6afe_clk **clks; + int num_clks; +}; + +static int clk_q6afe_prepare(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, clk->rate); +} + +static void clk_q6afe_unprepare(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + q6afe_set_lpass_clock(clk->dev, clk->afe_clk_id, clk->attributes, + Q6AFE_LPASS_CLK_ROOT_DEFAULT, 0); +} + +static int clk_q6afe_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + clk->rate = rate; + + return 0; +} + +static unsigned long clk_q6afe_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return clk->rate; +} + +static long clk_q6afe_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + return rate; +} + +static const struct clk_ops clk_q6afe_ops = { + .prepare = clk_q6afe_prepare, + .unprepare = clk_q6afe_unprepare, + .set_rate = clk_q6afe_set_rate, + .round_rate = clk_q6afe_round_rate, + .recalc_rate = clk_q6afe_recalc_rate, +}; + +static int clk_vote_q6afe_block(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + return q6afe_vote_lpass_core_hw(clk->dev, clk->afe_clk_id, + clk->name, &clk->handle); +} + +static void clk_unvote_q6afe_block(struct clk_hw *hw) +{ + struct q6afe_clk *clk = to_q6afe_clk(hw); + + q6afe_unvote_lpass_core_hw(clk->dev, clk->afe_clk_id, clk->handle); +} + +static const struct clk_ops clk_vote_q6afe_ops = { + .prepare = clk_vote_q6afe_block, + .unprepare = clk_unvote_q6afe_block, +}; + +struct q6afe_clk *q6afe_clks[Q6AFE_MAX_CLK_ID] = { + [LPASS_CLK_ID_PRI_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_IBIT), + [LPASS_CLK_ID_PRI_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_MI2S_EBIT), + [LPASS_CLK_ID_SEC_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_IBIT), + [LPASS_CLK_ID_SEC_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_MI2S_EBIT), + [LPASS_CLK_ID_TER_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_IBIT), + [LPASS_CLK_ID_TER_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_MI2S_EBIT), + [LPASS_CLK_ID_QUAD_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_IBIT), + [LPASS_CLK_ID_QUAD_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_MI2S_EBIT), + [LPASS_CLK_ID_SPEAKER_I2S_IBIT] = + Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_IBIT), + [LPASS_CLK_ID_SPEAKER_I2S_EBIT] = + Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_EBIT), + [LPASS_CLK_ID_SPEAKER_I2S_OSR] = + Q6AFE_CLK(LPASS_CLK_ID_SPEAKER_I2S_OSR), + [LPASS_CLK_ID_QUI_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_IBIT), + [LPASS_CLK_ID_QUI_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_EBIT), + [LPASS_CLK_ID_SEN_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_IBIT), + [LPASS_CLK_ID_SEN_MI2S_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEN_MI2S_EBIT), + [LPASS_CLK_ID_INT0_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT0_MI2S_IBIT), + [LPASS_CLK_ID_INT1_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT1_MI2S_IBIT), + [LPASS_CLK_ID_INT2_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT2_MI2S_IBIT), + [LPASS_CLK_ID_INT3_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT3_MI2S_IBIT), + [LPASS_CLK_ID_INT4_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT4_MI2S_IBIT), + [LPASS_CLK_ID_INT5_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), + [LPASS_CLK_ID_INT6_MI2S_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), + [LPASS_CLK_ID_QUI_MI2S_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), + [LPASS_CLK_ID_PRI_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_IBIT), + [LPASS_CLK_ID_PRI_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_PCM_EBIT), + [LPASS_CLK_ID_SEC_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_IBIT), + [LPASS_CLK_ID_SEC_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_PCM_EBIT), + [LPASS_CLK_ID_TER_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_IBIT), + [LPASS_CLK_ID_TER_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_PCM_EBIT), + [LPASS_CLK_ID_QUAD_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_IBIT), + [LPASS_CLK_ID_QUAD_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_PCM_EBIT), + [LPASS_CLK_ID_QUIN_PCM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_IBIT), + [LPASS_CLK_ID_QUIN_PCM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_PCM_EBIT), + [LPASS_CLK_ID_QUI_PCM_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUI_PCM_OSR), + [LPASS_CLK_ID_PRI_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_IBIT), + [LPASS_CLK_ID_PRI_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_PRI_TDM_EBIT), + [LPASS_CLK_ID_SEC_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_IBIT), + [LPASS_CLK_ID_SEC_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_SEC_TDM_EBIT), + [LPASS_CLK_ID_TER_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_IBIT), + [LPASS_CLK_ID_TER_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_TER_TDM_EBIT), + [LPASS_CLK_ID_QUAD_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_IBIT), + [LPASS_CLK_ID_QUAD_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUAD_TDM_EBIT), + [LPASS_CLK_ID_QUIN_TDM_IBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_IBIT), + [LPASS_CLK_ID_QUIN_TDM_EBIT] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_EBIT), + [LPASS_CLK_ID_QUIN_TDM_OSR] = Q6AFE_CLK(LPASS_CLK_ID_QUIN_TDM_OSR), + [LPASS_CLK_ID_MCLK_1] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_1), + [LPASS_CLK_ID_MCLK_2] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_2), + [LPASS_CLK_ID_MCLK_3] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_3), + [LPASS_CLK_ID_MCLK_4] = Q6AFE_CLK(LPASS_CLK_ID_MCLK_4), + [LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE] = + Q6AFE_CLK(LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE), + [LPASS_CLK_ID_INT_MCLK_0] = Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_0), + [LPASS_CLK_ID_INT_MCLK_1] = Q6AFE_CLK(LPASS_CLK_ID_INT_MCLK_1), + [LPASS_CLK_ID_WSA_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), + [LPASS_CLK_ID_WSA_CORE_NPL_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), + [LPASS_CLK_ID_VA_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_MCLK), + [LPASS_CLK_ID_TX_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_TX_CORE_MCLK), + [LPASS_CLK_ID_TX_CORE_NPL_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_TX_CORE_NPL_MCLK), + [LPASS_CLK_ID_RX_CORE_MCLK] = Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_MCLK), + [LPASS_CLK_ID_RX_CORE_NPL_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_RX_CORE_NPL_MCLK), + [LPASS_CLK_ID_VA_CORE_2X_MCLK] = + Q6AFE_CLK(LPASS_CLK_ID_VA_CORE_2X_MCLK), + [LPASS_HW_AVTIMER_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_AVTIMER_VOTE, + Q6AFE_LPASS_CORE_AVTIMER_BLOCK, + "LPASS_AVTIMER_MACRO"), + [LPASS_HW_MACRO_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_MACRO_VOTE, + Q6AFE_LPASS_CORE_HW_MACRO_BLOCK, + "LPASS_HW_MACRO"), + [LPASS_HW_DCODEC_VOTE] = Q6AFE_VOTE_CLK(LPASS_HW_DCODEC_VOTE, + Q6AFE_LPASS_CORE_HW_DCODEC_BLOCK, + "LPASS_HW_DCODEC"), +}; + +static struct clk_hw *q6afe_of_clk_hw_get(struct of_phandle_args *clkspec, + void *data) +{ + struct q6afe_cc *cc = data; + unsigned int idx = clkspec->args[0]; + unsigned int attr = clkspec->args[1]; + + if (idx >= cc->num_clks || attr > LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR) { + dev_err(cc->dev, "Invalid clk specifier (%d, %d)\n", idx, attr); + return ERR_PTR(-EINVAL); + } + + if (cc->clks[idx]) { + cc->clks[idx]->attributes = attr; + return &cc->clks[idx]->hw; + } + + return ERR_PTR(-ENOENT); +} + +static int q6afe_clock_dev_probe(struct platform_device *pdev) +{ + struct q6afe_cc *cc; + struct device *dev = &pdev->dev; + int i, ret; + + cc = devm_kzalloc(dev, sizeof(*cc), GFP_KERNEL); + if (!cc) + return -ENOMEM; + + cc->clks = &q6afe_clks[0]; + cc->num_clks = ARRAY_SIZE(q6afe_clks); + for (i = 0; i < ARRAY_SIZE(q6afe_clks); i++) { + if (!q6afe_clks[i]) + continue; + + q6afe_clks[i]->dev = dev; + + ret = devm_clk_hw_register(dev, &q6afe_clks[i]->hw); + if (ret) + return ret; + } + + ret = of_clk_add_hw_provider(dev->of_node, q6afe_of_clk_hw_get, cc); + if (ret) + return ret; + + dev_set_drvdata(dev, cc); + + return 0; +} + +static const struct of_device_id q6afe_clock_device_id[] = { + { .compatible = "qcom,q6afe-clocks" }, + {}, +}; +MODULE_DEVICE_TABLE(of, q6afe_clock_device_id); + +static struct platform_driver q6afe_clock_platform_driver = { + .driver = { + .name = "q6afe-clock", + .of_match_table = of_match_ptr(q6afe_clock_device_id), + }, + .probe = q6afe_clock_dev_probe, +}; +module_platform_driver(q6afe_clock_platform_driver); + +MODULE_DESCRIPTION("Q6 Audio Frontend clock driver"); +MODULE_LICENSE("GPL v2"); -- cgit From 0b2cbce6898600aae5e87285f1c2000162d59c76 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 17 Sep 2020 14:11:17 +0800 Subject: ASoC: fsl_sai: Add new added registers and new bit definition On i.MX8MQ/i.MX8MN/i.MX8MM platform, the sai IP is upgraded. There are some new registers and new bit definition. This patch is to complete the register list. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/1600323079-5317-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 23 +++++++++++++++++++ sound/soc/fsl/fsl_sai.h | 59 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index b2d65e53dbc4..24ca528ca2be 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -796,6 +796,8 @@ static struct reg_default fsl_sai_reg_defaults_ofs8[] = { {FSL_SAI_RCR4(8), 0}, {FSL_SAI_RCR5(8), 0}, {FSL_SAI_RMR, 0}, + {FSL_SAI_MCTL, 0}, + {FSL_SAI_MDIV, 0}, }; static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) @@ -836,6 +838,18 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg) case FSL_SAI_RFR6: case FSL_SAI_RFR7: case FSL_SAI_RMR: + case FSL_SAI_MCTL: + case FSL_SAI_MDIV: + case FSL_SAI_VERID: + case FSL_SAI_PARAM: + case FSL_SAI_TTCTN: + case FSL_SAI_RTCTN: + case FSL_SAI_TTCTL: + case FSL_SAI_TBCTN: + case FSL_SAI_TTCAP: + case FSL_SAI_RTCTL: + case FSL_SAI_RBCTN: + case FSL_SAI_RTCAP: return true; default: return false; @@ -850,6 +864,10 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg) if (reg == FSL_SAI_TCSR(ofs) || reg == FSL_SAI_RCSR(ofs)) return true; + /* Set VERID and PARAM be volatile for reading value in probe */ + if (ofs == 8 && (reg == FSL_SAI_VERID || reg == FSL_SAI_PARAM)) + return true; + switch (reg) { case FSL_SAI_TFR0: case FSL_SAI_TFR1: @@ -903,6 +921,10 @@ static bool fsl_sai_writeable_reg(struct device *dev, unsigned int reg) case FSL_SAI_TDR7: case FSL_SAI_TMR: case FSL_SAI_RMR: + case FSL_SAI_MCTL: + case FSL_SAI_MDIV: + case FSL_SAI_TTCTL: + case FSL_SAI_RTCTL: return true; default: return false; @@ -951,6 +973,7 @@ static int fsl_sai_probe(struct platform_device *pdev) if (sai->soc_data->reg_offset == 8) { fsl_sai_regmap_config.reg_defaults = fsl_sai_reg_defaults_ofs8; + fsl_sai_regmap_config.max_register = FSL_SAI_MDIV; fsl_sai_regmap_config.num_reg_defaults = ARRAY_SIZE(fsl_sai_reg_defaults_ofs8); } diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 736a437450c8..d16fc4241f41 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -14,6 +14,8 @@ SNDRV_PCM_FMTBIT_S32_LE) /* SAI Register Map Register */ +#define FSL_SAI_VERID 0x00 /* SAI Version ID Register */ +#define FSL_SAI_PARAM 0x04 /* SAI Parameter Register */ #define FSL_SAI_TCSR(ofs) (0x00 + ofs) /* SAI Transmit Control */ #define FSL_SAI_TCR1(ofs) (0x04 + ofs) /* SAI Transmit Configuration 1 */ #define FSL_SAI_TCR2(ofs) (0x08 + ofs) /* SAI Transmit Configuration 2 */ @@ -37,6 +39,10 @@ #define FSL_SAI_TFR6 0x58 /* SAI Transmit FIFO 6 */ #define FSL_SAI_TFR7 0x5C /* SAI Transmit FIFO 7 */ #define FSL_SAI_TMR 0x60 /* SAI Transmit Mask */ +#define FSL_SAI_TTCTL 0x70 /* SAI Transmit Timestamp Control Register */ +#define FSL_SAI_TTCTN 0x74 /* SAI Transmit Timestamp Counter Register */ +#define FSL_SAI_TBCTN 0x78 /* SAI Transmit Bit Counter Register */ +#define FSL_SAI_TTCAP 0x7C /* SAI Transmit Timestamp Capture */ #define FSL_SAI_RCSR(ofs) (0x80 + ofs) /* SAI Receive Control */ #define FSL_SAI_RCR1(ofs) (0x84 + ofs)/* SAI Receive Configuration 1 */ #define FSL_SAI_RCR2(ofs) (0x88 + ofs) /* SAI Receive Configuration 2 */ @@ -60,6 +66,13 @@ #define FSL_SAI_RFR6 0xd8 /* SAI Receive FIFO 6 */ #define FSL_SAI_RFR7 0xdc /* SAI Receive FIFO 7 */ #define FSL_SAI_RMR 0xe0 /* SAI Receive Mask */ +#define FSL_SAI_RTCTL 0xf0 /* SAI Receive Timestamp Control Register */ +#define FSL_SAI_RTCTN 0xf4 /* SAI Receive Timestamp Counter Register */ +#define FSL_SAI_RBCTN 0xf8 /* SAI Receive Bit Counter Register */ +#define FSL_SAI_RTCAP 0xfc /* SAI Receive Timestamp Capture */ + +#define FSL_SAI_MCTL 0x100 /* SAI MCLK Control Register */ +#define FSL_SAI_MDIV 0x104 /* SAI MCLK Divide Register */ #define FSL_SAI_xCSR(tx, ofs) (tx ? FSL_SAI_TCSR(ofs) : FSL_SAI_RCSR(ofs)) #define FSL_SAI_xCR1(tx, ofs) (tx ? FSL_SAI_TCR1(ofs) : FSL_SAI_RCR1(ofs)) @@ -73,6 +86,7 @@ /* SAI Transmit/Receive Control Register */ #define FSL_SAI_CSR_TERE BIT(31) +#define FSL_SAI_CSR_SE BIT(30) #define FSL_SAI_CSR_FR BIT(25) #define FSL_SAI_CSR_SR BIT(24) #define FSL_SAI_CSR_xF_SHIFT 16 @@ -106,6 +120,7 @@ #define FSL_SAI_CR2_MSEL(ID) ((ID) << 26) #define FSL_SAI_CR2_BCP BIT(25) #define FSL_SAI_CR2_BCD_MSTR BIT(24) +#define FSL_SAI_CR2_BYP BIT(23) /* BCLK bypass */ #define FSL_SAI_CR2_DIV_MASK 0xff /* SAI Transmit and Receive Configuration 3 Register */ @@ -115,6 +130,13 @@ #define FSL_SAI_CR3_WDFL_MASK 0x1f /* SAI Transmit and Receive Configuration 4 Register */ + +#define FSL_SAI_CR4_FCONT BIT(28) +#define FSL_SAI_CR4_FCOMB_SHIFT BIT(26) +#define FSL_SAI_CR4_FCOMB_SOFT BIT(27) +#define FSL_SAI_CR4_FCOMB_MASK (0x3 << 26) +#define FSL_SAI_CR4_FPACK_8 (0x2 << 24) +#define FSL_SAI_CR4_FPACK_16 (0x3 << 24) #define FSL_SAI_CR4_FRSZ(x) (((x) - 1) << 16) #define FSL_SAI_CR4_FRSZ_MASK (0x1f << 16) #define FSL_SAI_CR4_SYWD(x) (((x) - 1) << 8) @@ -134,6 +156,43 @@ #define FSL_SAI_CR5_FBT(x) ((x) << 8) #define FSL_SAI_CR5_FBT_MASK (0x1f << 8) +/* SAI MCLK Control Register */ +#define FSL_SAI_MCTL_MCLK_EN BIT(30) /* MCLK Enable */ +#define FSL_SAI_MCTL_MSEL_MASK (0x3 << 24) +#define FSL_SAI_MCTL_MSEL(ID) ((ID) << 24) +#define FSL_SAI_MCTL_MSEL_BUS 0 +#define FSL_SAI_MCTL_MSEL_MCLK1 BIT(24) +#define FSL_SAI_MCTL_MSEL_MCLK2 BIT(25) +#define FSL_SAI_MCTL_MSEL_MCLK3 (BIT(24) | BIT(25)) +#define FSL_SAI_MCTL_DIV_EN BIT(23) +#define FSL_SAI_MCTL_DIV_MASK 0xFF + +/* SAI VERID Register */ +#define FSL_SAI_VERID_MAJOR_SHIFT 24 +#define FSL_SAI_VERID_MAJOR_MASK GENMASK(31, 24) +#define FSL_SAI_VERID_MINOR_SHIFT 16 +#define FSL_SAI_VERID_MINOR_MASK GENMASK(23, 16) +#define FSL_SAI_VERID_FEATURE_SHIFT 0 +#define FSL_SAI_VERID_FEATURE_MASK GENMASK(15, 0) +#define FSL_SAI_VERID_EFIFO_EN BIT(0) +#define FSL_SAI_VERID_TSTMP_EN BIT(1) + +/* SAI PARAM Register */ +#define FSL_SAI_PARAM_SPF_SHIFT 16 +#define FSL_SAI_PARAM_SPF_MASK GENMASK(19, 16) +#define FSL_SAI_PARAM_WPF_SHIFT 8 +#define FSL_SAI_PARAM_WPF_MASK GENMASK(11, 8) +#define FSL_SAI_PARAM_DLN_MASK GENMASK(3, 0) + +/* SAI MCLK Divide Register */ +#define FSL_SAI_MDIV_MASK 0xFFFFF + +/* SAI timestamp and bitcounter */ +#define FSL_SAI_xTCTL_TSEN BIT(0) +#define FSL_SAI_xTCTL_TSINC BIT(1) +#define FSL_SAI_xTCTL_RTSC BIT(8) +#define FSL_SAI_xTCTL_RBC BIT(9) + /* SAI type */ #define FSL_SAI_DMA BIT(0) #define FSL_SAI_USE_AC97 BIT(1) -- cgit From 1dc658b13c1c365274b27bfc3c4d4f2955348fb8 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 17 Sep 2020 14:11:18 +0800 Subject: ASoC: fsl_sai: Add fsl_sai_check_version function fsl_sai_check_version can help to parse the version info in VERID and PARAM registers. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/1600323079-5317-3-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/fsl/fsl_sai.h | 28 ++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 24ca528ca2be..738b4dda7847 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -946,6 +946,48 @@ static struct regmap_config fsl_sai_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static int fsl_sai_check_version(struct device *dev) +{ + struct fsl_sai *sai = dev_get_drvdata(dev); + unsigned char ofs = sai->soc_data->reg_offset; + unsigned int val; + int ret; + + if (FSL_SAI_TCSR(ofs) == FSL_SAI_VERID) + return 0; + + ret = regmap_read(sai->regmap, FSL_SAI_VERID, &val); + if (ret < 0) + return ret; + + dev_dbg(dev, "VERID: 0x%016X\n", val); + + sai->verid.major = (val & FSL_SAI_VERID_MAJOR_MASK) >> + FSL_SAI_VERID_MAJOR_SHIFT; + sai->verid.minor = (val & FSL_SAI_VERID_MINOR_MASK) >> + FSL_SAI_VERID_MINOR_SHIFT; + sai->verid.feature = val & FSL_SAI_VERID_FEATURE_MASK; + + ret = regmap_read(sai->regmap, FSL_SAI_PARAM, &val); + if (ret < 0) + return ret; + + dev_dbg(dev, "PARAM: 0x%016X\n", val); + + /* Max slots per frame, power of 2 */ + sai->param.slot_num = 1 << + ((val & FSL_SAI_PARAM_SPF_MASK) >> FSL_SAI_PARAM_SPF_SHIFT); + + /* Words per fifo, power of 2 */ + sai->param.fifo_depth = 1 << + ((val & FSL_SAI_PARAM_WPF_MASK) >> FSL_SAI_PARAM_WPF_SHIFT); + + /* Number of datalines implemented */ + sai->param.dataline = val & FSL_SAI_PARAM_DLN_MASK; + + return 0; +} + static int fsl_sai_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -1070,6 +1112,11 @@ static int fsl_sai_probe(struct platform_device *pdev) platform_set_drvdata(pdev, sai); + /* Get sai version */ + ret = fsl_sai_check_version(&pdev->dev); + if (ret < 0) + dev_warn(&pdev->dev, "Error reading SAI version: %d\n", ret); + pm_runtime_enable(&pdev->dev); regcache_cache_only(sai->regmap, true); diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index d16fc4241f41..ba7425a9e217 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -223,6 +223,32 @@ struct fsl_sai_soc_data { unsigned int reg_offset; }; +/** + * struct fsl_sai_verid - version id data + * @major: major version number + * @minor: minor version number + * @feature: feature specification number + * 0000000000000000b - Standard feature set + * 0000000000000000b - Standard feature set + */ +struct fsl_sai_verid { + u32 major; + u32 minor; + u32 feature; +}; + +/** + * struct fsl_sai_param - parameter data + * @slot_num: The maximum number of slots per frame + * @fifo_depth: The number of words in each FIFO (depth) + * @dataline: The number of datalines implemented + */ +struct fsl_sai_param { + u32 slot_num; + u32 fifo_depth; + u32 dataline; +}; + struct fsl_sai { struct platform_device *pdev; struct regmap *regmap; @@ -243,6 +269,8 @@ struct fsl_sai { const struct fsl_sai_soc_data *soc_data; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; + struct fsl_sai_verid verid; + struct fsl_sai_param param; }; #define TX 1 -- cgit From a57d4e8730c1a55b2547ff81aef4753b67121cb8 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Thu, 17 Sep 2020 14:11:19 +0800 Subject: ASoC: fsl_sai: Set MCLK input or output direction SAI support select MCLK direction with version.major > 3 and version.minor > 1, the default direction is input, set it to be output according to DT property. Signed-off-by: Shengjiu Wang Acked-by: Nicolin Chen Reviewed-by: Fabio Estevam Link: https://lore.kernel.org/r/1600323079-5317-4-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index 738b4dda7847..5117c1cd5682 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -1117,6 +1117,13 @@ static int fsl_sai_probe(struct platform_device *pdev) if (ret < 0) dev_warn(&pdev->dev, "Error reading SAI version: %d\n", ret); + /* Select MCLK direction */ + if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && + sai->verid.major >= 3 && sai->verid.minor >= 1) { + regmap_update_bits(sai->regmap, FSL_SAI_MCTL, + FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); + } + pm_runtime_enable(&pdev->dev); regcache_cache_only(sai->regmap, true); -- cgit From 251e5c8694db01cd10828e39c07f90d958d7b303 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 2 Sep 2020 15:30:42 +0200 Subject: ASoC: codec: tlv320aic32x4: fix missing aic32x4_disable_regulators() in error path The regulators need to be disabled in the aic32x4_register_clocks() failure case as well. Fixes: 9d4befff5a95 ("ASoC: codec: tlv3204: Moving GPIO reset and add ADC reset") Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/20200902133043.19504-1-matthias.schiffer@ew.tq-group.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 8dcea566b375..a45fb496082c 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1230,8 +1230,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) &soc_component_dev_aic32x4, &aic32x4_dai, 1); if (ret) { dev_err(dev, "Failed to register component\n"); - aic32x4_disable_regulators(aic32x4); - return ret; + goto err_disable_regulators; } if (gpio_is_valid(aic32x4->rstn_gpio)) { @@ -1242,9 +1241,14 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); if (ret) - return ret; + goto err_disable_regulators; return 0; + +err_disable_regulators: + aic32x4_disable_regulators(aic32x4); + + return ret; } EXPORT_SYMBOL(aic32x4_probe); -- cgit From df44bc16e616809172cda90fd816596ded4ea219 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Wed, 2 Sep 2020 15:30:43 +0200 Subject: ASoC: codec: tlv320aic32x4: do software reset before clock registration To avoid the actual PLL settings to differ from the state expected by the clock driver, the codec should only be fully reset before the clocks are registered. But we also need to ensure that the software reset happens at all before clock registration, as not all boards have a reset GPIO. Move the software reset from aic32x4_component_probe() to aic32x4_probe() and reorder the reset and registration sequence: 1. Reset via GPIO (if available) 2. Reset via software 3. Register component 4. Register clocks Note that aic32x4_component_probe() is only called after aic32x4_probe() has finished, so the reset in aic32x4_component_probe() was happening too late. Signed-off-by: Matthias Schiffer Link: https://lore.kernel.org/r/20200902133043.19504-2-matthias.schiffer@ew.tq-group.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index a45fb496082c..470dc0ef0359 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -981,8 +981,6 @@ static int aic32x4_component_probe(struct snd_soc_component *component) if (ret) return ret; - snd_soc_component_write(component, AIC32X4_RESET, 0x01); - if (aic32x4->setup) aic32x4_setup_gpios(component); @@ -1226,6 +1224,16 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) return ret; } + if (gpio_is_valid(aic32x4->rstn_gpio)) { + ndelay(10); + gpio_set_value_cansleep(aic32x4->rstn_gpio, 1); + mdelay(1); + } + + ret = regmap_write(regmap, AIC32X4_RESET, 0x01); + if (ret) + goto err_disable_regulators; + ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic32x4, &aic32x4_dai, 1); if (ret) { @@ -1233,12 +1241,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap) goto err_disable_regulators; } - if (gpio_is_valid(aic32x4->rstn_gpio)) { - ndelay(10); - gpio_set_value_cansleep(aic32x4->rstn_gpio, 1); - mdelay(1); - } - ret = aic32x4_register_clocks(dev, aic32x4->mclk_name); if (ret) goto err_disable_regulators; -- cgit From 30ee3738f849b3f4af623c20adec73cdc4573a2e Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Thu, 17 Sep 2020 13:36:09 +0300 Subject: ASoC: SOF: Intel: Add support for tgl-h SOF will support tgl-h and tgl-lp in different FW binaries due to hardware difference, so create another dev_desc entry with FW name of sof-tgl-h.ri and dsp_desc named tglh_chip_info for tgl-h. Fixes: c8d2e2bfaeffa ("ASoC: SOF: Intel: add PCI IDs for ICL-H and TGL-H") Signed-off-by: Rander Wang Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200917103609.2559916-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/intel/hda.h | 1 + sound/soc/sof/intel/tgl.c | 16 ++++++++++++++++ sound/soc/sof/sof-pci-dev.c | 18 +++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index f0f8f95c082b..659868da4068 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -737,6 +737,7 @@ extern const struct sof_intel_dsp_desc cnl_chip_info; extern const struct sof_intel_dsp_desc skl_chip_info; extern const struct sof_intel_dsp_desc icl_chip_info; extern const struct sof_intel_dsp_desc tgl_chip_info; +extern const struct sof_intel_dsp_desc tglh_chip_info; extern const struct sof_intel_dsp_desc ehl_chip_info; extern const struct sof_intel_dsp_desc jsl_chip_info; diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index f8d04fd66ceb..0278b67de1ec 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -135,3 +135,19 @@ const struct sof_intel_dsp_desc tgl_chip_info = { .ssp_base_offset = CNL_SSP_BASE_OFFSET, }; EXPORT_SYMBOL_NS(tgl_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); + +const struct sof_intel_dsp_desc tglh_chip_info = { + /* Tigerlake-H */ + .cores_num = 2, + .init_core_mask = 1, + .host_managed_cores_mask = BIT(0), + .ipc_req = CNL_DSP_REG_HIPCIDR, + .ipc_req_mask = CNL_DSP_REG_HIPCIDR_BUSY, + .ipc_ack = CNL_DSP_REG_HIPCIDA, + .ipc_ack_mask = CNL_DSP_REG_HIPCIDA_DONE, + .ipc_ctl = CNL_DSP_REG_HIPCCTL, + .rom_init_timeout = 300, + .ssp_count = ICL_SSP_COUNT, + .ssp_base_offset = CNL_SSP_BASE_OFFSET, +}; +EXPORT_SYMBOL_NS(tglh_chip_info, SND_SOC_SOF_INTEL_HDA_COMMON); diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 3ec380945466..8c53f6935417 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -223,6 +223,22 @@ static const struct sof_dev_desc tgl_desc = { .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", .ops = &sof_tgl_ops, }; + +static const struct sof_dev_desc tglh_desc = { + .machines = snd_soc_acpi_intel_tgl_machines, + .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines, + .resindex_lpe_base = 0, + .resindex_pcicfg_base = -1, + .resindex_imr_base = -1, + .irqindex_host_ipc = -1, + .resindex_dma_base = -1, + .chip_info = &tglh_chip_info, + .default_fw_path = "intel/sof", + .default_tplg_path = "intel/sof-tplg", + .default_fw_filename = "sof-tgl-h.ri", + .nocodec_tplg_filename = "sof-tgl-nocodec.tplg", + .ops = &sof_tgl_ops, +}; #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) @@ -457,7 +473,7 @@ static const struct pci_device_id sof_pci_ids[] = { { PCI_DEVICE(0x8086, 0xa0c8), /* TGL-LP */ .driver_data = (unsigned long)&tgl_desc}, { PCI_DEVICE(0x8086, 0x43c8), /* TGL-H */ - .driver_data = (unsigned long)&tgl_desc}, + .driver_data = (unsigned long)&tglh_desc}, #endif #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) -- cgit From dcde34c47d8f7befc647fda65e8efa8cc0e795ca Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 16 Sep 2020 11:15:55 -0700 Subject: ASoC: qcom: lpass-sc7180: Add MODULE_DEVICE_TABLE The lpass-sc7180 driver can be built as a module but is lacking a MODULE_DEVICE_TABLE. This means it won't auto-load. Fix this oversight. Fixes: 24caf8d9eb10 ("ASoC: qcom: lpass-sc7180: Add platform driver for lpass audio") Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200916111545.1.I4c3758817d94c433bafeac344a395e21ea6657e3@changeid Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-sc7180.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index 167bf2cbc556..a8a3d8f8f567 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -200,6 +200,7 @@ static const struct of_device_id sc7180_lpass_cpu_device_id[] = { {.compatible = "qcom,sc7180-lpass-cpu", .data = &sc7180_data}, {} }; +MODULE_DEVICE_TABLE(of, sc7180_lpass_cpu_device_id); static struct platform_driver sc7180_lpass_cpu_platform_driver = { .driver = { -- cgit From 42d5031d3ee858bc14df704439eefdbf38b8f628 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 15 Sep 2020 14:06:04 -0500 Subject: ASoC: tlv320adcx140: Add the config to configure Tx ASI output Add code to allow the ASI Tx output to be placed into High-z mode during unused ASI cycles. This allows for other devices that may be on the bus to drive the ASI out. By default the 320adcx140 sends 0's for unused cycles. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200915190606.1744-4-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 11 +++++++++++ sound/soc/codecs/tlv320adcx140.h | 1 + 2 files changed, 12 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index cf5cbe314f7e..28dbd7d5e149 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -876,6 +876,7 @@ static int adcx140_codec_probe(struct snd_soc_component *component) u32 gpi_input_val = 0; int i; int ret; + bool tx_high_z; ret = device_property_read_u32(adcx140->dev, "ti,mic-bias-source", &bias_source); @@ -967,6 +968,16 @@ static int adcx140_codec_probe(struct snd_soc_component *component) if (ret) dev_err(adcx140->dev, "setting MIC bias failed %d\n", ret); + tx_high_z = device_property_read_bool(adcx140->dev, "ti,asi-tx-drive"); + if (tx_high_z) { + ret = regmap_update_bits(adcx140->regmap, ADCX140_ASI_CFG0, + ADCX140_TX_FILL, ADCX140_TX_FILL); + if (ret) { + dev_err(adcx140->dev, "Setting Tx drive failed %d\n", ret); + goto out; + } + } + adcx140_pwr_ctrl(adcx140, true); out: return ret; diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h index 94c6d1fd2977..107bd7927d9c 100644 --- a/sound/soc/codecs/tlv320adcx140.h +++ b/sound/soc/codecs/tlv320adcx140.h @@ -146,5 +146,6 @@ #define ADCX140_GPO_CFG_MAX 4 #define ADCX140_GPO_DRV_MAX 5 +#define ADCX140_TX_FILL BIT(0) #endif /* _TLV320ADCX140_ */ -- cgit From 337d348b8399adf1a19c8d65f6407939b4743fc9 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 15 Sep 2020 21:57:00 +0800 Subject: ASoC: ak4458: Add DSD support for ak4458 and ak4497 Ak4458 can't support DSD512 format, but ak4497 can, so add a new enum variable (enum ak4458_type) in ak4458_drvdata to distinguish these two platforms. Ak4497 has two kinds of DSD input pin, it can be selected by the dsd-path property from DT. In hw_params(), bit clock is calculated according to different DSD format (DSD64, DSD128, DSD256, DSD512), then registers are configured. Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1600178220-28973-2-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/codecs/ak4458.c | 89 +++++++++++++++++++++++++++++++++++++++++++---- sound/soc/codecs/ak4458.h | 5 ++- 2 files changed, 86 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 763e6839428f..1010c9ee2e83 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -28,14 +28,21 @@ static const char *ak4458_supply_names[AK4458_NUM_SUPPLIES] = { "AVDD", }; +enum ak4458_type { + AK4458 = 0, + AK4497 = 1, +}; + struct ak4458_drvdata { struct snd_soc_dai_driver *dai_drv; const struct snd_soc_component_driver *comp_drv; + enum ak4458_type type; }; /* AK4458 Codec Private Data */ struct ak4458_priv { struct regulator_bulk_data supplies[AK4458_NUM_SUPPLIES]; + const struct ak4458_drvdata *drvdata; struct device *dev; struct regmap *regmap; struct gpio_desc *reset_gpiod; @@ -45,6 +52,7 @@ struct ak4458_priv { int fmt; int slots; int slot_width; + u32 dsd_path; /* For ak4497 */ }; static const struct reg_default ak4458_reg_defaults[] = { @@ -325,12 +333,54 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, struct snd_soc_component *component = dai->component; struct ak4458_priv *ak4458 = snd_soc_component_get_drvdata(component); int pcm_width = max(params_physical_width(params), ak4458->slot_width); - int nfs1; - u8 format; + u8 format, dsdsel0, dsdsel1; + int nfs1, dsd_bclk; nfs1 = params_rate(params); ak4458->fs = nfs1; + /* calculate bit clock */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_DSD_U8: + case SNDRV_PCM_FORMAT_DSD_U16_LE: + case SNDRV_PCM_FORMAT_DSD_U16_BE: + case SNDRV_PCM_FORMAT_DSD_U32_LE: + case SNDRV_PCM_FORMAT_DSD_U32_BE: + dsd_bclk = nfs1 * params_physical_width(params); + switch (dsd_bclk) { + case 2822400: + dsdsel0 = 0; + dsdsel1 = 0; + break; + case 5644800: + dsdsel0 = 1; + dsdsel1 = 0; + break; + case 11289600: + dsdsel0 = 0; + dsdsel1 = 1; + break; + case 22579200: + if (ak4458->drvdata->type == AK4497) { + dsdsel0 = 1; + dsdsel1 = 1; + } else { + dev_err(dai->dev, "DSD512 not supported.\n"); + return -EINVAL; + } + break; + default: + dev_err(dai->dev, "Unsupported dsd bclk.\n"); + return -EINVAL; + } + + snd_soc_component_update_bits(component, AK4458_06_DSD1, + AK4458_DSDSEL_MASK, dsdsel0); + snd_soc_component_update_bits(component, AK4458_09_DSD2, + AK4458_DSDSEL_MASK, dsdsel1); + break; + } + /* Master Clock Frequency Auto Setting Mode Enable */ snd_soc_component_update_bits(component, AK4458_00_CONTROL1, 0x80, 0x80); @@ -355,6 +405,9 @@ static int ak4458_hw_params(struct snd_pcm_substream *substream, case SND_SOC_DAIFMT_DSP_B: format = AK4458_DIF_32BIT_MSB; break; + case SND_SOC_DAIFMT_PDM: + format = AK4458_DIF_32BIT_MSB; + break; default: return -EINVAL; } @@ -393,6 +446,7 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) case SND_SOC_DAIFMT_LEFT_J: case SND_SOC_DAIFMT_RIGHT_J: case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_PDM: ak4458->fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK; break; default: @@ -401,6 +455,12 @@ static int ak4458_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) return -EINVAL; } + /* DSD mode */ + snd_soc_component_update_bits(component, AK4458_02_CONTROL3, + AK4458_DP_MASK, + ak4458->fmt == SND_SOC_DAIFMT_PDM ? + AK4458_DP_MASK : 0); + ak4458_rstn_control(component, 0); ak4458_rstn_control(component, 1); @@ -472,7 +532,10 @@ static int ak4458_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, #define AK4458_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ SNDRV_PCM_FMTBIT_S24_LE |\ - SNDRV_PCM_FMTBIT_S32_LE) + SNDRV_PCM_FMTBIT_S32_LE |\ + SNDRV_PCM_FMTBIT_DSD_U8 |\ + SNDRV_PCM_FMTBIT_DSD_U16_LE |\ + SNDRV_PCM_FMTBIT_DSD_U32_LE) static const unsigned int ak4458_rates[] = { 8000, 11025, 16000, 22050, @@ -564,6 +627,13 @@ static int ak4458_init(struct snd_soc_component *component) if (ret < 0) return ret; + if (ak4458->drvdata->type == AK4497) { + ret = snd_soc_component_update_bits(component, AK4458_09_DSD2, + 0x4, (ak4458->dsd_path << 2)); + if (ret < 0) + return ret; + } + return ak4458_rstn_control(component, 1); } @@ -668,11 +738,13 @@ static const struct regmap_config ak4458_regmap = { static const struct ak4458_drvdata ak4458_drvdata = { .dai_drv = &ak4458_dai, .comp_drv = &soc_codec_dev_ak4458, + .type = AK4458, }; static const struct ak4458_drvdata ak4497_drvdata = { .dai_drv = &ak4497_dai, .comp_drv = &soc_codec_dev_ak4497, + .type = AK4497, }; static const struct dev_pm_ops ak4458_pm = { @@ -684,7 +756,6 @@ static const struct dev_pm_ops ak4458_pm = { static int ak4458_i2c_probe(struct i2c_client *i2c) { struct ak4458_priv *ak4458; - const struct ak4458_drvdata *drvdata; int ret, i; ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); @@ -698,7 +769,7 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, ak4458); ak4458->dev = &i2c->dev; - drvdata = of_device_get_match_data(&i2c->dev); + ak4458->drvdata = of_device_get_match_data(&i2c->dev); ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", GPIOD_OUT_LOW); @@ -710,6 +781,9 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak4458->mute_gpiod)) return PTR_ERR(ak4458->mute_gpiod); + /* Optional property for ak4497 */ + of_property_read_u32(i2c->dev.of_node, "dsd-path", &ak4458->dsd_path); + for (i = 0; i < ARRAY_SIZE(ak4458->supplies); i++) ak4458->supplies[i].supply = ak4458_supply_names[i]; @@ -720,8 +794,9 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) return ret; } - ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv, - drvdata->dai_drv, 1); + ret = devm_snd_soc_register_component(ak4458->dev, + ak4458->drvdata->comp_drv, + ak4458->drvdata->dai_drv, 1); if (ret < 0) { dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret); return ret; diff --git a/sound/soc/codecs/ak4458.h b/sound/soc/codecs/ak4458.h index f906215f7e4e..9548c5d78621 100644 --- a/sound/soc/codecs/ak4458.h +++ b/sound/soc/codecs/ak4458.h @@ -83,4 +83,7 @@ #define AK4458_ATS_SHIFT 6 #define AK4458_ATS_MASK GENMASK(7, 6) -#endif /* _AK4458_H */ +#define AK4458_DSDSEL_MASK (0x1 << 0) +#define AK4458_DP_MASK (0x1 << 7) + +#endif -- cgit From d5214321498a43558d9ffcce4153775c2c296bad Mon Sep 17 00:00:00 2001 From: Camel Guo Date: Fri, 18 Sep 2020 13:40:25 +0200 Subject: ASoC: tlv320adcx140: Add support for configuring GPIO pin Add support to configure the GPIO pin to the specific configuration. The GPIO pin can be configured as GPO, IRQ, SDOUT2, PDMCLK, MICBASE_EN, GPI, MCLK, SDIN, PDMDIN1, PDMDIN2, PDMDIN3 or PDMDIN4 and the output drive can be configured with various configuration. Signed-off-by: Camel Guo Link: https://lore.kernel.org/r/20200918114025.18205-2-camel.guo@axis.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320adcx140.c | 40 ++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tlv320adcx140.h | 5 +++++ 2 files changed, 45 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 28dbd7d5e149..53a80246aee1 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -861,6 +861,42 @@ static int adcx140_configure_gpo(struct adcx140_priv *adcx140) } +static int adcx140_configure_gpio(struct adcx140_priv *adcx140) +{ + int gpio_count = 0; + u32 gpio_outputs[ADCX140_NUM_GPIO_CFGS]; + u32 gpio_output_val = 0; + int ret; + + gpio_count = device_property_count_u32(adcx140->dev, + "ti,gpio-config"); + if (gpio_count == 0) + return 0; + + if (gpio_count != ADCX140_NUM_GPIO_CFGS) + return -EINVAL; + + ret = device_property_read_u32_array(adcx140->dev, "ti,gpio-config", + gpio_outputs, gpio_count); + if (ret) + return ret; + + if (gpio_outputs[0] > ADCX140_GPIO_CFG_MAX) { + dev_err(adcx140->dev, "GPIO config out of range\n"); + return -EINVAL; + } + + if (gpio_outputs[1] > ADCX140_GPIO_DRV_MAX) { + dev_err(adcx140->dev, "GPIO drive out of range\n"); + return -EINVAL; + } + + gpio_output_val = gpio_outputs[0] << ADCX140_GPIO_SHIFT + | gpio_outputs[1]; + + return regmap_write(adcx140->regmap, ADCX140_GPIO_CFG0, gpio_output_val); +} + static int adcx140_codec_probe(struct snd_soc_component *component) { struct adcx140_priv *adcx140 = snd_soc_component_get_drvdata(component); @@ -958,6 +994,10 @@ static int adcx140_codec_probe(struct snd_soc_component *component) return ret; } + ret = adcx140_configure_gpio(adcx140); + if (ret) + return ret; + ret = adcx140_configure_gpo(adcx140); if (ret) goto out; diff --git a/sound/soc/codecs/tlv320adcx140.h b/sound/soc/codecs/tlv320adcx140.h index 107bd7927d9c..d7d4e3a88b5c 100644 --- a/sound/soc/codecs/tlv320adcx140.h +++ b/sound/soc/codecs/tlv320adcx140.h @@ -148,4 +148,9 @@ #define ADCX140_TX_FILL BIT(0) +#define ADCX140_NUM_GPIO_CFGS 2 +#define ADCX140_GPIO_SHIFT 4 +#define ADCX140_GPIO_CFG_MAX 15 +#define ADCX140_GPIO_DRV_MAX 5 + #endif /* _TLV320ADCX140_ */ -- cgit From 2b987515e1d18836d9df96a5876290c8c8208348 Mon Sep 17 00:00:00 2001 From: Wang Qing Date: Thu, 17 Sep 2020 15:55:52 +0800 Subject: ALSA: asihpi: fix spellint typo in comments Change the comment typo: "ununsed" -> "unused". Signed-off-by: Wang Qing Link: https://lore.kernel.org/r/1600329372-2266-1-git-send-email-wangqing@vivo.com Signed-off-by: Takashi Iwai --- sound/pci/asihpi/hpios.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/asihpi/hpios.h b/sound/pci/asihpi/hpios.h index 26f7cf455a1e..9e551bc46264 100644 --- a/sound/pci/asihpi/hpios.h +++ b/sound/pci/asihpi/hpios.h @@ -67,7 +67,7 @@ struct hpi_ioctl_linux { }; /* Conflict?: H is already used by a number of drivers hid, bluetooth hci, - and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is ununsed command + and some sound drivers sb16, hdsp, emu10k. AFAIK 0xFC is unused command */ #define HPI_IOCTL_LINUX _IOWR('H', 0xFC, struct hpi_ioctl_linux) -- cgit From 18d122c0287b29e70bc312a994c7ee79738cec77 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Sep 2020 11:56:19 +0200 Subject: ALSA: compat_ioctl: avoid compat_alloc_user_space Using compat_alloc_user_space() tends to add complexity to the ioctl handling, so I am trying to remove it everywhere. The two callers in sound/core can rewritten to just call the same code that operates on a kernel pointer as the native handler. Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20200918095642.1446243-1-arnd@arndb.de Signed-off-by: Takashi Iwai --- sound/core/control.c | 38 +++++++++++++++++++++++++------------- sound/core/control_compat.c | 14 ++++++-------- sound/core/hwdep.c | 27 +++++++++++++++++---------- sound/core/hwdep_compat.c | 23 +++++++---------------- 4 files changed, 55 insertions(+), 47 deletions(-) (limited to 'sound') diff --git a/sound/core/control.c b/sound/core/control.c index aa0c0cf182af..e014598142df 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -717,22 +717,19 @@ static int snd_ctl_card_info(struct snd_card *card, struct snd_ctl_file * ctl, } static int snd_ctl_elem_list(struct snd_card *card, - struct snd_ctl_elem_list __user *_list) + struct snd_ctl_elem_list *list) { - struct snd_ctl_elem_list list; struct snd_kcontrol *kctl; struct snd_ctl_elem_id id; unsigned int offset, space, jidx; int err = 0; - if (copy_from_user(&list, _list, sizeof(list))) - return -EFAULT; - offset = list.offset; - space = list.space; + offset = list->offset; + space = list->space; down_read(&card->controls_rwsem); - list.count = card->controls_count; - list.used = 0; + list->count = card->controls_count; + list->used = 0; if (space > 0) { list_for_each_entry(kctl, &card->controls, list) { if (offset >= kctl->count) { @@ -741,12 +738,12 @@ static int snd_ctl_elem_list(struct snd_card *card, } for (jidx = offset; jidx < kctl->count; jidx++) { snd_ctl_build_ioff(&id, kctl, jidx); - if (copy_to_user(list.pids + list.used, &id, + if (copy_to_user(list->pids + list->used, &id, sizeof(id))) { err = -EFAULT; goto out; } - list.used++; + list->used++; if (!--space) goto out; } @@ -755,11 +752,26 @@ static int snd_ctl_elem_list(struct snd_card *card, } out: up_read(&card->controls_rwsem); - if (!err && copy_to_user(_list, &list, sizeof(list))) - err = -EFAULT; return err; } +static int snd_ctl_elem_list_user(struct snd_card *card, + struct snd_ctl_elem_list __user *_list) +{ + struct snd_ctl_elem_list list; + int err; + + if (copy_from_user(&list, _list, sizeof(list))) + return -EFAULT; + err = snd_ctl_elem_list(card, &list); + if (err) + return err; + if (copy_to_user(_list, &list, sizeof(list))) + return -EFAULT; + + return 0; +} + /* Check whether the given kctl info is valid */ static int snd_ctl_check_elem_info(struct snd_card *card, const struct snd_ctl_elem_info *info) @@ -1703,7 +1715,7 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg case SNDRV_CTL_IOCTL_CARD_INFO: return snd_ctl_card_info(card, ctl, cmd, argp); case SNDRV_CTL_IOCTL_ELEM_LIST: - return snd_ctl_elem_list(card, argp); + return snd_ctl_elem_list_user(card, argp); case SNDRV_CTL_IOCTL_ELEM_INFO: return snd_ctl_elem_info_user(ctl, argp); case SNDRV_CTL_IOCTL_ELEM_READ: diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c index 02df1d7db9a1..1d708aab9c98 100644 --- a/sound/core/control_compat.c +++ b/sound/core/control_compat.c @@ -22,24 +22,22 @@ struct snd_ctl_elem_list32 { static int snd_ctl_elem_list_compat(struct snd_card *card, struct snd_ctl_elem_list32 __user *data32) { - struct snd_ctl_elem_list __user *data; + struct snd_ctl_elem_list data = {}; compat_caddr_t ptr; int err; - data = compat_alloc_user_space(sizeof(*data)); - /* offset, space, used, count */ - if (copy_in_user(data, data32, 4 * sizeof(u32))) + if (copy_from_user(&data, data32, 4 * sizeof(u32))) return -EFAULT; /* pids */ - if (get_user(ptr, &data32->pids) || - put_user(compat_ptr(ptr), &data->pids)) + if (get_user(ptr, &data32->pids)) return -EFAULT; - err = snd_ctl_elem_list(card, data); + data.pids = compat_ptr(ptr); + err = snd_ctl_elem_list(card, &data); if (err < 0) return err; /* copy the result */ - if (copy_in_user(data32, data, 4 * sizeof(u32))) + if (copy_to_user(data32, &data, 4 * sizeof(u32))) return -EFAULT; return 0; } diff --git a/sound/core/hwdep.c b/sound/core/hwdep.c index 21edb8ac95eb..0c029892880a 100644 --- a/sound/core/hwdep.c +++ b/sound/core/hwdep.c @@ -203,28 +203,35 @@ static int snd_hwdep_dsp_status(struct snd_hwdep *hw, } static int snd_hwdep_dsp_load(struct snd_hwdep *hw, - struct snd_hwdep_dsp_image __user *_info) + struct snd_hwdep_dsp_image *info) { - struct snd_hwdep_dsp_image info; int err; if (! hw->ops.dsp_load) return -ENXIO; - memset(&info, 0, sizeof(info)); - if (copy_from_user(&info, _info, sizeof(info))) - return -EFAULT; - if (info.index >= 32) + if (info->index >= 32) return -EINVAL; /* check whether the dsp was already loaded */ - if (hw->dsp_loaded & (1u << info.index)) + if (hw->dsp_loaded & (1u << info->index)) return -EBUSY; - err = hw->ops.dsp_load(hw, &info); + err = hw->ops.dsp_load(hw, info); if (err < 0) return err; - hw->dsp_loaded |= (1u << info.index); + hw->dsp_loaded |= (1u << info->index); return 0; } +static int snd_hwdep_dsp_load_user(struct snd_hwdep *hw, + struct snd_hwdep_dsp_image __user *_info) +{ + struct snd_hwdep_dsp_image info = {}; + + if (copy_from_user(&info, _info, sizeof(info))) + return -EFAULT; + return snd_hwdep_dsp_load(hw, &info); +} + + static long snd_hwdep_ioctl(struct file * file, unsigned int cmd, unsigned long arg) { @@ -238,7 +245,7 @@ static long snd_hwdep_ioctl(struct file * file, unsigned int cmd, case SNDRV_HWDEP_IOCTL_DSP_STATUS: return snd_hwdep_dsp_status(hw, argp); case SNDRV_HWDEP_IOCTL_DSP_LOAD: - return snd_hwdep_dsp_load(hw, argp); + return snd_hwdep_dsp_load_user(hw, argp); } if (hw->ops.ioctl) return hw->ops.ioctl(hw, file, cmd, arg); diff --git a/sound/core/hwdep_compat.c b/sound/core/hwdep_compat.c index bc81db9cb3d4..a0b76706c083 100644 --- a/sound/core/hwdep_compat.c +++ b/sound/core/hwdep_compat.c @@ -19,26 +19,17 @@ struct snd_hwdep_dsp_image32 { static int snd_hwdep_dsp_load_compat(struct snd_hwdep *hw, struct snd_hwdep_dsp_image32 __user *src) { - struct snd_hwdep_dsp_image __user *dst; + struct snd_hwdep_dsp_image info = {}; compat_caddr_t ptr; - u32 val; - dst = compat_alloc_user_space(sizeof(*dst)); - - /* index and name */ - if (copy_in_user(dst, src, 4 + 64)) - return -EFAULT; - if (get_user(ptr, &src->image) || - put_user(compat_ptr(ptr), &dst->image)) - return -EFAULT; - if (get_user(val, &src->length) || - put_user(val, &dst->length)) - return -EFAULT; - if (get_user(val, &src->driver_data) || - put_user(val, &dst->driver_data)) + if (copy_from_user(&info, src, 4 + 64) || + get_user(ptr, &src->image) || + get_user(info.length, &src->length) || + get_user(info.driver_data, &src->driver_data)) return -EFAULT; + info.image = compat_ptr(ptr); - return snd_hwdep_dsp_load(hw, dst); + return snd_hwdep_dsp_load(hw, &info); } enum { -- cgit From 69b08bdfa8181bc7babd7d81c93dc60142c4bfd3 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 21 Sep 2020 17:17:38 +0300 Subject: ALSA: hda - add Intel DG1 PCI and HDMI ids Add Intel DG1 PCI id to list of supported HDA controllers and add its HDMI id as well. Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921141741.2983072-2-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 +++ sound/pci/hda/patch_hdmi.c | 1 + 2 files changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 36a9dbc33aa0..904d3af5b99e 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -2493,6 +2493,9 @@ static const struct pci_device_id azx_ids[] = { /* Tigerlake-H */ { PCI_DEVICE(0x8086, 0x43c8), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, + /* DG1 */ + { PCI_DEVICE(0x8086, 0x490d), + .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, /* Elkhart Lake */ { PCI_DEVICE(0x8086, 0x4b55), .driver_data = AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE}, diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 402050088090..055440740184 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -4269,6 +4269,7 @@ HDA_CODEC_ENTRY(0x8086280c, "Cannonlake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi), HDA_CODEC_ENTRY(0x8086280f, "Icelake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x80862812, "Tigerlake HDMI", patch_i915_tgl_hdmi), +HDA_CODEC_ENTRY(0x80862814, "DG1 HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x80862816, "Rocketlake HDMI", patch_i915_tgl_hdmi), HDA_CODEC_ENTRY(0x8086281a, "Jasperlake HDMI", patch_i915_icl_hdmi), HDA_CODEC_ENTRY(0x8086281b, "Elkhartlake HDMI", patch_i915_icl_hdmi), -- cgit From 1bee263dfda57e45ad39c59a663c123a357ce38b Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 21 Sep 2020 17:17:39 +0300 Subject: ALSA: hda - controller is in GPU on the DG1 Add Intel DG1 to the CONTROLLER_IN_GPU list to ensure audio power is requested whenever programming the controller. Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921141741.2983072-3-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 904d3af5b99e..61e495187b1a 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -368,7 +368,8 @@ enum { #define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ ((pci)->device == 0x0c0c) || \ ((pci)->device == 0x0d0c) || \ - ((pci)->device == 0x160c)) + ((pci)->device == 0x160c) || \ + ((pci)->device == 0x490d)) #define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98) -- cgit From 7b882fe3e3e8bfd6427bf61780e59d5bed6699b4 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 21 Sep 2020 17:17:40 +0300 Subject: ALSA: hda - handle multiple i915 device instances Currently i915_component_master_match() will return the first matching i915 instance. This does not work in case system has multiple i915 and HDA audio controller instances. Add a new connectivity check that handles following cases: - i915 and HDA controller on same PCI bus - discrete GPU with embedded HDA audio controller connected via PCI bridge Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921141741.2983072-4-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 3c2db3816029..50b2c1db429b 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -73,11 +73,51 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk); +/** + * Returns true if the devices can be connected for audio. + */ +static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac) +{ + struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus; + + /* directly connected on the same bus */ + if (bus_a == bus_b) + return true; + + /* + * on i915 discrete GPUs with embedded HDA audio, the two + * devices are connected via 2nd level PCI bridge + */ + bus_a = bus_a->parent; + bus_b = bus_b->parent; + if (!bus_a || !bus_b) + return false; + bus_a = bus_a->parent; + bus_b = bus_b->parent; + if (bus_a && bus_a == bus_b) + return true; + + return false; +} + static int i915_component_master_match(struct device *dev, int subcomponent, void *data) { - return !strcmp(dev->driver->name, "i915") && - subcomponent == I915_COMPONENT_AUDIO; + struct pci_dev *hdac_pci, *i915_pci; + struct hdac_bus *bus = data; + + if (!dev_is_pci(dev)) + return 0; + + hdac_pci = to_pci_dev(bus->dev); + i915_pci = to_pci_dev(dev); + + if (!strcmp(dev->driver->name, "i915") && + subcomponent == I915_COMPONENT_AUDIO && + connectivity_check(i915_pci, hdac_pci)) + return 1; + + return 0; } /* check whether intel graphics is present */ -- cgit From 534ad9afb19b5ac810a5e1c638f371e81e0c1ed6 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 21 Sep 2020 17:17:41 +0300 Subject: ALSA: hda - fix CONTROLLER_IN_GPU macro name The CONTROLLER_IN_GPU() macro has different semantics than the similarly named macro in hda_intel.c. The name is also misleading as the macro is used to apply a Intel HSW/BDW programming logic for HDA controller clock configuration. Rename macro to reflect the actual implementation. Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921141741.2983072-5-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 50b2c1db429b..d236e497435d 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -13,7 +13,7 @@ static struct completion bind_complete; -#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \ +#define IS_HSW_CONTROLLER(pci) (((pci)->device == 0x0a0c) || \ ((pci)->device == 0x0c0c) || \ ((pci)->device == 0x0d0c) || \ ((pci)->device == 0x160c)) @@ -41,7 +41,7 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus) if (!acomp || !acomp->ops || !acomp->ops->get_cdclk_freq) return; /* only for i915 binding */ - if (!CONTROLLER_IN_GPU(pci)) + if (!IS_HSW_CONTROLLER(pci)) return; /* only HSW/BDW */ cdclk_freq = acomp->ops->get_cdclk_freq(acomp->dev); -- cgit From 2263063fc4880d544a1eb87713f642384fe03cb7 Mon Sep 17 00:00:00 2001 From: Keyon Jie Date: Mon, 21 Sep 2020 13:45:44 +0300 Subject: ASoC: SOF: topology: fix the process being scheduled on core0 always In commit 783898ce68de ("ASoC: SOF: append extended data to sof_ipc_comp_process") the process components are set to run on the fixed core 0, this break us from scheduling components on any other DSP core. Since we can get the DSP core index from swidget->core, it is duplicated to pass the extra 'core' argument for those sof_widget_load_xx() functions. Here removes the duplicate 'core' argument and get component core from swidget->core directly to fix the issue mentioned above. Fixes: 783898ce68de ("ASoC: SOF: append extended data to sof_ipc_comp_process") Signed-off-by: Keyon Jie Reviewed-by: Jaska Uimonen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921104544.2897112-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/topology.c | 82 +++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 47 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index eaa1122d5a68..69313fbdb636 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1465,13 +1465,11 @@ static int sof_connect_dai_widget(struct snd_soc_component *scomp, * @ipc_size: IPC payload size that will be updated depending on valid * extended data. * @index: ID of the pipeline the component belongs to - * @core: index of the DSP core that the component should run on * * Return: The pointer to the new allocated component, NULL if failed. */ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, - size_t *ipc_size, int index, - int core) + size_t *ipc_size, int index) { u8 nil_uuid[SOF_UUID_SIZE] = {0}; struct sof_ipc_comp *comp; @@ -1490,7 +1488,7 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, comp->hdr.cmd = SOF_IPC_GLB_TPLG_MSG | SOF_IPC_TPLG_COMP_NEW; comp->id = swidget->comp_id; comp->pipeline_id = index; - comp->core = core; + comp->core = swidget->core; /* handle the extended data if needed */ if (total_size > *ipc_size) { @@ -1505,7 +1503,7 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, } static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r, struct snd_sof_dai *dai) @@ -1517,7 +1515,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, int ret; comp_dai = (struct sof_ipc_comp_dai *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!comp_dai) return -ENOMEM; @@ -1571,7 +1569,7 @@ finish: */ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1590,7 +1588,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, buffer->comp.id = swidget->comp_id; buffer->comp.type = SOF_COMP_BUFFER; buffer->comp.pipeline_id = index; - buffer->comp.core = core; + buffer->comp.core = swidget->core; ret = sof_parse_tokens(scomp, buffer, buffer_tokens, ARRAY_SIZE(buffer_tokens), private->array, @@ -1642,7 +1640,7 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, */ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, enum sof_ipc_stream_direction dir, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) @@ -1654,7 +1652,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, int ret; host = (struct sof_ipc_comp_host *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!host) return -ENOMEM; @@ -1779,7 +1777,7 @@ err: */ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1790,7 +1788,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, int ret; mixer = (struct sof_ipc_comp_mixer *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!mixer) return -ENOMEM; @@ -1824,7 +1822,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, * Mux topology */ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1835,7 +1833,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, int ret; mux = (struct sof_ipc_comp_mux *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!mux) return -ENOMEM; @@ -1870,7 +1868,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, */ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1884,7 +1882,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, int ret; volume = (struct sof_ipc_comp_volume *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!volume) return -ENOMEM; @@ -1946,7 +1944,7 @@ err: */ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -1957,7 +1955,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, int ret; src = (struct sof_ipc_comp_src *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!src) return -ENOMEM; @@ -2003,7 +2001,7 @@ err: */ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -2014,7 +2012,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, int ret; asrc = (struct sof_ipc_comp_asrc *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!asrc) return -ENOMEM; @@ -2062,7 +2060,7 @@ err: */ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -2073,7 +2071,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, int ret; tone = (struct sof_ipc_comp_tone *) - sof_comp_alloc(swidget, &ipc_size, index, core); + sof_comp_alloc(swidget, &ipc_size, index); if (!tone) return -ENOMEM; @@ -2229,7 +2227,7 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, } process = (struct sof_ipc_comp_process *) - sof_comp_alloc(swidget, &ipc_size, index, 0); + sof_comp_alloc(swidget, &ipc_size, index); if (!process) { ret = -ENOMEM; goto out; @@ -2307,7 +2305,7 @@ out: */ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, - struct snd_sof_widget *swidget, int core, + struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, struct sof_ipc_comp_reply *r) { @@ -2322,7 +2320,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, } memset(&config, 0, sizeof(config)); - config.comp.core = core; + config.comp.core = swidget->core; /* get the process token */ ret = sof_parse_tokens(scomp, &config, process_tokens, @@ -2450,8 +2448,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return -ENOMEM; } - ret = sof_widget_load_dai(scomp, index, swidget, comp.core, - tw, &reply, dai); + ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, dai); if (ret == 0) { sof_connect_dai_widget(scomp, w, tw, dai); list_add(&dai->list, &sdev->dai_list); @@ -2461,12 +2458,10 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } break; case snd_soc_dapm_mixer: - ret = sof_widget_load_mixer(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_pga: - ret = sof_widget_load_pga(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); /* Find scontrol for this pga and set readback offset*/ list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { if (scontrol->comp_id == swidget->comp_id) { @@ -2476,41 +2471,34 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } break; case snd_soc_dapm_buffer: - ret = sof_widget_load_buffer(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_scheduler: - ret = sof_widget_load_pipeline(scomp, index, swidget, - tw, &reply); + ret = sof_widget_load_pipeline(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_aif_out: - ret = sof_widget_load_pcm(scomp, index, swidget, comp.core, + ret = sof_widget_load_pcm(scomp, index, swidget, SOF_IPC_STREAM_CAPTURE, tw, &reply); break; case snd_soc_dapm_aif_in: - ret = sof_widget_load_pcm(scomp, index, swidget, comp.core, + ret = sof_widget_load_pcm(scomp, index, swidget, SOF_IPC_STREAM_PLAYBACK, tw, &reply); break; case snd_soc_dapm_src: - ret = sof_widget_load_src(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_asrc: - ret = sof_widget_load_asrc(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_siggen: - ret = sof_widget_load_siggen(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_effect: - ret = sof_widget_load_process(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_process(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: - ret = sof_widget_load_mux(scomp, index, swidget, comp.core, - tw, &reply); + ret = sof_widget_load_mux(scomp, index, swidget, tw, &reply); break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: -- cgit From 641088722244f59fed00b68f7f5aaa3d56c1d73d Mon Sep 17 00:00:00 2001 From: Guillermo Rodríguez Date: Fri, 18 Sep 2020 15:43:16 +0200 Subject: ASoC: cs42l51: add additional ADC volume controls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add volume controls for: - Analog programmable gain amplifier (PGA) (-3 .. +12 dB) - ADC attenuator (0 .. -96 dB) Signed-off-by: Guillermo Rodríguez Acked-by: David Rhodes Link: https://lore.kernel.org/r/20200918134317.22574-1-guille.rodriguez@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs42l51.c | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index 764f2ef8f59d..097c4e8d9950 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -122,6 +122,9 @@ static const char *chan_mix[] = { "R L", }; +static const DECLARE_TLV_DB_SCALE(pga_tlv, -300, 50, 0); +static const DECLARE_TLV_DB_SCALE(adc_att_tlv, -9600, 100, 0); + static SOC_ENUM_SINGLE_EXT_DECL(cs42l51_chan_mix, chan_mix); static const struct snd_kcontrol_new cs42l51_snd_controls[] = { @@ -138,6 +141,12 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = { 0, 0x19, 0x7F, adc_pcm_tlv), SOC_DOUBLE_R("ADC Mixer Switch", CS42L51_ADCA_VOL, CS42L51_ADCB_VOL, 7, 1, 1), + SOC_DOUBLE_R_SX_TLV("ADC Attenuator Volume", + CS42L51_ADCA_ATT, CS42L51_ADCB_ATT, + 0, 0xA0, 96, adc_att_tlv), + SOC_DOUBLE_R_SX_TLV("PGA Volume", + CS42L51_ALC_PGA_CTL, CS42L51_ALC_PGB_CTL, + 0, 0x1A, 30, pga_tlv), SOC_SINGLE("Playback Deemphasis Switch", CS42L51_DAC_CTL, 3, 1, 0), SOC_SINGLE("Auto-Mute Switch", CS42L51_DAC_CTL, 2, 1, 0), SOC_SINGLE("Soft Ramp Switch", CS42L51_DAC_CTL, 1, 1, 0), -- cgit From 43437d0417a36bc9174deedce4ecc2c516ffde57 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Mon, 21 Sep 2020 13:50:38 +0300 Subject: ASoC: SOF: pm: Fix prepare callback behavior for OF usecase On i.MX platforms PM is not managed via ACPI although CONFIG_ACPI can be set. So, in order to correctly set the system target state we introduce a flag for platforms that require to use acpi target states. Signed-off-by: Daniel Baluta Reviewed-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921105038.2909899-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/pm.c | 12 +++++++----- sound/soc/sof/sof-pci-dev.c | 9 +++++++++ 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index a5f7c7f024a1..c83fb6255961 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -305,15 +305,17 @@ EXPORT_SYMBOL(snd_sof_suspend); int snd_sof_prepare(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + const struct sof_dev_desc *desc = sdev->pdata->desc; + + /* will suspend to S3 by default */ + sdev->system_suspend_target = SOF_SUSPEND_S3; + + if (!desc->use_acpi_target_states) + return 0; #if defined(CONFIG_ACPI) if (acpi_target_system_state() == ACPI_STATE_S0) sdev->system_suspend_target = SOF_SUSPEND_S0IX; - else - sdev->system_suspend_target = SOF_SUSPEND_S3; -#else - /* will suspend to S3 by default */ - sdev->system_suspend_target = SOF_SUSPEND_S3; #endif return 0; diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c index 8c53f6935417..8f62e3487dc1 100644 --- a/sound/soc/sof/sof-pci-dev.c +++ b/sound/soc/sof/sof-pci-dev.c @@ -77,6 +77,7 @@ static const struct dmi_system_id community_key_platforms[] = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) static const struct sof_dev_desc bxt_desc = { .machines = snd_soc_acpi_intel_bxt_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -94,6 +95,7 @@ static const struct sof_dev_desc bxt_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_GEMINILAKE) static const struct sof_dev_desc glk_desc = { .machines = snd_soc_acpi_intel_glk_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -139,6 +141,7 @@ static const struct sof_dev_desc tng_desc = { static const struct sof_dev_desc cnl_desc = { .machines = snd_soc_acpi_intel_cnl_machines, .alt_machines = snd_soc_acpi_intel_cnl_sdw_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -157,6 +160,7 @@ static const struct sof_dev_desc cnl_desc = { static const struct sof_dev_desc cfl_desc = { .machines = snd_soc_acpi_intel_cfl_machines, .alt_machines = snd_soc_acpi_intel_cfl_sdw_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -175,6 +179,7 @@ static const struct sof_dev_desc cfl_desc = { static const struct sof_dev_desc cml_desc = { .machines = snd_soc_acpi_intel_cml_machines, .alt_machines = snd_soc_acpi_intel_cml_sdw_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -193,6 +198,7 @@ static const struct sof_dev_desc cml_desc = { static const struct sof_dev_desc icl_desc = { .machines = snd_soc_acpi_intel_icl_machines, .alt_machines = snd_soc_acpi_intel_icl_sdw_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -211,6 +217,7 @@ static const struct sof_dev_desc icl_desc = { static const struct sof_dev_desc tgl_desc = { .machines = snd_soc_acpi_intel_tgl_machines, .alt_machines = snd_soc_acpi_intel_tgl_sdw_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -244,6 +251,7 @@ static const struct sof_dev_desc tglh_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_ELKHARTLAKE) static const struct sof_dev_desc ehl_desc = { .machines = snd_soc_acpi_intel_ehl_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, @@ -261,6 +269,7 @@ static const struct sof_dev_desc ehl_desc = { #if IS_ENABLED(CONFIG_SND_SOC_SOF_JASPERLAKE) static const struct sof_dev_desc jsl_desc = { .machines = snd_soc_acpi_intel_jsl_machines, + .use_acpi_target_states = true, .resindex_lpe_base = 0, .resindex_pcicfg_base = -1, .resindex_imr_base = -1, -- cgit From 5b51b9221f711d205e361f8d1182fd6aea667296 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 21 Sep 2020 13:47:46 +0300 Subject: ASoC: SOF: imx: add missing MODULE_LICENSE() for imx-common Fix build warning: WARNING: modpost: missing MODULE_LICENSE() in sound/soc/sof/imx/imx-common.o Fixes: 18ebffe4d043 ("ASoC: SOF: imx: Add debug support for imx platforms") Reported-by: Stephen Rothwell Signed-off-by: Kai Vehmanen Reviewed-by: Daniel Baluta Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200921104746.2903507-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/imx/imx-common.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c index 63d8a5d7bc44..5fee637834c2 100644 --- a/sound/soc/sof/imx/imx-common.c +++ b/sound/soc/sof/imx/imx-common.c @@ -4,6 +4,7 @@ // // Common helpers for the audio DSP on i.MX8 +#include #include #include "../ops.h" @@ -70,3 +71,5 @@ void imx8_dump(struct snd_sof_dev *sdev, u32 flags) IMX8_STACK_DUMP_SIZE); } EXPORT_SYMBOL(imx8_dump); + +MODULE_LICENSE("Dual BSD/GPL"); -- cgit From 534c0f4391a497d10c2b5eb3df2016221b6ec4f6 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 10:01:30 -0500 Subject: ASoC: tas2562: Add the TAS2564 compatible Add the TAS2564 as a supported amplifier. This amplifier is register, bitmap and feature compatible to the TAS2562. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918150130.21015-2-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 99920c691d28..56d410141ee0 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -58,6 +58,7 @@ struct tas2562_data { enum tas256x_model { TAS2562, TAS2563, + TAS2564, }; static int tas2562_set_bias_level(struct snd_soc_component *component, @@ -761,6 +762,7 @@ static int tas2562_probe(struct i2c_client *client, static const struct i2c_device_id tas2562_id[] = { { "tas2562", TAS2562 }, { "tas2563", TAS2563 }, + { "tas2564", TAS2564 }, { } }; MODULE_DEVICE_TABLE(i2c, tas2562_id); @@ -768,6 +770,7 @@ MODULE_DEVICE_TABLE(i2c, tas2562_id); static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562", }, { .compatible = "ti,tas2563", }, + { .compatible = "ti,tas2564", }, { }, }; MODULE_DEVICE_TABLE(of, tas2562_of_match); -- cgit From 5f2df2a4583b0d7b85054f0c1820f11a01936d35 Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 21 Sep 2020 17:42:44 +0800 Subject: ASoC: rt700: wait for the delayed work to finish when the system suspends To avoid the IO error, we need to cancel the delayed work and wait for it to finish. Signed-off-by: Shuming Fan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200921094244.31869-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt700-sdw.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 1d24bf040718..af6e17e457b4 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -490,6 +490,9 @@ static int __maybe_unused rt700_dev_suspend(struct device *dev) if (!rt700->hw_init) return 0; + cancel_delayed_work_sync(&rt700->jack_detect_work); + cancel_delayed_work_sync(&rt700->jack_btn_check_work); + regcache_cache_only(rt700->regmap, true); return 0; -- cgit From b0bcbe615756d5923eec4e95996e4041627e5741 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:40 -0500 Subject: ASoC: tas2770: Fix calling reset in probe tas2770_reset is called during i2c probe. The reset calls the snd_soc_component_write which depends on the tas2770->component being available. The component pointer is not set until codec_probe so move the reset to the codec_probe after the pointer is set. Fixes: 1a476abc723e6 ("tas2770: add tas2770 smart PA kernel driver") Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-1-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index c09851834395..03d7ad1885b8 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -575,6 +575,8 @@ static int tas2770_codec_probe(struct snd_soc_component *component) tas2770->component = component; + tas2770_reset(tas2770); + return 0; } @@ -771,8 +773,6 @@ static int tas2770_i2c_probe(struct i2c_client *client, tas2770->channel_size = 0; tas2770->slot_width = 0; - tas2770_reset(tas2770); - result = tas2770_register_codec(tas2770); if (result) dev_err(tas2770->dev, "Register codec failed.\n"); -- cgit From 4272caf34aa4699eca8e6e7f4a8e5ea2bde275c9 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:41 -0500 Subject: ASoC: tas2770: Add missing bias level power states Add the BIAS_STANDBY and BIAS_PREPARE to the set_bias_level or else the driver will return -EINVAL which is not correct as they are valid states. Fixes: 1a476abc723e6 ("tas2770: add tas2770 smart PA kernel driver") Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-2-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 03d7ad1885b8..7c6f61946ab3 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -57,7 +57,12 @@ static int tas2770_set_bias_level(struct snd_soc_component *component, TAS2770_PWR_CTRL_MASK, TAS2770_PWR_CTRL_ACTIVE); break; - + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, + TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, TAS2770_PWR_CTRL_MUTE); + break; case SND_SOC_BIAS_OFF: snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, -- cgit From 4b8ab8a7761fe2ba1c4e741703a848cb8f390f79 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:43 -0500 Subject: ASoC: tas2770: Fix required DT properties in the code The devicetree binding indicates that the ti,asi-format, ti,imon-slot-no and ti,vmon-slot-no are not required but the driver requires them or it fails to probe. Honor the binding and allow these entries to be optional and set the corresponding values to the default values for each as defined in the data sheet. Fixes: 1a476abc723e6 ("tas2770: add tas2770 smart PA kernel driver") Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-4-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 7c6f61946ab3..bdfdad5f4f64 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -708,29 +708,28 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format", &tas2770->asi_format); if (rc) { - dev_err(tas2770->dev, "Looking up %s property failed %d\n", - "ti,asi-format", rc); - goto end; + dev_info(tas2770->dev, "Property %s is missing setting default slot\n", + "ti,asi-format"); + tas2770->asi_format = 0; } rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", &tas2770->i_sense_slot); if (rc) { - dev_err(tas2770->dev, "Looking up %s property failed %d\n", - "ti,imon-slot-no", rc); - goto end; + dev_info(tas2770->dev, "Property %s is missing setting default slot\n", + "ti,imon-slot-no"); + tas2770->i_sense_slot = 0; } rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", &tas2770->v_sense_slot); if (rc) { - dev_err(tas2770->dev, "Looking up %s property failed %d\n", - "ti,vmon-slot-no", rc); - goto end; + dev_info(tas2770->dev, "Property %s is missing setting default slot\n", + "ti,vmon-slot-no"); + tas2770->v_sense_slot = 2; } -end: - return rc; + return 0; } static int tas2770_i2c_probe(struct i2c_client *client, -- cgit From cadab0aefcbadf488b16caf2770430e69f4d7839 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:46 -0500 Subject: ASoC: tas2770: Fix error handling with update_bits snd_soc_update_bits returns a 1 when the bit was successfully updated, returns a 0 is no update was needed and a negative if the call failed. The code is currently failing the case of a successful update by just checking for a non-zero number. Modify these checks and return the error code only if there is a negative. Fixes: 1a476abc723e6 ("tas2770: add tas2770 smart PA kernel driver") Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-7-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 52 +++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 28 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index bdfdad5f4f64..15cdd8b11a67 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -140,23 +140,18 @@ static int tas2770_dac_event(struct snd_soc_dapm_widget *w, TAS2770_PWR_CTRL, TAS2770_PWR_CTRL_MASK, TAS2770_PWR_CTRL_MUTE); - if (ret) - goto end; break; case SND_SOC_DAPM_PRE_PMD: ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, TAS2770_PWR_CTRL_MASK, TAS2770_PWR_CTRL_SHUTDOWN); - if (ret) - goto end; break; default: dev_err(tas2770->dev, "Not supported evevt\n"); return -EINVAL; } -end: if (ret < 0) return ret; @@ -248,6 +243,9 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) return -EINVAL; } + if (ret < 0) + return ret; + tas2770->channel_size = bitwidth; ret = snd_soc_component_update_bits(component, @@ -256,16 +254,15 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) TAS2770_TDM_CFG_REG5_50_MASK, TAS2770_TDM_CFG_REG5_VSNS_ENABLE | tas2770->v_sense_slot); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, TAS2770_TDM_CFG_REG6_ISNS_MASK | TAS2770_TDM_CFG_REG6_50_MASK, TAS2770_TDM_CFG_REG6_ISNS_ENABLE | tas2770->i_sense_slot); - -end: if (ret < 0) return ret; @@ -283,36 +280,35 @@ static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_SMP_MASK, TAS2770_TDM_CFG_REG0_SMP_48KHZ); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_31_MASK, TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); - if (ret) - goto end; break; case 44100: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_SMP_MASK, TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_31_MASK, TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); - if (ret) - goto end; break; case 96000: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_SMP_MASK, TAS2770_TDM_CFG_REG0_SMP_48KHZ); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_31_MASK, @@ -323,8 +319,9 @@ static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_SMP_MASK, TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_31_MASK, @@ -335,22 +332,22 @@ static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_SMP_MASK, TAS2770_TDM_CFG_REG0_SMP_48KHZ); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_31_MASK, TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); - if (ret) - goto end; break; case 17640: ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_SMP_MASK, TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); - if (ret) - goto end; + if (ret < 0) + return ret; + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, TAS2770_TDM_CFG_REG0_31_MASK, @@ -360,7 +357,6 @@ static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) ret = -EINVAL; } -end: if (ret < 0) return ret; -- cgit From 501ef013390b774e8e61000a78d1d640d6c3411d Mon Sep 17 00:00:00 2001 From: Shuming Fan Date: Mon, 21 Sep 2020 17:43:08 +0800 Subject: ASoC: rt711: wait for the delayed work to finish when the system suspends To avoid the IO error, we need to cancel the delayed work and wait for it to finish. Signed-off-by: Shuming Fan Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200921094308.31921-1-shumingf@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt711-sdw.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 7efff130a638..9eabd30521c7 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -491,6 +491,10 @@ static int __maybe_unused rt711_dev_suspend(struct device *dev) if (!rt711->hw_init) return 0; + cancel_delayed_work_sync(&rt711->jack_detect_work); + cancel_delayed_work_sync(&rt711->jack_btn_check_work); + cancel_work_sync(&rt711->calibration_work); + regcache_cache_only(rt711->regmap, true); return 0; -- cgit From 5b4458ebb4c8007dae7eaeb88cb52b2bb4879894 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 11 Sep 2020 19:31:38 +0200 Subject: ASoC: tlv320aic32x4: Ensure a minimum delay before clock stabilization As indicated in the datasheet, a 10ms delay must be observed after programming the divisors. The lack of delay prevents the codec to work properly and the playback appears extremely slow and totally un-audible on a custom sama5 based board. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20200911173140.29984-2-miquel.raynal@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4-clk.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c index 156c153c12ab..2f78e6820c75 100644 --- a/sound/soc/codecs/tlv320aic32x4-clk.c +++ b/sound/soc/codecs/tlv320aic32x4-clk.c @@ -230,7 +230,14 @@ static int clk_aic32x4_pll_set_rate(struct clk_hw *hw, if (ret < 0) return -EINVAL; - return clk_aic32x4_pll_set_muldiv(pll, &settings); + ret = clk_aic32x4_pll_set_muldiv(pll, &settings); + if (ret) + return ret; + + /* 10ms is the delay to wait before the clocks are stable */ + msleep(10); + + return 0; } static int clk_aic32x4_pll_set_parent(struct clk_hw *hw, u8 index) -- cgit From 40b37136287ba6b34aa2f1f6123f3d6d205dc2f0 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 11 Sep 2020 19:31:39 +0200 Subject: ASoC: tlv320aic32x4: Fix bdiv clock rate derivation Current code expects a single channel to be always used. Fix this situation by forwarding the number of channels used. Then fix the derivation of the bdiv clock rate. Fixes: 96c3bb00239d ("ASoC: tlv320aic32x4: Dynamically Determine Clocking") Suggested-by: Alexandre Belloni Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20200911173140.29984-3-miquel.raynal@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 467802875c13..2e2d8e463655 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -665,7 +665,7 @@ static int aic32x4_set_processing_blocks(struct snd_soc_component *component, } static int aic32x4_setup_clocks(struct snd_soc_component *component, - unsigned int sample_rate) + unsigned int sample_rate, unsigned int channels) { u8 aosr; u16 dosr; @@ -753,7 +753,9 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component, dosr); clk_set_rate(clocks[5].clk, - sample_rate * 32); + sample_rate * 32 * + channels); + return 0; } } @@ -775,7 +777,8 @@ static int aic32x4_hw_params(struct snd_pcm_substream *substream, u8 iface1_reg = 0; u8 dacsetup_reg = 0; - aic32x4_setup_clocks(component, params_rate(params)); + aic32x4_setup_clocks(component, params_rate(params), + params_channels(params)); switch (params_width(params)) { case 16: -- cgit From ec96690de82cee2cb028c07b1e72cb4a446ad03a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 11 Sep 2020 19:31:40 +0200 Subject: ASoC: tlv320aic32x4: Enable fast charge At power-up the analog circuits may take up to one full second before being charged with the default configuration. Using the analog blocks before they are ready generates a *very* crappy sound. Enable the fast charge feature, which will require a bit more power than normal charge but will definitely speed up the starting operation by shrinking this delay to up to 40 ms. Signed-off-by: Miquel Raynal Link: https://lore.kernel.org/r/20200911173140.29984-4-miquel.raynal@bootlin.com Signed-off-by: Mark Brown --- sound/soc/codecs/tlv320aic32x4.c | 8 ++++++++ sound/soc/codecs/tlv320aic32x4.h | 7 +++++++ 2 files changed, 15 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c index 2e2d8e463655..ea8cd4487536 100644 --- a/sound/soc/codecs/tlv320aic32x4.c +++ b/sound/soc/codecs/tlv320aic32x4.c @@ -1013,6 +1013,14 @@ static int aic32x4_component_probe(struct snd_soc_component *component) AIC32X4_LADC_EN | AIC32X4_RADC_EN); snd_soc_component_write(component, AIC32X4_ADCSETUP, tmp_reg); + /* + * Enable the fast charging feature and ensure the needed 40ms ellapsed + * before using the analog circuits. + */ + snd_soc_component_write(component, AIC32X4_REFPOWERUP, + AIC32X4_REFPOWERUP_40MS); + msleep(40); + return 0; } diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h index 38f47704bb75..7550122e9f8a 100644 --- a/sound/soc/codecs/tlv320aic32x4.h +++ b/sound/soc/codecs/tlv320aic32x4.h @@ -96,6 +96,7 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_FLOATINGINPUT AIC32X4_REG(1, 58) #define AIC32X4_LMICPGAVOL AIC32X4_REG(1, 59) #define AIC32X4_RMICPGAVOL AIC32X4_REG(1, 60) +#define AIC32X4_REFPOWERUP AIC32X4_REG(1, 123) /* Bits, masks, and shifts */ @@ -205,6 +206,12 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name); #define AIC32X4_RMICPGANIN_IN1L_10K 0x10 #define AIC32X4_RMICPGANIN_CM1R_10K 0x40 +/* AIC32X4_REFPOWERUP */ +#define AIC32X4_REFPOWERUP_SLOW 0x04 +#define AIC32X4_REFPOWERUP_40MS 0x05 +#define AIC32X4_REFPOWERUP_80MS 0x06 +#define AIC32X4_REFPOWERUP_120MS 0x07 + /* Common mask and enable for all of the dividers */ #define AIC32X4_DIVEN BIT(7) #define AIC32X4_DIV_MASK GENMASK(6, 0) -- cgit From 3331bcd6a2f2dbe9c1fa764df695422c99e2f1fb Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Sep 2020 14:08:10 +0300 Subject: ASoC: SOF: control: fix size checks for ext_bytes control .get() cppcheck complains twice: sound/soc/sof/control.c:436:2: style: Assignment of function parameter has no effect outside the function. [uselessAssignmentArg] size -= sizeof(const struct snd_ctl_tlv); ^ sound/soc/sof/control.c:436:7: style: Variable 'size' is assigned a value that is never used. [unreadVariable] size -= sizeof(const struct snd_ctl_tlv); Somehow we dropped the checks for the size argument when upstreaming the code, somewhere between v5 and v6. Re-add a size check to avoid providing userspace with more data that it asked for. Also fix all error codes, we should return -ENOSPC instead of -EINVAL. Fixes: c3078f5397046 ('ASoC: SOF: Add Sound Open Firmware KControl support') Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921110814.2910477-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 58f8c998e6af..8d499d0e331d 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -432,7 +432,9 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, * Decrement the limit by ext bytes header size to * ensure the user space buffer is not exceeded. */ - size -= sizeof(const struct snd_ctl_tlv); + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + size -= sizeof(struct snd_ctl_tlv); /* set the ABI header values */ cdata->data->magic = SOF_ABI_MAGIC; @@ -448,6 +450,10 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + header.numid = scontrol->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) -- cgit From ec5a97624a8de4f44b090cf53bd48c05458e0b17 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Sep 2020 14:08:11 +0300 Subject: ASoC: SOF: control: fix size checks for volatile ext_bytes control .get() Mirror addition of checks for regular ext_bytes controls. Fixes: 783560d02dd61 ('ASoC: SOF: Implement snd_sof_bytes_ext_volatile_get kcontrol IO') Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921110814.2910477-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 8d499d0e331d..9465611156d5 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -369,6 +369,14 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ int ret; int err; + /* + * Decrement the limit by ext bytes header size to + * ensure the user space buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + size -= sizeof(struct snd_ctl_tlv); + ret = pm_runtime_get_sync(scomp->dev); if (ret < 0 && ret != -EACCES) { dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); @@ -396,6 +404,12 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ data_size = cdata->data->size + sizeof(const struct sof_abi_hdr); + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) { + ret = -ENOSPC; + goto out; + } + header.numid = scontrol->cmd; header.length = data_size; if (copy_to_user(tlvd, &header, sizeof(const struct snd_ctl_tlv))) { -- cgit From 2ca210112ad91880d2d5a3f85fecc838600afbce Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Mon, 21 Sep 2020 14:08:12 +0300 Subject: ASoC: SOF: control: add size checks for ext_bytes control .put() Make sure the TLV header and size are consistent before copying from userspace. Fixes: c3078f5397046 ('ASoC: SOF: Add Sound Open Firmware KControl support') Signed-off-by: Pierre-Louis Bossart Reviewed-by: Ranjani Sridharan Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200921110814.2910477-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/sof/control.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 9465611156d5..0352d2b61358 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -300,6 +300,10 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; + /* make sure we have at least a header */ + if (size < sizeof(struct snd_ctl_tlv)) + return -EINVAL; + /* * The beginning of bytes data contains a header from where * the length (as bytes) is needed to know the correct copy @@ -308,6 +312,13 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, if (copy_from_user(&header, tlvd, sizeof(const struct snd_ctl_tlv))) return -EFAULT; + /* make sure TLV info is consistent */ + if (header.length + sizeof(struct snd_ctl_tlv) > size) { + dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n", + header.length, sizeof(struct snd_ctl_tlv), size); + return -EINVAL; + } + /* be->max is coming from topology */ if (header.length > be->max) { dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", -- cgit From 8adcdbe63aa7745ecd253b6dcf03cd2d30ece8f5 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Mon, 21 Sep 2020 10:38:20 -0500 Subject: ASoC: tas2562: Add the TAS2110 class-D amplifier Add the TAS2110 amplifier to the TAS2562 driver. The TAS2110 is register and bitmap compatible. The chips differ in that the TAS2110 does not have the I/V Sense feedback path. Since these features do not exist the device needs to be registered without these controls. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200921153820.18357-2-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index 56d410141ee0..da820e8d59a1 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -53,12 +53,14 @@ struct tas2562_data { int v_sense_slot; int i_sense_slot; int volume_lvl; + int model_id; }; enum tas256x_model { TAS2562, TAS2563, TAS2564, + TAS2110, }; static int tas2562_set_bias_level(struct snd_soc_component *component, @@ -569,6 +571,40 @@ static const struct snd_kcontrol_new tas2562_snd_controls[] = { }, }; +static const struct snd_soc_dapm_widget tas2110_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2562_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route tas2110_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + { "DAC", NULL, "ASI1 Sel" }, + { "OUT", NULL, "DAC" }, +}; + +static const struct snd_soc_component_driver soc_component_dev_tas2110 = { + .probe = tas2562_codec_probe, + .suspend = tas2562_suspend, + .resume = tas2562_resume, + .set_bias_level = tas2562_set_bias_level, + .controls = tas2562_snd_controls, + .num_controls = ARRAY_SIZE(tas2562_snd_controls), + .dapm_widgets = tas2110_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2110_dapm_widgets), + .dapm_routes = tas2110_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2110_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + static const struct snd_soc_dapm_widget tas2562_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2562_asi1_mux), @@ -703,6 +739,9 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) tas2562->sdz_gpio = NULL; } + if (tas2562->model_id == TAS2110) + return ret; + ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", &tas2562->i_sense_slot); if (ret) { @@ -741,6 +780,7 @@ static int tas2562_probe(struct i2c_client *client, data->client = client; data->dev = &client->dev; + data->model_id = id->driver_data; tas2562_parse_dt(data); @@ -753,6 +793,12 @@ static int tas2562_probe(struct i2c_client *client, dev_set_drvdata(&client->dev, data); + if (data->model_id == TAS2110) + return devm_snd_soc_register_component(dev, + &soc_component_dev_tas2110, + tas2562_dai, + ARRAY_SIZE(tas2562_dai)); + return devm_snd_soc_register_component(dev, &soc_component_dev_tas2562, tas2562_dai, ARRAY_SIZE(tas2562_dai)); @@ -763,6 +809,7 @@ static const struct i2c_device_id tas2562_id[] = { { "tas2562", TAS2562 }, { "tas2563", TAS2563 }, { "tas2564", TAS2564 }, + { "tas2110", TAS2110 }, { } }; MODULE_DEVICE_TABLE(i2c, tas2562_id); @@ -771,6 +818,7 @@ static const struct of_device_id tas2562_of_match[] = { { .compatible = "ti,tas2562", }, { .compatible = "ti,tas2563", }, { .compatible = "ti,tas2564", }, + { .compatible = "ti,tas2110", }, { }, }; MODULE_DEVICE_TABLE(of, tas2562_of_match); -- cgit From d3d71c99b541040da198f43da3bbd85d8e9598cb Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:44 -0500 Subject: ASoC: tas2770: Fix unbalanced calls to pm_runtime Fix the unbalanced call to the pm_runtime_disable when removing the module. pm_runtime_enable is not called nor is the pm_runtime setup in the code. Remove the i2c_remove function and the pm_runtime_disable. Fixes: 1a476abc723e6 ("tas2770: add tas2770 smart PA kernel driver") Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-5-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 9 --------- 1 file changed, 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 15cdd8b11a67..3226c6d4493e 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -781,13 +780,6 @@ end: return result; } -static int tas2770_i2c_remove(struct i2c_client *client) -{ - pm_runtime_disable(&client->dev); - return 0; -} - - static const struct i2c_device_id tas2770_i2c_id[] = { { "tas2770", 0}, { } @@ -808,7 +800,6 @@ static struct i2c_driver tas2770_i2c_driver = { .of_match_table = of_match_ptr(tas2770_of_match), }, .probe = tas2770_i2c_probe, - .remove = tas2770_i2c_remove, .id_table = tas2770_i2c_id, }; -- cgit From ec9377dca2ca77eaf4fbdb09ac803f379b10d731 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:45 -0500 Subject: ASoC: tas2770: Convert bit mask to GENMASK in header Update the hardcoded masks with the GENMASK macro. Also update some of the hardcoded bits with the BIT macro Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-6-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.h | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index 96683971ee9b..07e3556fc702 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -19,7 +19,7 @@ #define TAS2770_RST BIT(0) /* Power Control */ #define TAS2770_PWR_CTRL TAS2770_REG(0X0, 0x02) -#define TAS2770_PWR_CTRL_MASK 0x3 +#define TAS2770_PWR_CTRL_MASK GENMASK(1, 0) #define TAS2770_PWR_CTRL_ACTIVE 0x0 #define TAS2770_PWR_CTRL_MUTE BIT(0) #define TAS2770_PWR_CTRL_SHUTDOWN 0x2 @@ -37,43 +37,43 @@ #define TAS2770_TDM_CFG_REG0_SMP_MASK BIT(5) #define TAS2770_TDM_CFG_REG0_SMP_48KHZ 0x0 #define TAS2770_TDM_CFG_REG0_SMP_44_1KHZ BIT(5) -#define TAS2770_TDM_CFG_REG0_31_MASK 0xe +#define TAS2770_TDM_CFG_REG0_31_MASK GENMASK(3, 1) #define TAS2770_TDM_CFG_REG0_31_44_1_48KHZ 0x6 #define TAS2770_TDM_CFG_REG0_31_88_2_96KHZ 0x8 #define TAS2770_TDM_CFG_REG0_31_176_4_192KHZ 0xa /* TDM Configuration Reg1 */ #define TAS2770_TDM_CFG_REG1 TAS2770_REG(0X0, 0x0B) -#define TAS2770_TDM_CFG_REG1_MASK 0x3e +#define TAS2770_TDM_CFG_REG1_MASK GENMASK(5, 1) #define TAS2770_TDM_CFG_REG1_51_SHIFT 1 #define TAS2770_TDM_CFG_REG1_RX_MASK BIT(0) #define TAS2770_TDM_CFG_REG1_RX_RSING 0x0 #define TAS2770_TDM_CFG_REG1_RX_FALING BIT(0) /* TDM Configuration Reg2 */ #define TAS2770_TDM_CFG_REG2 TAS2770_REG(0X0, 0x0C) -#define TAS2770_TDM_CFG_REG2_RXW_MASK 0xc +#define TAS2770_TDM_CFG_REG2_RXW_MASK GENMASK(3, 2) #define TAS2770_TDM_CFG_REG2_RXW_16BITS 0x0 #define TAS2770_TDM_CFG_REG2_RXW_24BITS 0x8 #define TAS2770_TDM_CFG_REG2_RXW_32BITS 0xc -#define TAS2770_TDM_CFG_REG2_RXS_MASK 0x3 +#define TAS2770_TDM_CFG_REG2_RXS_MASK GENMASK(1, 0) #define TAS2770_TDM_CFG_REG2_RXS_16BITS 0x0 #define TAS2770_TDM_CFG_REG2_RXS_24BITS BIT(0) #define TAS2770_TDM_CFG_REG2_RXS_32BITS 0x2 /* TDM Configuration Reg3 */ #define TAS2770_TDM_CFG_REG3 TAS2770_REG(0X0, 0x0D) -#define TAS2770_TDM_CFG_REG3_RXS_MASK 0xf0 +#define TAS2770_TDM_CFG_REG3_RXS_MASK GENMASK(7, 4) #define TAS2770_TDM_CFG_REG3_RXS_SHIFT 0x4 -#define TAS2770_TDM_CFG_REG3_30_MASK 0xf +#define TAS2770_TDM_CFG_REG3_30_MASK GENMASK(3, 0) #define TAS2770_TDM_CFG_REG3_30_SHIFT 0 /* TDM Configuration Reg5 */ #define TAS2770_TDM_CFG_REG5 TAS2770_REG(0X0, 0x0F) #define TAS2770_TDM_CFG_REG5_VSNS_MASK BIT(6) #define TAS2770_TDM_CFG_REG5_VSNS_ENABLE BIT(6) -#define TAS2770_TDM_CFG_REG5_50_MASK 0x3f +#define TAS2770_TDM_CFG_REG5_50_MASK GENMASK(5, 0) /* TDM Configuration Reg6 */ #define TAS2770_TDM_CFG_REG6 TAS2770_REG(0X0, 0x10) #define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6) #define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6) -#define TAS2770_TDM_CFG_REG6_50_MASK 0x3f +#define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0) /* Brown Out Prevention Reg0 */ #define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B) /* Interrupt MASK Reg0 */ @@ -116,15 +116,16 @@ /* Revision and PG ID */ #define TAS2770_REV_AND_GPID TAS2770_REG(0X0, 0x7D) -#define TAS2770_POWER_ACTIVE 0 -#define TAS2770_POWER_MUTE 1 -#define TAS2770_POWER_SHUTDOWN 2 -#define ERROR_OVER_CURRENT 0x0000001 -#define ERROR_DIE_OVERTEMP 0x0000002 -#define ERROR_OVER_VOLTAGE 0x0000004 -#define ERROR_UNDER_VOLTAGE 0x0000008 -#define ERROR_BROWNOUT 0x0000010 -#define ERROR_CLASSD_PWR 0x0000020 +#define TAS2770_POWER_ACTIVE 0 +#define TAS2770_POWER_MUTE BIT(0) +#define TAS2770_POWER_SHUTDOWN BIT(1) + +#define ERROR_OVER_CURRENT BIT(0) +#define ERROR_DIE_OVERTEMP BIT(1) +#define ERROR_OVER_VOLTAGE BIT(2) +#define ERROR_UNDER_VOLTAGE BIT(3) +#define ERROR_BROWNOUT BIT(4) +#define ERROR_CLASSD_PWR BIT(5) struct tas2770_priv { struct device *dev; -- cgit From d3964aff7331cd9695d0c18655e053b08837ff78 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:47 -0500 Subject: ASoC: tas2770: Fix the spacing and new lines Fix up the spacing for argument alignment and add new lines to separate code. Eliminate unneccessary goto statements when the error code could just be returned. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-8-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 300 ++++++++++++++++++++------------------------- 1 file changed, 131 insertions(+), 169 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 3226c6d4493e..b17cf0a7f785 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -39,6 +39,7 @@ static void tas2770_reset(struct tas2770_priv *tas2770) msleep(20); gpiod_set_value_cansleep(tas2770->reset_gpio, 1); } + snd_soc_component_write(tas2770->component, TAS2770_SW_RST, TAS2770_RST); } @@ -51,27 +52,24 @@ static int tas2770_set_bias_level(struct snd_soc_component *component, switch (level) { case SND_SOC_BIAS_ON: - snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_ACTIVE); + snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); break; case SND_SOC_BIAS_STANDBY: case SND_SOC_BIAS_PREPARE: - snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, TAS2770_PWR_CTRL_MUTE); + snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_MUTE); break; case SND_SOC_BIAS_OFF: - snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_SHUTDOWN); + snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); break; default: - dev_err(tas2770->dev, - "wrong power level setting %d\n", level); + dev_err(tas2770->dev, "wrong power level setting %d\n", level); return -EINVAL; } @@ -83,11 +81,9 @@ static int tas2770_codec_suspend(struct snd_soc_component *component) { int ret; - ret = snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_SHUTDOWN); - + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); if (ret < 0) return ret; @@ -98,11 +94,9 @@ static int tas2770_codec_resume(struct snd_soc_component *component) { int ret; - ret = snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_ACTIVE); - + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); if (ret < 0) return ret; @@ -135,16 +129,14 @@ static int tas2770_dac_event(struct snd_soc_dapm_widget *w, switch (event) { case SND_SOC_DAPM_POST_PMU: - ret = snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_MUTE); + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_MUTE); break; case SND_SOC_DAPM_PRE_PMD: - ret = snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_SHUTDOWN); + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); break; default: dev_err(tas2770->dev, "Not supported evevt\n"); @@ -164,14 +156,11 @@ static const struct snd_kcontrol_new vsense_switch = static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, - &tas2770_asi1_mux), - SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, - &isense_switch), - SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, - &vsense_switch), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch), SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event, - SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), SND_SOC_DAPM_OUTPUT("OUT"), SND_SOC_DAPM_SIGGEN("VMON"), SND_SOC_DAPM_SIGGEN("IMON") @@ -194,15 +183,13 @@ static int tas2770_mute(struct snd_soc_dai *dai, int mute, int direction) int ret; if (mute) - ret = snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_MUTE); + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_MUTE); else - ret = snd_soc_component_update_bits(component, - TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_ACTIVE); + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); if (ret < 0) return ret; @@ -217,24 +204,21 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) switch (bitwidth) { case SNDRV_PCM_FORMAT_S16_LE: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG2, - TAS2770_TDM_CFG_REG2_RXW_MASK, - TAS2770_TDM_CFG_REG2_RXW_16BITS); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_16BITS); tas2770->v_sense_slot = tas2770->i_sense_slot + 2; break; case SNDRV_PCM_FORMAT_S24_LE: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG2, - TAS2770_TDM_CFG_REG2_RXW_MASK, - TAS2770_TDM_CFG_REG2_RXW_24BITS); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_24BITS); tas2770->v_sense_slot = tas2770->i_sense_slot + 4; break; case SNDRV_PCM_FORMAT_S32_LE: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG2, - TAS2770_TDM_CFG_REG2_RXW_MASK, - TAS2770_TDM_CFG_REG2_RXW_32BITS); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXW_MASK, + TAS2770_TDM_CFG_REG2_RXW_32BITS); tas2770->v_sense_slot = tas2770->i_sense_slot + 4; break; @@ -247,21 +231,19 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) tas2770->channel_size = bitwidth; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG5, - TAS2770_TDM_CFG_REG5_VSNS_MASK | - TAS2770_TDM_CFG_REG5_50_MASK, - TAS2770_TDM_CFG_REG5_VSNS_ENABLE | + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, + TAS2770_TDM_CFG_REG5_VSNS_MASK | + TAS2770_TDM_CFG_REG5_50_MASK, + TAS2770_TDM_CFG_REG5_VSNS_ENABLE | tas2770->v_sense_slot); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG6, - TAS2770_TDM_CFG_REG6_ISNS_MASK | - TAS2770_TDM_CFG_REG6_50_MASK, - TAS2770_TDM_CFG_REG6_ISNS_ENABLE | - tas2770->i_sense_slot); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG6, + TAS2770_TDM_CFG_REG6_ISNS_MASK | + TAS2770_TDM_CFG_REG6_50_MASK, + TAS2770_TDM_CFG_REG6_ISNS_ENABLE | + tas2770->i_sense_slot); if (ret < 0) return ret; @@ -275,82 +257,70 @@ static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) switch (samplerate) { case 48000: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_48KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_48KHZ); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); break; case 44100: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); break; case 96000: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_48KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_48KHZ); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); break; case 88200: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); break; case 19200: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_48KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_48KHZ); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); break; case 17640: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK, + TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_31_MASK, + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); break; default: ret = -EINVAL; @@ -373,23 +343,19 @@ static int tas2770_hw_params(struct snd_pcm_substream *substream, int ret; ret = tas2770_set_bitwidth(tas2770, params_format(params)); - if (ret < 0) - goto end; - - - ret = tas2770_set_samplerate(tas2770, params_rate(params)); + if (ret) + return ret; -end: - return ret; + return tas2770_set_samplerate(tas2770, params_rate(params)); } static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; - int ret; struct snd_soc_component *component = dai->component; struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; + int ret; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: @@ -412,8 +378,8 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1, - TAS2770_TDM_CFG_REG1_RX_MASK, - asi_cfg_1); + TAS2770_TDM_CFG_REG1_RX_MASK, + asi_cfg_1); if (ret < 0) return ret; @@ -437,8 +403,8 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) } ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG1, - TAS2770_TDM_CFG_REG1_MASK, - (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT)); + TAS2770_TDM_CFG_REG1_MASK, + (tdm_rx_start_slot << TAS2770_TDM_CFG_REG1_51_SHIFT)); if (ret < 0) return ret; @@ -464,6 +430,7 @@ static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, if (slots == 1) { if (tx_mask != 1) return -EINVAL; + left_slot = 0; right_slot = 0; } else { @@ -481,43 +448,36 @@ static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, return -EINVAL; ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3, - TAS2770_TDM_CFG_REG3_30_MASK, - (left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT)); + TAS2770_TDM_CFG_REG3_30_MASK, + (left_slot << TAS2770_TDM_CFG_REG3_30_SHIFT)); if (ret < 0) return ret; ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG3, - TAS2770_TDM_CFG_REG3_RXS_MASK, - (right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT)); + TAS2770_TDM_CFG_REG3_RXS_MASK, + (right_slot << TAS2770_TDM_CFG_REG3_RXS_SHIFT)); if (ret < 0) return ret; switch (slot_width) { case 16: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG2, - TAS2770_TDM_CFG_REG2_RXS_MASK, - TAS2770_TDM_CFG_REG2_RXS_16BITS); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_16BITS); break; - case 24: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG2, - TAS2770_TDM_CFG_REG2_RXS_MASK, - TAS2770_TDM_CFG_REG2_RXS_24BITS); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_24BITS); break; - case 32: - ret = snd_soc_component_update_bits(component, - TAS2770_TDM_CFG_REG2, - TAS2770_TDM_CFG_REG2_RXS_MASK, - TAS2770_TDM_CFG_REG2_RXS_32BITS); + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG2, + TAS2770_TDM_CFG_REG2_RXS_MASK, + TAS2770_TDM_CFG_REG2_RXS_32BITS); break; - case 0: /* Do not change slot width */ ret = 0; break; - default: ret = -EINVAL; } @@ -585,11 +545,9 @@ static DECLARE_TLV_DB_SCALE(tas2770_playback_volume, -12750, 50, 0); static const struct snd_kcontrol_new tas2770_snd_controls[] = { SOC_SINGLE_TLV("Speaker Playback Volume", TAS2770_PLAY_CFG_REG2, - 0, TAS2770_PLAY_CFG_REG2_VMAX, 1, - tas2770_playback_volume), - SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0, - 0, 0x14, 0, - tas2770_digital_tlv), + 0, TAS2770_PLAY_CFG_REG2_VMAX, 1, tas2770_playback_volume), + SOC_SINGLE_TLV("Amp Gain Volume", TAS2770_PLAY_CFG_REG0, 0, 0x14, 0, + tas2770_digital_tlv), }; static const struct snd_soc_component_driver soc_component_driver_tas2770 = { @@ -650,6 +608,7 @@ static bool tas2770_volatile(struct device *dev, unsigned int reg) case TAS2770_TEMP_LSB: return true; } + return false; } @@ -668,6 +627,7 @@ static bool tas2770_writeable(struct device *dev, unsigned int reg) case TAS2770_REV_AND_GPID: return false; } + return true; } @@ -701,26 +661,29 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) int rc = 0; rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format", - &tas2770->asi_format); + &tas2770->asi_format); if (rc) { dev_info(tas2770->dev, "Property %s is missing setting default slot\n", - "ti,asi-format"); + "ti,asi-format"); + tas2770->asi_format = 0; } rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", - &tas2770->i_sense_slot); + &tas2770->i_sense_slot); if (rc) { dev_info(tas2770->dev, "Property %s is missing setting default slot\n", - "ti,imon-slot-no"); + "ti,imon-slot-no"); + tas2770->i_sense_slot = 0; } rc = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", - &tas2770->v_sense_slot); + &tas2770->v_sense_slot); if (rc) { dev_info(tas2770->dev, "Property %s is missing setting default slot\n", - "ti,vmon-slot-no"); + "ti,vmon-slot-no"); + tas2770->v_sense_slot = 2; } @@ -733,22 +696,23 @@ static int tas2770_i2c_probe(struct i2c_client *client, struct tas2770_priv *tas2770; int result; - tas2770 = devm_kzalloc(&client->dev, - sizeof(struct tas2770_priv), GFP_KERNEL); + tas2770 = devm_kzalloc(&client->dev, sizeof(struct tas2770_priv), + GFP_KERNEL); if (!tas2770) return -ENOMEM; - tas2770->dev = &client->dev; + tas2770->dev = &client->dev; i2c_set_clientdata(client, tas2770); dev_set_drvdata(&client->dev, tas2770); + tas2770->power_state = TAS2770_POWER_SHUTDOWN; tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap); if (IS_ERR(tas2770->regmap)) { result = PTR_ERR(tas2770->regmap); dev_err(&client->dev, "Failed to allocate register map: %d\n", - result); - goto end; + result); + return result; } if (client->dev.of_node) { @@ -756,7 +720,7 @@ static int tas2770_i2c_probe(struct i2c_client *client, if (result) { dev_err(tas2770->dev, "%s: Failed to parse devicetree\n", __func__); - goto end; + return result; } } @@ -776,7 +740,6 @@ static int tas2770_i2c_probe(struct i2c_client *client, if (result) dev_err(tas2770->dev, "Register codec failed.\n"); -end: return result; } @@ -802,7 +765,6 @@ static struct i2c_driver tas2770_i2c_driver = { .probe = tas2770_i2c_probe, .id_table = tas2770_i2c_id, }; - module_i2c_driver(tas2770_i2c_driver); MODULE_AUTHOR("Shi Fu "); -- cgit From be05ab41c61858cce557a1fe863ed00f38e31e97 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Fri, 18 Sep 2020 14:05:48 -0500 Subject: ASoC: tas2770: Refactor sample rate function Refactor the tas2770_set_samplerate to simplify the code and access the I2C bus only once per rate request. The ramp rate and sample rate bits are contained in the same register so a single call to the snd_soc_update_bits function is all that is needed Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200918190548.12598-9-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 75 ++++++++++++---------------------------------- 1 file changed, 19 insertions(+), 56 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index b17cf0a7f785..386aaa11fa08 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -252,80 +252,43 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) { - int ret; struct snd_soc_component *component = tas2770->component; + int ramp_rate_val; + int ret; switch (samplerate) { case 48000: - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_48KHZ); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ | + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ; break; case 44100: - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_44_1_48KHZ); + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ | + TAS2770_TDM_CFG_REG0_31_44_1_48KHZ; break; case 96000: - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_48KHZ); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ | + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ; break; case 88200: - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_88_2_96KHZ); + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ | + TAS2770_TDM_CFG_REG0_31_88_2_96KHZ; break; case 19200: - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_48KHZ); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_48KHZ | + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ; break; case 17640: - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_SMP_MASK, - TAS2770_TDM_CFG_REG0_SMP_44_1KHZ); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, - TAS2770_TDM_CFG_REG0_31_MASK, - TAS2770_TDM_CFG_REG0_31_176_4_192KHZ); + ramp_rate_val = TAS2770_TDM_CFG_REG0_SMP_44_1KHZ | + TAS2770_TDM_CFG_REG0_31_176_4_192KHZ; break; default: - ret = -EINVAL; + return -EINVAL; } + ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG0, + TAS2770_TDM_CFG_REG0_SMP_MASK | + TAS2770_TDM_CFG_REG0_31_MASK, + ramp_rate_val); if (ret < 0) return ret; -- cgit From 163cd1059a85d225b811ddb4192fabd1553f77f1 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 21 Sep 2020 13:08:41 +0300 Subject: ASoC: hdac: make SOF HDA codec driver probe deterministic To provide backward compatibility to older systems, the SOF HDA driver allows user to specify which HDMI codec driver to use at runtime via kernel parameter. This mechanism has a subtle flaw in that it assumes the codec drivers not to be loaded when the SOF PCI driver is loaded. The problem is rooted in use of the hdev->type field. snd_hdac_ext_bus_device_init() initializes this field to HDA_DEV_ASOC. This signals the HDA core that ASoC drivers should be considered in driver matching (hda_bus_match()). The SOF and SST drivers continue by overriding this field to HDA_DEV_LEGACY and proceeding to load driver modules with request_module(). Correct drivers will get loaded and attached. If however the codec drivers are already loaded when snd_hdac_ext_bus_device_init() is called, the matching will not work as expected as device type is still set to HDA_DEV_ASOC. Specifically if hdac-hdmi is attached when machine driver is configured to use hdac-hda, this leads to out-of-bounds memory access in hda_dsp_hdmi_build_controls(). Fix the issue by adding codec type as a parameter to snd_hdac_ext_bus_device_init() and ensuring type is set correctly from the start. Fixes: 139c7febad1a ("ASoC: SOF: Intel: add support for snd-hda-codec-hdmi") Signed-off-by: Kai Vehmanen Reviewed-by: Guennadi Liakhovetski Reviewed-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200921100841.2882662-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/hda/ext/hdac_ext_bus.c | 5 +++-- sound/soc/intel/skylake/skl.c | 4 ++-- sound/soc/sof/intel/hda-codec.c | 17 ++++++++--------- 3 files changed, 13 insertions(+), 13 deletions(-) (limited to 'sound') diff --git a/sound/hda/ext/hdac_ext_bus.c b/sound/hda/ext/hdac_ext_bus.c index d0a604c939df..765c40a6ccba 100644 --- a/sound/hda/ext/hdac_ext_bus.c +++ b/sound/hda/ext/hdac_ext_bus.c @@ -70,11 +70,12 @@ static void default_release(struct device *dev) * @bus: hdac bus to attach to * @addr: codec address * @hdev: hdac device to init + * @type: codec type (HDAC_DEV_*) to use for this device * * Returns zero for success or a negative error code. */ int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, - struct hdac_device *hdev) + struct hdac_device *hdev, int type) { char name[15]; int ret; @@ -88,7 +89,7 @@ int snd_hdac_ext_bus_device_init(struct hdac_bus *bus, int addr, dev_err(bus->dev, "device init failed for hdac device\n"); return ret; } - hdev->type = HDA_DEV_ASOC; + hdev->type = type; hdev->dev.release = default_release; ret = snd_hdac_device_register(hdev); diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index ea00cf61d1a8..8b993722f74e 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -721,7 +721,7 @@ static int probe_codec(struct hdac_bus *bus, int addr) hda_codec->codec.bus = skl_to_hbus(skl); hdev = &hda_codec->codec.core; - err = snd_hdac_ext_bus_device_init(bus, addr, hdev); + err = snd_hdac_ext_bus_device_init(bus, addr, hdev, HDA_DEV_ASOC); if (err < 0) return err; @@ -736,7 +736,7 @@ static int probe_codec(struct hdac_bus *bus, int addr) if (!hdev) return -ENOMEM; - return snd_hdac_ext_bus_device_init(bus, addr, hdev); + return snd_hdac_ext_bus_device_init(bus, addr, hdev, HDA_DEV_ASOC); #endif /* CONFIG_SND_SOC_INTEL_SKYLAKE_HDAUDIO_CODEC */ } diff --git a/sound/soc/sof/intel/hda-codec.c b/sound/soc/sof/intel/hda-codec.c index f6ba3b593e1f..6875fa570c2c 100644 --- a/sound/soc/sof/intel/hda-codec.c +++ b/sound/soc/sof/intel/hda-codec.c @@ -117,6 +117,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) struct hdac_hda_priv *hda_priv; struct hda_codec *codec; + int type = HDA_DEV_LEGACY; #endif struct hda_bus *hbus = sof_to_hbus(sdev); struct hdac_device *hdev; @@ -143,7 +144,11 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, hdev = &hda_priv->codec.core; codec = &hda_priv->codec; - ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev); + /* only probe ASoC codec drivers for HDAC-HDMI */ + if (!hda_codec_use_common_hdmi && (resp & 0xFFFF0000) == IDISP_VID_INTEL) + type = HDA_DEV_ASOC; + + ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, type); if (ret < 0) return ret; @@ -161,13 +166,7 @@ static int hda_codec_probe(struct snd_sof_dev *sdev, int address, else codec->probe_id = 0; - /* - * if common HDMI codec driver is not used, codec load - * is skipped here and hdac_hdmi is used instead - */ - if (hda_codec_use_common_hdmi || - (resp & 0xFFFF0000) != IDISP_VID_INTEL) { - hdev->type = HDA_DEV_LEGACY; + if (type == HDA_DEV_LEGACY) { ret = hda_codec_load_module(codec); /* * handle ret==0 (no driver bound) as an error, but pass @@ -188,7 +187,7 @@ error: if (!hdev) return -ENOMEM; - ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev); + ret = snd_hdac_ext_bus_device_init(&hbus->core, address, hdev, HDA_DEV_ASOC); return ret; #endif -- cgit From 6564d0ad67efb2d977e130e7448505ee538af016 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Sep 2020 10:49:53 +0200 Subject: ALSA: ctl: Workaround for lockdep warning wrt card->ctl_files_rwlock The recent change in lockdep for read lock caused the deadlock warnings in ALSA control code which uses the read_lock() for notification and else while write_lock_irqsave() is used for adding and removing the list entry. Although a deadlock would practically never hit in a real usage (the addition and the deletion can't happen with the notification), it's better to fix the read_lock() usage in a semantically correct way. This patch replaces the read_lock() calls with read_lock_irqsave() version for avoiding a reported deadlock. The notification code path takes the irq disablement in anyway, and other code paths are very short execution, hence there shouldn't be any big performance hit by this change. Fixes: e918188611f0 ("locking: More accurate annotations for read_lock()") Reported-by: syzbot+561a74f84100162990b2@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/20200922084953.29018-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/control.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/core/control.c b/sound/core/control.c index e014598142df..421ddc76f264 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -150,14 +150,14 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, return; if (card->shutdown) return; - read_lock(&card->ctl_files_rwlock); + read_lock_irqsave(&card->ctl_files_rwlock, flags); #if IS_ENABLED(CONFIG_SND_MIXER_OSS) card->mixer_oss_change_count++; #endif list_for_each_entry(ctl, &card->ctl_files, list) { if (!ctl->subscribed) continue; - spin_lock_irqsave(&ctl->read_lock, flags); + spin_lock(&ctl->read_lock); list_for_each_entry(ev, &ctl->events, list) { if (ev->id.numid == id->numid) { ev->mask |= mask; @@ -174,10 +174,10 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, } _found: wake_up(&ctl->change_sleep); - spin_unlock_irqrestore(&ctl->read_lock, flags); + spin_unlock(&ctl->read_lock); kill_fasync(&ctl->fasync, SIGIO, POLL_IN); } - read_unlock(&card->ctl_files_rwlock); + read_unlock_irqrestore(&card->ctl_files_rwlock, flags); } EXPORT_SYMBOL(snd_ctl_notify); @@ -1951,8 +1951,9 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type) { struct snd_ctl_file *kctl; int subdevice = -1; + unsigned long flags; - read_lock(&card->ctl_files_rwlock); + read_lock_irqsave(&card->ctl_files_rwlock, flags); list_for_each_entry(kctl, &card->ctl_files, list) { if (kctl->pid == task_pid(current)) { subdevice = kctl->preferred_subdevice[type]; @@ -1960,7 +1961,7 @@ int snd_ctl_get_preferred_subdevice(struct snd_card *card, int type) break; } } - read_unlock(&card->ctl_files_rwlock); + read_unlock_irqrestore(&card->ctl_files_rwlock, flags); return subdevice; } EXPORT_SYMBOL_GPL(snd_ctl_get_preferred_subdevice); @@ -2009,13 +2010,14 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) { struct snd_card *card = device->device_data; struct snd_ctl_file *ctl; + unsigned long flags; - read_lock(&card->ctl_files_rwlock); + read_lock_irqsave(&card->ctl_files_rwlock, flags); list_for_each_entry(ctl, &card->ctl_files, list) { wake_up(&ctl->change_sleep); kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } - read_unlock(&card->ctl_files_rwlock); + read_unlock_irqrestore(&card->ctl_files_rwlock, flags); return snd_unregister_device(&card->ctl_dev); } -- cgit From 55c5cc63ab3277aa20637dc20f6528987ac23743 Mon Sep 17 00:00:00 2001 From: Cheng-Yi Chiang Date: Tue, 22 Sep 2020 14:23:16 +0800 Subject: ASoC: hdmi-codec: Use set_jack ops to set jack Use set_jack ops to set jack so machine drivers do not need to include hdmi-codec.h explicitly. Signed-off-by: Cheng-Yi Chiang Reviewed-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20200922062316.1172935-1-cychiang@chromium.org Signed-off-by: Mark Brown --- sound/soc/codecs/hdmi-codec.c | 12 ++++-------- sound/soc/mediatek/mt8173/mt8173-rt5650.c | 5 ++--- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 5 ++--- sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c | 5 ++--- sound/soc/rockchip/rockchip_max98090.c | 3 +-- 5 files changed, 11 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 8c6f540533ba..403d4c6a49a8 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -698,13 +698,9 @@ static void plugged_cb(struct device *dev, bool plugged) hdmi_codec_jack_report(hcp, 0); } -/** - * hdmi_codec_set_jack_detect - register HDMI plugged callback - * @component: the hdmi-codec instance - * @jack: ASoC jack to report (dis)connection events on - */ -int hdmi_codec_set_jack_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack) +static int hdmi_codec_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, + void *data) { struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component); int ret = -EOPNOTSUPP; @@ -720,7 +716,6 @@ int hdmi_codec_set_jack_detect(struct snd_soc_component *component, } return ret; } -EXPORT_SYMBOL_GPL(hdmi_codec_set_jack_detect); static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai) { @@ -806,6 +801,7 @@ static const struct snd_soc_component_driver hdmi_driver = { .use_pmdown_time = 1, .endianness = 1, .non_legacy_dai_naming = 1, + .set_jack = hdmi_codec_set_jack, }; static int hdmi_codec_probe(struct platform_device *pdev) diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c index 347b095d478d..c28ebf891cb0 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -11,7 +11,6 @@ #include #include #include -#include #include "../../codecs/rt5645.h" #define MCLK_FOR_CODECS 12288000 @@ -154,8 +153,8 @@ static int mt8173_rt5650_hdmi_init(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - return hdmi_codec_set_jack_detect(asoc_rtd_to_codec(rtd, 0)->component, - &mt8173_rt5650_hdmi_jack); + return snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, + &mt8173_rt5650_hdmi_jack, NULL); } enum { diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 68fe23b96b14..15e48cde4921 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -375,8 +374,8 @@ static int mt8183_da7219_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - return hdmi_codec_set_jack_detect(asoc_rtd_to_codec(rtd, 0)->component, - &priv->hdmi_jack); + return snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, + &priv->hdmi_jack, NULL); } static struct snd_soc_dai_link mt8183_da7219_dai_links[] = { diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 07410d7afaa9..327dfad41e31 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include @@ -350,8 +349,8 @@ mt8183_mt6358_ts3a227_max98357_hdmi_init(struct snd_soc_pcm_runtime *rtd) if (ret) return ret; - return hdmi_codec_set_jack_detect(asoc_rtd_to_codec(rtd, 0)->component, - &priv->hdmi_jack); + return snd_soc_component_set_jack(asoc_rtd_to_codec(rtd, 0)->component, + &priv->hdmi_jack, NULL); } static struct snd_soc_dai_link mt8183_mt6358_ts3a227_dai_links[] = { diff --git a/sound/soc/rockchip/rockchip_max98090.c b/sound/soc/rockchip/rockchip_max98090.c index 9acfd024aa5d..c8f1a28a92b7 100644 --- a/sound/soc/rockchip/rockchip_max98090.c +++ b/sound/soc/rockchip/rockchip_max98090.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -238,7 +237,7 @@ static int rk_hdmi_init(struct snd_soc_pcm_runtime *runtime) return ret; } - return hdmi_codec_set_jack_detect(component, &rk_hdmi_jack); + return snd_soc_component_set_jack(component, &rk_hdmi_jack, NULL); } /* max98090 dai_link */ -- cgit From 90e2a588c9e743f104849fb2da4c121e1a487201 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Tue, 22 Sep 2020 09:24:11 -0500 Subject: ASoC: tas2562: Remove duplicate code for I/V sense Remove duplicate code for programming the I/V sense the call to update the register was duplicated in commit 09ed395b05feb ("ASoC: tas2562: Add voltage sense slot configuration"). Fixes: 09ed395b05feb ("ASoC: tas2562: Add voltage sense slot configuration") Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200922142411.10364-1-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2562.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index da820e8d59a1..f1ff204e3ad0 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -253,18 +253,6 @@ static int tas2562_set_dai_tdm_slot(struct snd_soc_dai *dai, if (ret < 0) return ret; - ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG5, - TAS2562_TDM_CFG5_VSNS_SLOT_MASK, - tas2562->v_sense_slot); - if (ret < 0) - return ret; - - ret = snd_soc_component_update_bits(component, TAS2562_TDM_CFG6, - TAS2562_TDM_CFG6_ISNS_SLOT_MASK, - tas2562->i_sense_slot); - if (ret < 0) - return ret; - return 0; } -- cgit From 50b18e4a2608e3897f3787eaa7dfa869b40d9923 Mon Sep 17 00:00:00 2001 From: Necip Fazil Yildiran Date: Thu, 17 Sep 2020 17:18:04 +0300 Subject: ASoC: cros_ec_codec: fix kconfig dependency warning for SND_SOC_CROS_EC_CODEC When SND_SOC_CROS_EC_CODEC is enabled and CRYPTO is disabled, it results in the following Kbuild warning: WARNING: unmet direct dependencies detected for CRYPTO_LIB_SHA256 Depends on [n]: CRYPTO [=n] Selected by [y]: - SND_SOC_CROS_EC_CODEC [=y] && SOUND [=y] && !UML && SND [=y] && SND_SOC [=y] && CROS_EC [=y] The reason is that SND_SOC_CROS_EC_CODEC selects CRYPTO_LIB_SHA256 without depending on or selecting CRYPTO while CRYPTO_LIB_SHA256 is subordinate to CRYPTO. Honor the kconfig menu hierarchy to remove kconfig dependency warnings. Fixes: 93fa0af4790a ("ASoC: cros_ec_codec: switch to library API for SHA-256") Signed-off-by: Necip Fazil Yildiran Link: https://lore.kernel.org/r/20200917141803.92889-1-fazilyildiran@gmail.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 1cf686eb6a51..af3b93cdb6c9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -542,6 +542,7 @@ config SND_SOC_CQ0093VC config SND_SOC_CROS_EC_CODEC tristate "codec driver for ChromeOS EC" depends on CROS_EC + select CRYPTO select CRYPTO_LIB_SHA256 help If you say yes here you will get support for the -- cgit From cdc01a1558dedcee3daee7e1802d0349a07edb87 Mon Sep 17 00:00:00 2001 From: František Kučera Date: Tue, 22 Sep 2020 16:42:06 +0200 Subject: ALSA: usb-audio: Add mixer support for Pioneer DJ DJM-250MK2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch extends support for DJM-250MK2 and allows mapping playback and capture channels to available sources. Configures the card through USB commands. Signed-off-by: František Kučera Link: https://lore.kernel.org/r/20200922144206.10472-1-konference@frantovo.cz Signed-off-by: Takashi Iwai --- sound/usb/mixer_quirks.c | 213 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) (limited to 'sound') diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index 199cdbfdc761..df036a359f2f 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -2602,6 +2602,216 @@ static int snd_bbfpro_controls_create(struct usb_mixer_interface *mixer) return 0; } +/* + * Pioneer DJ DJM-250MK2 and maybe other DJM models + * + * For playback, no duplicate mapping should be set. + * There are three mixer stereo channels (CH1, CH2, AUX) + * and three stereo sources (Playback 1-2, Playback 3-4, Playback 5-6). + * Each channel should be mapped just once to one source. + * If mapped multiple times, only one source will play on given channel + * (sources are not mixed together). + * + * For recording, duplicate mapping is OK. We will get the same signal multiple times. + * + * Channels 7-8 are in both directions fixed to FX SEND / FX RETURN. + * + * See also notes in the quirks-table.h file. + */ + +struct snd_pioneer_djm_option { + const u16 wIndex; + const u16 wValue; + const char *name; +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_capture_level[] = { + { .name = "-5 dB", .wValue = 0x0300, .wIndex = 0x8003 }, + { .name = "-10 dB", .wValue = 0x0200, .wIndex = 0x8003 }, + { .name = "-15 dB", .wValue = 0x0100, .wIndex = 0x8003 }, + { .name = "-19 dB", .wValue = 0x0000, .wIndex = 0x8003 } +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_capture_ch12[] = { + { .name = "CH1 Control Tone PHONO", .wValue = 0x0103, .wIndex = 0x8002 }, + { .name = "CH1 Control Tone LINE", .wValue = 0x0100, .wIndex = 0x8002 }, + { .name = "Post CH1 Fader", .wValue = 0x0106, .wIndex = 0x8002 }, + { .name = "Cross Fader A", .wValue = 0x0107, .wIndex = 0x8002 }, + { .name = "Cross Fader B", .wValue = 0x0108, .wIndex = 0x8002 }, + { .name = "MIC", .wValue = 0x0109, .wIndex = 0x8002 }, + { .name = "AUX", .wValue = 0x010d, .wIndex = 0x8002 }, + { .name = "REC OUT", .wValue = 0x010a, .wIndex = 0x8002 } +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_capture_ch34[] = { + { .name = "CH2 Control Tone PHONO", .wValue = 0x0203, .wIndex = 0x8002 }, + { .name = "CH2 Control Tone LINE", .wValue = 0x0200, .wIndex = 0x8002 }, + { .name = "Post CH2 Fader", .wValue = 0x0206, .wIndex = 0x8002 }, + { .name = "Cross Fader A", .wValue = 0x0207, .wIndex = 0x8002 }, + { .name = "Cross Fader B", .wValue = 0x0208, .wIndex = 0x8002 }, + { .name = "MIC", .wValue = 0x0209, .wIndex = 0x8002 }, + { .name = "AUX", .wValue = 0x020d, .wIndex = 0x8002 }, + { .name = "REC OUT", .wValue = 0x020a, .wIndex = 0x8002 } +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_capture_ch56[] = { + { .name = "REC OUT", .wValue = 0x030a, .wIndex = 0x8002 }, + { .name = "Post CH1 Fader", .wValue = 0x0311, .wIndex = 0x8002 }, + { .name = "Post CH2 Fader", .wValue = 0x0312, .wIndex = 0x8002 }, + { .name = "Cross Fader A", .wValue = 0x0307, .wIndex = 0x8002 }, + { .name = "Cross Fader B", .wValue = 0x0308, .wIndex = 0x8002 }, + { .name = "MIC", .wValue = 0x0309, .wIndex = 0x8002 }, + { .name = "AUX", .wValue = 0x030d, .wIndex = 0x8002 } +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_playback_12[] = { + { .name = "CH1", .wValue = 0x0100, .wIndex = 0x8016 }, + { .name = "CH2", .wValue = 0x0101, .wIndex = 0x8016 }, + { .name = "AUX", .wValue = 0x0104, .wIndex = 0x8016 } +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_playback_34[] = { + { .name = "CH1", .wValue = 0x0200, .wIndex = 0x8016 }, + { .name = "CH2", .wValue = 0x0201, .wIndex = 0x8016 }, + { .name = "AUX", .wValue = 0x0204, .wIndex = 0x8016 } +}; + +static const struct snd_pioneer_djm_option snd_pioneer_djm_options_playback_56[] = { + { .name = "CH1", .wValue = 0x0300, .wIndex = 0x8016 }, + { .name = "CH2", .wValue = 0x0301, .wIndex = 0x8016 }, + { .name = "AUX", .wValue = 0x0304, .wIndex = 0x8016 } +}; + +struct snd_pioneer_djm_option_group { + const char *name; + const struct snd_pioneer_djm_option *options; + const size_t count; + const u16 default_value; +}; + +#define snd_pioneer_djm_option_group_item(_name, suffix, _default_value) { \ + .name = _name, \ + .options = snd_pioneer_djm_options_##suffix, \ + .count = ARRAY_SIZE(snd_pioneer_djm_options_##suffix), \ + .default_value = _default_value } + +static const struct snd_pioneer_djm_option_group snd_pioneer_djm_option_groups[] = { + snd_pioneer_djm_option_group_item("Master Capture Level Capture Switch", capture_level, 0), + snd_pioneer_djm_option_group_item("Capture 1-2 Capture Switch", capture_ch12, 2), + snd_pioneer_djm_option_group_item("Capture 3-4 Capture Switch", capture_ch34, 2), + snd_pioneer_djm_option_group_item("Capture 5-6 Capture Switch", capture_ch56, 0), + snd_pioneer_djm_option_group_item("Playback 1-2 Playback Switch", playback_12, 0), + snd_pioneer_djm_option_group_item("Playback 3-4 Playback Switch", playback_34, 1), + snd_pioneer_djm_option_group_item("Playback 5-6 Playback Switch", playback_56, 2) +}; + +// layout of the kcontrol->private_value: +#define SND_PIONEER_DJM_VALUE_MASK 0x0000ffff +#define SND_PIONEER_DJM_GROUP_MASK 0xffff0000 +#define SND_PIONEER_DJM_GROUP_SHIFT 16 + +static int snd_pioneer_djm_controls_info(struct snd_kcontrol *kctl, struct snd_ctl_elem_info *info) +{ + u16 group_index = kctl->private_value >> SND_PIONEER_DJM_GROUP_SHIFT; + size_t count; + const char *name; + const struct snd_pioneer_djm_option_group *group; + + if (group_index >= ARRAY_SIZE(snd_pioneer_djm_option_groups)) + return -EINVAL; + + group = &snd_pioneer_djm_option_groups[group_index]; + count = group->count; + if (info->value.enumerated.item >= count) + info->value.enumerated.item = count - 1; + name = group->options[info->value.enumerated.item].name; + strlcpy(info->value.enumerated.name, name, sizeof(info->value.enumerated.name)); + info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + info->count = 1; + info->value.enumerated.items = count; + return 0; +} + +static int snd_pioneer_djm_controls_update(struct usb_mixer_interface *mixer, u16 group, u16 value) +{ + int err; + + if (group >= ARRAY_SIZE(snd_pioneer_djm_option_groups) + || value >= snd_pioneer_djm_option_groups[group].count) + return -EINVAL; + + err = snd_usb_lock_shutdown(mixer->chip); + if (err) + return err; + + err = snd_usb_ctl_msg( + mixer->chip->dev, usb_sndctrlpipe(mixer->chip->dev, 0), + USB_REQ_SET_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + snd_pioneer_djm_option_groups[group].options[value].wValue, + snd_pioneer_djm_option_groups[group].options[value].wIndex, + NULL, 0); + + snd_usb_unlock_shutdown(mixer->chip); + return err; +} + +static int snd_pioneer_djm_controls_get(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *elem) +{ + elem->value.enumerated.item[0] = kctl->private_value & SND_PIONEER_DJM_VALUE_MASK; + return 0; +} + +static int snd_pioneer_djm_controls_put(struct snd_kcontrol *kctl, struct snd_ctl_elem_value *elem) +{ + struct usb_mixer_elem_list *list = snd_kcontrol_chip(kctl); + struct usb_mixer_interface *mixer = list->mixer; + unsigned long private_value = kctl->private_value; + u16 group = (private_value & SND_PIONEER_DJM_GROUP_MASK) >> SND_PIONEER_DJM_GROUP_SHIFT; + u16 value = elem->value.enumerated.item[0]; + + kctl->private_value = (group << SND_PIONEER_DJM_GROUP_SHIFT) | value; + + return snd_pioneer_djm_controls_update(mixer, group, value); +} + +static int snd_pioneer_djm_controls_resume(struct usb_mixer_elem_list *list) +{ + unsigned long private_value = list->kctl->private_value; + u16 group = (private_value & SND_PIONEER_DJM_GROUP_MASK) >> SND_PIONEER_DJM_GROUP_SHIFT; + u16 value = (private_value & SND_PIONEER_DJM_VALUE_MASK); + + return snd_pioneer_djm_controls_update(list->mixer, group, value); +} + +static int snd_pioneer_djm_controls_create(struct usb_mixer_interface *mixer) +{ + int err, i; + const struct snd_pioneer_djm_option_group *group; + struct snd_kcontrol_new knew = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .index = 0, + .info = snd_pioneer_djm_controls_info, + .get = snd_pioneer_djm_controls_get, + .put = snd_pioneer_djm_controls_put + }; + + for (i = 0; i < ARRAY_SIZE(snd_pioneer_djm_option_groups); i++) { + group = &snd_pioneer_djm_option_groups[i]; + knew.name = group->name; + knew.private_value = (i << SND_PIONEER_DJM_GROUP_SHIFT) | group->default_value; + err = snd_pioneer_djm_controls_update(mixer, i, group->default_value); + if (err) + return err; + err = add_single_ctl_with_resume(mixer, 0, snd_pioneer_djm_controls_resume, + &knew, NULL); + if (err) + return err; + } + return 0; +} + int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) { int err = 0; @@ -2706,6 +2916,9 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) case USB_ID(0x2a39, 0x3fb0): /* RME Babyface Pro FS */ err = snd_bbfpro_controls_create(mixer); break; + case USB_ID(0x2b73, 0x0017): /* Pioneer DJ DJM-250MK2 */ + err = snd_pioneer_djm_controls_create(mixer); + break; } return err; -- cgit From 2759caad2600d503c3b0ed800e7e03d2cd7a4c05 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 22 Sep 2020 10:38:56 +0200 Subject: ALSA: seq: oss: Avoid mutex lock for a long-time ioctl Recently we applied a fix to cover the whole OSS sequencer ioctls with the mutex for dealing with the possible races. This works fine in general, but in theory, this may lead to unexpectedly long stall if an ioctl like SNDCTL_SEQ_SYNC is issued and an event with the far future timestamp was queued. For fixing such a potential stall, this patch changes the mutex lock applied conditionally excluding such an ioctl command. Also, change the mutex_lock() with the interruptible version for user to allow escaping from the big-hammer mutex. Fixes: 80982c7e834e ("ALSA: seq: oss: Serialize ioctls") Suggested-by: Pavel Machek Link: https://lore.kernel.org/r/20200922083856.28572-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/seq/oss/seq_oss.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c index c8b9c0b315d8..250a92b18726 100644 --- a/sound/core/seq/oss/seq_oss.c +++ b/sound/core/seq/oss/seq_oss.c @@ -174,9 +174,12 @@ odev_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (snd_BUG_ON(!dp)) return -ENXIO; - mutex_lock(®ister_mutex); + if (cmd != SNDCTL_SEQ_SYNC && + mutex_lock_interruptible(®ister_mutex)) + return -ERESTARTSYS; rc = snd_seq_oss_ioctl(dp, cmd, arg); - mutex_unlock(®ister_mutex); + if (cmd != SNDCTL_SEQ_SYNC) + mutex_unlock(®ister_mutex); return rc; } -- cgit From f1bf9a6b4e5ed3a764c1f5715a02f438b7c2889f Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Sep 2020 11:05:10 +0300 Subject: ASoC: Intel: sof_sdw: remove ternary operator cppcheck reports the following warning: sound/soc/intel/boards/sof_sdw.c:866:46: style: Clarify calculation precedence for '&' and '?'. [clarifyCalculation] hdmi_num = sof_sdw_quirk & SOF_SDW_TGL_HDMI ? ^ There's no reason to use the ternary operator here, we might as well use a regular if-else construct. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Jaska Uimonen Reviewed-by: Kai Vehmanen Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923080514.3242858-3-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 210b66d1f9a2..79c3c19317fe 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -863,8 +863,10 @@ static int sof_card_dai_links_create(struct device *dev, for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) codec_info_list[i].amp_num = 0; - hdmi_num = sof_sdw_quirk & SOF_SDW_TGL_HDMI ? - SOF_TGL_HDMI_COUNT : SOF_PRE_TGL_HDMI_COUNT; + if (sof_sdw_quirk & SOF_SDW_TGL_HDMI) + hdmi_num = SOF_TGL_HDMI_COUNT; + else + hdmi_num = SOF_PRE_TGL_HDMI_COUNT; ssp_mask = SOF_SSP_GET_PORT(sof_sdw_quirk); /* -- cgit From f93808308aab34071259fa8cffbda273bc346ea7 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Sep 2020 11:05:11 +0300 Subject: ASoC: Intel: add codec name prefix to ACPI machine description The current SOF machine driver adds a name prefix for each codec, mainly to differentiate ALSA controls for left and right amplifiers. This is a good idea, but the machine driver duplicates some of the information that already exists in ACPI descriptors, so add those prefixes there. Follow-up patches will make use of the information encoded in these tables and remove duplication. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923080514.3242858-4-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/soc-acpi-intel-cml-match.c | 10 ++++++++++ sound/soc/intel/common/soc-acpi-intel-cnl-match.c | 1 + sound/soc/intel/common/soc-acpi-intel-icl-match.c | 6 ++++++ sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 14 ++++++++++++++ 4 files changed, 31 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/common/soc-acpi-intel-cml-match.c b/sound/soc/intel/common/soc-acpi-intel-cml-match.c index ec01884ef93d..26dde88bb227 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cml-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cml-match.c @@ -98,6 +98,7 @@ static const struct snd_soc_acpi_adr_device rt700_1_adr[] = { .adr = 0x000110025D070000, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt700" } }; @@ -115,6 +116,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { .adr = 0x000020025D071100, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt711" } }; @@ -123,6 +125,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt1308-1" } }; @@ -131,6 +134,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "rt1308-1" } }; @@ -139,6 +143,7 @@ static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { .adr = 0x000220025D130800, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "rt1308-2" } }; @@ -147,6 +152,7 @@ static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { .adr = 0x000320025D071500, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt715" } }; @@ -155,6 +161,7 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { .adr = 0x000030025D071101, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt711" } }; @@ -163,6 +170,7 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { .adr = 0x000131025D131601, /* unique ID is set for some reason */ .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "rt1316-1" } }; @@ -171,6 +179,7 @@ static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { .adr = 0x000230025D131601, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "rt1316-2" } }; @@ -179,6 +188,7 @@ static const struct snd_soc_acpi_adr_device rt714_3_adr[] = { .adr = 0x000330025D071401, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt714" } }; diff --git a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c index 7d61e0da808b..b80f032a8b76 100644 --- a/sound/soc/intel/common/soc-acpi-intel-cnl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-cnl-match.c @@ -39,6 +39,7 @@ static const struct snd_soc_acpi_adr_device rt5682_2_adr[] = { .adr = 0x000220025D568200, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt5682" } }; diff --git a/sound/soc/intel/common/soc-acpi-intel-icl-match.c b/sound/soc/intel/common/soc-acpi-intel-icl-match.c index ebe13197410f..9a529a785288 100644 --- a/sound/soc/intel/common/soc-acpi-intel-icl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-icl-match.c @@ -59,6 +59,7 @@ static const struct snd_soc_acpi_adr_device rt700_0_adr[] = { .adr = 0x000010025D070000, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt700" } }; @@ -76,6 +77,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { .adr = 0x000020025D071100, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt711" } }; @@ -84,6 +86,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt1308-1" } }; @@ -92,6 +95,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "rt1308-1" } }; @@ -100,6 +104,7 @@ static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { .adr = 0x000220025D130800, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "rt1308-2" } }; @@ -108,6 +113,7 @@ static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { .adr = 0x000320025D071500, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt715" } }; diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 6816847bee40..76f4eaf684b0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -40,6 +40,7 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { .adr = 0x000020025D071100, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt711" } }; @@ -48,11 +49,13 @@ static const struct snd_soc_acpi_adr_device rt1308_1_dual_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "rt1308-1" }, { .adr = 0x000122025D130800, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "rt1308-2" } }; @@ -61,6 +64,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt1308-1" } }; @@ -69,6 +73,7 @@ static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { .adr = 0x000120025D130800, .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "rt1308-1" } }; @@ -77,6 +82,7 @@ static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { .adr = 0x000220025D130800, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "rt1308-2" } }; @@ -85,6 +91,7 @@ static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { .adr = 0x000320025D071500, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt715" } }; @@ -93,11 +100,13 @@ static const struct snd_soc_acpi_adr_device mx8373_1_adr[] = { .adr = 0x000123019F837300, .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "Right" }, { .adr = 0x000127019F837300, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "Left" } }; @@ -106,6 +115,7 @@ static const struct snd_soc_acpi_adr_device rt5682_0_adr[] = { .adr = 0x000021025D568200, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt5682" } }; @@ -114,6 +124,7 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = { .adr = 0x000030025D071101, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt711" } }; @@ -122,6 +133,7 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group1_adr[] = { .adr = 0x000131025D131601, /* unique ID is set for some reason */ .num_endpoints = 1, .endpoints = &spk_l_endpoint, + .name_prefix = "rt1316-1" } }; @@ -130,6 +142,7 @@ static const struct snd_soc_acpi_adr_device rt1316_2_group1_adr[] = { .adr = 0x000230025D131601, .num_endpoints = 1, .endpoints = &spk_r_endpoint, + .name_prefix = "rt1316-2" } }; @@ -138,6 +151,7 @@ static const struct snd_soc_acpi_adr_device rt714_3_adr[] = { .adr = 0x000330025D071401, .num_endpoints = 1, .endpoints = &single_endpoint, + .name_prefix = "rt714" } }; -- cgit From 23c8aa3ebabf0008c1d05c38e9723e5deb720ba0 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Sep 2020 11:05:12 +0300 Subject: ASoC: Intel: sof_sdw: remove hard-coded codec_conf table Now that the ACPI machine params provide all the information needed, allocate the card codec_conf dynamically and set .dlc and .prefix_name. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923080514.3242858-5-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 149 +++++++++++++++++++-------------------- 1 file changed, 73 insertions(+), 76 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 79c3c19317fe..7c15a8d7557b 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -136,75 +136,6 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = { {} }; -static struct snd_soc_codec_conf codec_conf[] = { - { - .dlc = COMP_CODEC_CONF("sdw:0:25d:711:0"), - .name_prefix = "rt711", - }, - { - .dlc = COMP_CODEC_CONF("sdw:0:25d:711:1"), - .name_prefix = "rt711", - }, - /* rt1308 w/ I2S connection */ - { - .dlc = COMP_CODEC_CONF("i2c-10EC1308:00"), - .name_prefix = "rt1308-1", - }, - /* rt1308 left on link 1 */ - { - .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0"), - .name_prefix = "rt1308-1", - }, - /* two 1308s on link1 with different unique id */ - { - .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0:0"), - .name_prefix = "rt1308-1", - }, - { - .dlc = COMP_CODEC_CONF("sdw:1:25d:1308:0:2"), - .name_prefix = "rt1308-2", - }, - /* rt1308 right on link 2 */ - { - .dlc = COMP_CODEC_CONF("sdw:2:25d:1308:0"), - .name_prefix = "rt1308-2", - }, - { - .dlc = COMP_CODEC_CONF("sdw:3:25d:715:0"), - .name_prefix = "rt715", - }, - /* two MAX98373s on link1 with different unique id */ - { - .dlc = COMP_CODEC_CONF("sdw:1:19f:8373:0:3"), - .name_prefix = "Right", - }, - { - .dlc = COMP_CODEC_CONF("sdw:1:19f:8373:0:7"), - .name_prefix = "Left", - }, - { - .dlc = COMP_CODEC_CONF("sdw:0:25d:5682:0"), - .name_prefix = "rt5682", - }, - /* rt5682 on link2 */ - { - .dlc = COMP_CODEC_CONF("sdw:2:25d:5682:0"), - .name_prefix = "rt5682", - }, - { - .dlc = COMP_CODEC_CONF("sdw:1:25d:1316:1"), - .name_prefix = "rt1316-1", - }, - { - .dlc = COMP_CODEC_CONF("sdw:2:25d:1316:1"), - .name_prefix = "rt1316-2", - }, - { - .dlc = COMP_CODEC_CONF("sdw:3:25d:714:1"), - .name_prefix = "rt714", - }, -}; - static struct snd_soc_dai_link_component dmic_component[] = { { .name = "dmic-codec", @@ -538,10 +469,19 @@ static bool is_unique_device(const struct snd_soc_acpi_link_adr *link, static int create_codec_dai_name(struct device *dev, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link_component *codec, - int offset) + int offset, + struct snd_soc_codec_conf *codec_conf, + int codec_count, + int *codec_conf_index) { int i; + /* sanity check */ + if (*codec_conf_index + link->num_adr > codec_count) { + dev_err(dev, "codec_conf: out-of-bounds access requested\n"); + return -EINVAL; + } + for (i = 0; i < link->num_adr; i++) { unsigned int sdw_version, unique_id, mfg_id; unsigned int link_id, part_id, class_id; @@ -583,6 +523,11 @@ static int create_codec_dai_name(struct device *dev, codec[comp_index].dai_name = codec_info_list[codec_index].dai_name; + + codec_conf[*codec_conf_index].dlc = codec[comp_index]; + codec_conf[*codec_conf_index].name_prefix = link->adr_d[i].name_prefix; + + ++*codec_conf_index; } return 0; @@ -701,7 +646,10 @@ static int create_sdw_dailink(struct device *dev, int *be_index, int sdw_be_num, int sdw_cpu_dai_num, struct snd_soc_dai_link_component *cpus, const struct snd_soc_acpi_link_adr *link, - int *cpu_id, bool *group_generated) + int *cpu_id, bool *group_generated, + struct snd_soc_codec_conf *codec_conf, + int codec_count, + int *codec_conf_index) { const struct snd_soc_acpi_link_adr *link_next; struct snd_soc_dai_link_component *codecs; @@ -739,7 +687,8 @@ static int create_sdw_dailink(struct device *dev, int *be_index, if (cpu_dai_id[i] != ffs(link_next->mask) - 1) continue; - ret = create_codec_dai_name(dev, link_next, codecs, codec_idx); + ret = create_codec_dai_name(dev, link_next, codecs, codec_idx, + codec_conf, codec_count, codec_conf_index); if (ret < 0) return ret; @@ -836,6 +785,42 @@ static inline int get_next_be_id(struct snd_soc_dai_link *links, #define IDISP_CODEC_MASK 0x4 +static int sof_card_codec_conf_alloc(struct device *dev, + struct snd_soc_acpi_mach_params *mach_params, + struct snd_soc_codec_conf **codec_conf, + int *codec_conf_count) +{ + const struct snd_soc_acpi_link_adr *adr_link; + struct snd_soc_codec_conf *c_conf; + int num_codecs = 0; + int i; + + adr_link = mach_params->links; + if (!adr_link) + return -EINVAL; + + /* generate DAI links by each sdw link */ + for (; adr_link->num_adr; adr_link++) { + for (i = 0; i < adr_link->num_adr; i++) { + if (!adr_link->adr_d[i].name_prefix) { + dev_err(dev, "codec 0x%llx does not have a name prefix\n", + adr_link->adr_d[i].adr); + return -EINVAL; + } + } + num_codecs += adr_link->num_adr; + } + + c_conf = devm_kzalloc(dev, num_codecs * sizeof(*c_conf), GFP_KERNEL); + if (!c_conf) + return -ENOMEM; + + *codec_conf = c_conf; + *codec_conf_count = num_codecs; + + return 0; +} + static int sof_card_dai_links_create(struct device *dev, struct snd_soc_acpi_mach *mach, struct snd_soc_card *card) @@ -847,6 +832,9 @@ static int sof_card_dai_links_create(struct device *dev, struct snd_soc_acpi_mach_params *mach_params; const struct snd_soc_acpi_link_adr *adr_link; struct snd_soc_dai_link_component *cpus; + struct snd_soc_codec_conf *codec_conf; + int codec_conf_count; + int codec_conf_index = 0; bool group_generated[SDW_MAX_GROUPS]; int ssp_codec_index, ssp_mask; struct snd_soc_dai_link *links; @@ -859,6 +847,13 @@ static int sof_card_dai_links_create(struct device *dev, int comp_num; int ret; + mach_params = &mach->mach_params; + + /* allocate codec conf, will be populated when dailinks are created */ + ret = sof_card_codec_conf_alloc(dev, mach_params, &codec_conf, &codec_conf_count); + if (ret < 0) + return ret; + /* reset amp_num to ensure amp_num++ starts from 0 in each probe */ for (i = 0; i < ARRAY_SIZE(codec_info_list); i++) codec_info_list[i].amp_num = 0; @@ -879,7 +874,6 @@ static int sof_card_dai_links_create(struct device *dev, ssp_num = ssp_codec_index >= 0 ? hweight_long(ssp_mask) : 0; comp_num = hdmi_num + ssp_num; - mach_params = &mach->mach_params; ret = get_sdw_dailink_info(mach_params->links, &sdw_be_num, &sdw_cpu_dai_num); if (ret < 0) { @@ -943,7 +937,9 @@ static int sof_card_dai_links_create(struct device *dev, ret = create_sdw_dailink(dev, &be_id, links, sdw_be_num, sdw_cpu_dai_num, cpus, adr_link, - &cpu_id, group_generated); + &cpu_id, group_generated, + codec_conf, codec_conf_count, + &codec_conf_index); if (ret < 0) { dev_err(dev, "failed to create dai link %d", be_id); return -ENOMEM; @@ -1074,6 +1070,9 @@ DMIC: card->dai_link = links; card->num_links = num_links; + card->codec_conf = codec_conf; + card->num_configs = codec_conf_count; + return 0; } @@ -1100,8 +1099,6 @@ static struct snd_soc_card card_sof_sdw = { .name = "soundwire", .owner = THIS_MODULE, .late_probe = sof_sdw_card_late_probe, - .codec_conf = codec_conf, - .num_configs = ARRAY_SIZE(codec_conf), }; static int mc_probe(struct platform_device *pdev) -- cgit From fbcc27d18115f5e2bdad15a087831190c3f05d9b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Sep 2020 11:05:13 +0300 Subject: ASoC: Intel: sof_sdw_rt700: add codec prefix Somehow for this codec we never used any prefix for the controls, likely because the test platform has a single SoundWire device. Follow the convention and use the codec prefix across the board to avoid possible conflicts. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923080514.3242858-6-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt700.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw_rt700.c b/sound/soc/intel/boards/sof_sdw_rt700.c index bff69cfe27f4..21e7e4a81779 100644 --- a/sound/soc/intel/boards/sof_sdw_rt700.c +++ b/sound/soc/intel/boards/sof_sdw_rt700.c @@ -23,9 +23,9 @@ static const struct snd_soc_dapm_widget rt700_widgets[] = { static const struct snd_soc_dapm_route rt700_map[] = { /* Headphones */ - { "Headphones", NULL, "HP" }, - { "Speaker", NULL, "SPK" }, - { "MIC2", NULL, "AMIC" }, + { "Headphones", NULL, "rt700 HP" }, + { "Speaker", NULL, "rt700 SPK" }, + { "rt700 MIC2", NULL, "AMIC" }, }; static const struct snd_kcontrol_new rt700_controls[] = { -- cgit From e787f5b5b14061bf76518d780b9bc0e9e7dd2739 Mon Sep 17 00:00:00 2001 From: Bard Liao Date: Wed, 23 Sep 2020 11:05:14 +0300 Subject: ASoC: Intel: add support for new SoundWire hardware layout on TGL The creativity of hardware folks is endless, with a complete permutation of rt711 (was link0 now link1), rt1308 (was link1 now link2) and rt715 (was link3 now link0). Someday we will get all this information from platform firmware, for now let's add the mapping table. Signed-off-by: Bard Liao Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Guennadi Liakhovetski Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923080514.3242858-7-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 9 ++++ sound/soc/intel/common/soc-acpi-intel-tgl-match.c | 53 +++++++++++++++++++++++ 2 files changed, 62 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 7c15a8d7557b..7fc6731aeb97 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -48,6 +48,15 @@ static int sof_sdw_quirk_cb(const struct dmi_system_id *id) } static const struct dmi_system_id sof_sdw_quirk_table[] = { + { + .callback = sof_sdw_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0A3E") + }, + .driver_data = (void *)(SOF_RT711_JD_SRC_JD2 | + SOF_RT715_DAI_ID_FIX), + }, { .callback = sof_sdw_quirk_cb, .matches = { diff --git a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c index 76f4eaf684b0..9f243e60b95c 100644 --- a/sound/soc/intel/common/soc-acpi-intel-tgl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-tgl-match.c @@ -44,6 +44,15 @@ static const struct snd_soc_acpi_adr_device rt711_0_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt711_1_adr[] = { + { + .adr = 0x000120025D071100, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt711" + } +}; + static const struct snd_soc_acpi_adr_device rt1308_1_dual_adr[] = { { .adr = 0x000120025D130800, @@ -68,6 +77,15 @@ static const struct snd_soc_acpi_adr_device rt1308_1_single_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt1308_2_single_adr[] = { + { + .adr = 0x000220025D130800, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt1308-1" + } +}; + static const struct snd_soc_acpi_adr_device rt1308_1_group1_adr[] = { { .adr = 0x000120025D130800, @@ -86,6 +104,15 @@ static const struct snd_soc_acpi_adr_device rt1308_2_group1_adr[] = { } }; +static const struct snd_soc_acpi_adr_device rt715_0_adr[] = { + { + .adr = 0x000021025D071500, + .num_endpoints = 1, + .endpoints = &single_endpoint, + .name_prefix = "rt715" + } +}; + static const struct snd_soc_acpi_adr_device rt715_3_adr[] = { { .adr = 0x000320025D071500, @@ -235,6 +262,25 @@ static const struct snd_soc_acpi_link_adr tgl_3_in_1_mono_amp[] = { {} }; +static const struct snd_soc_acpi_link_adr tgl_sdw_rt711_link1_rt1308_link2_rt715_link0[] = { + { + .mask = BIT(1), + .num_adr = ARRAY_SIZE(rt711_1_adr), + .adr_d = rt711_1_adr, + }, + { + .mask = BIT(2), + .num_adr = ARRAY_SIZE(rt1308_2_single_adr), + .adr_d = rt1308_2_single_adr, + }, + { + .mask = BIT(0), + .num_adr = ARRAY_SIZE(rt715_0_adr), + .adr_d = rt715_0_adr, + }, + {} +}; + static const struct snd_soc_acpi_link_adr tgl_3_in_1_sdca[] = { { .mask = BIT(0), @@ -295,6 +341,13 @@ EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_tgl_machines); /* this table is used when there is no I2S codec present */ struct snd_soc_acpi_mach snd_soc_acpi_intel_tgl_sdw_machines[] = { + { + .link_mask = 0x7, + .links = tgl_sdw_rt711_link1_rt1308_link2_rt715_link0, + .drv_name = "sof_sdw", + .sof_fw_filename = "sof-tgl.ri", + .sof_tplg_filename = "sof-tgl-rt715-rt711-rt1308-mono.tplg", + }, { .link_mask = 0xF, /* 4 active links required */ .links = tgl_3_in_1_default, -- cgit From 7cc3b56f7324ae120cf3ce57cf0366398eb02f60 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 23 Sep 2020 11:05:09 +0300 Subject: ASOC: Intel: sof_sdw: restore playback functionality with max98373 amps The Max98373 amplifier provides I/V feedback information, which keeps a DAPM path active even when there is no playback happening. This prevents entry in low-power mode. Rather than adding new controls and require UCM/user interaction, the method previously applied is to enable/disable the Speaker pin during the dailink trigger operations. Recent changes in the SoundWire stream management moved the stream trigger to the dailink trigger. This change removed the Maxim-specific pin handling and resulted in a regression. This patch restores functionality by combining the SoundWire stream trigger with the pin enable/disable. Fixes: ae3a3918edf57 ('ASoC: Intel: sof_sdw: add dailink .trigger callback') Fixes: 06998d49bcac8 ('ASoC: Intel: sof_sdw: add dailink .prepare and .hw_free callback') Signed-off-by: Rander Wang Signed-off-by: Pierre-Louis Bossart Reviewed-by: Guennadi Liakhovetski Reviewed-by: Ranjani Sridharan Reviewed-by: Keyon Jie Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923080514.3242858-2-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 6 +++--- sound/soc/intel/boards/sof_sdw_common.h | 3 +++ sound/soc/intel/boards/sof_sdw_max98373.c | 36 ++++++++++++++++++++++++++++++- 3 files changed, 41 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index 7fc6731aeb97..b56df04775c2 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -165,7 +165,7 @@ int sdw_startup(struct snd_pcm_substream *substream) return sdw_startup_stream(substream); } -static int sdw_prepare(struct snd_pcm_substream *substream) +int sdw_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct sdw_stream_runtime *sdw_stream; @@ -184,7 +184,7 @@ static int sdw_prepare(struct snd_pcm_substream *substream) return sdw_prepare_stream(sdw_stream); } -static int sdw_trigger(struct snd_pcm_substream *substream, int cmd) +int sdw_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct sdw_stream_runtime *sdw_stream; @@ -224,7 +224,7 @@ static int sdw_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static int sdw_hw_free(struct snd_pcm_substream *substream) +int sdw_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct sdw_stream_runtime *sdw_stream; diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 6a5d46589baf..f3cb6796363e 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -79,6 +79,9 @@ struct mc_private { extern unsigned long sof_sdw_quirk; int sdw_startup(struct snd_pcm_substream *substream); +int sdw_prepare(struct snd_pcm_substream *substream); +int sdw_trigger(struct snd_pcm_substream *substream, int cmd); +int sdw_hw_free(struct snd_pcm_substream *substream); void sdw_shutdown(struct snd_pcm_substream *substream); /* generic HDMI support */ diff --git a/sound/soc/intel/boards/sof_sdw_max98373.c b/sound/soc/intel/boards/sof_sdw_max98373.c index 905582aaf58c..cfdf970c5800 100644 --- a/sound/soc/intel/boards/sof_sdw_max98373.c +++ b/sound/soc/intel/boards/sof_sdw_max98373.c @@ -55,9 +55,43 @@ static int spk_init(struct snd_soc_pcm_runtime *rtd) return ret; } +static int max98373_sdw_trigger(struct snd_pcm_substream *substream, int cmd) +{ + int ret; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + /* enable max98373 first */ + ret = max98373_trigger(substream, cmd); + if (ret < 0) + break; + + ret = sdw_trigger(substream, cmd); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = sdw_trigger(substream, cmd); + if (ret < 0) + break; + + ret = max98373_trigger(substream, cmd); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + static const struct snd_soc_ops max_98373_sdw_ops = { .startup = sdw_startup, - .trigger = max98373_trigger, + .prepare = sdw_prepare, + .trigger = max98373_sdw_trigger, + .hw_free = sdw_hw_free, .shutdown = sdw_shutdown, }; -- cgit From a3f18f82a2baf7b4fe434b96ad644a541e6c8c12 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Wed, 23 Sep 2020 10:29:39 +0300 Subject: ASoC: Intel: hda_dsp_common: use static function in conditional block cppcheck reports the following warning: sound/soc/intel/boards/hda_dsp_common.c:17:0: style: The function 'hda_dsp_hdmi_pcm_handle' is never used. [unusedFunction] Fix by moving to static inside compilation block. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Jaska Uimonen Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200923072939.3100468-1-kai.vehmanen@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/hda_dsp_common.c | 7 ++++--- sound/soc/intel/boards/hda_dsp_common.h | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/hda_dsp_common.c b/sound/soc/intel/boards/hda_dsp_common.c index 244b57fba64c..91ad2a0ad1ce 100644 --- a/sound/soc/intel/boards/hda_dsp_common.c +++ b/sound/soc/intel/boards/hda_dsp_common.c @@ -10,12 +10,14 @@ #include "hda_dsp_common.h" +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) + /* * Search card topology and return PCM device number * matching Nth HDMI device (zero-based index). */ -struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, - int hdmi_idx) +static struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, + int hdmi_idx) { struct snd_soc_pcm_runtime *rtd; struct snd_pcm *spcm; @@ -34,7 +36,6 @@ struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, return NULL; } -#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) /* * Search card topology and register HDMI PCM related controls * to codec driver. diff --git a/sound/soc/intel/boards/hda_dsp_common.h b/sound/soc/intel/boards/hda_dsp_common.h index 727edd256962..ea4ae9285cf0 100644 --- a/sound/soc/intel/boards/hda_dsp_common.h +++ b/sound/soc/intel/boards/hda_dsp_common.h @@ -15,9 +15,6 @@ #include #include "../../codecs/hdac_hda.h" -struct snd_pcm *hda_dsp_hdmi_pcm_handle(struct snd_soc_card *card, - int hdmi_idx); - #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) int hda_dsp_hdmi_build_controls(struct snd_soc_card *card, struct snd_soc_component *comp); -- cgit From 5d0b9dfe0de26b8c4242145cbf7de3a5a2e98293 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 23 Sep 2020 08:25:56 -0500 Subject: ASoC: tas2770: Add shutdown capability via a GPIO Add the hardware shutdown mechanism to shutdown and wake up the device via a GPIO. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200923132600.10652-2-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 53 ++++++++++++++++++++++++++++++++++------------ sound/soc/codecs/tas2770.h | 1 + 2 files changed, 40 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 386aaa11fa08..9f7363927c50 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -79,28 +79,42 @@ static int tas2770_set_bias_level(struct snd_soc_component *component, #ifdef CONFIG_PM static int tas2770_codec_suspend(struct snd_soc_component *component) { - int ret; + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret = 0; - ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_SHUTDOWN); - if (ret < 0) - return ret; + if (tas2770->sdz_gpio) { + gpiod_set_value_cansleep(tas2770->sdz_gpio, 0); + } else { + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_SHUTDOWN); + if (ret < 0) + return ret; - return 0; + ret = 0; + } + + return ret; } static int tas2770_codec_resume(struct snd_soc_component *component) { - int ret; + struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); + int ret = 0; - ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, - TAS2770_PWR_CTRL_MASK, - TAS2770_PWR_CTRL_ACTIVE); - if (ret < 0) - return ret; + if (tas2770->sdz_gpio) { + gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); + } else { + ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, + TAS2770_PWR_CTRL_MASK, + TAS2770_PWR_CTRL_ACTIVE); + if (ret < 0) + return ret; - return 0; + ret = 0; + } + + return ret; } #else #define tas2770_codec_suspend NULL @@ -498,6 +512,9 @@ static int tas2770_codec_probe(struct snd_soc_component *component) tas2770->component = component; + if (tas2770->sdz_gpio) + gpiod_set_value_cansleep(tas2770->sdz_gpio, 1); + tas2770_reset(tas2770); return 0; @@ -650,6 +667,14 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) tas2770->v_sense_slot = 2; } + tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(tas2770->sdz_gpio)) { + if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tas2770->sdz_gpio = NULL; + } + return 0; } diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index 07e3556fc702..b3fc4a487033 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -134,6 +134,7 @@ struct tas2770_priv { int power_state; int asi_format; struct gpio_desc *reset_gpio; + struct gpio_desc *sdz_gpio; int sampling_rate; int channel_size; int slot_width; -- cgit From c0a30e2e07e35f219666788c8549156eb8d74105 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 23 Sep 2020 08:25:57 -0500 Subject: ASoC: tas2770: Set regcache when shutting down and waking device Set the regcache to cache data and mark cache as dirty when the device is shutdown when suspend is called. When the device is woken up then sync the cache and set to not caching the data. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200923132600.10652-3-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 9f7363927c50..c42e653cd653 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -82,14 +82,20 @@ static int tas2770_codec_suspend(struct snd_soc_component *component) struct tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component); int ret = 0; + regcache_cache_only(tas2770->regmap, true); + regcache_mark_dirty(tas2770->regmap); + if (tas2770->sdz_gpio) { gpiod_set_value_cansleep(tas2770->sdz_gpio, 0); } else { ret = snd_soc_component_update_bits(component, TAS2770_PWR_CTRL, TAS2770_PWR_CTRL_MASK, TAS2770_PWR_CTRL_SHUTDOWN); - if (ret < 0) + if (ret < 0) { + regcache_cache_only(tas2770->regmap, false); + regcache_sync(tas2770->regmap); return ret; + } ret = 0; } @@ -110,11 +116,11 @@ static int tas2770_codec_resume(struct snd_soc_component *component) TAS2770_PWR_CTRL_ACTIVE); if (ret < 0) return ret; - - ret = 0; } - return ret; + regcache_cache_only(tas2770->regmap, false); + + return regcache_sync(tas2770->regmap); } #else #define tas2770_codec_suspend NULL -- cgit From dd7d9052064b4bda94a89dbc1618927319602366 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 23 Sep 2020 08:25:59 -0500 Subject: ASoC: tas2770: Remove ti,asi-format code Remove the code to support the asi-format binding property. The code does nothing except read the property and set a variable. No additional action is taken except to reset the variable. The property is supposed to set the rising or falling RX edge detection of the SBCLK but this edge detection is done by checking the DAI_FMT_INV_MASK. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200923132600.10652-5-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 11 ----------- sound/soc/codecs/tas2770.h | 1 - 2 files changed, 12 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index c42e653cd653..c7a6f7e8200c 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -391,8 +391,6 @@ static int tas2770_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) if (ret < 0) return ret; - tas2770->asi_format = fmt; - return 0; } @@ -646,15 +644,6 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770) { int rc = 0; - rc = fwnode_property_read_u32(dev->fwnode, "ti,asi-format", - &tas2770->asi_format); - if (rc) { - dev_info(tas2770->dev, "Property %s is missing setting default slot\n", - "ti,asi-format"); - - tas2770->asi_format = 0; - } - rc = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", &tas2770->i_sense_slot); if (rc) { diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index b3fc4a487033..856a7c5cff5a 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -132,7 +132,6 @@ struct tas2770_priv { struct regmap *regmap; struct snd_soc_component *component; int power_state; - int asi_format; struct gpio_desc *reset_gpio; struct gpio_desc *sdz_gpio; int sampling_rate; -- cgit From 3121420cf9b4db7f2bafcdc0e562f60779bf365d Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 23 Sep 2020 08:26:00 -0500 Subject: ASoC: tas2770: Remove unused variables Remove unused variables in the private struct and the code as these variables are initially set and then there is no additional code utilizing these variables. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20200923132600.10652-6-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/tas2770.c | 11 ----------- sound/soc/codecs/tas2770.h | 8 ++------ 2 files changed, 2 insertions(+), 17 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index c7a6f7e8200c..a91a0a31e74d 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -249,8 +249,6 @@ static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth) if (ret < 0) return ret; - tas2770->channel_size = bitwidth; - ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG5, TAS2770_TDM_CFG_REG5_VSNS_MASK | TAS2770_TDM_CFG_REG5_50_MASK, @@ -312,7 +310,6 @@ static int tas2770_set_samplerate(struct tas2770_priv *tas2770, int samplerate) if (ret < 0) return ret; - tas2770->sampling_rate = samplerate; return 0; } @@ -400,8 +397,6 @@ static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct snd_soc_component *component = dai->component; - struct tas2770_priv *tas2770 = - snd_soc_component_get_drvdata(component); int left_slot, right_slot; int ret; @@ -466,7 +461,6 @@ static int tas2770_set_dai_tdm_slot(struct snd_soc_dai *dai, if (ret < 0) return ret; - tas2770->slot_width = slot_width; return 0; } @@ -688,8 +682,6 @@ static int tas2770_i2c_probe(struct i2c_client *client, i2c_set_clientdata(client, tas2770); dev_set_drvdata(&client->dev, tas2770); - tas2770->power_state = TAS2770_POWER_SHUTDOWN; - tas2770->regmap = devm_regmap_init_i2c(client, &tas2770_i2c_regmap); if (IS_ERR(tas2770->regmap)) { result = PTR_ERR(tas2770->regmap); @@ -716,9 +708,6 @@ static int tas2770_i2c_probe(struct i2c_client *client, } } - tas2770->channel_size = 0; - tas2770->slot_width = 0; - result = tas2770_register_codec(tas2770); if (result) dev_err(tas2770->dev, "Register codec failed.\n"); diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h index 856a7c5cff5a..d156666bcc55 100644 --- a/sound/soc/codecs/tas2770.h +++ b/sound/soc/codecs/tas2770.h @@ -128,15 +128,11 @@ #define ERROR_CLASSD_PWR BIT(5) struct tas2770_priv { - struct device *dev; - struct regmap *regmap; struct snd_soc_component *component; - int power_state; struct gpio_desc *reset_gpio; struct gpio_desc *sdz_gpio; - int sampling_rate; - int channel_size; - int slot_width; + struct regmap *regmap; + struct device *dev; int v_sense_slot; int i_sense_slot; }; -- cgit From 502f389a0fd2d987ea7405cb57c02817328b6f7a Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Thu, 24 Sep 2020 19:10:27 +0300 Subject: ALSA: hda - remove kerneldoc for internal hdac_i915 function Drop the kerneldoc markup for connectivity_check() as it's an static helper function. Fixes the following make W=1 warning: sound/hda/hdac_i915.c:80: warning: Function parameter or member 'i915' not described in 'connectivity_check' sound/hda/hdac_i915.c:80: warning: Function parameter or member 'hdac' not described in 'connectivity_check' Fixes: 7b882fe3e3e8 ('ALSA: hda - handle multiple i915 device instances') Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200924161027.3402260-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_i915.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index d236e497435d..5f0a1aa6ad84 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -73,9 +73,7 @@ void snd_hdac_i915_set_bclk(struct hdac_bus *bus) } EXPORT_SYMBOL_GPL(snd_hdac_i915_set_bclk); -/** - * Returns true if the devices can be connected for audio. - */ +/* returns true if the devices can be connected for audio */ static bool connectivity_check(struct pci_dev *i915, struct pci_dev *hdac) { struct pci_bus *bus_a = i915->bus, *bus_b = hdac->bus; -- cgit From 64952377548517a14d0a6521856dde9fd8356c90 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 25 Sep 2020 10:18:29 +0100 Subject: ASoC: cs47l15: Fix EPOUT->HPOUT1 Mono Mux routing EPOUT is always mono so should have a permanent routing through the HPOUT1 Mono Mux. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20200925091830.7675-1-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l15.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c index a591e7457d11..254f9d96e766 100644 --- a/sound/soc/codecs/cs47l15.c +++ b/sound/soc/codecs/cs47l15.c @@ -1089,6 +1089,7 @@ static const struct snd_soc_dapm_route cs47l15_dapm_routes[] = { { "HPOUT1 Demux", NULL, "OUT1R" }, { "OUT1R", NULL, "HPOUT1 Mono Mux" }, + { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, { "HPOUTL", "HPOUT", "HPOUT1 Demux" }, { "HPOUTR", "HPOUT", "HPOUT1 Demux" }, @@ -1268,7 +1269,6 @@ static irqreturn_t cs47l15_adsp2_irq(int irq, void *data) static const struct snd_soc_dapm_route cs47l15_mono_routes[] = { { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" }, - { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, }; static int cs47l15_component_probe(struct snd_soc_component *component) -- cgit From b03acae25e4a4ad600e91a8737c7eff2efe7d66a Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Fri, 25 Sep 2020 10:18:30 +0100 Subject: ASoC: cs47l35: Fix EPOUT->HPOUT1 Mono Mux routing EPOUT is always mono so should have a permanent routing through the HPOUT1 Mono Mux. Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20200925091830.7675-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/cs47l35.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c index 7f5dd01f40c9..e967609da8a3 100644 --- a/sound/soc/codecs/cs47l35.c +++ b/sound/soc/codecs/cs47l35.c @@ -1305,6 +1305,7 @@ static const struct snd_soc_dapm_route cs47l35_dapm_routes[] = { { "SPKOUTP", NULL, "OUT4L" }, { "OUT1R", NULL, "HPOUT1 Mono Mux" }, + { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, { "HPOUTL", "HPOUT", "HPOUT1 Demux" }, { "HPOUTR", "HPOUT", "HPOUT1 Demux" }, @@ -1550,7 +1551,6 @@ static irqreturn_t cs47l35_adsp2_irq(int irq, void *data) static const struct snd_soc_dapm_route cs47l35_mono_routes[] = { { "HPOUT1 Mono Mux", "HPOUT", "OUT1L" }, - { "HPOUT1 Mono Mux", "EPOUT", "OUT1L" }, }; static int cs47l35_component_probe(struct snd_soc_component *component) -- cgit From 22a16145af824f91014d07f8664114859900b9e6 Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Fri, 18 Sep 2020 18:26:00 +0800 Subject: ASoC: fsl_sai: Instantiate snd_soc_dai_driver Instantiate snd_soc_dai_driver for independent symmetric control. Otherwise the symmetric setting may be overwritten by other instance. Fixes: 08fdf65e37d5 ("ASoC: fsl_sai: Add asynchronous mode support") Signed-off-by: Shengjiu Wang Link: https://lore.kernel.org/r/1600424760-32071-1-git-send-email-shengjiu.wang@nxp.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_sai.c | 19 +++++++++++-------- sound/soc/fsl/fsl_sai.h | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c index cdff739924e2..2ea354dd5434 100644 --- a/sound/soc/fsl/fsl_sai.c +++ b/sound/soc/fsl/fsl_sai.c @@ -694,7 +694,7 @@ static int fsl_sai_dai_probe(struct snd_soc_dai *cpu_dai) return 0; } -static struct snd_soc_dai_driver fsl_sai_dai = { +static struct snd_soc_dai_driver fsl_sai_dai_template = { .probe = fsl_sai_dai_probe, .playback = { .stream_name = "CPU-Playback", @@ -966,12 +966,15 @@ static int fsl_sai_probe(struct platform_device *pdev) return ret; } + memcpy(&sai->cpu_dai_drv, &fsl_sai_dai_template, + sizeof(fsl_sai_dai_template)); + /* Sync Tx with Rx as default by following old DT binding */ sai->synchronous[RX] = true; sai->synchronous[TX] = false; - fsl_sai_dai.symmetric_rates = 1; - fsl_sai_dai.symmetric_channels = 1; - fsl_sai_dai.symmetric_samplebits = 1; + sai->cpu_dai_drv.symmetric_rates = 1; + sai->cpu_dai_drv.symmetric_channels = 1; + sai->cpu_dai_drv.symmetric_samplebits = 1; if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) && of_find_property(np, "fsl,sai-asynchronous", NULL)) { @@ -988,9 +991,9 @@ static int fsl_sai_probe(struct platform_device *pdev) /* Discard all settings for asynchronous mode */ sai->synchronous[RX] = false; sai->synchronous[TX] = false; - fsl_sai_dai.symmetric_rates = 0; - fsl_sai_dai.symmetric_channels = 0; - fsl_sai_dai.symmetric_samplebits = 0; + sai->cpu_dai_drv.symmetric_rates = 0; + sai->cpu_dai_drv.symmetric_channels = 0; + sai->cpu_dai_drv.symmetric_samplebits = 0; } if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && @@ -1020,7 +1023,7 @@ static int fsl_sai_probe(struct platform_device *pdev) regcache_cache_only(sai->regmap, true); ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, - &fsl_sai_dai, 1); + &sai->cpu_dai_drv, 1); if (ret) goto err_pm_disable; diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h index 6aba7d28f5f3..677ecfc1ec68 100644 --- a/sound/soc/fsl/fsl_sai.h +++ b/sound/soc/fsl/fsl_sai.h @@ -180,6 +180,7 @@ struct fsl_sai { unsigned int bclk_ratio; const struct fsl_sai_soc_data *soc_data; + struct snd_soc_dai_driver cpu_dai_drv; struct snd_dmaengine_dai_dma_data dma_params_rx; struct snd_dmaengine_dai_dma_data dma_params_tx; }; -- cgit From 156d0273f62fd437b25dced944788d8784f0212c Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 25 Sep 2020 17:35:51 +0100 Subject: ASoC: qdsp6: add ifdef CONFIG_OF around of_device_id Add ifdef CONFIG_OF around of_device_id table to fix below W=1 compile test warning with !CONFIG_OF: sound/soc/qcom/qdsp6/q6afe-clocks.c:254:34: warning: unused variable 'q6afe_clock_device_id' [-Wunused-const-variable] Fix this warning for across all qdsp6 drivers. Reported-by: kernel test robot Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200925163552.20717-2-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6adm.c | 2 ++ sound/soc/qcom/qdsp6/q6afe-clocks.c | 2 ++ sound/soc/qcom/qdsp6/q6afe-dai.c | 2 ++ sound/soc/qcom/qdsp6/q6afe.c | 2 ++ sound/soc/qcom/qdsp6/q6asm-dai.c | 2 ++ sound/soc/qcom/qdsp6/q6asm.c | 3 +++ sound/soc/qcom/qdsp6/q6core.c | 2 ++ sound/soc/qcom/qdsp6/q6routing.c | 2 ++ 8 files changed, 17 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6adm.c b/sound/soc/qcom/qdsp6/q6adm.c index 2f3ea6beb066..72f29720398c 100644 --- a/sound/soc/qcom/qdsp6/q6adm.c +++ b/sound/soc/qcom/qdsp6/q6adm.c @@ -611,11 +611,13 @@ static int q6adm_remove(struct apr_device *adev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6adm_device_id[] = { { .compatible = "qcom,q6adm" }, {}, }; MODULE_DEVICE_TABLE(of, q6adm_device_id); +#endif static struct apr_driver qcom_q6adm_driver = { .probe = q6adm_probe, diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c index 2967f4546af5..25b409597d51 100644 --- a/sound/soc/qcom/qdsp6/q6afe-clocks.c +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -251,11 +251,13 @@ static int q6afe_clock_dev_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6afe_clock_device_id[] = { { .compatible = "qcom,q6afe-clocks" }, {}, }; MODULE_DEVICE_TABLE(of, q6afe_clock_device_id); +#endif static struct platform_driver q6afe_clock_platform_driver = { .driver = { diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c index d58b86a98114..4e1f101281e7 100644 --- a/sound/soc/qcom/qdsp6/q6afe-dai.c +++ b/sound/soc/qcom/qdsp6/q6afe-dai.c @@ -1689,11 +1689,13 @@ static int q6afe_dai_dev_probe(struct platform_device *pdev) q6afe_dais, ARRAY_SIZE(q6afe_dais)); } +#ifdef CONFIG_OF static const struct of_device_id q6afe_dai_device_id[] = { { .compatible = "qcom,q6afe-dais" }, {}, }; MODULE_DEVICE_TABLE(of, q6afe_dai_device_id); +#endif static struct platform_driver q6afe_dai_platform_driver = { .driver = { diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c index 688878a002a4..0ca1e4aae518 100644 --- a/sound/soc/qcom/qdsp6/q6afe.c +++ b/sound/soc/qcom/qdsp6/q6afe.c @@ -1750,11 +1750,13 @@ static int q6afe_remove(struct apr_device *adev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6afe_device_id[] = { { .compatible = "qcom,q6afe" }, {}, }; MODULE_DEVICE_TABLE(of, q6afe_device_id); +#endif static struct apr_driver qcom_q6afe_driver = { .probe = q6afe_probe, diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c index a1dd31f306ce..c9ac9c1d26c4 100644 --- a/sound/soc/qcom/qdsp6/q6asm-dai.c +++ b/sound/soc/qcom/qdsp6/q6asm-dai.c @@ -1334,11 +1334,13 @@ static int q6asm_dai_probe(struct platform_device *pdev) pdata->dais, pdata->num_dais); } +#ifdef CONFIG_OF static const struct of_device_id q6asm_dai_device_id[] = { { .compatible = "qcom,q6asm-dais" }, {}, }; MODULE_DEVICE_TABLE(of, q6asm_dai_device_id); +#endif static struct platform_driver q6asm_dai_platform_driver = { .driver = { diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index d745a02fcd5f..790abc135223 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -1733,11 +1733,14 @@ static int q6asm_remove(struct apr_device *adev) return 0; } + +#ifdef CONFIG_OF static const struct of_device_id q6asm_device_id[] = { { .compatible = "qcom,q6asm" }, {}, }; MODULE_DEVICE_TABLE(of, q6asm_device_id); +#endif static struct apr_driver qcom_q6asm_driver = { .probe = q6asm_probe, diff --git a/sound/soc/qcom/qdsp6/q6core.c b/sound/soc/qcom/qdsp6/q6core.c index ae314a652efe..5358fefd4210 100644 --- a/sound/soc/qcom/qdsp6/q6core.c +++ b/sound/soc/qcom/qdsp6/q6core.c @@ -354,11 +354,13 @@ static int q6core_exit(struct apr_device *adev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6core_device_id[] = { { .compatible = "qcom,q6core" }, {}, }; MODULE_DEVICE_TABLE(of, q6core_device_id); +#endif static struct apr_driver qcom_q6core_driver = { .probe = q6core_probe, diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c index b12539fae6ed..53185e26fea1 100644 --- a/sound/soc/qcom/qdsp6/q6routing.c +++ b/sound/soc/qcom/qdsp6/q6routing.c @@ -1143,11 +1143,13 @@ static int q6pcm_routing_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF static const struct of_device_id q6pcm_routing_device_id[] = { { .compatible = "qcom,q6adm-routing" }, {}, }; MODULE_DEVICE_TABLE(of, q6pcm_routing_device_id); +#endif static struct platform_driver q6pcm_routing_platform_driver = { .driver = { -- cgit From 5d0576bba9eb37bf07dc58a91568a2332a22fbcd Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 25 Sep 2020 17:35:52 +0100 Subject: ASoC: q6asm: fix kernel doc warnings This patch fixes below kernel doc warnings on not describing all the parmeters sound/soc/qcom/qdsp6/q6asm.c:927: warning: Function parameter or member 'stream_id' not described in 'q6asm_open_write' sound/soc/qcom/qdsp6/q6asm.c:927: warning: Function parameter or member 'is_gapless' not described in 'q6asm_open_write' sound/soc/qcom/qdsp6/q6asm.c:1053: warning: Function parameter or member 'stream_id' not described in 'q6asm_run' Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200925163552.20717-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6asm.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6asm.c b/sound/soc/qcom/qdsp6/q6asm.c index 790abc135223..c547c560cb24 100644 --- a/sound/soc/qcom/qdsp6/q6asm.c +++ b/sound/soc/qcom/qdsp6/q6asm.c @@ -915,9 +915,11 @@ err: /** * q6asm_open_write() - Open audio client for writing * @ac: audio client pointer + * @stream_id: stream id of q6asm session * @format: audio sample format * @codec_profile: compressed format profile * @bits_per_sample: bits per sample + * @is_gapless: flag to indicate if this is a gapless stream * * Return: Will be an negative value on error or zero on success */ @@ -1042,6 +1044,7 @@ static int __q6asm_run(struct audio_client *ac, uint32_t stream_id, * q6asm_run() - start the audio client * * @ac: audio client pointer + * @stream_id: stream id of q6asm session * @flags: flags associated with write * @msw_ts: timestamp msw * @lsw_ts: timestamp lsw -- cgit From 4fefc39894d8975d58b02b4b5f635d20891778ca Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 24 Sep 2020 18:15:17 +0300 Subject: ASoC: SOF: Activate runtime PM with SOF OF device SOF boots the DSP at probe and keeps it up all the time. With this change, after booting if no one is using the DSP the SOF core will turn off the DSP to save power. Reviewed-by: Kai Vehmanen Reviewed-by: Paul Olaru Reviewed-by: Pierre-Louis Bossart Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20200924151518.15841-2-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-of-dev.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index f492c5dfa659..de9acc0e33cb 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -56,7 +56,11 @@ static void sof_of_probe_complete(struct device *dev) /* allow runtime_pm */ pm_runtime_set_autosuspend_delay(dev, SND_SOF_SUSPEND_DELAY_MS); pm_runtime_use_autosuspend(dev); + pm_runtime_set_active(dev); pm_runtime_enable(dev); + + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); } static int sof_of_probe(struct platform_device *pdev) -- cgit From dd759805d6f78262cb9aa63ef37e3bc263ef1978 Mon Sep 17 00:00:00 2001 From: Daniel Baluta Date: Thu, 24 Sep 2020 18:15:18 +0300 Subject: ASoC: SOF: Add .prepare/.complete callbacks Use SOF defined callbacks (snd_sof_prepare/snd_sof_complete) in order to update internal SOF system suspend target. Reviewed-by: Kai Vehmanen Reviewed-by: Paul Olaru Reviewed-by: Pierre-Louis Bossart Signed-off-by: Daniel Baluta Link: https://lore.kernel.org/r/20200924151518.15841-3-daniel.baluta@oss.nxp.com Signed-off-by: Mark Brown --- sound/soc/sof/sof-of-dev.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/sof/sof-of-dev.c b/sound/soc/sof/sof-of-dev.c index de9acc0e33cb..85ff0db88eb7 100644 --- a/sound/soc/sof/sof-of-dev.c +++ b/sound/soc/sof/sof-of-dev.c @@ -46,6 +46,8 @@ static struct sof_dev_desc sof_of_imx8mp_desc = { #endif static const struct dev_pm_ops sof_of_pm = { + .prepare = snd_sof_prepare, + .complete = snd_sof_complete, SET_SYSTEM_SLEEP_PM_OPS(snd_sof_suspend, snd_sof_resume) SET_RUNTIME_PM_OPS(snd_sof_runtime_suspend, snd_sof_runtime_resume, NULL) -- cgit From f95cc5c18c15a425c3dceec48df6b4e27a202dda Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Mon, 21 Sep 2020 01:59:18 +0000 Subject: ASoC: fsl: imx-audmix: Use devm_kcalloc() instead of devm_kzalloc() A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "devm_kcalloc". Signed-off-by: Xu Wang Link: https://lore.kernel.org/r/20200921015918.24157-1-vulab@iscas.ac.cn Signed-off-by: Mark Brown --- sound/soc/fsl/imx-audmix.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/imx-audmix.c b/sound/soc/fsl/imx-audmix.c index 202fb8950078..cbdc0a2c09c5 100644 --- a/sound/soc/fsl/imx-audmix.c +++ b/sound/soc/fsl/imx-audmix.c @@ -185,20 +185,20 @@ static int imx_audmix_probe(struct platform_device *pdev) return -ENOMEM; priv->num_dai = 2 * num_dai; - priv->dai = devm_kzalloc(&pdev->dev, priv->num_dai * + priv->dai = devm_kcalloc(&pdev->dev, priv->num_dai, sizeof(struct snd_soc_dai_link), GFP_KERNEL); if (!priv->dai) return -ENOMEM; priv->num_dai_conf = num_dai; - priv->dai_conf = devm_kzalloc(&pdev->dev, priv->num_dai_conf * + priv->dai_conf = devm_kcalloc(&pdev->dev, priv->num_dai_conf, sizeof(struct snd_soc_codec_conf), GFP_KERNEL); if (!priv->dai_conf) return -ENOMEM; priv->num_dapm_routes = 3 * num_dai; - priv->dapm_routes = devm_kzalloc(&pdev->dev, priv->num_dapm_routes * + priv->dapm_routes = devm_kcalloc(&pdev->dev, priv->num_dapm_routes, sizeof(struct snd_soc_dapm_route), GFP_KERNEL); if (!priv->dapm_routes) @@ -208,7 +208,7 @@ static int imx_audmix_probe(struct platform_device *pdev) struct snd_soc_dai_link_component *dlc; /* for CPU/Codec/Platform x 2 */ - dlc = devm_kzalloc(&pdev->dev, 6 * sizeof(*dlc), GFP_KERNEL); + dlc = devm_kcalloc(&pdev->dev, 6, sizeof(*dlc), GFP_KERNEL); if (!dlc) { dev_err(&pdev->dev, "failed to allocate dai_link\n"); return -ENOMEM; -- cgit From 7e6799d8f87d7ab7ae55a229654d161b5bfae874 Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Fri, 18 Sep 2020 22:24:33 +0530 Subject: ASoC: qcom: lpass-cpu: Enable MI2S BCLK and LRCLK together Update lpass-cpu.c to enable I2S BCLK and LRCLK together. Remove BCLK enable in lpass_cpu_daiops_startup and add in lpass_cpu_daiops_trigger API. Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Mandadapu Link: https://lore.kernel.org/r/1600448073-6709-1-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 1ee6d8b3f550..0718a0fff9cb 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -80,14 +80,6 @@ static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, dev_err(dai->dev, "error in enabling mi2s osr clk: %d\n", ret); return ret; } - - ret = clk_prepare_enable(drvdata->mi2s_bit_clk[dai->driver->id]); - if (ret) { - dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); - clk_disable_unprepare(drvdata->mi2s_osr_clk[dai->driver->id]); - return ret; - } - return 0; } @@ -310,6 +302,14 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, if (ret) dev_err(dai->dev, "error writing to i2sctl reg: %d\n", ret); + + ret = clk_prepare_enable(drvdata->mi2s_bit_clk[id]); + if (ret) { + dev_err(dai->dev, "error in enabling mi2s bit clk: %d\n", ret); + clk_disable_unprepare(drvdata->mi2s_osr_clk[id]); + return ret; + } + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: -- cgit From d56a7ed2d8f93d95de3f3217c8d563233fde858b Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Sat, 26 Sep 2020 18:18:44 +0100 Subject: ASoC: q6afe-clocks: Fix typo in SPDX Licence Looks like there was a major typo in SPDX Licence version, Not sure how it was missed. This patch is to fix it. Reported-by: Lukas Bulwahn Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200926171844.7792-1-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/qdsp6/q6afe-clocks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/qcom/qdsp6/q6afe-clocks.c b/sound/soc/qcom/qdsp6/q6afe-clocks.c index 25b409597d51..2efc2eaa0424 100644 --- a/sound/soc/qcom/qdsp6/q6afe-clocks.c +++ b/sound/soc/qcom/qdsp6/q6afe-clocks.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-1.0 +// SPDX-License-Identifier: GPL-2.0 // Copyright (c) 2020, Linaro Limited #include -- cgit From 00a0b46c99e26b30ea27c1fb4f4dbdfc6f8b1c49 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:00:40 +0900 Subject: ASoC: soc-dai: add mark for snd_soc_dai_startup/shutdown() soc_pcm_open() does rollback when failed (A), but, it is almost same as soc_pcm_close(). static int soc_pcm_open(xxx) { ... if (ret < 0) goto xxx_err; ... return 0; ^ config_err: | ... | rtd_startup_err: (A) ... | component_err: | ... v return ret; } The difference is soc_pcm_close() is for all dai/component/substream, rollback is for succeeded part only. This kind of duplicated code can be a hotbed of bugs, thus, we want to share soc_pcm_close() and rollback. Now, soc_pcm_open/close() are handling => 1) snd_soc_dai_startup/shutdown() 2) snd_soc_link_startup/shutdown() 3) snd_soc_component_module_get/put() 4) snd_soc_component_open/close() 5) pm_runtime_put/get() This patch is for 1) snd_soc_dai_startup/shutdown(). The idea of having bit-flag or counter is not enough for this purpose. For example if one DAI is used for 2xPlaybacks for some reasons, and if 1st Playback was succeeded but 2nd Playback was failed, 2nd Playback rollback doesn't need to call shutdown. But it has succeeded bit-flag or counter via 1st Playback, thus, 2nd Playback rollback will call unneeded shutdown. And 1st Playback's necessary shutdown will not be called, because bit-flag or counter was cleared by wrong 2nd Playback rollback. To avoid such case, this patch marks substream pointer when startup() was succeeded. If rollback needed, it will check rollback flag and marked substream pointer. One note here is that it cares *current* startup() only now. but we might want to check *whole* marked substream in the future. This patch is using macro named "push/pop", so that it can be easily update. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87lfgubwoc.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-dai.c | 21 ++++++++++++++++++++- sound/soc/soc-dapm.c | 4 ++-- sound/soc/soc-pcm.c | 4 ++-- 3 files changed, 24 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c index 0dbd312aad08..4705c3da6280 100644 --- a/sound/soc/soc-dai.c +++ b/sound/soc/soc-dai.c @@ -32,6 +32,14 @@ static inline int _soc_dai_ret(struct snd_soc_dai *dai, return ret; } +/* + * We might want to check substream by using list. + * In such case, we can update these macros. + */ +#define soc_dai_mark_push(dai, substream, tgt) ((dai)->mark_##tgt = substream) +#define soc_dai_mark_pop(dai, substream, tgt) ((dai)->mark_##tgt = NULL) +#define soc_dai_mark_match(dai, substream, tgt) ((dai)->mark_##tgt == substream) + /** * snd_soc_dai_set_sysclk - configure DAI system or master clock. * @dai: DAI @@ -348,15 +356,26 @@ int snd_soc_dai_startup(struct snd_soc_dai *dai, dai->driver->ops->startup) ret = dai->driver->ops->startup(substream, dai); + /* mark substream if succeeded */ + if (ret == 0) + soc_dai_mark_push(dai, substream, startup); + return soc_dai_ret(dai, ret); } void snd_soc_dai_shutdown(struct snd_soc_dai *dai, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + int rollback) { + if (rollback && !soc_dai_mark_match(dai, substream, startup)) + return; + if (dai->driver->ops && dai->driver->ops->shutdown) dai->driver->ops->shutdown(substream, dai); + + /* remove marked substream */ + soc_dai_mark_pop(dai, substream, startup); } snd_pcm_sframes_t snd_soc_dai_delay(struct snd_soc_dai *dai, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 3273161e2787..980f2c330b87 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -3968,14 +3968,14 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w, snd_soc_dapm_widget_for_each_source_path(w, path) { source = path->source->priv; snd_soc_dai_deactivate(source, substream->stream); - snd_soc_dai_shutdown(source, substream); + snd_soc_dai_shutdown(source, substream, 0); } substream->stream = SNDRV_PCM_STREAM_PLAYBACK; snd_soc_dapm_widget_for_each_sink_path(w, path) { sink = path->sink->priv; snd_soc_dai_deactivate(sink, substream->stream); - snd_soc_dai_shutdown(sink, substream); + snd_soc_dai_shutdown(sink, substream, 0); } break; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 4c9d4cd8cf0b..00ed55e40819 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -682,7 +682,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) snd_soc_runtime_deactivate(rtd, substream->stream); for_each_rtd_dais(rtd, i, dai) - snd_soc_dai_shutdown(dai, substream); + snd_soc_dai_shutdown(dai, substream, 0); snd_soc_link_shutdown(substream); @@ -813,7 +813,7 @@ dynamic: config_err: for_each_rtd_dais_rollback(rtd, i, dai) - snd_soc_dai_shutdown(dai, substream); + snd_soc_dai_shutdown(dai, substream, 1); snd_soc_link_shutdown(substream); rtd_startup_err: -- cgit From 6064ed73cd2405d13c252b190ac64c03ab40e4b9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:00:57 +0900 Subject: ASoC: soc-link: add mark for snd_soc_link_startup/shutdown() soc_pcm_open() does rollback when failed (A), but, it is almost same as soc_pcm_close(). static int soc_pcm_open(xxx) { ... if (ret < 0) goto xxx_err; ... return 0; ^ config_err: | ... | rtd_startup_err: (A) ... | component_err: | ... v return ret; } The difference is soc_pcm_close() is for all dai/component/substream, rollback is for succeeded part only. This kind of duplicated code can be a hotbed of bugs, thus, we want to share soc_pcm_close() and rollback. Now, soc_pcm_open/close() are handling 1) snd_soc_dai_startup/shutdown() => 2) snd_soc_link_startup/shutdown() 3) snd_soc_component_module_get/put() 4) snd_soc_component_open/close() 5) pm_runtime_put/get() This patch is for 2) snd_soc_link_startup/shutdown(). The idea of having bit-flag or counter is not enough for this purpose. For example if one DAI is used for 2xPlaybacks for some reasons, and if 1st Playback was succeeded but 2nd Playback was failed, 2nd Playback rollback doesn't need to call shutdown. But it has succeeded bit-flag or counter via 1st Playback, thus, 2nd Playback rollback will call unneeded shutdown. And 1st Playback's necessary shutdown will not be called, because bit-flag or counter was cleared by wrong 2nd Playback rollback. To avoid such case, this patch marks substream pointer when startup() was succeeded. If rollback needed, it will check rollback flag and marked substream pointer. One note here is that it cares *current* startup() only now. but we might want to check *whole* marked substream in the future. This patch is using macro named "push/pop", so that it can be easily update. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87k0webwnv.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-link.c | 21 ++++++++++++++++++++- sound/soc/soc-pcm.c | 4 ++-- 2 files changed, 22 insertions(+), 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-link.c b/sound/soc/soc-link.c index cec70b19863e..2a8881978930 100644 --- a/sound/soc/soc-link.c +++ b/sound/soc/soc-link.c @@ -30,6 +30,14 @@ static inline int _soc_link_ret(struct snd_soc_pcm_runtime *rtd, return ret; } +/* + * We might want to check substream by using list. + * In such case, we can update these macros. + */ +#define soc_link_mark_push(rtd, substream, tgt) ((rtd)->mark_##tgt = substream) +#define soc_link_mark_pop(rtd, substream, tgt) ((rtd)->mark_##tgt = NULL) +#define soc_link_mark_match(rtd, substream, tgt) ((rtd)->mark_##tgt == substream) + int snd_soc_link_init(struct snd_soc_pcm_runtime *rtd) { int ret = 0; @@ -66,16 +74,27 @@ int snd_soc_link_startup(struct snd_pcm_substream *substream) rtd->dai_link->ops->startup) ret = rtd->dai_link->ops->startup(substream); + /* mark substream if succeeded */ + if (ret == 0) + soc_link_mark_push(rtd, substream, startup); + return soc_link_ret(rtd, ret); } -void snd_soc_link_shutdown(struct snd_pcm_substream *substream) +void snd_soc_link_shutdown(struct snd_pcm_substream *substream, + int rollback) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + if (rollback && !soc_link_mark_match(rtd, substream, startup)) + return; + if (rtd->dai_link->ops && rtd->dai_link->ops->shutdown) rtd->dai_link->ops->shutdown(substream); + + /* remove marked substream */ + soc_link_mark_pop(rtd, substream, startup); } int snd_soc_link_prepare(struct snd_pcm_substream *substream) diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 00ed55e40819..45c19643d8e3 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -684,7 +684,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) for_each_rtd_dais(rtd, i, dai) snd_soc_dai_shutdown(dai, substream, 0); - snd_soc_link_shutdown(substream); + snd_soc_link_shutdown(substream, 0); soc_pcm_components_close(substream); @@ -815,7 +815,7 @@ config_err: for_each_rtd_dais_rollback(rtd, i, dai) snd_soc_dai_shutdown(dai, substream, 1); - snd_soc_link_shutdown(substream); + snd_soc_link_shutdown(substream, 1); rtd_startup_err: soc_pcm_components_close(substream); component_err: -- cgit From 51aff91ad123e03d1461ec1d980efd1814dca69e Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:01:04 +0900 Subject: ASoC: soc-component: add mark for soc_pcm_components_open/close() soc_pcm_open() does rollback when failed (A), but, it is almost same as soc_pcm_close(). static int soc_pcm_open(xxx) { ... if (ret < 0) goto xxx_err; ... return 0; ^ config_err: | ... | rtd_startup_err: (A) ... | component_err: | ... v return ret; } The difference is soc_pcm_close() is for all dai/component/substream, rollback is for succeeded part only. This kind of duplicated code can be a hotbed of bugs, thus, we want to share soc_pcm_close() and rollback. Now, soc_pcm_open/close() are handling 1) snd_soc_dai_startup/shutdown() 2) snd_soc_link_startup/shutdown() => 3) snd_soc_component_module_get/put() => 4) snd_soc_component_open/close() 5) pm_runtime_put/get() This patch is for 3) snd_soc_component_module_get/put() 4) snd_soc_component_open/close(). The idea of having bit-flag or counter is not enough for this purpose. For example if one DAI is used for 2xPlaybacks for some reasons, and if 1st Playback was succeeded but 2nd Playback was failed, 2nd Playback rollback doesn't need to call shutdown. But it has succeeded bit-flag or counter via 1st Playback, thus, 2nd Playback rollback will call unneeded shutdown. And 1st Playback's necessary shutdown will not be called, because bit-flag or counter was cleared by wrong 2nd Playback rollback. To avoid such case, this patch marks substream pointer when open() was succeeded. If rollback needed, it will check rollback flag and marked substream pointer. One note here is that it cares *current* open() only now. but we might want to check *whole* marked substream in the future. This patch is using macro named "push/pop", so that it can be easily update. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87imbybwno.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-component.c | 35 +++++++++++++++++++++++++++++++++-- sound/soc/soc-pcm.c | 28 +++++++--------------------- 2 files changed, 40 insertions(+), 23 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 5504b92946e3..147b46e29ff4 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -33,6 +33,14 @@ static inline int _soc_component_ret(struct snd_soc_component *component, return ret; } +/* + * We might want to check substream by using list. + * In such case, we can update these macros. + */ +#define soc_component_mark_push(component, substream, tgt) ((component)->mark_##tgt = substream) +#define soc_component_mark_pop(component, substream, tgt) ((component)->mark_##tgt = NULL) +#define soc_component_mark_match(component, substream, tgt) ((component)->mark_##tgt == substream) + void snd_soc_component_set_aux(struct snd_soc_component *component, struct snd_soc_aux_dev *aux) { @@ -238,6 +246,7 @@ int snd_soc_component_set_jack(struct snd_soc_component *component, EXPORT_SYMBOL_GPL(snd_soc_component_set_jack); int snd_soc_component_module_get(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int upon_open) { int ret = 0; @@ -246,14 +255,25 @@ int snd_soc_component_module_get(struct snd_soc_component *component, !try_module_get(component->dev->driver->owner)) ret = -ENODEV; + /* mark substream if succeeded */ + if (ret == 0) + soc_component_mark_push(component, substream, module); + return soc_component_ret(component, ret); } void snd_soc_component_module_put(struct snd_soc_component *component, - int upon_open) + struct snd_pcm_substream *substream, + int upon_open, int rollback) { + if (rollback && !soc_component_mark_match(component, substream, module)) + return; + if (component->driver->module_get_upon_open == !!upon_open) module_put(component->dev->driver->owner); + + /* remove marked substream */ + soc_component_mark_pop(component, substream, module); } int snd_soc_component_open(struct snd_soc_component *component, @@ -264,17 +284,28 @@ int snd_soc_component_open(struct snd_soc_component *component, if (component->driver->open) ret = component->driver->open(component, substream); + /* mark substream if succeeded */ + if (ret == 0) + soc_component_mark_push(component, substream, open); + return soc_component_ret(component, ret); } int snd_soc_component_close(struct snd_soc_component *component, - struct snd_pcm_substream *substream) + struct snd_pcm_substream *substream, + int rollback) { int ret = 0; + if (rollback && !soc_component_mark_match(component, substream, open)) + return 0; + if (component->driver->close) ret = component->driver->close(component, substream); + /* remove marked substream */ + soc_component_mark_pop(component, substream, open); + return soc_component_ret(component, ret); } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 45c19643d8e3..d9f9c0a243df 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -609,14 +609,11 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream) static int soc_pcm_components_open(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_component *last = NULL; struct snd_soc_component *component; int i, ret = 0; for_each_rtd_components(rtd, i, component) { - last = component; - - ret = snd_soc_component_module_get_when_open(component); + ret = snd_soc_component_module_get_when_open(component, substream); if (ret < 0) { dev_err(component->dev, "ASoC: can't get module %s\n", @@ -626,7 +623,6 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream) ret = snd_soc_component_open(component, substream); if (ret < 0) { - snd_soc_component_module_put_when_close(component); dev_err(component->dev, "ASoC: can't open component %s: %d\n", component->name, ret); @@ -634,32 +630,22 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream) } } - if (ret < 0) { - /* rollback on error */ - for_each_rtd_components(rtd, i, component) { - if (component == last) - break; - - snd_soc_component_close(component, substream); - snd_soc_component_module_put_when_close(component); - } - } - return ret; } -static int soc_pcm_components_close(struct snd_pcm_substream *substream) +static int soc_pcm_components_close(struct snd_pcm_substream *substream, + int rollback) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; int i, r, ret = 0; for_each_rtd_components(rtd, i, component) { - r = snd_soc_component_close(component, substream); + r = snd_soc_component_close(component, substream, rollback); if (r < 0) ret = r; /* use last ret */ - snd_soc_component_module_put_when_close(component); + snd_soc_component_module_put_when_close(component, substream, rollback); } return ret; @@ -686,7 +672,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) snd_soc_link_shutdown(substream, 0); - soc_pcm_components_close(substream); + soc_pcm_components_close(substream, 0); snd_soc_dapm_stream_stop(rtd, substream->stream); @@ -817,7 +803,7 @@ config_err: snd_soc_link_shutdown(substream, 1); rtd_startup_err: - soc_pcm_components_close(substream); + soc_pcm_components_close(substream, 1); component_err: mutex_unlock(&rtd->card->pcm_mutex); -- cgit From 939a5cfb2a5609d2d6f996b5cd853397a82a92b9 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:01:17 +0900 Subject: ASoC: soc-component: add mark for snd_soc_pcm_component_pm_runtime_get/put() soc_pcm_open() does rollback when failed (A), but, it is almost same as soc_pcm_close(). static int soc_pcm_open(xxx) { ... if (ret < 0) goto xxx_err; ... return 0; ^ config_err: | ... | rtd_startup_err: (A) ... | component_err: | ... v return ret; } The difference is soc_pcm_close() is for all dai/component/substream, rollback is for succeeded part only. This kind of duplicated code can be a hotbed of bugs, thus, we want to share soc_pcm_close() and rollback. Now, soc_pcm_open/close() are handling 1) snd_soc_dai_startup/shutdown() 2) snd_soc_link_startup/shutdown() 3) snd_soc_component_module_get/put() 4) snd_soc_component_open/close() => 5) pm_runtime_put/get() This patch is for 5) pm_runtime_put/get(). The idea of having bit-flag or counter is not enough for this purpose. For example if one DAI is used for 2xPlaybacks for some reasons, and if 1st Playback was succeeded but 2nd Playback was failed, 2nd Playback rollback doesn't need to call shutdown. But it has succeeded bit-flag or counter via 1st Playback, thus, 2nd Playback rollback will call unneeded shutdown. And 1st Playback's necessary shutdown will not be called, because bit-flag or counter was cleared by wrong 2nd Playback rollback. To avoid such case, this patch marks substream pointer when get() was succeeded. If rollback needed, it will check rollback flag and marked substream pointer. One note here is that it cares *current* get() only now. but we might want to check *whole* marked substream in the future. This patch is using macro named "push/pop", so that it can be easily update. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87h7ribwnb.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-component.c | 38 ++++++++++++++++++++++++++++++++++++++ sound/soc/soc-compress.c | 30 ++++++++---------------------- sound/soc/soc-pcm.c | 17 ++++++----------- 3 files changed, 52 insertions(+), 33 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c index 147b46e29ff4..728e93f35ffb 100644 --- a/sound/soc/soc-component.c +++ b/sound/soc/soc-component.c @@ -9,6 +9,7 @@ // Kuninori Morimoto // #include +#include #include #define soc_component_ret(dai, ret) _soc_component_ret(dai, __func__, ret) @@ -836,3 +837,40 @@ int snd_soc_pcm_component_trigger(struct snd_pcm_substream *substream, return 0; } + +int snd_soc_pcm_component_pm_runtime_get(struct snd_soc_pcm_runtime *rtd, + void *stream) +{ + struct snd_soc_component *component; + int i, ret; + + for_each_rtd_components(rtd, i, component) { + ret = pm_runtime_get_sync(component->dev); + if (ret < 0 && ret != -EACCES) { + pm_runtime_put_noidle(component->dev); + return soc_component_ret(component, ret); + } + /* mark stream if succeeded */ + soc_component_mark_push(component, stream, pm); + } + + return 0; +} + +void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd, + void *stream, int rollback) +{ + struct snd_soc_component *component; + int i; + + for_each_rtd_components(rtd, i, component) { + if (rollback && !soc_component_mark_match(component, stream, pm)) + continue; + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + /* remove marked stream */ + soc_component_mark_pop(component, stream, pm); + } +} diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c index 415510909a82..3a6a60215e81 100644 --- a/sound/soc/soc-compress.c +++ b/sound/soc/soc-compress.c @@ -73,18 +73,13 @@ static int soc_compr_components_free(struct snd_compr_stream *cstream, static int soc_compr_open(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component = NULL, *save = NULL; + struct snd_soc_component *component = NULL; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); - int ret, i; + int ret; - for_each_rtd_components(rtd, i, component) { - ret = pm_runtime_get_sync(component->dev); - if (ret < 0 && ret != -EACCES) { - pm_runtime_put_noidle(component->dev); - save = component; - goto pm_err; - } - } + ret = snd_soc_pcm_component_pm_runtime_get(rtd, cstream); + if (ret < 0) + goto pm_err; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -113,12 +108,7 @@ machine_err: out: mutex_unlock(&rtd->card->pcm_mutex); pm_err: - for_each_rtd_components(rtd, i, component) { - if (component == save) - break; - pm_runtime_mark_last_busy(component->dev); - pm_runtime_put_autosuspend(component->dev); - } + snd_soc_pcm_component_pm_runtime_put(rtd, cstream, 1); return ret; } @@ -205,10 +195,9 @@ be_err: static int soc_compr_free(struct snd_compr_stream *cstream) { struct snd_soc_pcm_runtime *rtd = cstream->private_data; - struct snd_soc_component *component; struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int stream, i; + int stream; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -237,10 +226,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream) mutex_unlock(&rtd->card->pcm_mutex); - for_each_rtd_components(rtd, i, component) { - pm_runtime_mark_last_busy(component->dev); - pm_runtime_put_autosuspend(component->dev); - } + snd_soc_pcm_component_pm_runtime_put(rtd, cstream, 0); return 0; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index d9f9c0a243df..0934a06a9cc9 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -678,10 +678,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) mutex_unlock(&rtd->card->pcm_mutex); - for_each_rtd_components(rtd, i, component) { - pm_runtime_mark_last_busy(component->dev); - pm_runtime_put_autosuspend(component->dev); - } + snd_soc_pcm_component_pm_runtime_put(rtd, substream, 0); for_each_rtd_components(rtd, i, component) if (!snd_soc_component_active(component)) @@ -708,8 +705,9 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) for_each_rtd_components(rtd, i, component) pinctrl_pm_select_default_state(component->dev); - for_each_rtd_components(rtd, i, component) - pm_runtime_get_sync(component->dev); + ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); + if (ret < 0) + goto pm_err; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -806,11 +804,8 @@ rtd_startup_err: soc_pcm_components_close(substream, 1); component_err: mutex_unlock(&rtd->card->pcm_mutex); - - for_each_rtd_components(rtd, i, component) { - pm_runtime_mark_last_busy(component->dev); - pm_runtime_put_autosuspend(component->dev); - } +pm_err: + snd_soc_pcm_component_pm_runtime_put(rtd, substream, 1); for_each_rtd_components(rtd, i, component) if (!snd_soc_component_active(component)) -- cgit From 140a4532cdb8c44a664e7e871ea5dbaa4c2829bc Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:01:24 +0900 Subject: ASoC: soc-pcm: add soc_pcm_clean() and call it from soc_pcm_open/close() soc_pcm_open() does rollback when failed (A), but, it is almost same as soc_pcm_close(). static int soc_pcm_open(xxx) { ... if (ret < 0) goto xxx_err; ... return 0; ^ config_err: | ... | rtd_startup_err: (A) ... | component_err: | ... v return ret; } The difference is soc_pcm_close() is for all dai/component/substream, rollback is for succeeded part only. This kind of duplicated code can be a hotbed of bugs, thus, we want to share soc_pcm_close() and rollback. Now, soc_pcm_open/close() are handling 1) snd_soc_dai_startup/shutdown() 2) snd_soc_link_startup/shutdown() 3) snd_soc_component_module_get/put() 4) snd_soc_component_open/close() 5) pm_runtime_put/get() Now, 1) to 5) are handled. This patch adds new soc_pcm_clean() and call it from soc_pcm_open() as rollback, and from soc_pcm_close() as normal close handler. One note here is that it don't need to call snd_soc_runtime_deactivate() when rollback case, because it will be called without snd_soc_runtime_activate(). It also don't need to call snd_soc_dapm_stream_stop() when rollback case. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87ft72bwn4.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 69 +++++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 39 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 0934a06a9cc9..7f3e44918f19 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -651,12 +651,7 @@ static int soc_pcm_components_close(struct snd_pcm_substream *substream, return ret; } -/* - * Called by ALSA when a PCM substream is closed. Private data can be - * freed here. The cpu DAI, codec DAI, machine and components are also - * shutdown. - */ -static int soc_pcm_close(struct snd_pcm_substream *substream) +static int soc_pcm_clean(struct snd_pcm_substream *substream, int rollback) { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_component *component; @@ -665,20 +660,22 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); - snd_soc_runtime_deactivate(rtd, substream->stream); + if (!rollback) + snd_soc_runtime_deactivate(rtd, substream->stream); for_each_rtd_dais(rtd, i, dai) - snd_soc_dai_shutdown(dai, substream, 0); + snd_soc_dai_shutdown(dai, substream, rollback); - snd_soc_link_shutdown(substream, 0); + snd_soc_link_shutdown(substream, rollback); - soc_pcm_components_close(substream, 0); + soc_pcm_components_close(substream, rollback); - snd_soc_dapm_stream_stop(rtd, substream->stream); + if (!rollback) + snd_soc_dapm_stream_stop(rtd, substream->stream); mutex_unlock(&rtd->card->pcm_mutex); - snd_soc_pcm_component_pm_runtime_put(rtd, substream, 0); + snd_soc_pcm_component_pm_runtime_put(rtd, substream, rollback); for_each_rtd_components(rtd, i, component) if (!snd_soc_component_active(component)) @@ -687,6 +684,16 @@ static int soc_pcm_close(struct snd_pcm_substream *substream) return 0; } +/* + * Called by ALSA when a PCM substream is closed. Private data can be + * freed here. The cpu DAI, codec DAI, machine and components are also + * shutdown. + */ +static int soc_pcm_close(struct snd_pcm_substream *substream) +{ + return soc_pcm_clean(substream, 0); +} + /* * Called by ALSA when a PCM substream is opened, the runtime->hw record is * then initialized and any private data can be allocated. This also calls @@ -707,17 +714,17 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) - goto pm_err; + goto err; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); ret = soc_pcm_components_open(substream); if (ret < 0) - goto component_err; + goto err; ret = snd_soc_link_startup(substream); if (ret < 0) - goto rtd_startup_err; + goto err; /* startup the audio subsystem */ for_each_rtd_dais(rtd, i, dai) { @@ -726,7 +733,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) dev_err(dai->dev, "ASoC: can't open DAI %s: %d\n", dai->name, ret); - goto config_err; + goto err; } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) @@ -755,18 +762,18 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (!runtime->hw.rates) { printk(KERN_ERR "ASoC: %s <-> %s No matching rates\n", codec_dai_name, cpu_dai_name); - goto config_err; + goto err; } if (!runtime->hw.formats) { printk(KERN_ERR "ASoC: %s <-> %s No matching formats\n", codec_dai_name, cpu_dai_name); - goto config_err; + goto err; } if (!runtime->hw.channels_min || !runtime->hw.channels_max || runtime->hw.channels_min > runtime->hw.channels_max) { printk(KERN_ERR "ASoC: %s <-> %s No matching channels\n", codec_dai_name, cpu_dai_name); - goto config_err; + goto err; } soc_pcm_apply_msb(substream); @@ -776,7 +783,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) if (snd_soc_dai_active(dai)) { ret = soc_pcm_apply_symmetry(substream, dai); if (ret != 0) - goto config_err; + goto err; } } @@ -787,29 +794,13 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.channels_max); pr_debug("ASoC: min rate %d max rate %d\n", runtime->hw.rate_min, runtime->hw.rate_max); - dynamic: - snd_soc_runtime_activate(rtd, substream->stream); - - mutex_unlock(&rtd->card->pcm_mutex); - return 0; - -config_err: - for_each_rtd_dais_rollback(rtd, i, dai) - snd_soc_dai_shutdown(dai, substream, 1); - - snd_soc_link_shutdown(substream, 1); -rtd_startup_err: - soc_pcm_components_close(substream, 1); -component_err: +err: mutex_unlock(&rtd->card->pcm_mutex); -pm_err: - snd_soc_pcm_component_pm_runtime_put(rtd, substream, 1); - for_each_rtd_components(rtd, i, component) - if (!snd_soc_component_active(component)) - pinctrl_pm_select_sleep_state(component->dev); + if (ret < 0) + soc_pcm_clean(substream, 1); return ret; } -- cgit From ce820145a9ec04797a417fcb01b8ff02dcfd9846 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:01:29 +0900 Subject: ASoC: soc-pcm: remove unneeded dev_err() for snd_soc_dai_startup() snd_soc_dai_startup() itself will indicate error message, thus, soc_pcm_open() don't need to handle it. This patch removes it. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87eemmbwmy.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 7f3e44918f19..070fb71fc6f6 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -729,12 +729,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) /* startup the audio subsystem */ for_each_rtd_dais(rtd, i, dai) { ret = snd_soc_dai_startup(dai, substream); - if (ret < 0) { - dev_err(dai->dev, - "ASoC: can't open DAI %s: %d\n", - dai->name, ret); + if (ret < 0) goto err; - } if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) dai->tx_mask = 0; -- cgit From bcae16317bcfa45f6b767cf59e02e9cc72715d27 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Mon, 28 Sep 2020 09:01:36 +0900 Subject: ASoC: soc-pcm: remove unneeded dev_err() for snd_soc_component_module/open() snd_soc_component_module_get(), snd_soc_component_open() itself will indicate error message, thus, soc_pcm_components_open() don't need to handle it. This patch removes these. Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87d026bwms.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 070fb71fc6f6..09e8d703a502 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -614,20 +614,12 @@ static int soc_pcm_components_open(struct snd_pcm_substream *substream) for_each_rtd_components(rtd, i, component) { ret = snd_soc_component_module_get_when_open(component, substream); - if (ret < 0) { - dev_err(component->dev, - "ASoC: can't get module %s\n", - component->name); + if (ret < 0) break; - } ret = snd_soc_component_open(component, substream); - if (ret < 0) { - dev_err(component->dev, - "ASoC: can't open component %s: %d\n", - component->name, ret); + if (ret < 0) break; - } } return ret; -- cgit From 16346a3cf02ebb8906c6a0ba907f83ea62cc874b Mon Sep 17 00:00:00 2001 From: Jack Yu Date: Fri, 25 Sep 2020 16:05:09 -0500 Subject: ASoC: rt715: Add power-up delay to fix dmic pop sound issue. Add 400ms power-up delay recommended to fix pop noise on capture. Reviewed-by: Bard Liao Reviewed-by: Rander Wang Signed-off-by: Jack Yu Signed-off-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20200925210509.83353-1-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715.c | 2 +- sound/soc/codecs/rt715.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c index 099c8bd20006..532c5303e7ab 100644 --- a/sound/soc/codecs/rt715.c +++ b/sound/soc/codecs/rt715.c @@ -9,7 +9,6 @@ */ #include -#include #include #include #include @@ -498,6 +497,7 @@ static int rt715_set_bias_level(struct snd_soc_component *component, regmap_write(rt715->regmap, RT715_SET_AUDIO_POWER_STATE, AC_PWRST_D0); + msleep(RT715_POWER_UP_DELAY_MS); } break; diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index df0f24f9bc0c..d0d0fd2a6fdb 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -210,6 +210,8 @@ enum { RT715_AIFS, }; +#define RT715_POWER_UP_DELAY_MS 400 + int rt715_io_init(struct device *dev, struct sdw_slave *slave); int rt715_init(struct device *dev, struct regmap *sdw_regmap, struct regmap *regmap, struct sdw_slave *slave); -- cgit From 5ec3c854d1a7edb95b20999d480b0c16c717254a Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Mon, 28 Sep 2020 10:43:30 +0300 Subject: ASoC: ti: j721e-evm: Fix compiler warning when CONFIG_OF=n Remove the use of of_match_ptr() macro for of_match_table to fix compiler warning when CONFIG_OF=n: sound/soc/ti/j721e-evm.c:528:34: warning: unused variable 'j721e_audio_of_match' [-Wunused-const-variable] Reported-by: kernel test robot Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20200928074330.13029-1-peter.ujfalusi@ti.com Signed-off-by: Mark Brown --- sound/soc/ti/j721e-evm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/ti/j721e-evm.c b/sound/soc/ti/j721e-evm.c index 29b73303f3fc..a7c0484d44ec 100644 --- a/sound/soc/ti/j721e-evm.c +++ b/sound/soc/ti/j721e-evm.c @@ -895,7 +895,7 @@ static struct platform_driver j721e_soc_driver = { .driver = { .name = "j721e-audio", .pm = &snd_soc_pm_ops, - .of_match_table = of_match_ptr(j721e_audio_of_match), + .of_match_table = j721e_audio_of_match, }, .probe = j721e_soc_probe, }; -- cgit From 9fe0ec27557570e4b17735da6ce265bbe20691ed Mon Sep 17 00:00:00 2001 From: Oder Chiou Date: Mon, 28 Sep 2020 13:39:12 +0800 Subject: ASoC: rt5682: Enable the power of "MICBIAS" and "Vref2" for the DMIC clock The power of "MICBIAS" and "Vref2" was needed while the DMIC clcok was from the PLL output. Signed-off-by: Oder Chiou Link: https://lore.kernel.org/r/20200928053912.16664-1-oder_chiou@realtek.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt5682.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index 93ebf0279b62..a9acce7b6cca 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -1529,16 +1529,35 @@ static int set_dmic_power(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); - unsigned int delay = 50; + unsigned int delay = 50, val; if (rt5682->pdata.dmic_delay) delay = rt5682->pdata.dmic_delay; switch (event) { case SND_SOC_DAPM_POST_PMU: + val = snd_soc_component_read(component, RT5682_GLB_CLK); + val &= RT5682_SCLK_SRC_MASK; + if (val == RT5682_SCLK_SRC_PLL1 || val == RT5682_SCLK_SRC_PLL2) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, + RT5682_PWR_VREF2 | RT5682_PWR_MB, + RT5682_PWR_VREF2 | RT5682_PWR_MB); + /*Add delay to avoid pop noise*/ msleep(delay); break; + + case SND_SOC_DAPM_POST_PMD: + if (!rt5682->jack_type) { + if (!snd_soc_dapm_get_pin_status(w->dapm, "MICBIAS")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_MB, 0); + if (!snd_soc_dapm_get_pin_status(w->dapm, "Vref2")) + snd_soc_component_update_bits(component, + RT5682_PWR_ANLG_1, RT5682_PWR_VREF2, 0); + } + break; } return 0; @@ -1644,7 +1663,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0, set_dmic_clk, SND_SOC_DAPM_PRE_PMU), SND_SOC_DAPM_SUPPLY("DMIC1 Power", RT5682_DMIC_CTRL_1, - RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, SND_SOC_DAPM_POST_PMU), + RT5682_DMIC_1_EN_SFT, 0, set_dmic_power, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), /* Boost */ SND_SOC_DAPM_PGA("BST1 CBJ", SND_SOC_NOPM, -- cgit From d4edae9c508c845d92bd59c60c4089c3addad6a8 Mon Sep 17 00:00:00 2001 From: Lucas Tanure Date: Mon, 28 Sep 2020 12:18:21 +0100 Subject: ASoC: cs4234: Add support for Cirrus Logic CS4234 codec The CS4234 is a highly versatile CODEC that combines 4 channels of high performance analog to digital conversion, 4 channels of high performance digital to analog conversion for audio, and 1 channel of digital to analog conversion to provide a nondelayed audio reference signal to an external Class H tracking power supply. DAC5 is only supported as a 5th audio channel. Tracking Power Supply mode is not currently supported by the driver. In DSP_A mode the slots for DAC1-4 and optionally DAC5 can be set. The codec always claims 4 slots for DAC1-4 and these must be in the same nibble of the mask. The codec has a fixed mapping for ADC slots. In I2S/LJ modes the codec has a fixed mapping for DAC1-4 and ADC1-4. DAC5 is not available in these modes. The MCLK source must be preset to a valid frequency before probe() because it must be running all the time the codec is out of reset. The VA_SEL bit will be set automatically to 3.3v or 5v during probe() based on the reported voltage of the regulator supplying VA. Signed-off-by: Lucas Tanure Signed-off-by: Charles Keepax Signed-off-by: Richard Fitzgerald Link: https://lore.kernel.org/r/20200928111821.26967-2-rf@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 6 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/cs4234.c | 918 ++++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/cs4234.h | 287 +++++++++++++++ 4 files changed, 1213 insertions(+) create mode 100644 sound/soc/codecs/cs4234.c create mode 100644 sound/soc/codecs/cs4234.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index af3b93cdb6c9..a62e0fb467d9 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -64,6 +64,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS42L52 imply SND_SOC_CS42L56 imply SND_SOC_CS42L73 + imply SND_SOC_CS4234 imply SND_SOC_CS4265 imply SND_SOC_CS4270 imply SND_SOC_CS4271_I2C @@ -592,6 +593,11 @@ config SND_SOC_CS42L73 tristate "Cirrus Logic CS42L73 CODEC" depends on I2C +config SND_SOC_CS4234 + tristate "Cirrus Logic CS4234 CODEC" + depends on I2C + select REGMAP_I2C + config SND_SOC_CS4265 tristate "Cirrus Logic CS4265 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 2e5a79b55102..0404bc1ddcfb 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -57,6 +57,7 @@ snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o snd-soc-cs42l52-objs := cs42l52.o snd-soc-cs42l56-objs := cs42l56.o snd-soc-cs42l73-objs := cs42l73.o +snd-soc-cs4234-objs := cs4234.o snd-soc-cs4265-objs := cs4265.o snd-soc-cs4270-objs := cs4270.o snd-soc-cs4271-objs := cs4271.o @@ -364,6 +365,7 @@ obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o obj-$(CONFIG_SND_SOC_CS42L56) += snd-soc-cs42l56.o obj-$(CONFIG_SND_SOC_CS42L73) += snd-soc-cs42l73.o +obj-$(CONFIG_SND_SOC_CS4234) += snd-soc-cs4234.o obj-$(CONFIG_SND_SOC_CS4265) += snd-soc-cs4265.o obj-$(CONFIG_SND_SOC_CS4270) += snd-soc-cs4270.o obj-$(CONFIG_SND_SOC_CS4271) += snd-soc-cs4271.o diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c new file mode 100644 index 000000000000..2ea83233c3f1 --- /dev/null +++ b/sound/soc/codecs/cs4234.c @@ -0,0 +1,918 @@ +// SPDX-License-Identifier: GPL-2.0-only +// cs4234.c -- ALSA SoC CS4234 driver +// +// Copyright (C) 2020 Cirrus Logic, Inc. and +// Cirrus Logic International Semiconductor Ltd. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cs4234.h" + +struct cs4234 { + struct device *dev; + struct regmap *regmap; + struct gpio_desc *reset_gpio; + struct regulator_bulk_data core_supplies[2]; + int num_core_supplies; + struct completion vq_ramp_complete; + struct delayed_work vq_ramp_delay; + struct clk *mclk; + unsigned long mclk_rate; + unsigned long lrclk_rate; + unsigned int format; + struct snd_ratnum rate_dividers[2]; + struct snd_pcm_hw_constraint_ratnums rate_constraint; +}; + +/* -89.92dB to +6.02dB with step of 0.38dB */ +static const DECLARE_TLV_DB_SCALE(dac_tlv, -8992, 38, 0); + +static const char * const cs4234_dac14_delay_text[] = { + "0us", "100us", "150us", "200us", "225us", "250us", "275us", "300us", + "325us", "350us", "375us", "400us", "425us", "450us", "475us", "500us", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_dac14_group_delay, CS4234_TPS_CTRL, + CS4234_GRP_DELAY_SHIFT, cs4234_dac14_delay_text); + +static const char * const cs4234_noise_gate_text[] = { + "72dB", "78dB", "84dB", "90dB", "96dB", "102dB", "138dB", "Disabled", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_ll_noise_gate, CS4234_LOW_LAT_CTRL1, + CS4234_LL_NG_SHIFT, cs4234_noise_gate_text); +static SOC_ENUM_SINGLE_DECL(cs4234_dac14_noise_gate, CS4234_DAC_CTRL1, + CS4234_DAC14_NG_SHIFT, cs4234_noise_gate_text); +static SOC_ENUM_SINGLE_DECL(cs4234_dac5_noise_gate, CS4234_DAC_CTRL2, + CS4234_DAC5_NG_SHIFT, cs4234_noise_gate_text); + +static const char * const cs4234_dac5_config_fltr_sel_text[] = { + "Interpolation Filter", "Sample and Hold" +}; +static SOC_ENUM_SINGLE_DECL(cs4234_dac5_config_fltr_sel, CS4234_DAC_CTRL1, + CS4234_DAC5_CFG_FLTR_SHIFT, + cs4234_dac5_config_fltr_sel_text); + +static const char * const cs4234_mute_delay_text[] = { + "1x", "4x", "16x", "64x", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_mute_delay, CS4234_VOLUME_MODE, + CS4234_MUTE_DELAY_SHIFT, cs4234_mute_delay_text); + +static const char * const cs4234_minmax_delay_text[] = { + "1x", "2x", "4x", "8x", "16x", "32x", "64x", "128x", +}; +static SOC_ENUM_SINGLE_DECL(cs4234_min_delay, CS4234_VOLUME_MODE, + CS4234_MIN_DELAY_SHIFT, cs4234_minmax_delay_text); +static SOC_ENUM_SINGLE_DECL(cs4234_max_delay, CS4234_VOLUME_MODE, + CS4234_MAX_DELAY_SHIFT, cs4234_minmax_delay_text); + +static int cs4234_dac14_grp_delay_put(struct snd_kcontrol *kctrl, + struct snd_ctl_elem_value *uctrl) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kctrl); + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + unsigned int val = 0; + int ret = 0; + + snd_soc_dapm_mutex_lock(dapm); + + regmap_read(cs4234->regmap, CS4234_ADC_CTRL2, &val); + if ((val & 0x0F) != 0x0F) { // are all the ADCs powerdown + ret = -EBUSY; + dev_err(component->dev, "Can't change group delay while ADC are ON\n"); + goto exit; + } + + regmap_read(cs4234->regmap, CS4234_DAC_CTRL4, &val); + if ((val & 0x1F) != 0x1F) { // are all the DACs powerdown + ret = -EBUSY; + dev_err(component->dev, "Can't change group delay while DAC are ON\n"); + goto exit; + } + + ret = snd_soc_put_enum_double(kctrl, uctrl); +exit: + snd_soc_dapm_mutex_unlock(dapm); + + return ret; +} + +static void cs4234_vq_ramp_done(struct work_struct *work) +{ + struct delayed_work *dw = to_delayed_work(work); + struct cs4234 *cs4234 = container_of(dw, struct cs4234, vq_ramp_delay); + + complete_all(&cs4234->vq_ramp_complete); +} + +static int cs4234_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_PREPARE: + switch (snd_soc_component_get_bias_level(component)) { + case SND_SOC_BIAS_STANDBY: + wait_for_completion(&cs4234->vq_ramp_complete); + break; + default: + break; + } + break; + default: + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget cs4234_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("SDRX1", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX2", NULL, 1, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX3", NULL, 2, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX4", NULL, 3, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SDRX5", NULL, 4, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_DAC("DAC1", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC1_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC2", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC2_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC3", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC3_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC4", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC4_SHIFT, 1), + SND_SOC_DAPM_DAC("DAC5", NULL, CS4234_DAC_CTRL4, CS4234_PDN_DAC5_SHIFT, 1), + + SND_SOC_DAPM_OUTPUT("AOUT1"), + SND_SOC_DAPM_OUTPUT("AOUT2"), + SND_SOC_DAPM_OUTPUT("AOUT3"), + SND_SOC_DAPM_OUTPUT("AOUT4"), + SND_SOC_DAPM_OUTPUT("AOUT5"), + + SND_SOC_DAPM_INPUT("AIN1"), + SND_SOC_DAPM_INPUT("AIN2"), + SND_SOC_DAPM_INPUT("AIN3"), + SND_SOC_DAPM_INPUT("AIN4"), + + SND_SOC_DAPM_ADC("ADC1", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC1_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC2", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC2_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC3", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC3_SHIFT, 1), + SND_SOC_DAPM_ADC("ADC4", NULL, CS4234_ADC_CTRL2, CS4234_PDN_ADC4_SHIFT, 1), + + SND_SOC_DAPM_AIF_OUT("SDTX1", NULL, 0, SND_SOC_NOPM, 0, 1), + SND_SOC_DAPM_AIF_OUT("SDTX2", NULL, 1, SND_SOC_NOPM, 0, 1), + SND_SOC_DAPM_AIF_OUT("SDTX3", NULL, 2, SND_SOC_NOPM, 0, 1), + SND_SOC_DAPM_AIF_OUT("SDTX4", NULL, 3, SND_SOC_NOPM, 0, 1), +}; + +static const struct snd_soc_dapm_route cs4234_dapm_routes[] = { + /* Playback */ + { "AOUT1", NULL, "DAC1" }, + { "AOUT2", NULL, "DAC2" }, + { "AOUT3", NULL, "DAC3" }, + { "AOUT4", NULL, "DAC4" }, + { "AOUT5", NULL, "DAC5" }, + + { "DAC1", NULL, "SDRX1" }, + { "DAC2", NULL, "SDRX2" }, + { "DAC3", NULL, "SDRX3" }, + { "DAC4", NULL, "SDRX4" }, + { "DAC5", NULL, "SDRX5" }, + + { "SDRX1", NULL, "Playback" }, + { "SDRX2", NULL, "Playback" }, + { "SDRX3", NULL, "Playback" }, + { "SDRX4", NULL, "Playback" }, + { "SDRX5", NULL, "Playback" }, + + /* Capture */ + { "ADC1", NULL, "AIN1" }, + { "ADC2", NULL, "AIN2" }, + { "ADC3", NULL, "AIN3" }, + { "ADC4", NULL, "AIN4" }, + + { "SDTX1", NULL, "ADC1" }, + { "SDTX2", NULL, "ADC2" }, + { "SDTX3", NULL, "ADC3" }, + { "SDTX4", NULL, "ADC4" }, + + { "Capture", NULL, "SDTX1" }, + { "Capture", NULL, "SDTX2" }, + { "Capture", NULL, "SDTX3" }, + { "Capture", NULL, "SDTX4" }, +}; + +static const struct snd_kcontrol_new cs4234_snd_controls[] = { + SOC_SINGLE_TLV("Master Volume", CS4234_MASTER_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC1 Volume", CS4234_DAC1_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC2 Volume", CS4234_DAC2_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC3 Volume", CS4234_DAC3_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC4 Volume", CS4234_DAC4_VOL, 0, 0xff, 1, dac_tlv), + SOC_SINGLE_TLV("DAC5 Volume", CS4234_DAC5_VOL, 0, 0xff, 1, dac_tlv), + + SOC_SINGLE("DAC5 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC5_ATT_SHIFT, 1, 1), + SOC_SINGLE("DAC1-4 Soft Ramp Switch", CS4234_DAC_CTRL3, CS4234_DAC14_ATT_SHIFT, 1, 1), + + SOC_SINGLE("ADC HPF Switch", CS4234_ADC_CTRL1, CS4234_ENA_HPF_SHIFT, 1, 0), + + SOC_ENUM_EXT("DAC1-4 Group Delay", cs4234_dac14_group_delay, + snd_soc_get_enum_double, cs4234_dac14_grp_delay_put), + + SOC_SINGLE("ADC1 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC1_SHIFT, 1, 0), + SOC_SINGLE("ADC2 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC2_SHIFT, 1, 0), + SOC_SINGLE("ADC3 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC3_SHIFT, 1, 0), + SOC_SINGLE("ADC4 Invert Switch", CS4234_ADC_CTRL1, CS4234_INV_ADC4_SHIFT, 1, 0), + + SOC_SINGLE("DAC1 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC1_SHIFT, 1, 0), + SOC_SINGLE("DAC2 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC2_SHIFT, 1, 0), + SOC_SINGLE("DAC3 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC3_SHIFT, 1, 0), + SOC_SINGLE("DAC4 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC4_SHIFT, 1, 0), + SOC_SINGLE("DAC5 Invert Switch", CS4234_DAC_CTRL2, CS4234_INV_DAC5_SHIFT, 1, 0), + + SOC_SINGLE("ADC1 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC1_SHIFT, 1, 1), + SOC_SINGLE("ADC2 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC2_SHIFT, 1, 1), + SOC_SINGLE("ADC3 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC3_SHIFT, 1, 1), + SOC_SINGLE("ADC4 Switch", CS4234_ADC_CTRL2, CS4234_MUTE_ADC4_SHIFT, 1, 1), + + SOC_SINGLE("DAC1 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC1_SHIFT, 1, 1), + SOC_SINGLE("DAC2 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC2_SHIFT, 1, 1), + SOC_SINGLE("DAC3 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC3_SHIFT, 1, 1), + SOC_SINGLE("DAC4 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC4_SHIFT, 1, 1), + SOC_SINGLE("DAC5 Switch", CS4234_DAC_CTRL3, CS4234_MUTE_DAC5_SHIFT, 1, 1), + SOC_SINGLE("Low-latency Switch", CS4234_DAC_CTRL3, CS4234_MUTE_LL_SHIFT, 1, 1), + + SOC_SINGLE("DAC1 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL1_SHIFT, 1, 0), + SOC_SINGLE("DAC2 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL2_SHIFT, 1, 0), + SOC_SINGLE("DAC3 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL3_SHIFT, 1, 0), + SOC_SINGLE("DAC4 Low-latency Invert Switch", CS4234_LOW_LAT_CTRL1, + CS4234_INV_LL4_SHIFT, 1, 0), + + SOC_ENUM("Low-latency Noise Gate", cs4234_ll_noise_gate), + SOC_ENUM("DAC1-4 Noise Gate", cs4234_dac14_noise_gate), + SOC_ENUM("DAC5 Noise Gate", cs4234_dac5_noise_gate), + + SOC_SINGLE("DAC1-4 De-emphasis Switch", CS4234_DAC_CTRL1, + CS4234_DAC14_DE_SHIFT, 1, 0), + SOC_SINGLE("DAC5 De-emphasis Switch", CS4234_DAC_CTRL1, + CS4234_DAC5_DE_SHIFT, 1, 0), + + SOC_SINGLE("DAC5 Master Controlled Switch", CS4234_DAC_CTRL1, + CS4234_DAC5_MVC_SHIFT, 1, 0), + + SOC_ENUM("DAC5 Filter", cs4234_dac5_config_fltr_sel), + + SOC_ENUM("Mute Delay", cs4234_mute_delay), + SOC_ENUM("Ramp Minimum Delay", cs4234_min_delay), + SOC_ENUM("Ramp Maximum Delay", cs4234_max_delay), + +}; + +static int cs4234_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int format) +{ + struct snd_soc_component *component = codec_dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + unsigned int sp_ctrl = 0; + + cs4234->format = format & SND_SOC_DAIFMT_FORMAT_MASK; + switch (cs4234->format) { + case SND_SOC_DAIFMT_LEFT_J: + sp_ctrl |= CS4234_LEFT_J << CS4234_SP_FORMAT_SHIFT; + break; + case SND_SOC_DAIFMT_I2S: + sp_ctrl |= CS4234_I2S << CS4234_SP_FORMAT_SHIFT; + break; + case SND_SOC_DAIFMT_DSP_A: /* TDM mode in datasheet */ + sp_ctrl |= CS4234_TDM << CS4234_SP_FORMAT_SHIFT; + break; + default: + dev_err(component->dev, "Unsupported dai format\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + case SND_SOC_DAIFMT_CBM_CFM: + if (cs4234->format == SND_SOC_DAIFMT_DSP_A) { + dev_err(component->dev, "Unsupported DSP A format in master mode\n"); + return -EINVAL; + } + sp_ctrl |= CS4234_MST_SLV_MASK; + break; + default: + dev_err(component->dev, "Unsupported master/slave mode\n"); + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + sp_ctrl |= CS4234_INVT_SCLK_MASK; + break; + default: + dev_err(component->dev, "Unsupported inverted clock setting\n"); + return -EINVAL; + } + + regmap_update_bits(cs4234->regmap, CS4234_SP_CTRL, + CS4234_SP_FORMAT_MASK | CS4234_MST_SLV_MASK | CS4234_INVT_SCLK_MASK, + sp_ctrl); + + return 0; +} + +static int cs4234_dai_hw_params(struct snd_pcm_substream *sub, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + unsigned int mclk_mult, double_speed = 0; + int ret = 0, rate_ad, sample_width; + + cs4234->lrclk_rate = params_rate(params); + mclk_mult = cs4234->mclk_rate / cs4234->lrclk_rate; + + if (cs4234->lrclk_rate > 48000) { + double_speed = 1; + mclk_mult *= 2; + } + + switch (mclk_mult) { + case 256: + case 384: + case 512: + regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, + CS4234_SPEED_MODE_MASK, + double_speed << CS4234_SPEED_MODE_SHIFT); + regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, + CS4234_MCLK_RATE_MASK, + ((mclk_mult / 128) - 2) << CS4234_MCLK_RATE_SHIFT); + break; + default: + dev_err(component->dev, "Unsupported mclk/lrclk rate\n"); + return -EINVAL; + } + + switch (cs4234->lrclk_rate) { + case 48000: + case 96000: + rate_ad = CS4234_48K; + break; + case 44100: + case 88200: + rate_ad = CS4234_44K1; + break; + case 32000: + case 64000: + rate_ad = CS4234_32K; + break; + default: + dev_err(component->dev, "Unsupported LR clock\n"); + return -EINVAL; + } + regmap_update_bits(cs4234->regmap, CS4234_CLOCK_SP, CS4234_BASE_RATE_MASK, + rate_ad << CS4234_BASE_RATE_SHIFT); + + sample_width = params_width(params); + switch (sample_width) { + case 16: + sample_width = 0; + break; + case 18: + sample_width = 1; + break; + case 20: + sample_width = 2; + break; + case 24: + sample_width = 3; + break; + default: + dev_err(component->dev, "Unsupported sample width\n"); + return -EINVAL; + } + if (sub->stream == SNDRV_PCM_STREAM_CAPTURE) + regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH, + CS4234_SDOUTX_SW_MASK, + sample_width << CS4234_SDOUTX_SW_SHIFT); + else + regmap_update_bits(cs4234->regmap, CS4234_SAMPLE_WIDTH, + CS4234_INPUT_SW_MASK | CS4234_LOW_LAT_SW_MASK | CS4234_DAC5_SW_MASK, + sample_width << CS4234_INPUT_SW_SHIFT | + sample_width << CS4234_LOW_LAT_SW_SHIFT | + sample_width << CS4234_DAC5_SW_SHIFT); + + return ret; +} + +/* Scale MCLK rate by 64 to avoid overflow in the ratnum calculation */ +#define CS4234_MCLK_SCALE 64 + +static const struct snd_ratnum cs4234_dividers[] = { + { + .num = 0, + .den_min = 256 / CS4234_MCLK_SCALE, + .den_max = 512 / CS4234_MCLK_SCALE, + .den_step = 128 / CS4234_MCLK_SCALE, + }, + { + .num = 0, + .den_min = 128 / CS4234_MCLK_SCALE, + .den_max = 192 / CS4234_MCLK_SCALE, + .den_step = 64 / CS4234_MCLK_SCALE, + }, +}; + +static int cs4234_dai_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule) +{ + struct cs4234 *cs4234 = rule->private; + int mclk = cs4234->mclk_rate; + struct snd_interval ranges[] = { + { /* Single Speed Mode */ + .min = mclk / clamp(mclk / 30000, 256, 512), + .max = mclk / clamp(mclk / 50000, 256, 512), + }, + { /* Double Speed Mode */ + .min = mclk / clamp(mclk / 60000, 128, 256), + .max = mclk / clamp(mclk / 100000, 128, 256), + }, + }; + + return snd_interval_ranges(hw_param_interval(params, rule->var), + ARRAY_SIZE(ranges), ranges, 0); +} + +static int cs4234_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(comp); + int i, ret; + + switch (cs4234->format) { + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_I2S: + cs4234->rate_constraint.nrats = 2; + + /* + * Playback only supports 24-bit samples in these modes. + * Note: SNDRV_PCM_HW_PARAM_SAMPLE_BITS constrains the physical + * width, which we don't care about, so constrain the format. + */ + if (sub->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = snd_pcm_hw_constraint_mask64( + sub->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S24_3LE); + if (ret < 0) + return ret; + + ret = snd_pcm_hw_constraint_minmax(sub->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 1, 4); + if (ret < 0) + return ret; + } + + break; + case SND_SOC_DAIFMT_DSP_A: + cs4234->rate_constraint.nrats = 1; + break; + default: + dev_err(comp->dev, "Startup unsupported DAI format\n"); + return -EINVAL; + } + + for (i = 0; i < cs4234->rate_constraint.nrats; i++) + cs4234->rate_dividers[i].num = cs4234->mclk_rate / CS4234_MCLK_SCALE; + + ret = snd_pcm_hw_constraint_ratnums(sub->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &cs4234->rate_constraint); + if (ret < 0) + return ret; + + /* + * MCLK/rate may be a valid ratio but out-of-spec (e.g. 24576000/64000) + * so this rule limits the range of sample rate for given MCLK. + */ + return snd_pcm_hw_rule_add(sub->runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + cs4234_dai_rule_rate, cs4234, -1); +} + +static int cs4234_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct cs4234 *cs4234 = snd_soc_component_get_drvdata(component); + unsigned int slot_offset, dac5_slot, dac5_mask_group; + uint8_t dac5_masks[4]; + + if (slot_width != 32) { + dev_err(component->dev, "Unsupported slot width\n"); + return -EINVAL; + } + + /* Either 4 or 5 consecutive bits, DAC5 is optional */ + slot_offset = ffs(tx_mask) - 1; + tx_mask >>= slot_offset; + if ((slot_offset % 4) || ((tx_mask != 0x0F) && (tx_mask != 0x1F))) { + dev_err(component->dev, "Unsupported tx slots allocation\n"); + return -EINVAL; + } + + regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_DAC14_SRC_MASK, + (slot_offset / 4) << CS4234_DAC14_SRC_SHIFT); + regmap_update_bits(cs4234->regmap, CS4234_SP_DATA_SEL, CS4234_LL_SRC_MASK, + (slot_offset / 4) << CS4234_LL_SRC_SHIFT); + + if (tx_mask == 0x1F) { + dac5_slot = slot_offset + 4; + memset(dac5_masks, 0xFF, sizeof(dac5_masks)); + dac5_mask_group = dac5_slot / 8; + dac5_slot %= 8; + dac5_masks[dac5_mask_group] ^= BIT(7 - dac5_slot); + regmap_bulk_write(cs4234->regmap, + CS4234_SDIN1_MASK1, + dac5_masks, + ARRAY_SIZE(dac5_masks)); + } + + return 0; +} + +static const struct snd_soc_dai_ops cs4234_dai_ops = { + .set_fmt = cs4234_dai_set_fmt, + .hw_params = cs4234_dai_hw_params, + .startup = cs4234_dai_startup, + .set_tdm_slot = cs4234_dai_set_tdm_slot, +}; + +static struct snd_soc_dai_driver cs4234_dai[] = { + { + .name = "cs4234-dai", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 5, + .rates = CS4234_PCM_RATES, + .formats = CS4234_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = CS4234_PCM_RATES, + .formats = CS4234_FORMATS, + }, + .ops = &cs4234_dai_ops, + .symmetric_rates = 1, + }, +}; + +static const struct reg_default cs4234_default_reg[] = { + { CS4234_CLOCK_SP, 0x04}, + { CS4234_SAMPLE_WIDTH, 0xFF}, + { CS4234_SP_CTRL, 0x48}, + { CS4234_SP_DATA_SEL, 0x01}, + { CS4234_SDIN1_MASK1, 0xFF}, + { CS4234_SDIN1_MASK2, 0xFF}, + { CS4234_SDIN2_MASK1, 0xFF}, + { CS4234_SDIN2_MASK2, 0xFF}, + { CS4234_TPS_CTRL, 0x00}, + { CS4234_ADC_CTRL1, 0xC0}, + { CS4234_ADC_CTRL2, 0xFF}, + { CS4234_LOW_LAT_CTRL1, 0xE0}, + { CS4234_DAC_CTRL1, 0xE0}, + { CS4234_DAC_CTRL2, 0xE0}, + { CS4234_DAC_CTRL3, 0xBF}, + { CS4234_DAC_CTRL4, 0x1F}, + { CS4234_VOLUME_MODE, 0x87}, + { CS4234_MASTER_VOL, 0x10}, + { CS4234_DAC1_VOL, 0x10}, + { CS4234_DAC2_VOL, 0x10}, + { CS4234_DAC3_VOL, 0x10}, + { CS4234_DAC4_VOL, 0x10}, + { CS4234_DAC5_VOL, 0x10}, + { CS4234_INT_CTRL, 0x40}, + { CS4234_INT_MASK1, 0x10}, + { CS4234_INT_MASK2, 0x20}, +}; + +static bool cs4234_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4234_DEVID_AB ... CS4234_DEVID_EF: + case CS4234_REVID ... CS4234_DAC5_VOL: + case CS4234_INT_CTRL ... CS4234_MAX_REGISTER: + return true; + default: + return false; + } +} + +static bool cs4234_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4234_INT_NOTIFY1: + case CS4234_INT_NOTIFY2: + return true; + default: + return false; + } +} + +static bool cs4234_writeable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS4234_DEVID_AB ... CS4234_REVID: + case CS4234_INT_NOTIFY1 ... CS4234_INT_NOTIFY2: + return false; + default: + return true; + } +} + +static const struct snd_soc_component_driver soc_component_cs4234 = { + .dapm_widgets = cs4234_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4234_dapm_widgets), + .dapm_routes = cs4234_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cs4234_dapm_routes), + .controls = cs4234_snd_controls, + .num_controls = ARRAY_SIZE(cs4234_snd_controls), + .set_bias_level = cs4234_set_bias_level, + .non_legacy_dai_naming = 1, + .idle_bias_on = 1, + .suspend_bias_off = 1, +}; + +static const struct regmap_config cs4234_regmap = { + .reg_bits = 8, + .val_bits = 8, + + .max_register = CS4234_MAX_REGISTER, + .readable_reg = cs4234_readable_register, + .volatile_reg = cs4234_volatile_reg, + .writeable_reg = cs4234_writeable_register, + .reg_defaults = cs4234_default_reg, + .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg), + .cache_type = REGCACHE_RBTREE, + .use_single_read = true, + .use_single_write = true, +}; + +static const char * const cs4234_core_supplies[] = { + "VA", + "VL", +}; + +static void cs4234_shutdown(struct cs4234 *cs4234) +{ + cancel_delayed_work_sync(&cs4234->vq_ramp_delay); + reinit_completion(&cs4234->vq_ramp_complete); + + regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, + CS4234_VQ_RAMP_MASK); + msleep(50); + regcache_cache_only(cs4234->regmap, true); + /* Clear VQ Ramp Bit in cache for the next PowerUp */ + regmap_update_bits(cs4234->regmap, CS4234_DAC_CTRL4, CS4234_VQ_RAMP_MASK, 0); + gpiod_set_value_cansleep(cs4234->reset_gpio, 0); + regulator_bulk_disable(cs4234->num_core_supplies, cs4234->core_supplies); + clk_disable_unprepare(cs4234->mclk); +} + +static int cs4234_powerup(struct cs4234 *cs4234) +{ + int ret; + + ret = clk_prepare_enable(cs4234->mclk); + if (ret) { + dev_err(cs4234->dev, "Failed to enable mclk: %d\n", ret); + return ret; + } + + ret = regulator_bulk_enable(cs4234->num_core_supplies, cs4234->core_supplies); + if (ret) { + dev_err(cs4234->dev, "Failed to enable core supplies: %d\n", ret); + clk_disable_unprepare(cs4234->mclk); + return ret; + } + + usleep_range(CS4234_HOLD_RESET_TIME_US, 2 * CS4234_HOLD_RESET_TIME_US); + gpiod_set_value_cansleep(cs4234->reset_gpio, 1); + + /* Make sure hardware reset done 2 ms + (3000/MCLK) */ + usleep_range(CS4234_BOOT_TIME_US, CS4234_BOOT_TIME_US * 2); + + queue_delayed_work(system_power_efficient_wq, + &cs4234->vq_ramp_delay, + msecs_to_jiffies(CS4234_VQ_CHARGE_MS)); + + return 0; +} + +static int cs4234_i2c_probe(struct i2c_client *i2c_client, const struct i2c_device_id *id) +{ + struct cs4234 *cs4234; + struct device *dev = &i2c_client->dev; + unsigned int revid; + uint32_t devid; + uint8_t ids[3]; + int ret = 0, i; + + cs4234 = devm_kzalloc(dev, sizeof(*cs4234), GFP_KERNEL); + if (!cs4234) + return -ENOMEM; + i2c_set_clientdata(i2c_client, cs4234); + cs4234->dev = dev; + init_completion(&cs4234->vq_ramp_complete); + INIT_DELAYED_WORK(&cs4234->vq_ramp_delay, cs4234_vq_ramp_done); + + cs4234->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(cs4234->reset_gpio)) + return PTR_ERR(cs4234->reset_gpio); + + BUILD_BUG_ON(ARRAY_SIZE(cs4234->core_supplies) < ARRAY_SIZE(cs4234_core_supplies)); + + cs4234->num_core_supplies = ARRAY_SIZE(cs4234_core_supplies); + for (i = 0; i < ARRAY_SIZE(cs4234_core_supplies); i++) + cs4234->core_supplies[i].supply = cs4234_core_supplies[i]; + + ret = devm_regulator_bulk_get(dev, cs4234->num_core_supplies, cs4234->core_supplies); + if (ret) { + dev_err(dev, "Failed to request core supplies %d\n", ret); + return ret; + } + + cs4234->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(cs4234->mclk)) { + ret = PTR_ERR(cs4234->mclk); + dev_err(dev, "Failed to get the mclk: %d\n", ret); + return ret; + } + cs4234->mclk_rate = clk_get_rate(cs4234->mclk); + + if (cs4234->mclk_rate < 7680000 || cs4234->mclk_rate > 25600000) { + dev_err(dev, "Invalid Master Clock rate\n"); + return -EINVAL; + } + + cs4234->regmap = devm_regmap_init_i2c(i2c_client, &cs4234_regmap); + if (IS_ERR(cs4234->regmap)) { + ret = PTR_ERR(cs4234->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + return ret; + } + + ret = cs4234_powerup(cs4234); + if (ret) + return ret; + + ret = regmap_bulk_read(cs4234->regmap, CS4234_DEVID_AB, ids, ARRAY_SIZE(ids)); + if (ret < 0) { + dev_err(dev, "Failed to read DEVID: %d\n", ret); + goto fail_shutdown; + } + + devid = (ids[0] << 16) | (ids[1] << 8) | ids[2]; + if (devid != CS4234_SUPPORTED_ID) { + dev_err(dev, "Unknown device ID: %x\n", devid); + ret = -EINVAL; + goto fail_shutdown; + } + + ret = regmap_read(cs4234->regmap, CS4234_REVID, &revid); + if (ret < 0) { + dev_err(dev, "Failed to read CS4234_REVID: %d\n", ret); + goto fail_shutdown; + } + + dev_info(dev, "Cirrus Logic CS4234, Alpha Rev: %02X, Numeric Rev: %02X\n", + (revid & 0xF0) >> 4, revid & 0x0F); + + ret = regulator_get_voltage(cs4234->core_supplies[CS4234_SUPPLY_VA].consumer); + switch (ret) { + case 3135000 ... 3650000: + regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1, + CS4234_VA_SEL_MASK, + CS4234_3V3 << CS4234_VA_SEL_SHIFT); + break; + case 4750000 ... 5250000: + regmap_update_bits(cs4234->regmap, CS4234_ADC_CTRL1, + CS4234_VA_SEL_MASK, + CS4234_5V << CS4234_VA_SEL_SHIFT); + break; + default: + dev_err(dev, "Invalid VA voltage\n"); + ret = -EINVAL; + goto fail_shutdown; + } + + pm_runtime_set_active(&i2c_client->dev); + pm_runtime_enable(&i2c_client->dev); + + memcpy(&cs4234->rate_dividers, &cs4234_dividers, sizeof(cs4234_dividers)); + cs4234->rate_constraint.rats = cs4234->rate_dividers; + + ret = snd_soc_register_component(dev, &soc_component_cs4234, cs4234_dai, + ARRAY_SIZE(cs4234_dai)); + if (ret < 0) { + dev_err(dev, "Failed to register component:%d\n", ret); + pm_runtime_disable(&i2c_client->dev); + goto fail_shutdown; + } + + return ret; + +fail_shutdown: + cs4234_shutdown(cs4234); + + return ret; +} + +static int cs4234_i2c_remove(struct i2c_client *i2c_client) +{ + struct cs4234 *cs4234 = i2c_get_clientdata(i2c_client); + struct device *dev = &i2c_client->dev; + + snd_soc_unregister_component(dev); + pm_runtime_disable(dev); + cs4234_shutdown(cs4234); + + return 0; +} + +static int __maybe_unused cs4234_runtime_resume(struct device *dev) +{ + struct cs4234 *cs4234 = dev_get_drvdata(dev); + int ret; + + ret = cs4234_powerup(cs4234); + if (ret) + return ret; + + regcache_mark_dirty(cs4234->regmap); + regcache_cache_only(cs4234->regmap, false); + ret = regcache_sync(cs4234->regmap); + if (ret) { + dev_err(dev, "Failed to sync regmap: %d\n", ret); + cs4234_shutdown(cs4234); + return ret; + } + + return 0; +} + +static int __maybe_unused cs4234_runtime_suspend(struct device *dev) +{ + struct cs4234 *cs4234 = dev_get_drvdata(dev); + + cs4234_shutdown(cs4234); + + return 0; +} + +static const struct dev_pm_ops cs4234_pm = { + SET_RUNTIME_PM_OPS(cs4234_runtime_suspend, cs4234_runtime_resume, NULL) +}; + +static const struct of_device_id cs4234_of_match[] = { + { .compatible = "cirrus,cs4234", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4234_of_match); + +static struct i2c_driver cs4234_i2c_driver = { + .driver = { + .name = "cs4234", + .pm = &cs4234_pm, + .of_match_table = cs4234_of_match, + }, + .probe = cs4234_i2c_probe, + .remove = cs4234_i2c_remove, +}; +module_i2c_driver(cs4234_i2c_driver); + +MODULE_DESCRIPTION("ASoC Cirrus Logic CS4234 driver"); +MODULE_AUTHOR("Lucas Tanure "); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/cs4234.h b/sound/soc/codecs/cs4234.h new file mode 100644 index 000000000000..76a75afc198d --- /dev/null +++ b/sound/soc/codecs/cs4234.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ALSA SoC Audio driver for CS4234 codec + * + * Copyright (C) 2020 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + */ + +#ifndef CS4234_H +#define CS4234_H + +#define CS4234_DEVID_AB 0x01 +#define CS4234_DEVID_CD 0x02 +#define CS4234_DEVID_EF 0x03 +#define CS4234_REVID 0x05 + +#define CS4234_CLOCK_SP 0x06 +#define CS4234_BASE_RATE_MASK 0xC0 +#define CS4234_BASE_RATE_SHIFT 6 +#define CS4234_SPEED_MODE_MASK 0x30 +#define CS4234_SPEED_MODE_SHIFT 4 +#define CS4234_MCLK_RATE_MASK 0x0E +#define CS4234_MCLK_RATE_SHIFT 1 + +#define CS4234_SAMPLE_WIDTH 0x07 +#define CS4234_SDOUTX_SW_MASK 0xC0 +#define CS4234_SDOUTX_SW_SHIFT 6 +#define CS4234_INPUT_SW_MASK 0x30 +#define CS4234_INPUT_SW_SHIFT 4 +#define CS4234_LOW_LAT_SW_MASK 0x0C +#define CS4234_LOW_LAT_SW_SHIFT 2 +#define CS4234_DAC5_SW_MASK 0x03 +#define CS4234_DAC5_SW_SHIFT 0 + +#define CS4234_SP_CTRL 0x08 +#define CS4234_INVT_SCLK_MASK 0x80 +#define CS4234_INVT_SCLK_SHIFT 7 +#define CS4234_DAC5_SRC_MASK 0x70 +#define CS4234_DAC5_SRC_SHIFT 4 +#define CS4234_SP_FORMAT_MASK 0x0C +#define CS4234_SP_FORMAT_SHIFT 2 +#define CS4234_SDO_CHAIN_MASK 0x02 +#define CS4234_SDO_CHAIN_SHIFT 1 +#define CS4234_MST_SLV_MASK 0x01 +#define CS4234_MST_SLV_SHIFT 0 + +#define CS4234_SP_DATA_SEL 0x09 +#define CS4234_DAC14_SRC_MASK 0x38 +#define CS4234_DAC14_SRC_SHIFT 3 +#define CS4234_LL_SRC_MASK 0x07 +#define CS4234_LL_SRC_SHIFT 0 + +#define CS4234_SDIN1_MASK1 0x0A +#define CS4234_SDIN1_MASK2 0x0B +#define CS4234_SDIN2_MASK1 0x0C +#define CS4234_SDIN2_MASK2 0x0D + +#define CS4234_TPS_CTRL 0x0E +#define CS4234_TPS_MODE_MASK 0x80 +#define CS4234_TPS_MODE_SHIFT 7 +#define CS4234_TPS_OFST_MASK 0x70 +#define CS4234_TPS_OFST_SHIFT 4 +#define CS4234_GRP_DELAY_MASK 0x0F +#define CS4234_GRP_DELAY_SHIFT 0 + +#define CS4234_ADC_CTRL1 0x0F +#define CS4234_VA_SEL_MASK 0x20 +#define CS4234_VA_SEL_SHIFT 5 +#define CS4234_ENA_HPF_MASK 0x10 +#define CS4234_ENA_HPF_SHIFT 4 +#define CS4234_INV_ADC_MASK 0x0F +#define CS4234_INV_ADC4_MASK 0x08 +#define CS4234_INV_ADC4_SHIFT 3 +#define CS4234_INV_ADC3_MASK 0x04 +#define CS4234_INV_ADC3_SHIFT 2 +#define CS4234_INV_ADC2_MASK 0x02 +#define CS4234_INV_ADC2_SHIFT 1 +#define CS4234_INV_ADC1_MASK 0x01 +#define CS4234_INV_ADC1_SHIFT 0 + +#define CS4234_ADC_CTRL2 0x10 +#define CS4234_MUTE_ADC4_MASK 0x80 +#define CS4234_MUTE_ADC4_SHIFT 7 +#define CS4234_MUTE_ADC3_MASK 0x40 +#define CS4234_MUTE_ADC3_SHIFT 6 +#define CS4234_MUTE_ADC2_MASK 0x20 +#define CS4234_MUTE_ADC2_SHIFT 5 +#define CS4234_MUTE_ADC1_MASK 0x10 +#define CS4234_MUTE_ADC1_SHIFT 4 +#define CS4234_PDN_ADC4_MASK 0x08 +#define CS4234_PDN_ADC4_SHIFT 3 +#define CS4234_PDN_ADC3_MASK 0x04 +#define CS4234_PDN_ADC3_SHIFT 2 +#define CS4234_PDN_ADC2_MASK 0x02 +#define CS4234_PDN_ADC2_SHIFT 1 +#define CS4234_PDN_ADC1_MASK 0x01 +#define CS4234_PDN_ADC1_SHIFT 0 + +#define CS4234_LOW_LAT_CTRL1 0x11 +#define CS4234_LL_NG_MASK 0xE0 +#define CS4234_LL_NG_SHIFT 5 +#define CS4234_INV_LL_MASK 0x0F +#define CS4234_INV_LL4_MASK 0x08 +#define CS4234_INV_LL4_SHIFT 3 +#define CS4234_INV_LL3_MASK 0x04 +#define CS4234_INV_LL3_SHIFT 2 +#define CS4234_INV_LL2_MASK 0x02 +#define CS4234_INV_LL2_SHIFT 1 +#define CS4234_INV_LL1_MASK 0x01 +#define CS4234_INV_LL1_SHIFT 0 + +#define CS4234_DAC_CTRL1 0x12 +#define CS4234_DAC14_NG_MASK 0xE0 +#define CS4234_DAC14_NG_SHIFT 5 +#define CS4234_DAC14_DE_MASK 0x10 +#define CS4234_DAC14_DE_SHIFT 4 +#define CS4234_DAC5_DE_MASK 0x08 +#define CS4234_DAC5_DE_SHIFT 3 +#define CS4234_DAC5_MVC_MASK 0x04 +#define CS4234_DAC5_MVC_SHIFT 2 +#define CS4234_DAC5_CFG_FLTR_MASK 0x03 +#define CS4234_DAC5_CFG_FLTR_SHIFT 0 + +#define CS4234_DAC_CTRL2 0x13 +#define CS4234_DAC5_NG_MASK 0xE0 +#define CS4234_DAC5_NG_SHIFT 5 +#define CS4234_INV_DAC_MASK 0x1F +#define CS4234_INV_DAC5_MASK 0x10 +#define CS4234_INV_DAC5_SHIFT 4 +#define CS4234_INV_DAC4_MASK 0x08 +#define CS4234_INV_DAC4_SHIFT 3 +#define CS4234_INV_DAC3_MASK 0x04 +#define CS4234_INV_DAC3_SHIFT 2 +#define CS4234_INV_DAC2_MASK 0x02 +#define CS4234_INV_DAC2_SHIFT 1 +#define CS4234_INV_DAC1_MASK 0x01 +#define CS4234_INV_DAC1_SHIFT 0 + +#define CS4234_DAC_CTRL3 0x14 +#define CS4234_DAC5_ATT_MASK 0x80 +#define CS4234_DAC5_ATT_SHIFT 7 +#define CS4234_DAC14_ATT_MASK 0x40 +#define CS4234_DAC14_ATT_SHIFT 6 +#define CS4234_MUTE_LL_MASK 0x20 +#define CS4234_MUTE_LL_SHIFT 5 +#define CS4234_MUTE_DAC5_MASK 0x10 +#define CS4234_MUTE_DAC5_SHIFT 4 +#define CS4234_MUTE_DAC4_MASK 0x08 +#define CS4234_MUTE_DAC4_SHIFT 3 +#define CS4234_MUTE_DAC3_MASK 0x04 +#define CS4234_MUTE_DAC3_SHIFT 2 +#define CS4234_MUTE_DAC2_MASK 0x02 +#define CS4234_MUTE_DAC2_SHIFT 1 +#define CS4234_MUTE_DAC1_MASK 0x01 +#define CS4234_MUTE_DAC1_SHIFT 0 + +#define CS4234_DAC_CTRL4 0x15 +#define CS4234_VQ_RAMP_MASK 0x80 +#define CS4234_VQ_RAMP_SHIFT 7 +#define CS4234_TPS_GAIN_MASK 0x40 +#define CS4234_TPS_GAIN_SHIFT 6 +#define CS4234_PDN_DAC5_MASK 0x10 +#define CS4234_PDN_DAC5_SHIFT 4 +#define CS4234_PDN_DAC4_MASK 0x08 +#define CS4234_PDN_DAC4_SHIFT 3 +#define CS4234_PDN_DAC3_MASK 0x04 +#define CS4234_PDN_DAC3_SHIFT 2 +#define CS4234_PDN_DAC2_MASK 0x02 +#define CS4234_PDN_DAC2_SHIFT 1 +#define CS4234_PDN_DAC1_MASK 0x01 +#define CS4234_PDN_DAC1_SHIFT 0 + +#define CS4234_VOLUME_MODE 0x16 +#define CS4234_MUTE_DELAY_MASK 0xC0 +#define CS4234_MUTE_DELAY_SHIFT 6 +#define CS4234_MIN_DELAY_MASK 0x38 +#define CS4234_MIN_DELAY_SHIFT 3 +#define CS4234_MAX_DELAY_MASK 0x07 +#define CS4234_MAX_DELAY_SHIFT 0 + +#define CS4234_MASTER_VOL 0x17 +#define CS4234_DAC1_VOL 0x18 +#define CS4234_DAC2_VOL 0x19 +#define CS4234_DAC3_VOL 0x1A +#define CS4234_DAC4_VOL 0x1B +#define CS4234_DAC5_VOL 0x1C + +#define CS4234_INT_CTRL 0x1E +#define CS4234_INT_MODE_MASK 0x80 +#define CS4234_INT_MODE_SHIFT 7 +#define CS4234_INT_PIN_MASK 0x60 +#define CS4234_INT_PIN_SHIFT 5 + +#define CS4234_INT_MASK1 0x1F +#define CS4234_MSK_TST_MODE_MASK 0x80 +#define CS4234_MSK_TST_MODE_ERR_SHIFT 7 +#define CS4234_MSK_SP_ERR_MASK 0x40 +#define CS4234_MSK_SP_ERR_SHIFT 6 +#define CS4234_MSK_CLK_ERR_MASK 0x08 +#define CS4234_MSK_CLK_ERR_SHIFT 5 +#define CS4234_MSK_ADC4_OVFL_MASK 0x08 +#define CS4234_MSK_ADC4_OVFL_SHIFT 3 +#define CS4234_MSK_ADC3_OVFL_MASK 0x04 +#define CS4234_MSK_ADC3_OVFL_SHIFT 2 +#define CS4234_MSK_ADC2_OVFL_MASK 0x02 +#define CS4234_MSK_ADC2_OVFL_SHIFT 1 +#define CS4234_MSK_ADC1_OVFL_MASK 0x01 +#define CS4234_MSK_ADC1_OVFL_SHIFT 0 + +#define CS4234_INT_MASK2 0x20 +#define CS4234_MSK_DAC5_CLIP_MASK 0x10 +#define CS4234_MSK_DAC5_CLIP_SHIFT 4 +#define CS4234_MSK_DAC4_CLIP_MASK 0x08 +#define CS4234_MSK_DAC4_CLIP_SHIFT 3 +#define CS4234_MSK_DAC3_CLIP_MASK 0x04 +#define CS4234_MSK_DAC3_CLIP_SHIFT 2 +#define CS4234_MSK_DAC2_CLIP_MASK 0x02 +#define CS4234_MSK_DAC2_CLIP_SHIFT 1 +#define CS4234_MSK_DAC1_CLIP_MASK 0x01 +#define CS4234_MSK_DAC1_CLIP_SHIFT 0 + +#define CS4234_INT_NOTIFY1 0x21 +#define CS4234_TST_MODE_MASK 0x80 +#define CS4234_TST_MODE_SHIFT 7 +#define CS4234_SP_ERR_MASK 0x40 +#define CS4234_SP_ERR_SHIFT 6 +#define CS4234_CLK_MOD_ERR_MASK 0x08 +#define CS4234_CLK_MOD_ERR_SHIFT 5 +#define CS4234_ADC4_OVFL_MASK 0x08 +#define CS4234_ADC4_OVFL_SHIFT 3 +#define CS4234_ADC3_OVFL_MASK 0x04 +#define CS4234_ADC3_OVFL_SHIFT 2 +#define CS4234_ADC2_OVFL_MASK 0x02 +#define CS4234_ADC2_OVFL_SHIFT 1 +#define CS4234_ADC1_OVFL_MASK 0x01 +#define CS4234_ADC1_OVFL_SHIFT 0 + +#define CS4234_INT_NOTIFY2 0x22 +#define CS4234_DAC5_CLIP_MASK 0x10 +#define CS4234_DAC5_CLIP_SHIFT 4 +#define CS4234_DAC4_CLIP_MASK 0x08 +#define CS4234_DAC4_CLIP_SHIFT 3 +#define CS4234_DAC3_CLIP_MASK 0x04 +#define CS4234_DAC3_CLIP_SHIFT 2 +#define CS4234_DAC2_CLIP_MASK 0x02 +#define CS4234_DAC2_CLIP_SHIFT 1 +#define CS4234_DAC1_CLIP_MASK 0x01 +#define CS4234_DAC1_CLIP_SHIFT 0 + +#define CS4234_MAX_REGISTER CS4234_INT_NOTIFY2 + +#define CS4234_SUPPORTED_ID 0x423400 +#define CS4234_BOOT_TIME_US 3000 +#define CS4234_HOLD_RESET_TIME_US 1000 +#define CS4234_VQ_CHARGE_MS 1000 + +#define CS4234_PCM_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_64000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define CS4234_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \ + SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_3LE) + +enum cs4234_supplies { + CS4234_SUPPLY_VA = 0, + CS4234_SUPPLY_VL, +}; + +enum cs4234_va_sel { + CS4234_3V3 = 0, + CS4234_5V, +}; + +enum cs4234_sp_format { + CS4234_LEFT_J = 0, + CS4234_I2S, + CS4234_TDM, +}; + +enum cs4234_base_rate_advisory { + CS4234_48K = 0, + CS4234_44K1, + CS4234_32K, +}; + +#endif -- cgit From 19895e92dd2b914fe4e66cca862eb8c7f927bee6 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Fri, 25 Sep 2020 17:48:56 +0100 Subject: ASoC: lpass-platform: use devm_regmap_field_bulk_alloc use new devm_regmap_field_bulk_alloc to allocate fields as it make the code more readable! Signed-off-by: Srinivas Kandagatla Tested-by: Srinivasa Rao Mandadapu Link: https://lore.kernel.org/r/20200925164856.10315-3-srinivas.kandagatla@linaro.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 31 +++++++------------------------ 1 file changed, 7 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index df692ed95503..7ac26290082f 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -56,6 +56,7 @@ static int lpass_platform_alloc_dmactl_fields(struct device *dev, struct lpass_data *drvdata = dev_get_drvdata(dev); struct lpass_variant *v = drvdata->variant; struct lpaif_dmactl *rd_dmactl, *wr_dmactl; + int rval; drvdata->rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), GFP_KERNEL); @@ -70,31 +71,13 @@ static int lpass_platform_alloc_dmactl_fields(struct device *dev, rd_dmactl = drvdata->rd_dmactl; wr_dmactl = drvdata->wr_dmactl; - rd_dmactl->bursten = devm_regmap_field_alloc(dev, map, v->rdma_bursten); - rd_dmactl->wpscnt = devm_regmap_field_alloc(dev, map, v->rdma_wpscnt); - rd_dmactl->fifowm = devm_regmap_field_alloc(dev, map, v->rdma_fifowm); - rd_dmactl->intf = devm_regmap_field_alloc(dev, map, v->rdma_intf); - rd_dmactl->enable = devm_regmap_field_alloc(dev, map, v->rdma_enable); - rd_dmactl->dyncclk = devm_regmap_field_alloc(dev, map, v->rdma_dyncclk); + rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->bursten, + &v->rdma_bursten, 6); + if (rval) + return rval; - if (IS_ERR(rd_dmactl->bursten) || IS_ERR(rd_dmactl->wpscnt) || - IS_ERR(rd_dmactl->fifowm) || IS_ERR(rd_dmactl->intf) || - IS_ERR(rd_dmactl->enable) || IS_ERR(rd_dmactl->dyncclk)) - return -EINVAL; - - wr_dmactl->bursten = devm_regmap_field_alloc(dev, map, v->wrdma_bursten); - wr_dmactl->wpscnt = devm_regmap_field_alloc(dev, map, v->wrdma_wpscnt); - wr_dmactl->fifowm = devm_regmap_field_alloc(dev, map, v->wrdma_fifowm); - wr_dmactl->intf = devm_regmap_field_alloc(dev, map, v->wrdma_intf); - wr_dmactl->enable = devm_regmap_field_alloc(dev, map, v->wrdma_enable); - wr_dmactl->dyncclk = devm_regmap_field_alloc(dev, map, v->wrdma_dyncclk); - - if (IS_ERR(wr_dmactl->bursten) || IS_ERR(wr_dmactl->wpscnt) || - IS_ERR(wr_dmactl->fifowm) || IS_ERR(wr_dmactl->intf) || - IS_ERR(wr_dmactl->enable) || IS_ERR(wr_dmactl->dyncclk)) - return -EINVAL; - - return 0; + return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->bursten, + &v->wrdma_bursten, 6); } static int lpass_platform_pcmops_open(struct snd_soc_component *component, -- cgit From 13468bfa8c58731dc9ecda1cd9b22a191114f944 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Mon, 28 Sep 2020 16:01:17 +0800 Subject: ALSA: hda/realtek - set mic to auto detect on a HP AIO machine Recently we enabled a HP AIO machine, we found the mic on the machine couldn't record any sound and it couldn't detect plugging and unplugging as well. Through debugging we found the mic is set to manual detect mode, after setting it to auto detect mode, it could detect plugging and unplugging and could record sound. Cc: Signed-off-by: Hui Wang Link: https://lore.kernel.org/r/20200928080117.12435-1-hui.wang@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index d4f17b465892..4e5809648df1 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -6233,6 +6233,7 @@ enum { ALC269_FIXUP_LEMOTE_A190X, ALC256_FIXUP_INTEL_NUC8_RUGGED, ALC255_FIXUP_XIAOMI_HEADSET_MIC, + ALC274_FIXUP_HP_MIC, }; static const struct hda_fixup alc269_fixups[] = { @@ -7612,6 +7613,14 @@ static const struct hda_fixup alc269_fixups[] = { .chained = true, .chain_id = ALC289_FIXUP_ASUS_GA401 }, + [ALC274_FIXUP_HP_MIC] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0x45 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x5089 }, + { } + }, + }, }; static const struct snd_pci_quirk alc269_fixup_tbl[] = { @@ -7763,6 +7772,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT), + SND_PCI_QUIRK(0x103c, 0x874e, "HP", ALC274_FIXUP_HP_MIC), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), @@ -8088,6 +8098,7 @@ static const struct hda_model_fixup alc269_fixup_models[] = { {.id = ALC256_FIXUP_MEDION_HEADSET_NO_PRESENCE, .name = "alc256-medion-headset"}, {.id = ALC298_FIXUP_SAMSUNG_HEADPHONE_VERY_QUIET, .name = "alc298-samsung-headphone"}, {.id = ALC255_FIXUP_XIAOMI_HEADSET_MIC, .name = "alc255-xiaomi-headset"}, + {.id = ALC274_FIXUP_HP_MIC, .name = "alc274-hp-mic-detect"}, {} }; #define ALC225_STANDARD_PINS \ -- cgit From f4794c6064a83d2c57b264bd299c367d172d1044 Mon Sep 17 00:00:00 2001 From: Hui Wang Date: Wed, 30 Sep 2020 13:51:46 +0800 Subject: ALSA: hda - Don't register a cb func if it is registered already If the caller of enable_callback_mst() passes a cb func, the callee function will malloc memory and link this cb func to the list unconditionally. This will introduce problem if caller is in the hda_codec_ops.init() since the init() will be repeatedly called in the codec rt_resume(). So far, the patch_hdmi.c and patch_ca0132.c call enable_callback_mst() in the hda_codec_ops.init(). Signed-off-by: Hui Wang Cc: Link: https://lore.kernel.org/r/20200930055146.5665-1-hui.wang@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_jack.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c index 02cc682caa55..ded4813f8b54 100644 --- a/sound/pci/hda/hda_jack.c +++ b/sound/pci/hda/hda_jack.c @@ -275,6 +275,18 @@ int snd_hda_jack_detect_state_mst(struct hda_codec *codec, } EXPORT_SYMBOL_GPL(snd_hda_jack_detect_state_mst); +static bool func_is_already_in_callback_list(struct hda_jack_tbl *jack, + hda_jack_callback_fn func) +{ + struct hda_jack_callback *cb; + + for (cb = jack->callback; cb; cb = cb->next) { + if (cb->func == func) + return true; + } + return false; +} + /** * snd_hda_jack_detect_enable_mst - enable the jack-detection * @codec: the HDA codec @@ -297,7 +309,7 @@ snd_hda_jack_detect_enable_callback_mst(struct hda_codec *codec, hda_nid_t nid, jack = snd_hda_jack_tbl_new(codec, nid, dev_id); if (!jack) return ERR_PTR(-ENOMEM); - if (func) { + if (func && !func_is_already_in_callback_list(jack, func)) { callback = kzalloc(sizeof(*callback), GFP_KERNEL); if (!callback) return ERR_PTR(-ENOMEM); -- cgit From ad61b78ea8913e5cf9c91f5527428eed1bd2a862 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:33 +0800 Subject: ASoC: soc-core: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112933.46977-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/soc-core.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 681df493bb98..62370774e3ce 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1994,16 +1994,7 @@ static int soc_probe(struct platform_device *pdev) /* Bodge while we unpick instantiation */ card->dev = &pdev->dev; - return snd_soc_register_card(card); -} - -/* removes a socdev */ -static int soc_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - return 0; + return devm_snd_soc_register_card(&pdev->dev, card); } int snd_soc_poweroff(struct device *dev) @@ -2047,7 +2038,6 @@ static struct platform_driver soc_driver = { .pm = &snd_soc_pm_ops, }, .probe = soc_probe, - .remove = soc_remove, }; /** -- cgit From a0645daf16101bb9a6d87598c17e9a8b7bd60ea7 Mon Sep 17 00:00:00 2001 From: Harsha Priya Date: Wed, 30 Sep 2020 14:41:39 +0300 Subject: ALSA: HDA: Early Forbid of runtime PM For certain codecs (like Realtek), pm_runtime_forbid() is invoked in the probe function after build_controls(). In a stress test, its observed occasionally that runtime PM calls are invoked before controls are built. This causes the codec to be runtime suspended before probe completes. Because of this, not all controls are enumerated correctly, and audio does not work until system is rebooted. This issue being common across all codecs, pm_runtime_forbid() is called when the codec object is created to fix this issue. A codec enables or disables runtime pm in its own probe function. Multiple stress tests of 2000+ cycles has been done to test the fix. Signed-off-by: Harsha Priya Signed-off-by: Emmanuel Jillela Reviewed-by: Kailang Yang Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20200930114140.3839617-2-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index e96a87f1b611..a356c21edb90 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1000,6 +1000,9 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, if (err < 0) goto error; + /* PM runtime needs to be enabled later after binding codec */ + pm_runtime_forbid(&codec->core.dev); + return 0; error: -- cgit From 7a2ba46f3693ec8fbd508c4e693692e85ac14ed6 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Wed, 30 Sep 2020 14:41:40 +0300 Subject: ASoC: hdac_hda: allow runtime pm at end of probe Align with recent change to forbid runtime suspend during codec init in snd_hda_codec_device_new(), with matching call to allow suspend at end of hdac_hda_codec_probe(). In snd-hda-intel, call to snd_hda_set_power_save() at end of controller probe does the same thing, but ASoC controller drivers do not modify runtime settings for codecs, so this has to be done in codec drivers, and in this case in hdac_hda. Signed-off-by: Kai Vehmanen Acked-by: Mark Brown Link: https://lore.kernel.org/r/20200930114140.3839617-3-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/soc/codecs/hdac_hda.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c index 49e6f23fc766..390dd6c7f6a5 100644 --- a/sound/soc/codecs/hdac_hda.c +++ b/sound/soc/codecs/hdac_hda.c @@ -481,6 +481,9 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component) snd_hdac_display_power(hdev->bus, HDA_CODEC_IDX_CONTROLLER, false); + /* match for forbid call in snd_hda_codec_device_new() */ + pm_runtime_allow(&hdev->dev); + /* * hdac_device core already sets the state to active and calls * get_noresume. So enable runtime and set the device to suspend. -- cgit From 62e5d7774629aac671c8c56a0ba016dbffb6dff2 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Wed, 30 Sep 2020 16:53:30 +0200 Subject: ASoC: atmel-pcm: remove unnecessary include Since commit 95e0e07e710e ("ASoC: atmel-pcm: use generic dmaengine framework"), the driver is using dmaengine and is not using any definition from include/linux/platform_data/dma-atmel.h, stop including it. Signed-off-by: Alexandre Belloni Link: https://lore.kernel.org/r/20200930145330.3043528-1-alexandre.belloni@bootlin.com Signed-off-by: Mark Brown --- sound/soc/atmel/atmel-pcm-dma.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c index e597e35459ce..96a8c7dba98f 100644 --- a/sound/soc/atmel/atmel-pcm-dma.c +++ b/sound/soc/atmel/atmel-pcm-dma.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include -- cgit From 20441614d89867142060d3bcd79cc111d8ba7a8e Mon Sep 17 00:00:00 2001 From: Adam Brickman Date: Thu, 1 Oct 2020 16:24:25 +0100 Subject: ASoC: wm_adsp: Pass full name to snd_ctl_notify A call to wm_adsp_write_ctl() could cause a kernel crash if it does not retrieve a valid kcontrol from snd_soc_card_get_kcontrol(). This can happen due to a missing control name prefix. Then, snd_ctl_notify() crashes when it tries to use the id field. Modified wm_adsp_write_ctl() to incorporate the name_prefix (if applicable) such that it is able to retrieve a valid id field from the kcontrol once the platform has booted. Fixes: eb65ccdb0836 ("ASoC: wm_adsp: Expose mixer control API") Signed-off-by: Adam Brickman Signed-off-by: Charles Keepax Link: https://lore.kernel.org/r/20201001152425.8590-1-ckeepax@opensource.cirrus.com Signed-off-by: Mark Brown --- sound/soc/codecs/wm_adsp.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 410cca57da52..344bd2c33bea 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -2049,6 +2049,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, { struct wm_coeff_ctl *ctl; struct snd_kcontrol *kcontrol; + char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ret; ctl = wm_adsp_get_ctl(dsp, name, type, alg); @@ -2059,8 +2060,25 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, return -EINVAL; ret = wm_coeff_write_ctrl(ctl, buf, len); + if (ret) + return ret; + + if (ctl->flags & WMFW_CTL_FLAG_SYS) + return 0; + + if (dsp->component->name_prefix) + snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", + dsp->component->name_prefix, ctl->name); + else + snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", + ctl->name); + + kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); + if (!kcontrol) { + adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); + return -EINVAL; + } - kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl->name); snd_ctl_notify(dsp->component->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); -- cgit From bc772a46125f344ffabd7596c5b6b8c6ef703ea0 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:30 +0800 Subject: ASoC: fsl: imx-mc13783: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112930.46848-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/fsl/imx-mc13783.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c index dd9c1ac81cf5..d9dca7bbcae3 100644 --- a/sound/soc/fsl/imx-mc13783.c +++ b/sound/soc/fsl/imx-mc13783.c @@ -96,7 +96,7 @@ static int imx_mc13783_probe(struct platform_device *pdev) imx_mc13783.dev = &pdev->dev; - ret = snd_soc_register_card(&imx_mc13783); + ret = devm_snd_soc_register_card(&pdev->dev, &imx_mc13783); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -140,19 +140,11 @@ static int imx_mc13783_probe(struct platform_device *pdev) return ret; } -static int imx_mc13783_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&imx_mc13783); - - return 0; -} - static struct platform_driver imx_mc13783_audio_driver = { .driver = { .name = "imx_mc13783", }, .probe = imx_mc13783_probe, - .remove = imx_mc13783_remove }; module_platform_driver(imx_mc13783_audio_driver); -- cgit From f8eeca97f71c92e12a2381708d5908716257028d Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:35 +0800 Subject: ASoC: tegra: tegra_max98090: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112935.47035-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra_max98090.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c index af3e9e6daa40..9d8e16473ab9 100644 --- a/sound/soc/tegra/tegra_max98090.c +++ b/sound/soc/tegra/tegra_max98090.c @@ -246,7 +246,7 @@ static int tegra_max98090_probe(struct platform_device *pdev) if (ret) return ret; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -256,15 +256,6 @@ static int tegra_max98090_probe(struct platform_device *pdev) return 0; } -static int tegra_max98090_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static const struct of_device_id tegra_max98090_of_match[] = { { .compatible = "nvidia,tegra-audio-max98090", }, {}, @@ -277,7 +268,6 @@ static struct platform_driver tegra_max98090_driver = { .of_match_table = tegra_max98090_of_match, }, .probe = tegra_max98090_probe, - .remove = tegra_max98090_remove, }; module_platform_driver(tegra_max98090_driver); -- cgit From 1b59b995774a086ea74e740b2dd38bba6d3d6664 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:38 +0800 Subject: ASoC: tegra: tegra_wm8753: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112938.47599-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra_wm8753.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c index ec3ee0580867..fa41fa366daf 100644 --- a/sound/soc/tegra/tegra_wm8753.c +++ b/sound/soc/tegra/tegra_wm8753.c @@ -155,7 +155,7 @@ static int tegra_wm8753_driver_probe(struct platform_device *pdev) if (ret) return ret; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -165,15 +165,6 @@ static int tegra_wm8753_driver_probe(struct platform_device *pdev) return 0; } -static int tegra_wm8753_driver_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static const struct of_device_id tegra_wm8753_of_match[] = { { .compatible = "nvidia,tegra-audio-wm8753", }, {}, @@ -186,7 +177,6 @@ static struct platform_driver tegra_wm8753_driver = { .of_match_table = tegra_wm8753_of_match, }, .probe = tegra_wm8753_driver_probe, - .remove = tegra_wm8753_driver_remove, }; module_platform_driver(tegra_wm8753_driver); -- cgit From 31e1fc4f11e2ede512221eba9e7291ab19ec66da Mon Sep 17 00:00:00 2001 From: Zhang Qilong Date: Tue, 22 Sep 2020 09:51:23 +0800 Subject: ASoC: ti: omap-mcbsp: use devm_platform_ioremap_resource_byname Use the devm_platform_ioremap_resource_byname() helper instead of calling platform_get_resource_byname() and devm_ioremap_resource() separately. Signed-off-by: Zhang Qilong Link: https://lore.kernel.org/r/20200922015123.117489-1-zhangqilong3@huawei.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-mcbsp.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 6025b30bbe77..186cea91076f 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -620,11 +620,7 @@ static int omap_mcbsp_init(struct platform_device *pdev) 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); + mcbsp->io_base = devm_platform_ioremap_resource_byname(pdev, "mpu"); if (IS_ERR(mcbsp->io_base)) return PTR_ERR(mcbsp->io_base); -- cgit From c859926abc8ebc0562a16a12b4993eab690c2281 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:36 +0800 Subject: ASoC: tegra: tegra_rt5640: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112936.47441-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/tegra/tegra_rt5640.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c index d66d8659396b..c73bd23b3d67 100644 --- a/sound/soc/tegra/tegra_rt5640.c +++ b/sound/soc/tegra/tegra_rt5640.c @@ -192,7 +192,7 @@ static int tegra_rt5640_probe(struct platform_device *pdev) if (ret) return ret; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -202,15 +202,6 @@ static int tegra_rt5640_probe(struct platform_device *pdev) return 0; } -static int tegra_rt5640_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static const struct of_device_id tegra_rt5640_of_match[] = { { .compatible = "nvidia,tegra-audio-rt5640", }, {}, @@ -223,7 +214,6 @@ static struct platform_driver tegra_rt5640_driver = { .of_match_table = tegra_rt5640_of_match, }, .probe = tegra_rt5640_probe, - .remove = tegra_rt5640_remove, }; module_platform_driver(tegra_rt5640_driver); -- cgit From 1047bcac2169a05575476774bfd4a88f0a9c787d Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:32 +0800 Subject: ASoC: fsl: mx27vis-aic32x4: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112932.46926-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/fsl/mx27vis-aic32x4.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c index 4ead537e090a..8d3b1897370b 100644 --- a/sound/soc/fsl/mx27vis-aic32x4.c +++ b/sound/soc/fsl/mx27vis-aic32x4.c @@ -176,7 +176,7 @@ static int mx27vis_aic32x4_probe(struct platform_device *pdev) mx27vis_amp_muter_gpio = pdata->amp_muter_gpio; mx27vis_aic32x4.dev = &pdev->dev; - ret = snd_soc_register_card(&mx27vis_aic32x4); + ret = devm_snd_soc_register_card(&pdev->dev, &mx27vis_aic32x4); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -199,19 +199,11 @@ static int mx27vis_aic32x4_probe(struct platform_device *pdev) return ret; } -static int mx27vis_aic32x4_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&mx27vis_aic32x4); - - return 0; -} - static struct platform_driver mx27vis_aic32x4_audio_driver = { .driver = { .name = "mx27vis", }, .probe = mx27vis_aic32x4_probe, - .remove = mx27vis_aic32x4_remove, }; module_platform_driver(mx27vis_aic32x4_audio_driver); -- cgit From 27f41dfebf226c74691b4b30527459417b6510e8 Mon Sep 17 00:00:00 2001 From: Qinglang Miao Date: Tue, 29 Sep 2020 19:29:39 +0800 Subject: ASoC: tegra: trimslice.c: use devm_snd_soc_register_card() Using devm_snd_soc_register_card() can make the code shorter and cleaner. Signed-off-by: Qinglang Miao Link: https://lore.kernel.org/r/20200929112939.47661-1-miaoqinglang@huawei.com Signed-off-by: Mark Brown --- sound/soc/tegra/trimslice.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'sound') diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index cdb386d6e5c3..baae4cce7fc6 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -143,7 +143,7 @@ static int tegra_snd_trimslice_probe(struct platform_device *pdev) if (ret) return ret; - ret = snd_soc_register_card(card); + ret = devm_snd_soc_register_card(&pdev->dev, card); if (ret) { dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); @@ -153,15 +153,6 @@ static int tegra_snd_trimslice_probe(struct platform_device *pdev) return 0; } -static int tegra_snd_trimslice_remove(struct platform_device *pdev) -{ - struct snd_soc_card *card = platform_get_drvdata(pdev); - - snd_soc_unregister_card(card); - - return 0; -} - static const struct of_device_id trimslice_of_match[] = { { .compatible = "nvidia,tegra-audio-trimslice", }, {}, @@ -174,7 +165,6 @@ static struct platform_driver tegra_snd_trimslice_driver = { .of_match_table = trimslice_of_match, }, .probe = tegra_snd_trimslice_probe, - .remove = tegra_snd_trimslice_remove, }; module_platform_driver(tegra_snd_trimslice_driver); -- cgit From cb2fce94c84e2c2798dca45aa00d1e03294fab95 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 1 Oct 2020 10:32:48 +0900 Subject: ASoC: soc-pcm: ignore un-needed mutex_unlock() case on soc_pcm_open() commit 140a4532cdb8c ("ASoC: soc-pcm: add soc_pcm_clean() and call it from soc_pcm_open/close()") switch to call soc_pcm_clean() on soc_pcm_open() when rollback case. But, it uses "goto err" (A) *before* mutex_lock() (B) when error of snd_soc_pcm_component_pm_runtime_get(). The mutex_unlock() (C) is not needed in such case. This patch fix it. static int soc_pcm_open(...) { ... ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) (A) goto err; (B) mutex_lock_nested(...); ... err: (C) mutex_unlock(..); ... } Signed-off-by: Kuninori Morimoto Link: https://lore.kernel.org/r/87k0waag44.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 09e8d703a502..5d538520e2cf 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -706,7 +706,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) ret = snd_soc_pcm_component_pm_runtime_get(rtd, substream); if (ret < 0) - goto err; + goto pm_err; mutex_lock_nested(&rtd->card->pcm_mutex, rtd->card->pcm_subclass); @@ -786,7 +786,7 @@ dynamic: snd_soc_runtime_activate(rtd, substream->stream); err: mutex_unlock(&rtd->card->pcm_mutex); - +pm_err: if (ret < 0) soc_pcm_clean(substream, 1); -- cgit From 8e7875ae373a3690397c4e593629d3b9ad5ccf42 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Thu, 1 Oct 2020 14:07:41 +0900 Subject: ASoC: soc-pcm: add missing ret=0 at soc_pcm_open() commit 140a4532cdb8c ("ASoC: soc-pcm: add soc_pcm_clean() and call it from soc_pcm_open/close()") switched to use soc_pcm_clean() at soc_pcm_open(). But it removed "return 0", and missing "ret = 0", because of it, it always return -EINVAL eventhough no error. This patch adds missing "ret = 0" for success case. Reported-by: Marek Szyprowski Signed-off-by: Kuninori Morimoto Tested-by: Marek Szyprowski Link: https://lore.kernel.org/r/87ft6ya65z.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Mark Brown --- sound/soc/soc-pcm.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 5d538520e2cf..dcab9527ba3d 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -784,6 +784,7 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) runtime->hw.rate_max); dynamic: snd_soc_runtime_activate(rtd, substream->stream); + ret = 0; err: mutex_unlock(&rtd->card->pcm_mutex); pm_err: -- cgit From 601fd3a7d849cf8a5dbd3628b3c29af9e5377961 Mon Sep 17 00:00:00 2001 From: Tang Bin Date: Wed, 26 Aug 2020 23:09:18 +0800 Subject: ASoC: fsl_spdif: Fix unnecessary check in fsl_spdif_probe() The function fsl_spdif_probe() is only called with an openfirmware platform device. Therefore there is no need to check that the passed in device is NULL. Signed-off-by: Zhang Shengju Signed-off-by: Tang Bin Link: https://lore.kernel.org/r/20200826150918.16116-1-tangbin@cmss.chinamobile.com Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 455f96908377..9c5607780590 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1259,9 +1259,6 @@ static int fsl_spdif_probe(struct platform_device *pdev) void __iomem *regs; int irq, ret, i; - if (!np) - return -ENODEV; - spdif_priv = devm_kzalloc(&pdev->dev, sizeof(*spdif_priv), GFP_KERNEL); if (!spdif_priv) return -ENOMEM; -- cgit From 716a0c2881938d222bde67d7edf630ea0648b8f7 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sun, 25 Nov 2018 23:12:08 +0000 Subject: ALSA: usb-audio: fix spelling mistake "Frequence" -> "Frequency" There are spelling mistakes in equalizer name fields, fix them. Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20181125231208.14350-1-colin.king@canonical.com Signed-off-by: Takashi Iwai --- sound/usb/mixer_us16x08.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound') diff --git a/sound/usb/mixer_us16x08.c b/sound/usb/mixer_us16x08.c index a4d4d71db55b..92b1a6d9c931 100644 --- a/sound/usb/mixer_us16x08.c +++ b/sound/usb/mixer_us16x08.c @@ -1109,7 +1109,7 @@ static const struct snd_us16x08_control_params eq_controls[] = { .control_id = SND_US16X08_ID_EQLOWFREQ, .type = USB_MIXER_U8, .num_channels = 16, - .name = "EQ Low Frequence", + .name = "EQ Low Frequency", }, { /* EQ mid low gain */ .kcontrol_new = &snd_us16x08_eq_gain_ctl, @@ -1123,7 +1123,7 @@ static const struct snd_us16x08_control_params eq_controls[] = { .control_id = SND_US16X08_ID_EQLOWMIDFREQ, .type = USB_MIXER_U8, .num_channels = 16, - .name = "EQ MidLow Frequence", + .name = "EQ MidLow Frequency", }, { /* EQ mid low Q */ .kcontrol_new = &snd_us16x08_eq_mid_width_ctl, @@ -1144,7 +1144,7 @@ static const struct snd_us16x08_control_params eq_controls[] = { .control_id = SND_US16X08_ID_EQHIGHMIDFREQ, .type = USB_MIXER_U8, .num_channels = 16, - .name = "EQ MidHigh Frequence", + .name = "EQ MidHigh Frequency", }, { /* EQ mid high Q */ .kcontrol_new = &snd_us16x08_eq_mid_width_ctl, @@ -1165,7 +1165,7 @@ static const struct snd_us16x08_control_params eq_controls[] = { .control_id = SND_US16X08_ID_EQHIGHFREQ, .type = USB_MIXER_U8, .num_channels = 16, - .name = "EQ High Frequence", + .name = "EQ High Frequency", }, }; -- cgit From 08befca40026136c14c3cd84f9e36c4cd20a358e Mon Sep 17 00:00:00 2001 From: Qiu Wenbo Date: Fri, 2 Oct 2020 20:44:54 +0800 Subject: ALSA: hda/realtek - Add mute Led support for HP Elitebook 845 G7 After installing archlinux, the mute led and micmute led are not working at all. This patch fix this issue by applying a fixup from similar model. These mute leds are confirmed working on HP Elitebook 845 G7. Signed-off-by: Qiu Wenbo Cc: Link: https://lore.kernel.org/r/20201002124454.7240-1-qiuwenbo@kylinos.com.cn Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 4e5809648df1..cf896f398ae7 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -7773,6 +7773,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x103c, 0x8729, "HP", ALC285_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x8736, "HP", ALC285_FIXUP_HP_GPIO_AMP_INIT), SND_PCI_QUIRK(0x103c, 0x874e, "HP", ALC274_FIXUP_HP_MIC), + SND_PCI_QUIRK(0x103c, 0x8760, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877a, "HP", ALC285_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x103c, 0x877d, "HP", ALC236_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC), -- cgit From 4fac9b31d0b9d3b3e9dd1408f457139f94077bc5 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:34 +0200 Subject: ASoC: Intel: Add catpt base members Declare base structures, registers and extension routines for the catpt solution. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 86 ++++++++++++++++++ sound/soc/intel/catpt/dsp.c | 138 +++++++++++++++++++++++++++++ sound/soc/intel/catpt/loader.c | 46 ++++++++++ sound/soc/intel/catpt/registers.h | 179 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 449 insertions(+) create mode 100644 sound/soc/intel/catpt/core.h create mode 100644 sound/soc/intel/catpt/dsp.c create mode 100644 sound/soc/intel/catpt/loader.c create mode 100644 sound/soc/intel/catpt/registers.h (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h new file mode 100644 index 000000000000..48952e93d86f --- /dev/null +++ b/sound/soc/intel/catpt/core.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef __SND_SOC_INTEL_CATPT_CORE_H +#define __SND_SOC_INTEL_CATPT_CORE_H + +#include +#include "registers.h" + +struct catpt_dev; + +void catpt_sram_init(struct resource *sram, u32 start, u32 size); +void catpt_sram_free(struct resource *sram); +struct resource * +catpt_request_region(struct resource *root, resource_size_t size); + +static inline bool catpt_resource_overlapping(struct resource *r1, + struct resource *r2, + struct resource *ret) +{ + if (!resource_overlaps(r1, r2)) + return false; + ret->start = max(r1->start, r2->start); + ret->end = min(r1->end, r2->end); + return true; +} + +struct catpt_module_type { + bool loaded; + u32 entry_point; + u32 persistent_size; + u32 scratch_size; + /* DRAM, initial module state */ + u32 state_offset; + u32 state_size; + + struct list_head node; +}; + +struct catpt_spec { + struct snd_soc_acpi_mach *machines; + u8 core_id; + u32 host_dram_offset; + u32 host_iram_offset; + u32 host_shim_offset; + u32 host_dma_offset[CATPT_DMA_COUNT]; + u32 host_ssp_offset[CATPT_SSP_COUNT]; + u32 dram_mask; + u32 iram_mask; + void (*pll_shutdown)(struct catpt_dev *cdev, bool enable); + int (*power_up)(struct catpt_dev *cdev); + int (*power_down)(struct catpt_dev *cdev); +}; + +struct catpt_dev { + struct device *dev; + struct dw_dma_chip *dmac; + + void __iomem *pci_ba; + void __iomem *lpe_ba; + u32 lpe_base; + int irq; + + const struct catpt_spec *spec; + struct completion fw_ready; + + struct resource dram; + struct resource iram; + struct resource *scratch; +}; + +int catpt_dmac_probe(struct catpt_dev *cdev); +void catpt_dmac_remove(struct catpt_dev *cdev); +struct dma_chan *catpt_dma_request_config_chan(struct catpt_dev *cdev); +int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan, + dma_addr_t dst_addr, dma_addr_t src_addr, + size_t size); +int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan, + dma_addr_t dst_addr, dma_addr_t src_addr, + size_t size); + +#endif diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c new file mode 100644 index 000000000000..c78da206a9fe --- /dev/null +++ b/sound/soc/intel/catpt/dsp.c @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include "core.h" +#include "registers.h" + +static bool catpt_dma_filter(struct dma_chan *chan, void *param) +{ + return param == chan->device->dev; +} + +/* + * Either engine 0 or 1 can be used for image loading. + * Align with Windows driver equivalent and stick to engine 1. + */ +#define CATPT_DMA_DEVID 1 +#define CATPT_DMA_DSP_ADDR_MASK GENMASK(31, 20) + +struct dma_chan *catpt_dma_request_config_chan(struct catpt_dev *cdev) +{ + struct dma_slave_config config; + struct dma_chan *chan; + dma_cap_mask_t mask; + int ret; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + + chan = dma_request_channel(mask, catpt_dma_filter, cdev->dev); + if (!chan) { + dev_err(cdev->dev, "request channel failed\n"); + return ERR_PTR(-ENODEV); + } + + memset(&config, 0, sizeof(config)); + config.direction = DMA_MEM_TO_DEV; + config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + config.src_maxburst = 16; + config.dst_maxburst = 16; + + ret = dmaengine_slave_config(chan, &config); + if (ret) { + dev_err(cdev->dev, "slave config failed: %d\n", ret); + dma_release_channel(chan); + return ERR_PTR(ret); + } + + return chan; +} + +static int catpt_dma_memcpy(struct catpt_dev *cdev, struct dma_chan *chan, + dma_addr_t dst_addr, dma_addr_t src_addr, + size_t size) +{ + struct dma_async_tx_descriptor *desc; + enum dma_status status; + + desc = dmaengine_prep_dma_memcpy(chan, dst_addr, src_addr, size, + DMA_CTRL_ACK); + if (!desc) { + dev_err(cdev->dev, "prep dma memcpy failed\n"); + return -EIO; + } + + /* enable demand mode for dma channel */ + catpt_updatel_shim(cdev, HMDC, + CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), + CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id)); + dmaengine_submit(desc); + status = dma_wait_for_async_tx(desc); + /* regardless of status, disable access to HOST memory in demand mode */ + catpt_updatel_shim(cdev, HMDC, + CATPT_HMDC_HDDA(CATPT_DMA_DEVID, chan->chan_id), 0); + + return (status == DMA_COMPLETE) ? 0 : -EPROTO; +} + +int catpt_dma_memcpy_todsp(struct catpt_dev *cdev, struct dma_chan *chan, + dma_addr_t dst_addr, dma_addr_t src_addr, + size_t size) +{ + return catpt_dma_memcpy(cdev, chan, dst_addr | CATPT_DMA_DSP_ADDR_MASK, + src_addr, size); +} + +int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan, + dma_addr_t dst_addr, dma_addr_t src_addr, + size_t size) +{ + return catpt_dma_memcpy(cdev, chan, dst_addr, + src_addr | CATPT_DMA_DSP_ADDR_MASK, size); +} + +int catpt_dmac_probe(struct catpt_dev *cdev) +{ + struct dw_dma_chip *dmac; + int ret; + + dmac = devm_kzalloc(cdev->dev, sizeof(*dmac), GFP_KERNEL); + if (!dmac) + return -ENOMEM; + + dmac->regs = cdev->lpe_ba + cdev->spec->host_dma_offset[CATPT_DMA_DEVID]; + dmac->dev = cdev->dev; + dmac->irq = cdev->irq; + + ret = dma_coerce_mask_and_coherent(cdev->dev, DMA_BIT_MASK(31)); + if (ret) + return ret; + /* + * Caller is responsible for putting device in D0 to allow + * for I/O and memory access before probing DW. + */ + ret = dw_dma_probe(dmac); + if (ret) + return ret; + + cdev->dmac = dmac; + return 0; +} + +void catpt_dmac_remove(struct catpt_dev *cdev) +{ + /* + * As do_dma_remove() juggles with pm_runtime_get_xxx() and + * pm_runtime_put_xxx() while both ADSP and DW 'devices' are part of + * the same module, caller makes sure pm_runtime_disable() is invoked + * before removing DW to prevent postmortem resume and suspend. + */ + dw_dma_remove(cdev->dmac); +} diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c new file mode 100644 index 000000000000..3a7e5b396a86 --- /dev/null +++ b/sound/soc/intel/catpt/loader.c @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include "core.h" + +void catpt_sram_init(struct resource *sram, u32 start, u32 size) +{ + sram->start = start; + sram->end = start + size - 1; +} + +void catpt_sram_free(struct resource *sram) +{ + struct resource *res, *save; + + for (res = sram->child; res;) { + save = res->sibling; + release_resource(res); + kfree(res); + res = save; + } +} + +struct resource * +catpt_request_region(struct resource *root, resource_size_t size) +{ + struct resource *res = root->child; + resource_size_t addr = root->start; + + for (;;) { + if (res->start - addr >= size) + break; + addr = res->end + 1; + res = res->sibling; + if (!res) + return NULL; + } + + return __request_region(root, addr, size, NULL, 0); +} diff --git a/sound/soc/intel/catpt/registers.h b/sound/soc/intel/catpt/registers.h new file mode 100644 index 000000000000..a051d8b36d88 --- /dev/null +++ b/sound/soc/intel/catpt/registers.h @@ -0,0 +1,179 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef __SND_SOC_INTEL_CATPT_REGS_H +#define __SND_SOC_INTEL_CATPT_REGS_H + +#include +#include +#include + +#define CATPT_SHIM_REGS_SIZE 4096 +#define CATPT_DMA_REGS_SIZE 1024 +#define CATPT_DMA_COUNT 2 +#define CATPT_SSP_COUNT 2 +#define CATPT_SSP_REGS_SIZE 512 + +/* DSP Shim registers */ + +#define CATPT_SHIM_CS1 0x00 +#define CATPT_SHIM_ISC 0x18 +#define CATPT_SHIM_ISD 0x20 +#define CATPT_SHIM_IMC 0x28 +#define CATPT_SHIM_IMD 0x30 +#define CATPT_SHIM_IPCC 0x38 +#define CATPT_SHIM_IPCD 0x40 +#define CATPT_SHIM_CLKCTL 0x78 +#define CATPT_SHIM_CS2 0x80 +#define CATPT_SHIM_LTRC 0xE0 +#define CATPT_SHIM_HMDC 0xE8 + +#define CATPT_CS_LPCS BIT(31) +#define CATPT_CS_SFCR(ssp) BIT(27 + (ssp)) +#define CATPT_CS_S1IOCS BIT(23) +#define CATPT_CS_S0IOCS BIT(21) +#define CATPT_CS_PCE BIT(15) +#define CATPT_CS_SDPM(ssp) BIT(11 + (ssp)) +#define CATPT_CS_STALL BIT(10) +#define CATPT_CS_DCS GENMASK(6, 4) +/* b100 DSP core & audio fabric high clock */ +#define CATPT_CS_DCS_HIGH (0x4 << 4) +#define CATPT_CS_SBCS(ssp) BIT(2 + (ssp)) +#define CATPT_CS_RST BIT(1) + +#define CATPT_ISC_IPCDB BIT(1) +#define CATPT_ISC_IPCCD BIT(0) +#define CATPT_ISD_DCPWM BIT(31) +#define CATPT_ISD_IPCCB BIT(1) +#define CATPT_ISD_IPCDD BIT(0) + +#define CATPT_IMC_IPCDB BIT(1) +#define CATPT_IMC_IPCCD BIT(0) +#define CATPT_IMD_IPCCB BIT(1) +#define CATPT_IMD_IPCDD BIT(0) + +#define CATPT_IPCC_BUSY BIT(31) +#define CATPT_IPCC_DONE BIT(30) +#define CATPT_IPCD_BUSY BIT(31) +#define CATPT_IPCD_DONE BIT(30) + +#define CATPT_CLKCTL_CFCIP BIT(31) +#define CATPT_CLKCTL_SMOS GENMASK(25, 24) + +#define CATPT_HMDC_HDDA(e, ch) BIT(8 * (e) + (ch)) + +/* defaults to reset SHIM registers to after each power cycle */ +#define CATPT_CS_DEFAULT 0x8480040E +#define CATPT_ISC_DEFAULT 0x0 +#define CATPT_ISD_DEFAULT 0x0 +#define CATPT_IMC_DEFAULT 0x7FFF0003 +#define CATPT_IMD_DEFAULT 0x7FFF0003 +#define CATPT_IPCC_DEFAULT 0x0 +#define CATPT_IPCD_DEFAULT 0x0 +#define CATPT_CLKCTL_DEFAULT 0x7FF +#define CATPT_CS2_DEFAULT 0x0 +#define CATPT_LTRC_DEFAULT 0x0 +#define CATPT_HMDC_DEFAULT 0x0 + +/* PCI Configuration registers */ + +#define CATPT_PCI_PMCAPID 0x80 +#define CATPT_PCI_PMCS (CATPT_PCI_PMCAPID + PCI_PM_CTRL) +#define CATPT_PCI_VDRTCTL0 0xA0 +#define CATPT_PCI_VDRTCTL2 0xA8 + +#define CATPT_VDRTCTL2_DTCGE BIT(10) +#define CATPT_VDRTCTL2_DCLCGE BIT(1) +#define CATPT_VDRTCTL2_CGEALL 0xF7F + +/* LPT PCI Configuration bits */ + +#define LPT_VDRTCTL0_DSRAMPGE(b) BIT(16 + (b)) +#define LPT_VDRTCTL0_DSRAMPGE_MASK GENMASK(31, 16) +#define LPT_VDRTCTL0_ISRAMPGE(b) BIT(6 + (b)) +#define LPT_VDRTCTL0_ISRAMPGE_MASK GENMASK(15, 6) +#define LPT_VDRTCTL0_D3SRAMPGD BIT(2) +#define LPT_VDRTCTL0_D3PGD BIT(1) +#define LPT_VDRTCTL0_APLLSE BIT(0) + +/* WPT PCI Configuration bits */ + +#define WPT_VDRTCTL0_DSRAMPGE(b) BIT(12 + (b)) +#define WPT_VDRTCTL0_DSRAMPGE_MASK GENMASK(31, 12) +#define WPT_VDRTCTL0_ISRAMPGE(b) BIT(2 + (b)) +#define WPT_VDRTCTL0_ISRAMPGE_MASK GENMASK(11, 2) +#define WPT_VDRTCTL0_D3SRAMPGD BIT(1) +#define WPT_VDRTCTL0_D3PGD BIT(0) + +#define WPT_VDRTCTL2_APLLSE BIT(31) + +/* defaults to reset SSP registers to after each power cycle */ +#define CATPT_SSC0_DEFAULT 0x0 +#define CATPT_SSC1_DEFAULT 0x0 +#define CATPT_SSS_DEFAULT 0xF004 +#define CATPT_SSIT_DEFAULT 0x0 +#define CATPT_SSD_DEFAULT 0xC43893A3 +#define CATPT_SSTO_DEFAULT 0x0 +#define CATPT_SSPSP_DEFAULT 0x0 +#define CATPT_SSTSA_DEFAULT 0x0 +#define CATPT_SSRSA_DEFAULT 0x0 +#define CATPT_SSTSS_DEFAULT 0x0 +#define CATPT_SSCR2_DEFAULT 0x0 +#define CATPT_SSPSP2_DEFAULT 0x0 + +/* Physically the same block, access address differs between host and dsp */ +#define CATPT_DSP_DRAM_OFFSET 0x400000 +#define catpt_to_host_offset(offset) ((offset) & ~(CATPT_DSP_DRAM_OFFSET)) +#define catpt_to_dsp_offset(offset) ((offset) | CATPT_DSP_DRAM_OFFSET) + +#define CATPT_MEMBLOCK_SIZE 0x8000 +#define catpt_num_dram(cdev) (hweight_long((cdev)->spec->dram_mask)) +#define catpt_num_iram(cdev) (hweight_long((cdev)->spec->iram_mask)) +#define catpt_dram_size(cdev) (catpt_num_dram(cdev) * CATPT_MEMBLOCK_SIZE) +#define catpt_iram_size(cdev) (catpt_num_iram(cdev) * CATPT_MEMBLOCK_SIZE) + +/* registry I/O helpers */ + +#define catpt_shim_addr(cdev) \ + ((cdev)->lpe_ba + (cdev)->spec->host_shim_offset) +#define catpt_dma_addr(cdev, dma) \ + ((cdev)->lpe_ba + (cdev)->spec->host_dma_offset[dma]) +#define catpt_ssp_addr(cdev, ssp) \ + ((cdev)->lpe_ba + (cdev)->spec->host_ssp_offset[ssp]) +#define catpt_inbox_addr(cdev) \ + ((cdev)->lpe_ba + (cdev)->ipc.config.inbox_offset) +#define catpt_outbox_addr(cdev) \ + ((cdev)->lpe_ba + (cdev)->ipc.config.outbox_offset) + +#define catpt_writel_ssp(cdev, ssp, reg, val) \ + writel(val, catpt_ssp_addr(cdev, ssp) + (reg)) + +#define catpt_readl_shim(cdev, reg) \ + readl(catpt_shim_addr(cdev) + CATPT_SHIM_##reg) +#define catpt_writel_shim(cdev, reg, val) \ + writel(val, catpt_shim_addr(cdev) + CATPT_SHIM_##reg) +#define catpt_updatel_shim(cdev, reg, mask, val) \ + catpt_writel_shim(cdev, reg, \ + (catpt_readl_shim(cdev, reg) & ~(mask)) | (val)) + +#define catpt_readl_poll_shim(cdev, reg, val, cond, delay_us, timeout_us) \ + readl_poll_timeout(catpt_shim_addr(cdev) + CATPT_SHIM_##reg, \ + val, cond, delay_us, timeout_us) + +#define catpt_readl_pci(cdev, reg) \ + readl(cdev->pci_ba + CATPT_PCI_##reg) +#define catpt_writel_pci(cdev, reg, val) \ + writel(val, cdev->pci_ba + CATPT_PCI_##reg) +#define catpt_updatel_pci(cdev, reg, mask, val) \ + catpt_writel_pci(cdev, reg, \ + (catpt_readl_pci(cdev, reg) & ~(mask)) | (val)) + +#define catpt_readl_poll_pci(cdev, reg, val, cond, delay_us, timeout_us) \ + readl_poll_timeout((cdev)->pci_ba + CATPT_PCI_##reg, \ + val, cond, delay_us, timeout_us) + +#endif -- cgit From 92946c1d7ea8c5e19a4d7b4bd8896f04dc09c655 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:35 +0200 Subject: ASoC: Intel: catpt: Implement IPC protocol Implement IRQ handlers for immediate and delayed replies and notifications. Communication is synchronous and allows for serialization of maximum one message at a time. DSP may respond with ADSP_PENDING status for a request - known as delayed reply - and when situation occurs, framework keeps the lock and awaits upcoming response through IPCD channel which is handled in bottom-half. Immediate replies spawn no BH at all as their processing is very short. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 44 +++++ sound/soc/intel/catpt/ipc.c | 246 +++++++++++++++++++++++ sound/soc/intel/catpt/messages.h | 401 ++++++++++++++++++++++++++++++++++++++ sound/soc/intel/catpt/registers.h | 1 - 4 files changed, 691 insertions(+), 1 deletion(-) create mode 100644 sound/soc/intel/catpt/ipc.c create mode 100644 sound/soc/intel/catpt/messages.h (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 48952e93d86f..8819d928e891 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -9,6 +9,8 @@ #define __SND_SOC_INTEL_CATPT_CORE_H #include +#include +#include "messages.h" #include "registers.h" struct catpt_dev; @@ -29,6 +31,31 @@ static inline bool catpt_resource_overlapping(struct resource *r1, return true; } +struct catpt_ipc_msg { + union { + u32 header; + union catpt_global_msg rsp; + }; + void *data; + size_t size; +}; + +struct catpt_ipc { + struct device *dev; + + struct catpt_ipc_msg rx; + struct catpt_fw_ready config; + u32 default_timeout; + bool ready; + + spinlock_t lock; + struct mutex mutex; + struct completion done_completion; + struct completion busy_completion; +}; + +void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev); + struct catpt_module_type { bool loaded; u32 entry_point; @@ -59,6 +86,7 @@ struct catpt_spec { struct catpt_dev { struct device *dev; struct dw_dma_chip *dmac; + struct catpt_ipc ipc; void __iomem *pci_ba; void __iomem *lpe_ba; @@ -83,4 +111,20 @@ int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t dst_addr, dma_addr_t src_addr, size_t size); +irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id); +irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id); + +/* + * IPC handlers may return positive values which denote successful + * HOST <-> DSP communication yet failure to process specific request. + * Use below macro to convert returned non-zero values appropriately + */ +#define CATPT_IPC_ERROR(err) (((err) < 0) ? (err) : -EREMOTEIO) + +int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, + struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply, int timeout); +int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply); + #endif diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c new file mode 100644 index 000000000000..060fa8d97e50 --- /dev/null +++ b/sound/soc/intel/catpt/ipc.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include "core.h" +#include "messages.h" +#include "registers.h" + +#define CATPT_IPC_TIMEOUT_MS 300 + +void catpt_ipc_init(struct catpt_ipc *ipc, struct device *dev) +{ + ipc->dev = dev; + ipc->ready = false; + ipc->default_timeout = CATPT_IPC_TIMEOUT_MS; + init_completion(&ipc->done_completion); + init_completion(&ipc->busy_completion); + spin_lock_init(&ipc->lock); + mutex_init(&ipc->mutex); +} + +static int catpt_ipc_arm(struct catpt_ipc *ipc, struct catpt_fw_ready *config) +{ + /* + * Both tx and rx are put into and received from outbox. Inbox is + * only used for notifications where payload size is known upfront, + * thus no separate buffer is allocated for it. + */ + ipc->rx.data = devm_kzalloc(ipc->dev, config->outbox_size, GFP_KERNEL); + if (!ipc->rx.data) + return -ENOMEM; + + memcpy(&ipc->config, config, sizeof(*config)); + ipc->ready = true; + + return 0; +} + +static void catpt_ipc_msg_init(struct catpt_ipc *ipc, + struct catpt_ipc_msg *reply) +{ + lockdep_assert_held(&ipc->lock); + + ipc->rx.header = 0; + ipc->rx.size = reply ? reply->size : 0; + reinit_completion(&ipc->done_completion); + reinit_completion(&ipc->busy_completion); +} + +static void catpt_dsp_send_tx(struct catpt_dev *cdev, + const struct catpt_ipc_msg *tx) +{ + u32 header = tx->header | CATPT_IPCC_BUSY; + + memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size); + catpt_writel_shim(cdev, IPCC, header); +} + +static int catpt_wait_msg_completion(struct catpt_dev *cdev, int timeout) +{ + struct catpt_ipc *ipc = &cdev->ipc; + int ret; + + ret = wait_for_completion_timeout(&ipc->done_completion, + msecs_to_jiffies(timeout)); + if (!ret) + return -ETIMEDOUT; + if (ipc->rx.rsp.status != CATPT_REPLY_PENDING) + return 0; + + /* wait for delayed reply */ + ret = wait_for_completion_timeout(&ipc->busy_completion, + msecs_to_jiffies(timeout)); + return ret ? 0 : -ETIMEDOUT; +} + +static int catpt_dsp_do_send_msg(struct catpt_dev *cdev, + struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply, int timeout) +{ + struct catpt_ipc *ipc = &cdev->ipc; + unsigned long flags; + int ret; + + if (!ipc->ready) + return -EPERM; + if (request.size > ipc->config.outbox_size || + (reply && reply->size > ipc->config.outbox_size)) + return -EINVAL; + + spin_lock_irqsave(&ipc->lock, flags); + catpt_ipc_msg_init(ipc, reply); + catpt_dsp_send_tx(cdev, &request); + spin_unlock_irqrestore(&ipc->lock, flags); + + ret = catpt_wait_msg_completion(cdev, timeout); + if (ret) { + dev_crit(cdev->dev, "communication severed: %d, rebooting dsp..\n", + ret); + ipc->ready = false; + /* TODO: attempt recovery */ + return ret; + } + + ret = ipc->rx.rsp.status; + if (reply) { + reply->header = ipc->rx.header; + + if (!ret && reply->data) + memcpy(reply->data, ipc->rx.data, reply->size); + } + + return ret; +} + +int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, + struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply, int timeout) +{ + struct catpt_ipc *ipc = &cdev->ipc; + int ret; + + mutex_lock(&ipc->mutex); + ret = catpt_dsp_do_send_msg(cdev, request, reply, timeout); + mutex_unlock(&ipc->mutex); + + return ret; +} + +int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, + struct catpt_ipc_msg *reply) +{ + return catpt_dsp_send_msg_timeout(cdev, request, reply, + cdev->ipc.default_timeout); +} + +static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header) +{ + struct catpt_ipc *ipc = &cdev->ipc; + + ipc->rx.header = header; + if (ipc->rx.rsp.status != CATPT_REPLY_SUCCESS) + return; + + memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size); +} + +static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header) +{ + union catpt_notify_msg msg = CATPT_MSG(header); + struct catpt_ipc *ipc = &cdev->ipc; + + if (msg.fw_ready) { + struct catpt_fw_ready config; + /* to fit 32b header original address is shifted right by 3 */ + u32 off = msg.mailbox_address << 3; + + memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config)); + + catpt_ipc_arm(ipc, &config); + complete(&cdev->fw_ready); + return; + } + + switch (msg.global_msg_type) { + case CATPT_GLB_REQUEST_CORE_DUMP: + break; + + case CATPT_GLB_STREAM_MESSAGE: + switch (msg.stream_msg_type) { + case CATPT_STRM_NOTIFICATION: + break; + default: + catpt_dsp_copy_rx(cdev, header); + /* signal completion of delayed reply */ + complete(&ipc->busy_completion); + break; + } + break; + + default: + dev_warn(cdev->dev, "unknown response: %d received\n", + msg.global_msg_type); + break; + } +} + +irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id) +{ + struct catpt_dev *cdev = dev_id; + u32 ipcd; + + ipcd = catpt_readl_shim(cdev, IPCD); + + /* ensure there is delayed reply or notification to process */ + if (!(ipcd & CATPT_IPCD_BUSY)) + return IRQ_NONE; + + catpt_dsp_process_response(cdev, ipcd); + + /* tell DSP processing is completed */ + catpt_updatel_shim(cdev, IPCD, CATPT_IPCD_BUSY | CATPT_IPCD_DONE, + CATPT_IPCD_DONE); + /* unmask dsp BUSY interrupt */ + catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, 0); + + return IRQ_HANDLED; +} + +irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id) +{ + struct catpt_dev *cdev = dev_id; + irqreturn_t ret = IRQ_NONE; + u32 isc, ipcc; + + isc = catpt_readl_shim(cdev, ISC); + + /* immediate reply */ + if (isc & CATPT_ISC_IPCCD) { + /* mask host DONE interrupt */ + catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD); + + ipcc = catpt_readl_shim(cdev, IPCC); + catpt_dsp_copy_rx(cdev, ipcc); + complete(&cdev->ipc.done_completion); + + /* tell DSP processing is completed */ + catpt_updatel_shim(cdev, IPCC, CATPT_IPCC_DONE, 0); + /* unmask host DONE interrupt */ + catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, 0); + ret = IRQ_HANDLED; + } + + /* delayed reply or notification */ + if (isc & CATPT_ISC_IPCDB) { + /* mask dsp BUSY interrupt */ + catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB, CATPT_IMC_IPCDB); + ret = IRQ_WAKE_THREAD; + } + + return ret; +} diff --git a/sound/soc/intel/catpt/messages.h b/sound/soc/intel/catpt/messages.h new file mode 100644 index 000000000000..978a20b3f471 --- /dev/null +++ b/sound/soc/intel/catpt/messages.h @@ -0,0 +1,401 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#ifndef __SND_SOC_INTEL_CATPT_MSG_H +#define __SND_SOC_INTEL_CATPT_MSG_H + +struct catpt_dev; + +/* IPC messages base types */ + +enum catpt_reply_status { + CATPT_REPLY_SUCCESS = 0, + CATPT_REPLY_ERROR_INVALID_PARAM = 1, + CATPT_REPLY_UNKNOWN_MESSAGE_TYPE = 2, + CATPT_REPLY_OUT_OF_RESOURCES = 3, + CATPT_REPLY_BUSY = 4, + CATPT_REPLY_PENDING = 5, + CATPT_REPLY_FAILURE = 6, + CATPT_REPLY_INVALID_REQUEST = 7, + CATPT_REPLY_UNINITIALIZED = 8, + CATPT_REPLY_NOT_FOUND = 9, + CATPT_REPLY_SOURCE_NOT_STARTED = 10, +}; + +/* GLOBAL messages */ + +enum catpt_global_msg_type { + CATPT_GLB_GET_FW_VERSION = 0, + CATPT_GLB_ALLOCATE_STREAM = 3, + CATPT_GLB_FREE_STREAM = 4, + CATPT_GLB_STREAM_MESSAGE = 6, + CATPT_GLB_REQUEST_CORE_DUMP = 7, + CATPT_GLB_SET_DEVICE_FORMATS = 10, + CATPT_GLB_ENTER_DX_STATE = 12, + CATPT_GLB_GET_MIXER_STREAM_INFO = 13, +}; + +union catpt_global_msg { + u32 val; + struct { + u32 status:5; + u32 context:19; /* stream or module specific */ + u32 global_msg_type:5; + u32 fw_ready:1; + u32 done:1; + u32 busy:1; + }; +} __packed; + +#define CATPT_MSG(hdr) { .val = hdr } +#define CATPT_GLOBAL_MSG(msg_type) \ + { .global_msg_type = CATPT_GLB_##msg_type } + +#define BUILD_HASH_SIZE 40 + +struct catpt_fw_version { + u8 build; + u8 minor; + u8 major; + u8 type; + u8 build_hash[BUILD_HASH_SIZE]; + u32 log_providers_hash; +} __packed; + +int catpt_ipc_get_fw_version(struct catpt_dev *cdev, + struct catpt_fw_version *version); + +enum catpt_pin_id { + CATPT_PIN_ID_SYSTEM = 0, + CATPT_PIN_ID_REFERENCE = 1, + CATPT_PIN_ID_CAPTURE1 = 2, + CATPT_PIN_ID_CAPTURE2 = 3, + CATPT_PIN_ID_OFFLOAD1 = 4, + CATPT_PIN_ID_OFFLOAD2 = 5, + CATPT_PIN_ID_MIXER = 7, + CATPT_PIN_ID_BLUETOOTH_CAPTURE = 8, + CATPT_PIN_ID_BLUETOOTH_RENDER = 9, +}; + +enum catpt_path_id { + CATPT_PATH_SSP0_OUT = 0, + CATPT_PATH_SSP0_IN = 1, + CATPT_PATH_SSP1_OUT = 2, + CATPT_PATH_SSP1_IN = 3, + /* duplicated audio in capture path */ + CATPT_PATH_SSP0_IN_DUP = 4, +}; + +enum catpt_stream_type { + CATPT_STRM_TYPE_RENDER = 0, /* offload */ + CATPT_STRM_TYPE_SYSTEM = 1, + CATPT_STRM_TYPE_CAPTURE = 2, + CATPT_STRM_TYPE_LOOPBACK = 3, + CATPT_STRM_TYPE_BLUETOOTH_RENDER = 4, + CATPT_STRM_TYPE_BLUETOOTH_CAPTURE = 5, +}; + +enum catpt_format_id { + CATPT_FORMAT_PCM = 0, + CATPT_FORMAT_MP3 = 1, + CATPT_FORMAT_AAC = 2, + CATPT_FORMAT_WMA = 3, +}; + +enum catpt_channel_index { + CATPT_CHANNEL_LEFT = 0x0, + CATPT_CHANNEL_CENTER = 0x1, + CATPT_CHANNEL_RIGHT = 0x2, + CATPT_CHANNEL_LEFT_SURROUND = 0x3, + CATPT_CHANNEL_CENTER_SURROUND = 0x3, + CATPT_CHANNEL_RIGHT_SURROUND = 0x4, + CATPT_CHANNEL_LFE = 0x7, + CATPT_CHANNEL_INVALID = 0xF, +}; + +enum catpt_channel_config { + CATPT_CHANNEL_CONFIG_MONO = 0, /* One channel only */ + CATPT_CHANNEL_CONFIG_STEREO = 1, /* L & R */ + CATPT_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only */ + CATPT_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only */ + CATPT_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only */ + CATPT_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only */ + CATPT_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only */ + CATPT_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs */ + CATPT_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE */ + CATPT_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two */ + CATPT_CHANNEL_CONFIG_INVALID = 10, +}; + +enum catpt_interleaving_style { + CATPT_INTERLEAVING_PER_CHANNEL = 0, + CATPT_INTERLEAVING_PER_SAMPLE = 1, +}; + +struct catpt_audio_format { + u32 sample_rate; + u32 bit_depth; + u32 channel_map; + u32 channel_config; + u32 interleaving; + u8 num_channels; + u8 valid_bit_depth; + u8 reserved[2]; +} __packed; + +struct catpt_ring_info { + u32 page_table_addr; + u32 num_pages; + u32 size; + u32 offset; + u32 ring_first_page_pfn; +} __packed; + +#define CATPT_MODULE_COUNT (CATPT_MODID_LAST + 1) + +enum catpt_module_id { + CATPT_MODID_BASE_FW = 0x0, + CATPT_MODID_MP3 = 0x1, + CATPT_MODID_AAC_5_1 = 0x2, + CATPT_MODID_AAC_2_0 = 0x3, + CATPT_MODID_SRC = 0x4, + CATPT_MODID_WAVES = 0x5, + CATPT_MODID_DOLBY = 0x6, + CATPT_MODID_BOOST = 0x7, + CATPT_MODID_LPAL = 0x8, + CATPT_MODID_DTS = 0x9, + CATPT_MODID_PCM_CAPTURE = 0xA, + CATPT_MODID_PCM_SYSTEM = 0xB, + CATPT_MODID_PCM_REFERENCE = 0xC, + CATPT_MODID_PCM = 0xD, /* offload */ + CATPT_MODID_BLUETOOTH_RENDER = 0xE, + CATPT_MODID_BLUETOOTH_CAPTURE = 0xF, + CATPT_MODID_LAST = CATPT_MODID_BLUETOOTH_CAPTURE, +}; + +struct catpt_module_entry { + u32 module_id; + u32 entry_point; +} __packed; + +struct catpt_module_map { + u8 num_entries; + struct catpt_module_entry entries[]; +} __packed; + +struct catpt_memory_info { + u32 offset; + u32 size; +} __packed; + +#define CATPT_CHANNELS_MAX 4 +#define CATPT_ALL_CHANNELS_MASK UINT_MAX + +struct catpt_stream_info { + u32 stream_hw_id; + u32 reserved; + u32 read_pos_regaddr; + u32 pres_pos_regaddr; + u32 peak_meter_regaddr[CATPT_CHANNELS_MAX]; + u32 volume_regaddr[CATPT_CHANNELS_MAX]; +} __packed; + +int catpt_ipc_alloc_stream(struct catpt_dev *cdev, + enum catpt_path_id path_id, + enum catpt_stream_type type, + struct catpt_audio_format *afmt, + struct catpt_ring_info *rinfo, + u8 num_modules, + struct catpt_module_entry *modules, + struct resource *persistent, + struct resource *scratch, + struct catpt_stream_info *sinfo); +int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id); + +enum catpt_ssp_iface { + CATPT_SSP_IFACE_0 = 0, + CATPT_SSP_IFACE_1 = 1, + CATPT_SSP_IFACE_LAST = CATPT_SSP_IFACE_1, +}; + +#define CATPT_SSP_COUNT (CATPT_SSP_IFACE_LAST + 1) + +enum catpt_mclk_frequency { + CATPT_MCLK_OFF = 0, + CATPT_MCLK_FREQ_6_MHZ = 1, + CATPT_MCLK_FREQ_21_MHZ = 2, + CATPT_MCLK_FREQ_24_MHZ = 3, +}; + +enum catpt_ssp_mode { + CATPT_SSP_MODE_I2S_CONSUMER = 0, + CATPT_SSP_MODE_I2S_PROVIDER = 1, + CATPT_SSP_MODE_TDM_PROVIDER = 2, +}; + +struct catpt_ssp_device_format { + u32 iface; + u32 mclk; + u32 mode; + u16 clock_divider; + u8 channels; +} __packed; + +int catpt_ipc_set_device_format(struct catpt_dev *cdev, + struct catpt_ssp_device_format *devfmt); + +enum catpt_dx_state { + CATPT_DX_STATE_D3 = 3, +}; + +enum catpt_dx_type { + CATPT_DX_TYPE_FW_IMAGE = 0, + CATPT_DX_TYPE_MEMORY_DUMP = 1, +}; + +struct catpt_save_meminfo { + u32 offset; + u32 size; + u32 source; +} __packed; + +#define SAVE_MEMINFO_MAX 14 + +struct catpt_dx_context { + u32 num_meminfo; + struct catpt_save_meminfo meminfo[SAVE_MEMINFO_MAX]; +} __packed; + +int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state, + struct catpt_dx_context *context); + +struct catpt_mixer_stream_info { + u32 mixer_hw_id; + u32 peak_meter_regaddr[CATPT_CHANNELS_MAX]; + u32 volume_regaddr[CATPT_CHANNELS_MAX]; +} __packed; + +int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev, + struct catpt_mixer_stream_info *info); + +/* STREAM messages */ + +enum catpt_stream_msg_type { + CATPT_STRM_RESET_STREAM = 0, + CATPT_STRM_PAUSE_STREAM = 1, + CATPT_STRM_RESUME_STREAM = 2, + CATPT_STRM_STAGE_MESSAGE = 3, + CATPT_STRM_NOTIFICATION = 4, +}; + +enum catpt_stage_action { + CATPT_STG_SET_VOLUME = 1, + CATPT_STG_SET_WRITE_POSITION = 2, + CATPT_STG_MUTE_LOOPBACK = 3, +}; + +union catpt_stream_msg { + u32 val; + struct { + u32 status:5; + u32 reserved:7; + u32 stage_action:4; + u32 stream_hw_id:4; + u32 stream_msg_type:4; + u32 global_msg_type:5; + u32 fw_ready:1; + u32 done:1; + u32 busy:1; + }; +} __packed; + +#define CATPT_STREAM_MSG(msg_type) \ +{ \ + .stream_msg_type = CATPT_STRM_##msg_type, \ + .global_msg_type = CATPT_GLB_STREAM_MESSAGE } +#define CATPT_STAGE_MSG(msg_type) \ +{ \ + .stage_action = CATPT_STG_##msg_type, \ + .stream_msg_type = CATPT_STRM_STAGE_MESSAGE, \ + .global_msg_type = CATPT_GLB_STREAM_MESSAGE } + +int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id); +int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id); +int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id); + +/* STREAM messages - STAGE subtype */ + +enum catpt_audio_curve_type { + CATPT_AUDIO_CURVE_NONE = 0, + CATPT_AUDIO_CURVE_WINDOWS_FADE = 1, +}; + +int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id, + u32 channel, u32 volume, + u32 curve_duration, + enum catpt_audio_curve_type curve_type); + +int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id, + u32 pos, bool eob, bool ll); + +int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute); + +/* NOTIFICATION messages */ + +enum catpt_notify_reason { + CATPT_NOTIFY_POSITION_CHANGED = 0, + CATPT_NOTIFY_GLITCH_OCCURRED = 1, +}; + +union catpt_notify_msg { + u32 val; + struct { + u32 mailbox_address:29; + u32 fw_ready:1; + u32 done:1; + u32 busy:1; + }; + struct { + u32 status:5; + u32 reserved:7; + u32 notify_reason:4; + u32 stream_hw_id:4; + u32 stream_msg_type:4; + u32 global_msg_type:5; + u32 hdr:3; /* fw_ready, done, busy */ + }; +} __packed; + +#define FW_INFO_SIZE_MAX 100 + +struct catpt_fw_ready { + u32 inbox_offset; + u32 outbox_offset; + u32 inbox_size; + u32 outbox_size; + u32 fw_info_size; + char fw_info[FW_INFO_SIZE_MAX]; +} __packed; + +struct catpt_notify_position { + u32 stream_position; + u32 fw_cycle_count; +} __packed; + +enum catpt_glitch_type { + CATPT_GLITCH_UNDERRUN = 1, + CATPT_GLITCH_DECODER_ERROR = 2, + CATPT_GLITCH_DOUBLED_WRITE_POS = 3, +}; + +struct catpt_notify_glitch { + u32 type; + u64 presentation_pos; + u32 write_pos; +} __packed; + +#endif diff --git a/sound/soc/intel/catpt/registers.h b/sound/soc/intel/catpt/registers.h index a051d8b36d88..47280d82842e 100644 --- a/sound/soc/intel/catpt/registers.h +++ b/sound/soc/intel/catpt/registers.h @@ -15,7 +15,6 @@ #define CATPT_SHIM_REGS_SIZE 4096 #define CATPT_DMA_REGS_SIZE 1024 #define CATPT_DMA_COUNT 2 -#define CATPT_SSP_COUNT 2 #define CATPT_SSP_REGS_SIZE 512 /* DSP Shim registers */ -- cgit From 64b9b1b005743a7bb4443442347024fca56433ee Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:36 +0200 Subject: ASoC: Intel: catpt: Add IPC message handlers Declare global and stream IPC message handlers for all known message types. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 2 + sound/soc/intel/catpt/dsp.c | 106 +++++++++++++ sound/soc/intel/catpt/ipc.c | 4 + sound/soc/intel/catpt/messages.c | 313 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 425 insertions(+) create mode 100644 sound/soc/intel/catpt/messages.c (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 8819d928e891..3c860c0645dc 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -127,4 +127,6 @@ int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, struct catpt_ipc_msg *reply); +int catpt_coredump(struct catpt_dev *cdev); + #endif diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c index c78da206a9fe..d642ab373425 100644 --- a/sound/soc/intel/catpt/dsp.c +++ b/sound/soc/intel/catpt/dsp.c @@ -5,6 +5,7 @@ // Author: Cezary Rojewski // +#include #include #include #include "core.h" @@ -136,3 +137,108 @@ void catpt_dmac_remove(struct catpt_dev *cdev) */ dw_dma_remove(cdev->dmac); } + +#define CATPT_DUMP_MAGIC 0xcd42 +#define CATPT_DUMP_SECTION_ID_FILE 0x00 +#define CATPT_DUMP_SECTION_ID_IRAM 0x01 +#define CATPT_DUMP_SECTION_ID_DRAM 0x02 +#define CATPT_DUMP_SECTION_ID_REGS 0x03 +#define CATPT_DUMP_HASH_SIZE 20 + +struct catpt_dump_section_hdr { + u16 magic; + u8 core_id; + u8 section_id; + u32 size; +}; + +int catpt_coredump(struct catpt_dev *cdev) +{ + struct catpt_dump_section_hdr *hdr; + size_t dump_size, regs_size; + u8 *dump, *pos; + const char *eof; + char *info; + int i; + + regs_size = CATPT_SHIM_REGS_SIZE; + regs_size += CATPT_DMA_COUNT * CATPT_DMA_REGS_SIZE; + regs_size += CATPT_SSP_COUNT * CATPT_SSP_REGS_SIZE; + dump_size = resource_size(&cdev->dram); + dump_size += resource_size(&cdev->iram); + dump_size += regs_size; + /* account for header of each section and hash chunk */ + dump_size += 4 * sizeof(*hdr) + CATPT_DUMP_HASH_SIZE; + + dump = vzalloc(dump_size); + if (!dump) + return -ENOMEM; + + pos = dump; + + hdr = (struct catpt_dump_section_hdr *)pos; + hdr->magic = CATPT_DUMP_MAGIC; + hdr->core_id = cdev->spec->core_id; + hdr->section_id = CATPT_DUMP_SECTION_ID_FILE; + hdr->size = dump_size - sizeof(*hdr); + pos += sizeof(*hdr); + + info = cdev->ipc.config.fw_info; + eof = info + FW_INFO_SIZE_MAX; + /* navigate to fifth info segment (fw hash) */ + for (i = 0; i < 4 && info < eof; i++, info++) { + /* info segments are separated by space each */ + info = strnchr(info, eof - info, ' '); + if (!info) + break; + } + + if (i == 4 && info) + memcpy(pos, info, min_t(u32, eof - info, CATPT_DUMP_HASH_SIZE)); + pos += CATPT_DUMP_HASH_SIZE; + + hdr = (struct catpt_dump_section_hdr *)pos; + hdr->magic = CATPT_DUMP_MAGIC; + hdr->core_id = cdev->spec->core_id; + hdr->section_id = CATPT_DUMP_SECTION_ID_IRAM; + hdr->size = resource_size(&cdev->iram); + pos += sizeof(*hdr); + + memcpy_fromio(pos, cdev->lpe_ba + cdev->iram.start, hdr->size); + pos += hdr->size; + + hdr = (struct catpt_dump_section_hdr *)pos; + hdr->magic = CATPT_DUMP_MAGIC; + hdr->core_id = cdev->spec->core_id; + hdr->section_id = CATPT_DUMP_SECTION_ID_DRAM; + hdr->size = resource_size(&cdev->dram); + pos += sizeof(*hdr); + + memcpy_fromio(pos, cdev->lpe_ba + cdev->dram.start, hdr->size); + pos += hdr->size; + + hdr = (struct catpt_dump_section_hdr *)pos; + hdr->magic = CATPT_DUMP_MAGIC; + hdr->core_id = cdev->spec->core_id; + hdr->section_id = CATPT_DUMP_SECTION_ID_REGS; + hdr->size = regs_size; + pos += sizeof(*hdr); + + memcpy_fromio(pos, catpt_shim_addr(cdev), CATPT_SHIM_REGS_SIZE); + pos += CATPT_SHIM_REGS_SIZE; + + for (i = 0; i < CATPT_SSP_COUNT; i++) { + memcpy_fromio(pos, catpt_ssp_addr(cdev, i), + CATPT_SSP_REGS_SIZE); + pos += CATPT_SSP_REGS_SIZE; + } + for (i = 0; i < CATPT_DMA_COUNT; i++) { + memcpy_fromio(pos, catpt_dma_addr(cdev, i), + CATPT_DMA_REGS_SIZE); + pos += CATPT_DMA_REGS_SIZE; + } + + dev_coredumpv(cdev->dev, dump, dump_size, GFP_KERNEL); + + return 0; +} diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c index 060fa8d97e50..060c1ca6e080 100644 --- a/sound/soc/intel/catpt/ipc.c +++ b/sound/soc/intel/catpt/ipc.c @@ -168,6 +168,10 @@ static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header) switch (msg.global_msg_type) { case CATPT_GLB_REQUEST_CORE_DUMP: + dev_err(cdev->dev, "ADSP device coredump received\n"); + ipc->ready = false; + catpt_coredump(cdev); + /* TODO: attempt recovery */ break; case CATPT_GLB_STREAM_MESSAGE: diff --git a/sound/soc/intel/catpt/messages.c b/sound/soc/intel/catpt/messages.c new file mode 100644 index 000000000000..a793d114afa4 --- /dev/null +++ b/sound/soc/intel/catpt/messages.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include "core.h" +#include "messages.h" +#include "registers.h" + +int catpt_ipc_get_fw_version(struct catpt_dev *cdev, + struct catpt_fw_version *version) +{ + union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_FW_VERSION); + struct catpt_ipc_msg request = {{0}}, reply; + int ret; + + request.header = msg.val; + reply.size = sizeof(*version); + reply.data = version; + + ret = catpt_dsp_send_msg(cdev, request, &reply); + if (ret) + dev_err(cdev->dev, "get fw version failed: %d\n", ret); + + return ret; +} + +struct catpt_alloc_stream_input { + enum catpt_path_id path_id:8; + enum catpt_stream_type stream_type:8; + enum catpt_format_id format_id:8; + u8 reserved; + struct catpt_audio_format input_format; + struct catpt_ring_info ring_info; + u8 num_entries; + /* flex array with entries here */ + struct catpt_memory_info persistent_mem; + struct catpt_memory_info scratch_mem; + u32 num_notifications; /* obsolete */ +} __packed; + +int catpt_ipc_alloc_stream(struct catpt_dev *cdev, + enum catpt_path_id path_id, + enum catpt_stream_type type, + struct catpt_audio_format *afmt, + struct catpt_ring_info *rinfo, + u8 num_modules, + struct catpt_module_entry *modules, + struct resource *persistent, + struct resource *scratch, + struct catpt_stream_info *sinfo) +{ + union catpt_global_msg msg = CATPT_GLOBAL_MSG(ALLOCATE_STREAM); + struct catpt_alloc_stream_input input; + struct catpt_ipc_msg request, reply; + size_t size, arrsz; + u8 *payload; + off_t off; + int ret; + + off = offsetof(struct catpt_alloc_stream_input, persistent_mem); + arrsz = sizeof(*modules) * num_modules; + size = sizeof(input) + arrsz; + + payload = kzalloc(size, GFP_KERNEL); + if (!payload) + return -ENOMEM; + + memset(&input, 0, sizeof(input)); + input.path_id = path_id; + input.stream_type = type; + input.format_id = CATPT_FORMAT_PCM; + input.input_format = *afmt; + input.ring_info = *rinfo; + input.num_entries = num_modules; + input.persistent_mem.offset = catpt_to_dsp_offset(persistent->start); + input.persistent_mem.size = resource_size(persistent); + if (scratch) { + input.scratch_mem.offset = catpt_to_dsp_offset(scratch->start); + input.scratch_mem.size = resource_size(scratch); + } + + /* re-arrange the input: account for flex array 'entries' */ + memcpy(payload, &input, sizeof(input)); + memmove(payload + off + arrsz, payload + off, sizeof(input) - off); + memcpy(payload + off, modules, arrsz); + + request.header = msg.val; + request.size = size; + request.data = payload; + reply.size = sizeof(*sinfo); + reply.data = sinfo; + + ret = catpt_dsp_send_msg(cdev, request, &reply); + if (ret) + dev_err(cdev->dev, "alloc stream type %d failed: %d\n", + type, ret); + + kfree(payload); + return ret; +} + +int catpt_ipc_free_stream(struct catpt_dev *cdev, u8 stream_hw_id) +{ + union catpt_global_msg msg = CATPT_GLOBAL_MSG(FREE_STREAM); + struct catpt_ipc_msg request; + int ret; + + request.header = msg.val; + request.size = sizeof(stream_hw_id); + request.data = &stream_hw_id; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "free stream %d failed: %d\n", + stream_hw_id, ret); + + return ret; +} + +int catpt_ipc_set_device_format(struct catpt_dev *cdev, + struct catpt_ssp_device_format *devfmt) +{ + union catpt_global_msg msg = CATPT_GLOBAL_MSG(SET_DEVICE_FORMATS); + struct catpt_ipc_msg request; + int ret; + + request.header = msg.val; + request.size = sizeof(*devfmt); + request.data = devfmt; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "set device format failed: %d\n", ret); + + return ret; +} + +int catpt_ipc_enter_dxstate(struct catpt_dev *cdev, enum catpt_dx_state state, + struct catpt_dx_context *context) +{ + union catpt_global_msg msg = CATPT_GLOBAL_MSG(ENTER_DX_STATE); + struct catpt_ipc_msg request, reply; + int ret; + + request.header = msg.val; + request.size = sizeof(state); + request.data = &state; + reply.size = sizeof(*context); + reply.data = context; + + ret = catpt_dsp_send_msg(cdev, request, &reply); + if (ret) + dev_err(cdev->dev, "enter dx state failed: %d\n", ret); + + return ret; +} + +int catpt_ipc_get_mixer_stream_info(struct catpt_dev *cdev, + struct catpt_mixer_stream_info *info) +{ + union catpt_global_msg msg = CATPT_GLOBAL_MSG(GET_MIXER_STREAM_INFO); + struct catpt_ipc_msg request = {{0}}, reply; + int ret; + + request.header = msg.val; + reply.size = sizeof(*info); + reply.data = info; + + ret = catpt_dsp_send_msg(cdev, request, &reply); + if (ret) + dev_err(cdev->dev, "get mixer info failed: %d\n", ret); + + return ret; +} + +int catpt_ipc_reset_stream(struct catpt_dev *cdev, u8 stream_hw_id) +{ + union catpt_stream_msg msg = CATPT_STREAM_MSG(RESET_STREAM); + struct catpt_ipc_msg request = {{0}}; + int ret; + + msg.stream_hw_id = stream_hw_id; + request.header = msg.val; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "reset stream %d failed: %d\n", + stream_hw_id, ret); + + return ret; +} + +int catpt_ipc_pause_stream(struct catpt_dev *cdev, u8 stream_hw_id) +{ + union catpt_stream_msg msg = CATPT_STREAM_MSG(PAUSE_STREAM); + struct catpt_ipc_msg request = {{0}}; + int ret; + + msg.stream_hw_id = stream_hw_id; + request.header = msg.val; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "pause stream %d failed: %d\n", + stream_hw_id, ret); + + return ret; +} + +int catpt_ipc_resume_stream(struct catpt_dev *cdev, u8 stream_hw_id) +{ + union catpt_stream_msg msg = CATPT_STREAM_MSG(RESUME_STREAM); + struct catpt_ipc_msg request = {{0}}; + int ret; + + msg.stream_hw_id = stream_hw_id; + request.header = msg.val; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "resume stream %d failed: %d\n", + stream_hw_id, ret); + + return ret; +} + +struct catpt_set_volume_input { + u32 channel; + u32 target_volume; + u64 curve_duration; + u32 curve_type; +} __packed; + +int catpt_ipc_set_volume(struct catpt_dev *cdev, u8 stream_hw_id, + u32 channel, u32 volume, + u32 curve_duration, + enum catpt_audio_curve_type curve_type) +{ + union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_VOLUME); + struct catpt_ipc_msg request; + struct catpt_set_volume_input input; + int ret; + + msg.stream_hw_id = stream_hw_id; + input.channel = channel; + input.target_volume = volume; + input.curve_duration = curve_duration; + input.curve_type = curve_type; + + request.header = msg.val; + request.size = sizeof(input); + request.data = &input; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "set stream %d volume failed: %d\n", + stream_hw_id, ret); + + return ret; +} + +struct catpt_set_write_pos_input { + u32 new_write_pos; + bool end_of_buffer; + bool low_latency; +} __packed; + +int catpt_ipc_set_write_pos(struct catpt_dev *cdev, u8 stream_hw_id, + u32 pos, bool eob, bool ll) +{ + union catpt_stream_msg msg = CATPT_STAGE_MSG(SET_WRITE_POSITION); + struct catpt_ipc_msg request; + struct catpt_set_write_pos_input input; + int ret; + + msg.stream_hw_id = stream_hw_id; + input.new_write_pos = pos; + input.end_of_buffer = eob; + input.low_latency = ll; + + request.header = msg.val; + request.size = sizeof(input); + request.data = &input; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "set stream %d write pos failed: %d\n", + stream_hw_id, ret); + + return ret; +} + +int catpt_ipc_mute_loopback(struct catpt_dev *cdev, u8 stream_hw_id, bool mute) +{ + union catpt_stream_msg msg = CATPT_STAGE_MSG(MUTE_LOOPBACK); + struct catpt_ipc_msg request; + int ret; + + msg.stream_hw_id = stream_hw_id; + request.header = msg.val; + request.size = sizeof(mute); + request.data = &mute; + + ret = catpt_dsp_send_msg(cdev, request, NULL); + if (ret) + dev_err(cdev->dev, "mute loopback failed: %d\n", ret); + + return ret; +} -- cgit From ba202a7bc3da05ca4548c7247f9be769b4e8c9fa Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:37 +0200 Subject: ASoC: Intel: catpt: Define DSP operations Implement dsp lifecycle functions such as core RESET and STALL, SRAM power control and LP clock selection. This also adds functions for handling transport over DW DMA controller. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 34 +++++ sound/soc/intel/catpt/dsp.c | 334 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 368 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 3c860c0645dc..9e9b3dceedce 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -99,6 +99,10 @@ struct catpt_dev { struct resource dram; struct resource iram; struct resource *scratch; + + struct list_head stream_list; + spinlock_t list_lock; + struct mutex clk_mutex; }; int catpt_dmac_probe(struct catpt_dev *cdev); @@ -111,6 +115,16 @@ int catpt_dma_memcpy_fromdsp(struct catpt_dev *cdev, struct dma_chan *chan, dma_addr_t dst_addr, dma_addr_t src_addr, size_t size); +void lpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable); +void wpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable); +int lpt_dsp_power_up(struct catpt_dev *cdev); +int lpt_dsp_power_down(struct catpt_dev *cdev); +int wpt_dsp_power_up(struct catpt_dev *cdev); +int wpt_dsp_power_down(struct catpt_dev *cdev); +int catpt_dsp_stall(struct catpt_dev *cdev, bool stall); +void catpt_dsp_update_srampge(struct catpt_dev *cdev, struct resource *sram, + unsigned long mask); +int catpt_dsp_update_lpclock(struct catpt_dev *cdev); irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id); irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id); @@ -129,4 +143,24 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, int catpt_coredump(struct catpt_dev *cdev); +#include +#include + +struct snd_pcm_substream; +struct catpt_stream_template; + +struct catpt_stream_runtime { + struct snd_pcm_substream *substream; + + struct catpt_stream_template *template; + struct catpt_stream_info info; + struct resource *persistent; + struct snd_dma_buffer pgtbl; + + bool allocated; + bool prepared; + + struct list_head node; +}; + #endif diff --git a/sound/soc/intel/catpt/dsp.c b/sound/soc/intel/catpt/dsp.c index d642ab373425..7d2968571951 100644 --- a/sound/soc/intel/catpt/dsp.c +++ b/sound/soc/intel/catpt/dsp.c @@ -8,7 +8,10 @@ #include #include #include +#include +#include #include "core.h" +#include "messages.h" #include "registers.h" static bool catpt_dma_filter(struct dma_chan *chan, void *param) @@ -138,6 +141,337 @@ void catpt_dmac_remove(struct catpt_dev *cdev) dw_dma_remove(cdev->dmac); } +static void catpt_dsp_set_srampge(struct catpt_dev *cdev, struct resource *sram, + unsigned long mask, unsigned long new) +{ + unsigned long old; + u32 off = sram->start; + u32 b = __ffs(mask); + + old = catpt_readl_pci(cdev, VDRTCTL0) & mask; + dev_dbg(cdev->dev, "SRAMPGE [0x%08lx] 0x%08lx -> 0x%08lx", + mask, old, new); + + if (old == new) + return; + + catpt_updatel_pci(cdev, VDRTCTL0, mask, new); + /* wait for SRAM power gating to propagate */ + udelay(60); + + /* + * Dummy read as the very first access after block enable + * to prevent byte loss in future operations. + */ + for_each_clear_bit_from(b, &new, fls_long(mask)) { + u8 buf[4]; + + /* newly enabled: new bit=0 while old bit=1 */ + if (test_bit(b, &old)) { + dev_dbg(cdev->dev, "sanitize block %ld: off 0x%08x\n", + b - __ffs(mask), off); + memcpy_fromio(buf, cdev->lpe_ba + off, sizeof(buf)); + } + off += CATPT_MEMBLOCK_SIZE; + } +} + +void catpt_dsp_update_srampge(struct catpt_dev *cdev, struct resource *sram, + unsigned long mask) +{ + struct resource *res; + unsigned long new = 0; + + /* flag all busy blocks */ + for (res = sram->child; res; res = res->sibling) { + u32 h, l; + + h = (res->end - sram->start) / CATPT_MEMBLOCK_SIZE; + l = (res->start - sram->start) / CATPT_MEMBLOCK_SIZE; + new |= GENMASK(h, l); + } + + /* offset value given mask's start and invert it as ON=b0 */ + new = ~(new << __ffs(mask)) & mask; + + /* disable core clock gating */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0); + + catpt_dsp_set_srampge(cdev, sram, mask, new); + + /* enable core clock gating */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, + CATPT_VDRTCTL2_DCLCGE); +} + +int catpt_dsp_stall(struct catpt_dev *cdev, bool stall) +{ + u32 reg, val; + + val = stall ? CATPT_CS_STALL : 0; + catpt_updatel_shim(cdev, CS1, CATPT_CS_STALL, val); + + return catpt_readl_poll_shim(cdev, CS1, + reg, (reg & CATPT_CS_STALL) == val, + 500, 10000); +} + +static int catpt_dsp_reset(struct catpt_dev *cdev, bool reset) +{ + u32 reg, val; + + val = reset ? CATPT_CS_RST : 0; + catpt_updatel_shim(cdev, CS1, CATPT_CS_RST, val); + + return catpt_readl_poll_shim(cdev, CS1, + reg, (reg & CATPT_CS_RST) == val, + 500, 10000); +} + +void lpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable) +{ + u32 val; + + val = enable ? LPT_VDRTCTL0_APLLSE : 0; + catpt_updatel_pci(cdev, VDRTCTL0, LPT_VDRTCTL0_APLLSE, val); +} + +void wpt_dsp_pll_shutdown(struct catpt_dev *cdev, bool enable) +{ + u32 val; + + val = enable ? WPT_VDRTCTL2_APLLSE : 0; + catpt_updatel_pci(cdev, VDRTCTL2, WPT_VDRTCTL2_APLLSE, val); +} + +static int catpt_dsp_select_lpclock(struct catpt_dev *cdev, bool lp, bool waiti) +{ + u32 mask, reg, val; + int ret; + + mutex_lock(&cdev->clk_mutex); + + val = lp ? CATPT_CS_LPCS : 0; + reg = catpt_readl_shim(cdev, CS1) & CATPT_CS_LPCS; + dev_dbg(cdev->dev, "LPCS [0x%08lx] 0x%08x -> 0x%08x", + CATPT_CS_LPCS, reg, val); + + if (reg == val) { + mutex_unlock(&cdev->clk_mutex); + return 0; + } + + if (waiti) { + /* wait for DSP to signal WAIT state */ + ret = catpt_readl_poll_shim(cdev, ISD, + reg, (reg & CATPT_ISD_DCPWM), + 500, 10000); + if (ret) { + dev_err(cdev->dev, "await WAITI timeout\n"); + mutex_unlock(&cdev->clk_mutex); + return ret; + } + } + + ret = catpt_readl_poll_shim(cdev, CLKCTL, + reg, !(reg & CATPT_CLKCTL_CFCIP), + 500, 10000); + if (ret) + dev_warn(cdev->dev, "clock change still in progress\n"); + + /* default to DSP core & audio fabric high clock */ + val |= CATPT_CS_DCS_HIGH; + mask = CATPT_CS_LPCS | CATPT_CS_DCS; + catpt_updatel_shim(cdev, CS1, mask, val); + + ret = catpt_readl_poll_shim(cdev, CLKCTL, + reg, !(reg & CATPT_CLKCTL_CFCIP), + 500, 10000); + if (ret) + dev_warn(cdev->dev, "clock change still in progress\n"); + + /* update PLL accordingly */ + cdev->spec->pll_shutdown(cdev, lp); + + mutex_unlock(&cdev->clk_mutex); + return 0; +} + +int catpt_dsp_update_lpclock(struct catpt_dev *cdev) +{ + struct catpt_stream_runtime *stream; + + list_for_each_entry(stream, &cdev->stream_list, node) + if (stream->prepared) + return catpt_dsp_select_lpclock(cdev, false, true); + + return catpt_dsp_select_lpclock(cdev, true, true); +} + +/* bring registers to their defaults as HW won't reset itself */ +static void catpt_dsp_set_regs_defaults(struct catpt_dev *cdev) +{ + int i; + + catpt_writel_shim(cdev, CS1, CATPT_CS_DEFAULT); + catpt_writel_shim(cdev, ISC, CATPT_ISC_DEFAULT); + catpt_writel_shim(cdev, ISD, CATPT_ISD_DEFAULT); + catpt_writel_shim(cdev, IMC, CATPT_IMC_DEFAULT); + catpt_writel_shim(cdev, IMD, CATPT_IMD_DEFAULT); + catpt_writel_shim(cdev, IPCC, CATPT_IPCC_DEFAULT); + catpt_writel_shim(cdev, IPCD, CATPT_IPCD_DEFAULT); + catpt_writel_shim(cdev, CLKCTL, CATPT_CLKCTL_DEFAULT); + catpt_writel_shim(cdev, CS2, CATPT_CS2_DEFAULT); + catpt_writel_shim(cdev, LTRC, CATPT_LTRC_DEFAULT); + catpt_writel_shim(cdev, HMDC, CATPT_HMDC_DEFAULT); + + for (i = 0; i < CATPT_SSP_COUNT; i++) { + catpt_writel_ssp(cdev, i, SSCR0, CATPT_SSC0_DEFAULT); + catpt_writel_ssp(cdev, i, SSCR1, CATPT_SSC1_DEFAULT); + catpt_writel_ssp(cdev, i, SSSR, CATPT_SSS_DEFAULT); + catpt_writel_ssp(cdev, i, SSITR, CATPT_SSIT_DEFAULT); + catpt_writel_ssp(cdev, i, SSDR, CATPT_SSD_DEFAULT); + catpt_writel_ssp(cdev, i, SSTO, CATPT_SSTO_DEFAULT); + catpt_writel_ssp(cdev, i, SSPSP, CATPT_SSPSP_DEFAULT); + catpt_writel_ssp(cdev, i, SSTSA, CATPT_SSTSA_DEFAULT); + catpt_writel_ssp(cdev, i, SSRSA, CATPT_SSRSA_DEFAULT); + catpt_writel_ssp(cdev, i, SSTSS, CATPT_SSTSS_DEFAULT); + catpt_writel_ssp(cdev, i, SSCR2, CATPT_SSCR2_DEFAULT); + catpt_writel_ssp(cdev, i, SSPSP2, CATPT_SSPSP2_DEFAULT); + } +} + +int lpt_dsp_power_down(struct catpt_dev *cdev) +{ + catpt_dsp_reset(cdev, true); + + /* set 24Mhz clock for both SSPs */ + catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1), + CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1)); + catpt_dsp_select_lpclock(cdev, true, false); + + /* DRAM power gating all */ + catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask, + cdev->spec->dram_mask); + catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask, + cdev->spec->iram_mask); + + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D3hot); + /* give hw time to drop off */ + udelay(50); + + return 0; +} + +int lpt_dsp_power_up(struct catpt_dev *cdev) +{ + /* SRAM power gating none */ + catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask, 0); + catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask, 0); + + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D0); + /* give hw time to wake up */ + udelay(100); + + catpt_dsp_select_lpclock(cdev, false, false); + catpt_updatel_shim(cdev, CS1, + CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1), + CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1)); + /* stagger DSP reset after clock selection */ + udelay(50); + + catpt_dsp_reset(cdev, false); + /* generate int deassert msg to fix inversed int logic */ + catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB | CATPT_IMC_IPCCD, 0); + + return 0; +} + +int wpt_dsp_power_down(struct catpt_dev *cdev) +{ + u32 mask, val; + + /* disable core clock gating */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0); + + catpt_dsp_reset(cdev, true); + /* set 24Mhz clock for both SSPs */ + catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1), + CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1)); + catpt_dsp_select_lpclock(cdev, true, false); + /* disable MCLK */ + catpt_updatel_shim(cdev, CLKCTL, CATPT_CLKCTL_SMOS, 0); + + catpt_dsp_set_regs_defaults(cdev); + + /* switch clock gating */ + mask = CATPT_VDRTCTL2_CGEALL & (~CATPT_VDRTCTL2_DCLCGE); + val = mask & (~CATPT_VDRTCTL2_DTCGE); + catpt_updatel_pci(cdev, VDRTCTL2, mask, val); + /* enable DTCGE separatelly */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DTCGE, + CATPT_VDRTCTL2_DTCGE); + + /* SRAM power gating all */ + catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask, + cdev->spec->dram_mask); + catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask, + cdev->spec->iram_mask); + mask = WPT_VDRTCTL0_D3SRAMPGD | WPT_VDRTCTL0_D3PGD; + catpt_updatel_pci(cdev, VDRTCTL0, mask, WPT_VDRTCTL0_D3PGD); + + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D3hot); + /* give hw time to drop off */ + udelay(50); + + /* enable core clock gating */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, + CATPT_VDRTCTL2_DCLCGE); + udelay(50); + + return 0; +} + +int wpt_dsp_power_up(struct catpt_dev *cdev) +{ + u32 mask, val; + + /* disable core clock gating */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, 0); + + /* switch clock gating */ + mask = CATPT_VDRTCTL2_CGEALL & (~CATPT_VDRTCTL2_DCLCGE); + val = mask & (~CATPT_VDRTCTL2_DTCGE); + catpt_updatel_pci(cdev, VDRTCTL2, mask, val); + + catpt_updatel_pci(cdev, PMCS, PCI_PM_CTRL_STATE_MASK, PCI_D0); + + /* SRAM power gating none */ + mask = WPT_VDRTCTL0_D3SRAMPGD | WPT_VDRTCTL0_D3PGD; + catpt_updatel_pci(cdev, VDRTCTL0, mask, mask); + catpt_dsp_set_srampge(cdev, &cdev->dram, cdev->spec->dram_mask, 0); + catpt_dsp_set_srampge(cdev, &cdev->iram, cdev->spec->iram_mask, 0); + + catpt_dsp_set_regs_defaults(cdev); + + /* restore MCLK */ + catpt_updatel_shim(cdev, CLKCTL, CATPT_CLKCTL_SMOS, CATPT_CLKCTL_SMOS); + catpt_dsp_select_lpclock(cdev, false, false); + /* set 24Mhz clock for both SSPs */ + catpt_updatel_shim(cdev, CS1, CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1), + CATPT_CS_SBCS(0) | CATPT_CS_SBCS(1)); + catpt_dsp_reset(cdev, false); + + /* enable core clock gating */ + catpt_updatel_pci(cdev, VDRTCTL2, CATPT_VDRTCTL2_DCLCGE, + CATPT_VDRTCTL2_DCLCGE); + + /* generate int deassert msg to fix inversed int logic */ + catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCDB | CATPT_IMC_IPCCD, 0); + + return 0; +} + #define CATPT_DUMP_MAGIC 0xcd42 #define CATPT_DUMP_SECTION_ID_FILE 0x00 #define CATPT_DUMP_SECTION_ID_IRAM 0x01 -- cgit From a9aa6fb3eb6c7e0e7e117b3f2dfafef8c45b9ea6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:38 +0200 Subject: ASoC: Intel: catpt: Firmware loading and context restore For Lynxpoint and Wildcat Point solution, is it host's responsibility to allocate SRAM regions and ensure those already taken are not overwritten with other data until released. Blocks are transferred to SRAM - either IRAM or DRAM - via DW DMA controller. Once basefw is booted, ownership of DMA transfer is lost in favour of DSP. Hosts reponsibilities don't end on initial block allocation and binary transfer. During Dx transitions host must store FW runtime context from DRAM before putting AudioDSP subsystem into lower power state. Said context gets flashed after D0 entry to bring DSP right where it was just before suspending. Load and restore procedures are finalized with SRAM power gating and adequate clock level selection. This power gates unused EBBs and clock speed effectively reducing power consumption. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 12 + sound/soc/intel/catpt/loader.c | 619 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 631 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 9e9b3dceedce..260e5ae94a2c 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -100,9 +100,16 @@ struct catpt_dev { struct resource iram; struct resource *scratch; + struct catpt_mixer_stream_info mixer; + struct catpt_module_type modules[CATPT_MODULE_COUNT]; + struct catpt_ssp_device_format devfmt[CATPT_SSP_COUNT]; struct list_head stream_list; spinlock_t list_lock; struct mutex clk_mutex; + + struct catpt_dx_context dx_ctx; + void *dxbuf_vaddr; + dma_addr_t dxbuf_paddr; }; int catpt_dmac_probe(struct catpt_dev *cdev); @@ -141,6 +148,11 @@ int catpt_dsp_send_msg_timeout(struct catpt_dev *cdev, int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, struct catpt_ipc_msg *reply); +int catpt_first_boot_firmware(struct catpt_dev *cdev); +int catpt_boot_firmware(struct catpt_dev *cdev, bool restore); +int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan); +int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan); +int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan); int catpt_coredump(struct catpt_dev *cdev); #include diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 3a7e5b396a86..473e842e9901 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -6,8 +6,49 @@ // #include +#include #include #include "core.h" +#include "registers.h" + +/* FW load (200ms) plus operational delays */ +#define FW_READY_TIMEOUT_MS 250 + +#define FW_SIGNATURE "$SST" +#define FW_SIGNATURE_SIZE 4 + +struct catpt_fw_hdr { + char signature[FW_SIGNATURE_SIZE]; + u32 file_size; + u32 modules; + u32 file_format; + u32 reserved[4]; +} __packed; + +struct catpt_fw_mod_hdr { + char signature[FW_SIGNATURE_SIZE]; + u32 mod_size; + u32 blocks; + u16 slot; + u16 module_id; + u32 entry_point; + u32 persistent_size; + u32 scratch_size; +} __packed; + +enum catpt_ram_type { + CATPT_RAM_TYPE_IRAM = 1, + CATPT_RAM_TYPE_DRAM = 2, + /* DRAM with module's initial state */ + CATPT_RAM_TYPE_INSTANCE = 3, +}; + +struct catpt_fw_block_hdr { + u32 ram_type; + u32 size; + u32 ram_offset; + u32 rsvd; +} __packed; void catpt_sram_init(struct resource *sram, u32 start, u32 size) { @@ -44,3 +85,581 @@ catpt_request_region(struct resource *root, resource_size_t size) return __request_region(root, addr, size, NULL, 0); } + +int catpt_store_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) +{ + struct catpt_stream_runtime *stream; + + list_for_each_entry(stream, &cdev->stream_list, node) { + u32 off, size; + int ret; + + off = stream->persistent->start; + size = resource_size(stream->persistent); + dev_dbg(cdev->dev, "storing stream %d ctx: off 0x%08x size %d\n", + stream->info.stream_hw_id, off, size); + + ret = catpt_dma_memcpy_fromdsp(cdev, chan, + cdev->dxbuf_paddr + off, + cdev->lpe_base + off, + ALIGN(size, 4)); + if (ret) { + dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +int catpt_store_module_states(struct catpt_dev *cdev, struct dma_chan *chan) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cdev->modules); i++) { + struct catpt_module_type *type; + u32 off; + int ret; + + type = &cdev->modules[i]; + if (!type->loaded || !type->state_size) + continue; + + off = type->state_offset; + dev_dbg(cdev->dev, "storing mod %d state: off 0x%08x size %d\n", + i, off, type->state_size); + + ret = catpt_dma_memcpy_fromdsp(cdev, chan, + cdev->dxbuf_paddr + off, + cdev->lpe_base + off, + ALIGN(type->state_size, 4)); + if (ret) { + dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +int catpt_store_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +{ + int i; + + for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) { + struct catpt_save_meminfo *info; + u32 off; + int ret; + + info = &cdev->dx_ctx.meminfo[i]; + if (info->source != CATPT_DX_TYPE_MEMORY_DUMP) + continue; + + off = catpt_to_host_offset(info->offset); + if (off < cdev->dram.start || off > cdev->dram.end) + continue; + + dev_dbg(cdev->dev, "storing memdump: off 0x%08x size %d\n", + off, info->size); + + ret = catpt_dma_memcpy_fromdsp(cdev, chan, + cdev->dxbuf_paddr + off, + cdev->lpe_base + off, + ALIGN(info->size, 4)); + if (ret) { + dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int +catpt_restore_streams_context(struct catpt_dev *cdev, struct dma_chan *chan) +{ + struct catpt_stream_runtime *stream; + + list_for_each_entry(stream, &cdev->stream_list, node) { + u32 off, size; + int ret; + + off = stream->persistent->start; + size = resource_size(stream->persistent); + dev_dbg(cdev->dev, "restoring stream %d ctx: off 0x%08x size %d\n", + stream->info.stream_hw_id, off, size); + + ret = catpt_dma_memcpy_todsp(cdev, chan, + cdev->lpe_base + off, + cdev->dxbuf_paddr + off, + ALIGN(size, 4)); + if (ret) { + dev_err(cdev->dev, "memcpy fromdsp failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int catpt_restore_memdumps(struct catpt_dev *cdev, struct dma_chan *chan) +{ + int i; + + for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) { + struct catpt_save_meminfo *info; + u32 off; + int ret; + + info = &cdev->dx_ctx.meminfo[i]; + if (info->source != CATPT_DX_TYPE_MEMORY_DUMP) + continue; + + off = catpt_to_host_offset(info->offset); + if (off < cdev->dram.start || off > cdev->dram.end) + continue; + + dev_dbg(cdev->dev, "restoring memdump: off 0x%08x size %d\n", + off, info->size); + + ret = catpt_dma_memcpy_todsp(cdev, chan, + cdev->lpe_base + off, + cdev->dxbuf_paddr + off, + ALIGN(info->size, 4)); + if (ret) { + dev_err(cdev->dev, "restore block failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int catpt_restore_fwimage(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_block_hdr *blk) +{ + struct resource r1, r2, common; + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + blk, sizeof(*blk), false); + + r1.start = cdev->dram.start + blk->ram_offset; + r1.end = r1.start + blk->size - 1; + /* advance to data area */ + paddr += sizeof(*blk); + + for (i = 0; i < cdev->dx_ctx.num_meminfo; i++) { + struct catpt_save_meminfo *info; + u32 off; + int ret; + + info = &cdev->dx_ctx.meminfo[i]; + + if (info->source != CATPT_DX_TYPE_FW_IMAGE) + continue; + + off = catpt_to_host_offset(info->offset); + if (off < cdev->dram.start || off > cdev->dram.end) + continue; + + r2.start = off; + r2.end = r2.start + info->size - 1; + + if (!catpt_resource_overlapping(&r2, &r1, &common)) + continue; + /* calculate start offset of common data area */ + off = common.start - r1.start; + + dev_dbg(cdev->dev, "restoring fwimage: %pr\n", &common); + + ret = catpt_dma_memcpy_todsp(cdev, chan, common.start, + paddr + off, + resource_size(&common)); + if (ret) { + dev_err(cdev->dev, "memcpy todsp failed: %d\n", ret); + return ret; + } + } + + return 0; +} + +static int catpt_load_block(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_block_hdr *blk, bool alloc) +{ + struct resource *sram, *res; + dma_addr_t dst_addr; + int ret; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + blk, sizeof(*blk), false); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_IRAM: + sram = &cdev->iram; + break; + default: + sram = &cdev->dram; + break; + }; + + dst_addr = sram->start + blk->ram_offset; + if (alloc) { + res = __request_region(sram, dst_addr, blk->size, NULL, 0); + if (!res) + return -EBUSY; + } + + /* advance to data area */ + paddr += sizeof(*blk); + + ret = catpt_dma_memcpy_todsp(cdev, chan, dst_addr, paddr, blk->size); + if (ret) { + dev_err(cdev->dev, "memcpy error: %d\n", ret); + __release_region(sram, dst_addr, blk->size); + } + + return ret; +} + +static int catpt_restore_basefw(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_mod_hdr *basefw) +{ + u32 offset = sizeof(*basefw); + int ret, i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + basefw, sizeof(*basefw), false); + + /* restore basefw image */ + for (i = 0; i < basefw->blocks; i++) { + struct catpt_fw_block_hdr *blk; + + blk = (struct catpt_fw_block_hdr *)((u8 *)basefw + offset); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_IRAM: + ret = catpt_load_block(cdev, chan, paddr + offset, + blk, false); + break; + default: + ret = catpt_restore_fwimage(cdev, chan, paddr + offset, + blk); + break; + } + + if (ret) { + dev_err(cdev->dev, "restore block failed: %d\n", ret); + return ret; + } + + offset += sizeof(*blk) + blk->size; + } + + /* then proceed with memory dumps */ + ret = catpt_restore_memdumps(cdev, chan); + if (ret) + dev_err(cdev->dev, "restore memdumps failed: %d\n", ret); + + return ret; +} + +static int catpt_restore_module(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_mod_hdr *mod) +{ + u32 offset = sizeof(*mod); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + mod, sizeof(*mod), false); + + for (i = 0; i < mod->blocks; i++) { + struct catpt_fw_block_hdr *blk; + int ret; + + blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); + + switch (blk->ram_type) { + case CATPT_RAM_TYPE_INSTANCE: + /* restore module state */ + ret = catpt_dma_memcpy_todsp(cdev, chan, + cdev->lpe_base + blk->ram_offset, + cdev->dxbuf_paddr + blk->ram_offset, + ALIGN(blk->size, 4)); + break; + default: + ret = catpt_load_block(cdev, chan, paddr + offset, + blk, false); + break; + } + + if (ret) { + dev_err(cdev->dev, "restore block failed: %d\n", ret); + return ret; + } + + offset += sizeof(*blk) + blk->size; + } + + return 0; +} + +static int catpt_load_module(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_mod_hdr *mod) +{ + struct catpt_module_type *type; + u32 offset = sizeof(*mod); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + mod, sizeof(*mod), false); + + type = &cdev->modules[mod->module_id]; + + for (i = 0; i < mod->blocks; i++) { + struct catpt_fw_block_hdr *blk; + int ret; + + blk = (struct catpt_fw_block_hdr *)((u8 *)mod + offset); + + ret = catpt_load_block(cdev, chan, paddr + offset, blk, true); + if (ret) { + dev_err(cdev->dev, "load block failed: %d\n", ret); + return ret; + } + + /* + * Save state window coordinates - these will be + * used to capture module state on D0 exit. + */ + if (blk->ram_type == CATPT_RAM_TYPE_INSTANCE) { + type->state_offset = blk->ram_offset; + type->state_size = blk->size; + } + + offset += sizeof(*blk) + blk->size; + } + + /* init module type static info */ + type->loaded = true; + /* DSP expects address from module header substracted by 4 */ + type->entry_point = mod->entry_point - 4; + type->persistent_size = mod->persistent_size; + type->scratch_size = mod->scratch_size; + + return 0; +} + +static int catpt_restore_firmware(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_hdr *fw) +{ + u32 offset = sizeof(*fw); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + fw, sizeof(*fw), false); + + for (i = 0; i < fw->modules; i++) { + struct catpt_fw_mod_hdr *mod; + int ret; + + mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); + if (strncmp(fw->signature, mod->signature, + FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "module signature mismatch\n"); + return -EINVAL; + } + + if (mod->module_id > CATPT_MODID_LAST) + return -EINVAL; + + switch (mod->module_id) { + case CATPT_MODID_BASE_FW: + ret = catpt_restore_basefw(cdev, chan, paddr + offset, + mod); + break; + default: + ret = catpt_restore_module(cdev, chan, paddr + offset, + mod); + break; + } + + if (ret) { + dev_err(cdev->dev, "restore module failed: %d\n", ret); + return ret; + } + + offset += sizeof(*mod) + mod->mod_size; + } + + return 0; +} + +static int catpt_load_firmware(struct catpt_dev *cdev, + struct dma_chan *chan, dma_addr_t paddr, + struct catpt_fw_hdr *fw) +{ + u32 offset = sizeof(*fw); + int i; + + print_hex_dump_debug(__func__, DUMP_PREFIX_OFFSET, 8, 4, + fw, sizeof(*fw), false); + + for (i = 0; i < fw->modules; i++) { + struct catpt_fw_mod_hdr *mod; + int ret; + + mod = (struct catpt_fw_mod_hdr *)((u8 *)fw + offset); + if (strncmp(fw->signature, mod->signature, + FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "module signature mismatch\n"); + return -EINVAL; + } + + if (mod->module_id > CATPT_MODID_LAST) + return -EINVAL; + + ret = catpt_load_module(cdev, chan, paddr + offset, mod); + if (ret) { + dev_err(cdev->dev, "load module failed: %d\n", ret); + return ret; + } + + offset += sizeof(*mod) + mod->mod_size; + } + + return 0; +} + +static int catpt_load_image(struct catpt_dev *cdev, struct dma_chan *chan, + const char *name, const char *signature, + bool restore) +{ + struct catpt_fw_hdr *fw; + struct firmware *img; + dma_addr_t paddr; + void *vaddr; + int ret; + + ret = request_firmware((const struct firmware **)&img, name, cdev->dev); + if (ret) + return ret; + + fw = (struct catpt_fw_hdr *)img->data; + if (strncmp(fw->signature, signature, FW_SIGNATURE_SIZE)) { + dev_err(cdev->dev, "firmware signature mismatch\n"); + ret = -EINVAL; + goto release_fw; + } + + vaddr = dma_alloc_coherent(cdev->dev, img->size, &paddr, GFP_KERNEL); + if (!vaddr) { + ret = -ENOMEM; + goto release_fw; + } + + memcpy(vaddr, img->data, img->size); + fw = (struct catpt_fw_hdr *)vaddr; + if (restore) + ret = catpt_restore_firmware(cdev, chan, paddr, fw); + else + ret = catpt_load_firmware(cdev, chan, paddr, fw); + + dma_free_coherent(cdev->dev, img->size, vaddr, paddr); +release_fw: + release_firmware(img); + return ret; +} + +static int catpt_load_images(struct catpt_dev *cdev, bool restore) +{ + static const char *const names[] = { + "intel/IntcSST1.bin", + "intel/IntcSST2.bin", + }; + struct dma_chan *chan; + int ret; + + chan = catpt_dma_request_config_chan(cdev); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + ret = catpt_load_image(cdev, chan, names[cdev->spec->core_id - 1], + FW_SIGNATURE, restore); + if (ret) + goto release_dma_chan; + + if (!restore) + goto release_dma_chan; + ret = catpt_restore_streams_context(cdev, chan); + if (ret) + dev_err(cdev->dev, "restore streams ctx failed: %d\n", ret); +release_dma_chan: + dma_release_channel(chan); + return ret; +} + +int catpt_boot_firmware(struct catpt_dev *cdev, bool restore) +{ + int ret; + + catpt_dsp_stall(cdev, true); + + ret = catpt_load_images(cdev, restore); + if (ret) { + dev_err(cdev->dev, "load binaries failed: %d\n", ret); + return ret; + } + + reinit_completion(&cdev->fw_ready); + catpt_dsp_stall(cdev, false); + + ret = wait_for_completion_timeout(&cdev->fw_ready, + msecs_to_jiffies(FW_READY_TIMEOUT_MS)); + if (!ret) { + dev_err(cdev->dev, "firmware ready timeout\n"); + return -ETIMEDOUT; + } + + /* update sram pg & clock once done booting */ + catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); + catpt_dsp_update_srampge(cdev, &cdev->iram, cdev->spec->iram_mask); + + return catpt_dsp_update_lpclock(cdev); +} + +int catpt_first_boot_firmware(struct catpt_dev *cdev) +{ + struct resource *res; + int ret; + + ret = catpt_boot_firmware(cdev, false); + if (ret) { + dev_err(cdev->dev, "basefw boot failed: %d\n", ret); + return ret; + } + + /* restrict FW Core dump area */ + __request_region(&cdev->dram, 0, 0x200, NULL, 0); + /* restrict entire area following BASE_FW - highest offset in DRAM */ + for (res = cdev->dram.child; res->sibling; res = res->sibling) + ; + __request_region(&cdev->dram, res->end + 1, + cdev->dram.end - res->end, NULL, 0); + + ret = catpt_ipc_get_mixer_stream_info(cdev, &cdev->mixer); + if (ret) + return CATPT_IPC_ERROR(ret); + + /* update dram pg for scratch and restricted regions */ + catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); + + return 0; +} -- cgit From a126750fc86546bf86c7536923a77cfecc15e5e3 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:39 +0200 Subject: ASoC: Intel: catpt: PCM operations DSP designed for Lynxpoint and Wildcat Point offers no dynamic topology i.e. all pipelines are already defined within firmware and host is relegated to allocing stream for predefined pins. This is represented by 'catpt_topology' member. Implementation covers all available pin types: - system playback and capture - two offload streams - loopback (reference) - bluetooth playback and capture PCM DAI operations differentiate between those pins as some (mainly offload) are to be handled differently - DSP expects wp updates on each notify_position notification. System playback has no volume control capability as it is routed to mixer stream directly. Other primary streams - capture and two offloads - offer individual volume controls. Compared to sound/soc/intel/haswell this configures SSP device format automatically on pcm creation. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 8 + sound/soc/intel/catpt/ipc.c | 37 ++ sound/soc/intel/catpt/loader.c | 6 + sound/soc/intel/catpt/pcm.c | 1175 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1226 insertions(+) create mode 100644 sound/soc/intel/catpt/pcm.c (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index 260e5ae94a2c..a29b4c0232cb 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -175,4 +175,12 @@ struct catpt_stream_runtime { struct list_head node; }; +int catpt_register_plat_component(struct catpt_dev *cdev); +void catpt_stream_update_position(struct catpt_dev *cdev, + struct catpt_stream_runtime *stream, + struct catpt_notify_position *pos); +struct catpt_stream_runtime * +catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id); +int catpt_arm_stream_templates(struct catpt_dev *cdev); + #endif diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c index 060c1ca6e080..3cad33b1c658 100644 --- a/sound/soc/intel/catpt/ipc.c +++ b/sound/soc/intel/catpt/ipc.c @@ -138,6 +138,42 @@ int catpt_dsp_send_msg(struct catpt_dev *cdev, struct catpt_ipc_msg request, cdev->ipc.default_timeout); } +static void +catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg) +{ + struct catpt_stream_runtime *stream; + struct catpt_notify_position pos; + struct catpt_notify_glitch glitch; + + stream = catpt_stream_find(cdev, msg.stream_hw_id); + if (!stream) { + dev_warn(cdev->dev, "notify %d for non-existent stream %d\n", + msg.notify_reason, msg.stream_hw_id); + return; + } + + switch (msg.notify_reason) { + case CATPT_NOTIFY_POSITION_CHANGED: + memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos)); + + catpt_stream_update_position(cdev, stream, &pos); + break; + + case CATPT_NOTIFY_GLITCH_OCCURRED: + memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch)); + + dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n", + glitch.type, glitch.presentation_pos, + glitch.write_pos); + break; + + default: + dev_warn(cdev->dev, "unknown notification: %d received\n", + msg.notify_reason); + break; + } +} + static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header) { struct catpt_ipc *ipc = &cdev->ipc; @@ -177,6 +213,7 @@ static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header) case CATPT_GLB_STREAM_MESSAGE: switch (msg.stream_msg_type) { case CATPT_STRM_NOTIFICATION: + catpt_dsp_notify_stream(cdev, msg); break; default: catpt_dsp_copy_rx(cdev, header); diff --git a/sound/soc/intel/catpt/loader.c b/sound/soc/intel/catpt/loader.c index 473e842e9901..8a5f20abcadb 100644 --- a/sound/soc/intel/catpt/loader.c +++ b/sound/soc/intel/catpt/loader.c @@ -658,6 +658,12 @@ int catpt_first_boot_firmware(struct catpt_dev *cdev) if (ret) return CATPT_IPC_ERROR(ret); + ret = catpt_arm_stream_templates(cdev); + if (ret) { + dev_err(cdev->dev, "arm templates failed: %d\n", ret); + return ret; + } + /* update dram pg for scratch and restricted regions */ catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c new file mode 100644 index 000000000000..f78018c857b8 --- /dev/null +++ b/sound/soc/intel/catpt/pcm.c @@ -0,0 +1,1175 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include +#include +#include +#include "core.h" +#include "messages.h" + +struct catpt_stream_template { + enum catpt_path_id path_id; + enum catpt_stream_type type; + u32 persistent_size; + u8 num_entries; + struct catpt_module_entry entries[]; +}; + +static struct catpt_stream_template system_pb = { + .path_id = CATPT_PATH_SSP0_OUT, + .type = CATPT_STRM_TYPE_SYSTEM, + .num_entries = 1, + .entries = {{ CATPT_MODID_PCM_SYSTEM, 0 }}, +}; + +static struct catpt_stream_template system_cp = { + .path_id = CATPT_PATH_SSP0_IN, + .type = CATPT_STRM_TYPE_CAPTURE, + .num_entries = 1, + .entries = {{ CATPT_MODID_PCM_CAPTURE, 0 }}, +}; + +static struct catpt_stream_template offload_pb = { + .path_id = CATPT_PATH_SSP0_OUT, + .type = CATPT_STRM_TYPE_RENDER, + .num_entries = 1, + .entries = {{ CATPT_MODID_PCM, 0 }}, +}; + +static struct catpt_stream_template loopback_cp = { + .path_id = CATPT_PATH_SSP0_OUT, + .type = CATPT_STRM_TYPE_LOOPBACK, + .num_entries = 1, + .entries = {{ CATPT_MODID_PCM_REFERENCE, 0 }}, +}; + +static struct catpt_stream_template bluetooth_pb = { + .path_id = CATPT_PATH_SSP1_OUT, + .type = CATPT_STRM_TYPE_BLUETOOTH_RENDER, + .num_entries = 1, + .entries = {{ CATPT_MODID_BLUETOOTH_RENDER, 0 }}, +}; + +static struct catpt_stream_template bluetooth_cp = { + .path_id = CATPT_PATH_SSP1_IN, + .type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE, + .num_entries = 1, + .entries = {{ CATPT_MODID_BLUETOOTH_CAPTURE, 0 }}, +}; + +static struct catpt_stream_template *catpt_topology[] = { + [CATPT_STRM_TYPE_RENDER] = &offload_pb, + [CATPT_STRM_TYPE_SYSTEM] = &system_pb, + [CATPT_STRM_TYPE_CAPTURE] = &system_cp, + [CATPT_STRM_TYPE_LOOPBACK] = &loopback_cp, + [CATPT_STRM_TYPE_BLUETOOTH_RENDER] = &bluetooth_pb, + [CATPT_STRM_TYPE_BLUETOOTH_CAPTURE] = &bluetooth_cp, +}; + +static struct catpt_stream_template * +catpt_get_stream_template(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtm = substream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); + enum catpt_stream_type type; + + type = cpu_dai->driver->id; + + /* account for capture in bidirectional dais */ + switch (type) { + case CATPT_STRM_TYPE_SYSTEM: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + type = CATPT_STRM_TYPE_CAPTURE; + break; + case CATPT_STRM_TYPE_BLUETOOTH_RENDER: + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + type = CATPT_STRM_TYPE_BLUETOOTH_CAPTURE; + break; + default: + break; + }; + + return catpt_topology[type]; +} + +struct catpt_stream_runtime * +catpt_stream_find(struct catpt_dev *cdev, u8 stream_hw_id) +{ + struct catpt_stream_runtime *pos, *result = NULL; + + spin_lock(&cdev->list_lock); + list_for_each_entry(pos, &cdev->stream_list, node) { + if (pos->info.stream_hw_id == stream_hw_id) { + result = pos; + break; + } + } + + spin_unlock(&cdev->list_lock); + return result; +} + +static u32 catpt_stream_read_position(struct catpt_dev *cdev, + struct catpt_stream_runtime *stream) +{ + u32 pos; + + memcpy_fromio(&pos, cdev->lpe_ba + stream->info.read_pos_regaddr, + sizeof(pos)); + return pos; +} + +static u32 catpt_stream_volume(struct catpt_dev *cdev, + struct catpt_stream_runtime *stream, u32 channel) +{ + u32 volume, offset; + + if (channel >= CATPT_CHANNELS_MAX) + channel = 0; + + offset = stream->info.volume_regaddr[channel]; + memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume)); + return volume; +} + +static u32 catpt_mixer_volume(struct catpt_dev *cdev, + struct catpt_mixer_stream_info *info, u32 channel) +{ + u32 volume, offset; + + if (channel >= CATPT_CHANNELS_MAX) + channel = 0; + + offset = info->volume_regaddr[channel]; + memcpy_fromio(&volume, cdev->lpe_ba + offset, sizeof(volume)); + return volume; +} + +static void catpt_arrange_page_table(struct snd_pcm_substream *substream, + struct snd_dma_buffer *pgtbl) +{ + struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_dma_buffer *databuf = snd_pcm_get_dma_buf(substream); + int i, pages; + + pages = snd_sgbuf_aligned_pages(rtm->dma_bytes); + + for (i = 0; i < pages; i++) { + u32 pfn, offset; + u32 *page_table; + + pfn = PFN_DOWN(snd_sgbuf_get_addr(databuf, i * PAGE_SIZE)); + /* incrementing by 2 on even and 3 on odd */ + offset = ((i << 2) + i) >> 1; + page_table = (u32 *)(pgtbl->area + offset); + + if (i & 1) + *page_table |= (pfn << 4); + else + *page_table |= pfn; + } +} + +static u32 catpt_get_channel_map(enum catpt_channel_config config) +{ + switch (config) { + case CATPT_CHANNEL_CONFIG_MONO: + return GENMASK(31, 4) | CATPT_CHANNEL_CENTER; + + case CATPT_CHANNEL_CONFIG_STEREO: + return GENMASK(31, 8) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_RIGHT << 4); + + case CATPT_CHANNEL_CONFIG_2_POINT_1: + return GENMASK(31, 12) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_RIGHT << 4) + | (CATPT_CHANNEL_LFE << 8); + + case CATPT_CHANNEL_CONFIG_3_POINT_0: + return GENMASK(31, 12) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_CENTER << 4) + | (CATPT_CHANNEL_RIGHT << 8); + + case CATPT_CHANNEL_CONFIG_3_POINT_1: + return GENMASK(31, 16) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_CENTER << 4) + | (CATPT_CHANNEL_RIGHT << 8) + | (CATPT_CHANNEL_LFE << 12); + + case CATPT_CHANNEL_CONFIG_QUATRO: + return GENMASK(31, 16) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_RIGHT << 4) + | (CATPT_CHANNEL_LEFT_SURROUND << 8) + | (CATPT_CHANNEL_RIGHT_SURROUND << 12); + + case CATPT_CHANNEL_CONFIG_4_POINT_0: + return GENMASK(31, 16) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_CENTER << 4) + | (CATPT_CHANNEL_RIGHT << 8) + | (CATPT_CHANNEL_CENTER_SURROUND << 12); + + case CATPT_CHANNEL_CONFIG_5_POINT_0: + return GENMASK(31, 20) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_CENTER << 4) + | (CATPT_CHANNEL_RIGHT << 8) + | (CATPT_CHANNEL_LEFT_SURROUND << 12) + | (CATPT_CHANNEL_RIGHT_SURROUND << 16); + + case CATPT_CHANNEL_CONFIG_5_POINT_1: + return GENMASK(31, 24) | CATPT_CHANNEL_CENTER + | (CATPT_CHANNEL_LEFT << 4) + | (CATPT_CHANNEL_RIGHT << 8) + | (CATPT_CHANNEL_LEFT_SURROUND << 12) + | (CATPT_CHANNEL_RIGHT_SURROUND << 16) + | (CATPT_CHANNEL_LFE << 20); + + case CATPT_CHANNEL_CONFIG_DUAL_MONO: + return GENMASK(31, 8) | CATPT_CHANNEL_LEFT + | (CATPT_CHANNEL_LEFT << 4); + + default: + return U32_MAX; + } +} + +static enum catpt_channel_config catpt_get_channel_config(u32 num_channels) +{ + switch (num_channels) { + case 6: + return CATPT_CHANNEL_CONFIG_5_POINT_1; + case 5: + return CATPT_CHANNEL_CONFIG_5_POINT_0; + case 4: + return CATPT_CHANNEL_CONFIG_QUATRO; + case 3: + return CATPT_CHANNEL_CONFIG_2_POINT_1; + case 1: + return CATPT_CHANNEL_CONFIG_MONO; + case 2: + default: + return CATPT_CHANNEL_CONFIG_STEREO; + } +} + +static int catpt_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_stream_template *template; + struct catpt_stream_runtime *stream; + struct resource *res; + int ret; + + template = catpt_get_stream_template(substream); + + stream = kzalloc(sizeof(*stream), GFP_KERNEL); + if (!stream) + return -ENOMEM; + + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, cdev->dev, PAGE_SIZE, + &stream->pgtbl); + if (ret) + goto err_pgtbl; + + res = catpt_request_region(&cdev->dram, template->persistent_size); + if (!res) { + ret = -EBUSY; + goto err_request; + } + + catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); + + stream->template = template; + stream->persistent = res; + stream->substream = substream; + INIT_LIST_HEAD(&stream->node); + snd_soc_dai_set_dma_data(dai, substream, stream); + + spin_lock(&cdev->list_lock); + list_add_tail(&stream->node, &cdev->stream_list); + spin_unlock(&cdev->list_lock); + + return 0; + +err_request: + snd_dma_free_pages(&stream->pgtbl); +err_pgtbl: + kfree(stream); + return ret; +} + +static void catpt_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_stream_runtime *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + spin_lock(&cdev->list_lock); + list_del(&stream->node); + spin_unlock(&cdev->list_lock); + + release_resource(stream->persistent); + kfree(stream->persistent); + catpt_dsp_update_srampge(cdev, &cdev->dram, cdev->spec->dram_mask); + + snd_dma_free_pages(&stream->pgtbl); + kfree(stream); + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int catpt_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_stream_runtime *stream; + struct catpt_audio_format afmt; + struct catpt_ring_info rinfo; + struct snd_pcm_runtime *rtm = substream->runtime; + struct snd_dma_buffer *dmab; + int ret; + + stream = snd_soc_dai_get_dma_data(dai, substream); + if (stream->allocated) + return 0; + + memset(&afmt, 0, sizeof(afmt)); + afmt.sample_rate = params_rate(params); + afmt.bit_depth = params_physical_width(params); + afmt.valid_bit_depth = params_width(params); + afmt.num_channels = params_channels(params); + afmt.channel_config = catpt_get_channel_config(afmt.num_channels); + afmt.channel_map = catpt_get_channel_map(afmt.channel_config); + afmt.interleaving = CATPT_INTERLEAVING_PER_CHANNEL; + + dmab = snd_pcm_get_dma_buf(substream); + catpt_arrange_page_table(substream, &stream->pgtbl); + + memset(&rinfo, 0, sizeof(rinfo)); + rinfo.page_table_addr = stream->pgtbl.addr; + rinfo.num_pages = DIV_ROUND_UP(rtm->dma_bytes, PAGE_SIZE); + rinfo.size = rtm->dma_bytes; + rinfo.offset = 0; + rinfo.ring_first_page_pfn = PFN_DOWN(snd_sgbuf_get_addr(dmab, 0)); + + ret = catpt_ipc_alloc_stream(cdev, stream->template->path_id, + stream->template->type, + &afmt, &rinfo, + stream->template->num_entries, + stream->template->entries, + stream->persistent, + cdev->scratch, + &stream->info); + if (ret) + return CATPT_IPC_ERROR(ret); + + stream->allocated = true; + return 0; +} + +static int catpt_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_stream_runtime *stream; + + stream = snd_soc_dai_get_dma_data(dai, substream); + if (!stream->allocated) + return 0; + + catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id); + catpt_ipc_free_stream(cdev, stream->info.stream_hw_id); + + stream->allocated = false; + return 0; +} + +static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol); + +static int catpt_dai_apply_usettings(struct snd_soc_dai *dai, + struct catpt_stream_runtime *stream) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct snd_soc_component *component = dai->component; + struct snd_kcontrol *pos, *kctl = NULL; + const char *name; + int ret; + u32 id = stream->info.stream_hw_id; + + /* only selected streams have individual controls */ + switch (id) { + case CATPT_PIN_ID_OFFLOAD1: + name = "Media0 Playback Volume"; + break; + case CATPT_PIN_ID_OFFLOAD2: + name = "Media1 Playback Volume"; + break; + case CATPT_PIN_ID_CAPTURE1: + name = "Mic Capture Volume"; + break; + case CATPT_PIN_ID_REFERENCE: + name = "Loopback Mute"; + break; + default: + return 0; + }; + + list_for_each_entry(pos, &component->card->snd_card->controls, list) { + if (pos->private_data == component && + !strncmp(name, pos->id.name, sizeof(pos->id.name))) { + kctl = pos; + break; + } + } + if (!kctl) + return -ENOENT; + + if (stream->template->type != CATPT_STRM_TYPE_LOOPBACK) + return catpt_set_dspvol(cdev, id, (long *)kctl->private_value); + ret = catpt_ipc_mute_loopback(cdev, id, *(bool *)kctl->private_value); + if (ret) + return CATPT_IPC_ERROR(ret); + return 0; +} + +static int catpt_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_stream_runtime *stream; + int ret; + + stream = snd_soc_dai_get_dma_data(dai, substream); + if (stream->prepared) + return 0; + + ret = catpt_ipc_reset_stream(cdev, stream->info.stream_hw_id); + if (ret) + return CATPT_IPC_ERROR(ret); + + ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id); + if (ret) + return CATPT_IPC_ERROR(ret); + + ret = catpt_dsp_update_lpclock(cdev); + if (ret) + return ret; + + ret = catpt_dai_apply_usettings(dai, stream); + if (ret) + return ret; + + stream->prepared = true; + return 0; +} + +static int catpt_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_stream_runtime *stream; + struct snd_pcm_runtime *runtime = substream->runtime; + snd_pcm_uframes_t pos; + int ret; + + stream = snd_soc_dai_get_dma_data(dai, substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* only offload is set_write_pos driven */ + if (stream->template->type != CATPT_STRM_TYPE_RENDER) + goto resume_stream; + + pos = frames_to_bytes(runtime, runtime->start_threshold); + /* + * Dsp operates on buffer halves, thus max 2x set_write_pos + * (entire buffer filled) prior to stream start. + */ + ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, + pos, false, false); + if (ret) + return CATPT_IPC_ERROR(ret); + fallthrough; + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + resume_stream: + ret = catpt_ipc_resume_stream(cdev, stream->info.stream_hw_id); + if (ret) + return CATPT_IPC_ERROR(ret); + break; + + case SNDRV_PCM_TRIGGER_STOP: + stream->prepared = false; + catpt_dsp_update_lpclock(cdev); + fallthrough; + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = catpt_ipc_pause_stream(cdev, stream->info.stream_hw_id); + if (ret) + return CATPT_IPC_ERROR(ret); + break; + + default: + break; + } + + return 0; +} + +void catpt_stream_update_position(struct catpt_dev *cdev, + struct catpt_stream_runtime *stream, + struct catpt_notify_position *pos) +{ + struct snd_pcm_substream *substream = stream->substream; + struct snd_pcm_runtime *r = substream->runtime; + snd_pcm_uframes_t dsppos, newpos; + int ret; + + dsppos = bytes_to_frames(r, pos->stream_position); + + /* only offload is set_write_pos driven */ + if (stream->template->type != CATPT_STRM_TYPE_RENDER) + goto exit; + + if (dsppos >= r->buffer_size / 2) + newpos = r->buffer_size / 2; + else + newpos = 0; + /* + * Dsp operates on buffer halves, thus on every notify position + * (buffer half consumed) update wp to allow stream progression. + */ + ret = catpt_ipc_set_write_pos(cdev, stream->info.stream_hw_id, + frames_to_bytes(r, newpos), + false, false); + if (ret) { + dev_err(cdev->dev, "update position for stream %d failed: %d\n", + stream->info.stream_hw_id, ret); + return; + } +exit: + snd_pcm_period_elapsed(substream); +} + +/* 200 ms for 2 32-bit channels at 48kHz (native format) */ +#define CATPT_BUFFER_MAX_SIZE 76800 +#define CATPT_PCM_PERIODS_MAX 4 +#define CATPT_PCM_PERIODS_MIN 2 + +static const struct snd_pcm_hardware catpt_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME | + SNDRV_PCM_INFO_NO_PERIOD_WAKEUP, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + .period_bytes_min = PAGE_SIZE, + .period_bytes_max = CATPT_BUFFER_MAX_SIZE / CATPT_PCM_PERIODS_MIN, + .periods_min = CATPT_PCM_PERIODS_MIN, + .periods_max = CATPT_PCM_PERIODS_MAX, + .buffer_bytes_max = CATPT_BUFFER_MAX_SIZE, +}; + +static int catpt_component_pcm_construct(struct snd_soc_component *component, + struct snd_soc_pcm_runtime *rtm) +{ + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + + snd_pcm_set_managed_buffer_all(rtm->pcm, SNDRV_DMA_TYPE_DEV_SG, + cdev->dev, + catpt_pcm_hardware.buffer_bytes_max, + catpt_pcm_hardware.buffer_bytes_max); + + return 0; +} + +static int catpt_component_open(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtm = substream->private_data; + + if (rtm->dai_link->no_pcm) + return 0; + snd_soc_set_runtime_hwparams(substream, &catpt_pcm_hardware); + return 0; +} + +static snd_pcm_uframes_t +catpt_component_pointer(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + struct catpt_stream_runtime *stream; + struct snd_soc_pcm_runtime *rtm = substream->private_data; + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtm, 0); + u32 pos; + + if (rtm->dai_link->no_pcm) + return 0; + + stream = snd_soc_dai_get_dma_data(cpu_dai, substream); + pos = catpt_stream_read_position(cdev, stream); + + return bytes_to_frames(substream->runtime, pos); +} + +static const struct snd_soc_dai_ops catpt_fe_dai_ops = { + .startup = catpt_dai_startup, + .shutdown = catpt_dai_shutdown, + .hw_params = catpt_dai_hw_params, + .hw_free = catpt_dai_hw_free, + .prepare = catpt_dai_prepare, + .trigger = catpt_dai_trigger, +}; + +static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm, + struct snd_soc_dai *dai) +{ + struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtm, 0); + struct catpt_dev *cdev = dev_get_drvdata(dai->dev); + struct catpt_ssp_device_format devfmt; + int ret; + + devfmt.iface = dai->driver->id; + devfmt.channels = codec_dai->driver->capture.channels_max; + + switch (devfmt.iface) { + case CATPT_SSP_IFACE_0: + devfmt.mclk = CATPT_MCLK_FREQ_24_MHZ; + + switch (devfmt.channels) { + case 4: + devfmt.mode = CATPT_SSP_MODE_TDM_PROVIDER; + devfmt.clock_divider = 4; + break; + case 2: + default: + devfmt.mode = CATPT_SSP_MODE_I2S_PROVIDER; + devfmt.clock_divider = 9; + break; + } + break; + + case CATPT_SSP_IFACE_1: + devfmt.mclk = CATPT_MCLK_OFF; + devfmt.mode = CATPT_SSP_MODE_I2S_CONSUMER; + devfmt.clock_divider = 0; + break; + } + + ret = catpt_ipc_set_device_format(cdev, &devfmt); + if (ret) + return CATPT_IPC_ERROR(ret); + + /* store device format set for given SSP */ + memcpy(&cdev->devfmt[devfmt.iface], &devfmt, sizeof(devfmt)); + return 0; +} + +static struct snd_soc_dai_driver dai_drivers[] = { +/* FE DAIs */ +{ + .name = "System Pin", + .id = CATPT_STRM_TYPE_SYSTEM, + .ops = &catpt_fe_dai_ops, + .playback = { + .stream_name = "System Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .capture = { + .stream_name = "Analog Capture", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Offload0 Pin", + .id = CATPT_STRM_TYPE_RENDER, + .ops = &catpt_fe_dai_ops, + .playback = { + .stream_name = "Offload0 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Offload1 Pin", + .id = CATPT_STRM_TYPE_RENDER, + .ops = &catpt_fe_dai_ops, + .playback = { + .stream_name = "Offload1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Loopback Pin", + .id = CATPT_STRM_TYPE_LOOPBACK, + .ops = &catpt_fe_dai_ops, + .capture = { + .stream_name = "Loopback Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, +}, +{ + .name = "Bluetooth Pin", + .id = CATPT_STRM_TYPE_BLUETOOTH_RENDER, + .ops = &catpt_fe_dai_ops, + .playback = { + .stream_name = "Bluetooth Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "Bluetooth Capture", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_8000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, +}, +/* BE DAIs */ +{ + .name = "ssp0-port", + .id = CATPT_SSP_IFACE_0, + .pcm_new = catpt_dai_pcm_new, + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, +}, +{ + .name = "ssp1-port", + .id = CATPT_SSP_IFACE_1, + .pcm_new = catpt_dai_pcm_new, + .playback = { + .channels_min = 1, + .channels_max = 8, + }, + .capture = { + .channels_min = 1, + .channels_max = 8, + }, +}, +}; + +#define DSP_VOLUME_MAX S32_MAX /* 0db */ +#define DSP_VOLUME_STEP_MAX 30 + +static u32 ctlvol_to_dspvol(u32 value) +{ + if (value > DSP_VOLUME_STEP_MAX) + value = 0; + return DSP_VOLUME_MAX >> (DSP_VOLUME_STEP_MAX - value); +} + +static u32 dspvol_to_ctlvol(u32 volume) +{ + if (volume > DSP_VOLUME_MAX) + return DSP_VOLUME_STEP_MAX; + return volume ? __fls(volume) : 0; +} + +static int catpt_set_dspvol(struct catpt_dev *cdev, u8 stream_id, long *ctlvol) +{ + u32 dspvol; + int ret, i; + + for (i = 1; i < CATPT_CHANNELS_MAX; i++) + if (ctlvol[i] != ctlvol[0]) + break; + + if (i == CATPT_CHANNELS_MAX) { + dspvol = ctlvol_to_dspvol(ctlvol[0]); + + ret = catpt_ipc_set_volume(cdev, stream_id, + CATPT_ALL_CHANNELS_MASK, dspvol, + 0, CATPT_AUDIO_CURVE_NONE); + } else { + for (i = 0; i < CATPT_CHANNELS_MAX; i++) { + dspvol = ctlvol_to_dspvol(ctlvol[i]); + + ret = catpt_ipc_set_volume(cdev, stream_id, + i, dspvol, + 0, CATPT_AUDIO_CURVE_NONE); + if (ret) + break; + } + } + + if (ret) + return CATPT_IPC_ERROR(ret); + return 0; +} + +static int catpt_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = CATPT_CHANNELS_MAX; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = DSP_VOLUME_STEP_MAX; + return 0; +} + +static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + u32 dspvol; + int i; + + pm_runtime_get_sync(cdev->dev); + + for (i = 0; i < CATPT_CHANNELS_MAX; i++) { + dspvol = catpt_mixer_volume(cdev, &cdev->mixer, i); + ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); + } + + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_put_autosuspend(cdev->dev); + + return 0; +} + +static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + int ret; + + pm_runtime_get_sync(cdev->dev); + + ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id, + ucontrol->value.integer.value); + + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_put_autosuspend(cdev->dev); + + return ret; +} + +static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + enum catpt_pin_id pin_id) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + struct catpt_stream_runtime *stream; + long *ctlvol = (long *)kcontrol->private_value; + u32 dspvol; + int i; + + stream = catpt_stream_find(cdev, pin_id); + if (!stream) { + for (i = 0; i < CATPT_CHANNELS_MAX; i++) + ucontrol->value.integer.value[i] = ctlvol[i]; + return 0; + } + + pm_runtime_get_sync(cdev->dev); + + for (i = 0; i < CATPT_CHANNELS_MAX; i++) { + dspvol = catpt_stream_volume(cdev, stream, i); + ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol); + } + + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_put_autosuspend(cdev->dev); + + return 0; +} + +static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol, + enum catpt_pin_id pin_id) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + struct catpt_stream_runtime *stream; + long *ctlvol = (long *)kcontrol->private_value; + int ret, i; + + stream = catpt_stream_find(cdev, pin_id); + if (!stream) { + for (i = 0; i < CATPT_CHANNELS_MAX; i++) + ctlvol[i] = ucontrol->value.integer.value[i]; + return 0; + } + + pm_runtime_get_sync(cdev->dev); + + ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id, + ucontrol->value.integer.value); + + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_put_autosuspend(cdev->dev); + + if (ret) + return ret; + + for (i = 0; i < CATPT_CHANNELS_MAX; i++) + ctlvol[i] = ucontrol->value.integer.value[i]; + return 0; +} + +static int catpt_offload1_volume_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD1); +} + +static int catpt_offload1_volume_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD1); +} + +static int catpt_offload2_volume_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_OFFLOAD2); +} + +static int catpt_offload2_volume_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_OFFLOAD2); +} + +static int catpt_capture_volume_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + return catpt_stream_volume_get(kctl, uctl, CATPT_PIN_ID_CAPTURE1); +} + +static int catpt_capture_volume_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + return catpt_stream_volume_put(kctl, uctl, CATPT_PIN_ID_CAPTURE1); +} + +static int catpt_loopback_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + ucontrol->value.integer.value[0] = *(bool *)kcontrol->private_value; + return 0; +} + +static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct catpt_dev *cdev = dev_get_drvdata(component->dev); + struct catpt_stream_runtime *stream; + bool mute; + int ret; + + mute = (bool)ucontrol->value.integer.value[0]; + stream = catpt_stream_find(cdev, CATPT_PIN_ID_REFERENCE); + if (!stream) { + *(bool *)kcontrol->private_value = mute; + return 0; + } + + pm_runtime_get_sync(cdev->dev); + + ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute); + + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_put_autosuspend(cdev->dev); + + if (ret) + return CATPT_IPC_ERROR(ret); + + *(bool *)kcontrol->private_value = mute; + return 0; +} + +static int catpt_waves_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int catpt_waves_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + return 0; +} + +static int catpt_waves_param_get(struct snd_kcontrol *kcontrol, + unsigned int __user *bytes, + unsigned int size) +{ + return 0; +} + +static int catpt_waves_param_put(struct snd_kcontrol *kcontrol, + const unsigned int __user *bytes, + unsigned int size) +{ + return 0; +} + +static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(catpt_volume_tlv, -9000, 300, 1); + +#define CATPT_VOLUME_CTL(kname, sname) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = (kname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .info = catpt_volume_info, \ + .get = catpt_##sname##_volume_get, \ + .put = catpt_##sname##_volume_put, \ + .tlv.p = catpt_volume_tlv, \ + .private_value = (unsigned long) \ + &(long[CATPT_CHANNELS_MAX]) {0} } + +static const struct snd_kcontrol_new component_kcontrols[] = { +/* Master volume (mixer stream) */ +CATPT_VOLUME_CTL("Master Playback Volume", mixer), +/* Individual volume controls for offload and capture */ +CATPT_VOLUME_CTL("Media0 Playback Volume", offload1), +CATPT_VOLUME_CTL("Media1 Playback Volume", offload2), +CATPT_VOLUME_CTL("Mic Capture Volume", capture), +SOC_SINGLE_BOOL_EXT("Loopback Mute", (unsigned long)&(bool[1]) {0}, + catpt_loopback_switch_get, catpt_loopback_switch_put), +/* Enable or disable WAVES module */ +SOC_SINGLE_BOOL_EXT("Waves Switch", 0, + catpt_waves_switch_get, catpt_waves_switch_put), +/* WAVES module parameter control */ +SND_SOC_BYTES_TLV("Waves Set Param", 128, + catpt_waves_param_get, catpt_waves_param_put), +}; + +static const struct snd_soc_dapm_widget component_widgets[] = { + SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route component_routes[] = { + {"Playback VMixer", NULL, "System Playback"}, + {"Playback VMixer", NULL, "Offload0 Playback"}, + {"Playback VMixer", NULL, "Offload1 Playback"}, + + {"SSP0 CODEC OUT", NULL, "Playback VMixer"}, + + {"Analog Capture", NULL, "SSP0 CODEC IN"}, + {"Loopback Capture", NULL, "SSP0 CODEC IN"}, + + {"SSP1 BT OUT", NULL, "Bluetooth Playback"}, + {"Bluetooth Capture", NULL, "SSP1 BT IN"}, +}; + +static const struct snd_soc_component_driver catpt_comp_driver = { + .name = "catpt-platform", + + .pcm_construct = catpt_component_pcm_construct, + .open = catpt_component_open, + .pointer = catpt_component_pointer, + + .controls = component_kcontrols, + .num_controls = ARRAY_SIZE(component_kcontrols), + .dapm_widgets = component_widgets, + .num_dapm_widgets = ARRAY_SIZE(component_widgets), + .dapm_routes = component_routes, + .num_dapm_routes = ARRAY_SIZE(component_routes), +}; + +int catpt_arm_stream_templates(struct catpt_dev *cdev) +{ + struct resource *res; + u32 scratch_size = 0; + int i, j; + + for (i = 0; i < ARRAY_SIZE(catpt_topology); i++) { + struct catpt_stream_template *template; + struct catpt_module_entry *entry; + struct catpt_module_type *type; + + template = catpt_topology[i]; + template->persistent_size = 0; + + for (j = 0; j < template->num_entries; j++) { + entry = &template->entries[j]; + type = &cdev->modules[entry->module_id]; + + if (!type->loaded) + return -ENOENT; + + entry->entry_point = type->entry_point; + template->persistent_size += type->persistent_size; + if (type->scratch_size > scratch_size) + scratch_size = type->scratch_size; + } + } + + if (scratch_size) { + /* allocate single scratch area for all modules */ + res = catpt_request_region(&cdev->dram, scratch_size); + if (!res) + return -EBUSY; + cdev->scratch = res; + } + + return 0; +} + +int catpt_register_plat_component(struct catpt_dev *cdev) +{ + struct snd_soc_component *component; + int ret; + + component = devm_kzalloc(cdev->dev, sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + ret = snd_soc_component_initialize(component, &catpt_comp_driver, + cdev->dev); + if (ret) + return ret; + + component->name = catpt_comp_driver.name; + return snd_soc_add_component(component, dai_drivers, + ARRAY_SIZE(dai_drivers)); +} -- cgit From 7a10b66a5df965ea4074aae265068b3483fa9fc6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:40 +0200 Subject: ASoC: Intel: catpt: Device driver lifecycle Implement ACPI device probing and removal functions as well as handlers for its PM capabilities. Device probing also takes care of enumerating ADSP subsystem components. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/device.c | 348 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 sound/soc/intel/catpt/device.c (limited to 'sound') diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c new file mode 100644 index 000000000000..96a4c1584720 --- /dev/null +++ b/sound/soc/intel/catpt/device.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// +// Special thanks to: +// Marcin Barlik +// Piotr Papierkowski +// +// for sharing LPT-LP and WTP-LP AudioDSP architecture expertise and +// helping backtrack its historical background +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "core.h" +#include "registers.h" + +static int __maybe_unused catpt_suspend(struct device *dev) +{ + struct catpt_dev *cdev = dev_get_drvdata(dev); + struct dma_chan *chan; + int ret; + + chan = catpt_dma_request_config_chan(cdev); + if (IS_ERR(chan)) + return PTR_ERR(chan); + + memset(&cdev->dx_ctx, 0, sizeof(cdev->dx_ctx)); + ret = catpt_ipc_enter_dxstate(cdev, CATPT_DX_STATE_D3, &cdev->dx_ctx); + if (ret) { + ret = CATPT_IPC_ERROR(ret); + goto release_dma_chan; + } + + ret = catpt_dsp_stall(cdev, true); + if (ret) + goto release_dma_chan; + + ret = catpt_store_memdumps(cdev, chan); + if (ret) { + dev_err(cdev->dev, "store memdumps failed: %d\n", ret); + goto release_dma_chan; + } + + ret = catpt_store_module_states(cdev, chan); + if (ret) { + dev_err(cdev->dev, "store module states failed: %d\n", ret); + goto release_dma_chan; + } + + ret = catpt_store_streams_context(cdev, chan); + if (ret) + dev_err(cdev->dev, "store streams ctx failed: %d\n", ret); + +release_dma_chan: + dma_release_channel(chan); + if (ret) + return ret; + return cdev->spec->power_down(cdev); +} + +static int __maybe_unused catpt_resume(struct device *dev) +{ + struct catpt_dev *cdev = dev_get_drvdata(dev); + int ret, i; + + ret = cdev->spec->power_up(cdev); + if (ret) + return ret; + + if (!module_is_live(dev->driver->owner)) { + dev_info(dev, "module unloading, skipping fw boot\n"); + return 0; + } + + ret = catpt_boot_firmware(cdev, true); + if (ret) { + dev_err(cdev->dev, "boot firmware failed: %d\n", ret); + return ret; + } + + /* reconfigure SSP devices after Dx transition */ + for (i = 0; i < CATPT_SSP_COUNT; i++) { + if (cdev->devfmt[i].iface == UINT_MAX) + continue; + + ret = catpt_ipc_set_device_format(cdev, &cdev->devfmt[i]); + if (ret) + return CATPT_IPC_ERROR(ret); + } + + return 0; +} + +static int __maybe_unused catpt_runtime_suspend(struct device *dev) +{ + if (!module_is_live(dev->driver->owner)) { + dev_info(dev, "module unloading, skipping suspend\n"); + return 0; + } + return catpt_suspend(dev); +} + +static int __maybe_unused catpt_runtime_resume(struct device *dev) +{ + return catpt_resume(dev); +} + +static const struct dev_pm_ops catpt_dev_pm = { + SET_SYSTEM_SLEEP_PM_OPS(catpt_suspend, catpt_resume) + SET_RUNTIME_PM_OPS(catpt_runtime_suspend, catpt_runtime_resume, NULL) +}; + +/* machine board owned by CATPT is removed with this hook */ +static void board_pdev_unregister(void *data) +{ + platform_device_unregister(data); +} + +static int catpt_register_board(struct catpt_dev *cdev) +{ + const struct catpt_spec *spec = cdev->spec; + struct snd_soc_acpi_mach *mach; + struct platform_device *board; + + mach = snd_soc_acpi_find_machine(spec->machines); + if (!mach) { + dev_info(cdev->dev, "no machines present\n"); + return 0; + } + + mach->mach_params.platform = "catpt-platform"; + board = platform_device_register_data(NULL, mach->drv_name, + PLATFORM_DEVID_NONE, + (const void *)mach, sizeof(*mach)); + if (IS_ERR(board)) { + dev_err(cdev->dev, "board register failed\n"); + return PTR_ERR(board); + } + + return devm_add_action_or_reset(cdev->dev, board_pdev_unregister, + board); +} + +static int catpt_probe_components(struct catpt_dev *cdev) +{ + int ret; + + ret = cdev->spec->power_up(cdev); + if (ret) + return ret; + + ret = catpt_dmac_probe(cdev); + if (ret) { + dev_err(cdev->dev, "DMAC probe failed: %d\n", ret); + goto err_dmac_probe; + } + + ret = catpt_first_boot_firmware(cdev); + if (ret) { + dev_err(cdev->dev, "first fw boot failed: %d\n", ret); + goto err_boot_fw; + } + + ret = catpt_register_plat_component(cdev); + if (ret) { + dev_err(cdev->dev, "register plat comp failed: %d\n", ret); + goto err_boot_fw; + } + + ret = catpt_register_board(cdev); + if (ret) { + dev_err(cdev->dev, "register board failed: %d\n", ret); + goto err_reg_board; + } + + /* reflect actual ADSP state in pm_runtime */ + pm_runtime_set_active(cdev->dev); + + pm_runtime_set_autosuspend_delay(cdev->dev, 2000); + pm_runtime_use_autosuspend(cdev->dev); + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_enable(cdev->dev); + return 0; + +err_reg_board: + snd_soc_unregister_component(cdev->dev); +err_boot_fw: + catpt_dmac_remove(cdev); +err_dmac_probe: + cdev->spec->power_down(cdev); + + return ret; +} + +static void catpt_dev_init(struct catpt_dev *cdev, struct device *dev, + const struct catpt_spec *spec) +{ + cdev->dev = dev; + cdev->spec = spec; + init_completion(&cdev->fw_ready); + INIT_LIST_HEAD(&cdev->stream_list); + spin_lock_init(&cdev->list_lock); + mutex_init(&cdev->clk_mutex); + + /* + * Mark both device formats as uninitialized. Once corresponding + * cpu_dai's pcm is created, proper values are assigned. + */ + cdev->devfmt[CATPT_SSP_IFACE_0].iface = UINT_MAX; + cdev->devfmt[CATPT_SSP_IFACE_1].iface = UINT_MAX; + + catpt_ipc_init(&cdev->ipc, dev); + + catpt_sram_init(&cdev->dram, spec->host_dram_offset, + catpt_dram_size(cdev)); + catpt_sram_init(&cdev->iram, spec->host_iram_offset, + catpt_iram_size(cdev)); +} + +static int catpt_acpi_probe(struct platform_device *pdev) +{ + const struct catpt_spec *spec; + struct catpt_dev *cdev; + struct device *dev = &pdev->dev; + struct resource *res; + int ret; + + spec = device_get_match_data(dev); + if (!spec) + return -ENODEV; + + cdev = devm_kzalloc(dev, sizeof(*cdev), GFP_KERNEL); + if (!cdev) + return -ENOMEM; + + catpt_dev_init(cdev, dev, spec); + + /* map DSP bar address */ + cdev->lpe_ba = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(cdev->lpe_ba)) + return PTR_ERR(cdev->lpe_ba); + cdev->lpe_base = res->start; + + /* map PCI bar address */ + cdev->pci_ba = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(cdev->pci_ba)) + return PTR_ERR(cdev->pci_ba); + + /* alloc buffer for storing DRAM context during dx transitions */ + cdev->dxbuf_vaddr = dmam_alloc_coherent(dev, catpt_dram_size(cdev), + &cdev->dxbuf_paddr, GFP_KERNEL); + if (!cdev->dxbuf_vaddr) + return -ENOMEM; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + cdev->irq = ret; + + platform_set_drvdata(pdev, cdev); + + ret = devm_request_threaded_irq(dev, cdev->irq, catpt_dsp_irq_handler, + catpt_dsp_irq_thread, + IRQF_SHARED, "AudioDSP", cdev); + if (ret) + return ret; + + return catpt_probe_components(cdev); +} + +static int catpt_acpi_remove(struct platform_device *pdev) +{ + struct catpt_dev *cdev = platform_get_drvdata(pdev); + + pm_runtime_disable(cdev->dev); + + snd_soc_unregister_component(cdev->dev); + catpt_dmac_remove(cdev); + cdev->spec->power_down(cdev); + + catpt_sram_free(&cdev->iram); + catpt_sram_free(&cdev->dram); + + return 0; +} + +static struct catpt_spec lpt_desc = { + .machines = snd_soc_acpi_intel_haswell_machines, + .core_id = 0x01, + .host_dram_offset = 0x000000, + .host_iram_offset = 0x080000, + .host_shim_offset = 0x0E7000, + .host_dma_offset = { 0x0F0000, 0x0F8000 }, + .host_ssp_offset = { 0x0E8000, 0x0E9000 }, + .dram_mask = LPT_VDRTCTL0_DSRAMPGE_MASK, + .iram_mask = LPT_VDRTCTL0_ISRAMPGE_MASK, + .pll_shutdown = lpt_dsp_pll_shutdown, + .power_up = lpt_dsp_power_up, + .power_down = lpt_dsp_power_down, +}; + +static struct catpt_spec wpt_desc = { + .machines = snd_soc_acpi_intel_broadwell_machines, + .core_id = 0x02, + .host_dram_offset = 0x000000, + .host_iram_offset = 0x0A0000, + .host_shim_offset = 0x0FB000, + .host_dma_offset = { 0x0FE000, 0x0FF000 }, + .host_ssp_offset = { 0x0FC000, 0x0FD000 }, + .dram_mask = WPT_VDRTCTL0_DSRAMPGE_MASK, + .iram_mask = WPT_VDRTCTL0_ISRAMPGE_MASK, + .pll_shutdown = wpt_dsp_pll_shutdown, + .power_up = wpt_dsp_power_up, + .power_down = wpt_dsp_power_down, +}; + +static const struct acpi_device_id catpt_ids[] = { + { "INT33C8", (unsigned long)&lpt_desc }, + { "INT3438", (unsigned long)&wpt_desc }, + { } +}; +MODULE_DEVICE_TABLE(acpi, catpt_ids); + +static struct platform_driver catpt_acpi_driver = { + .probe = catpt_acpi_probe, + .remove = catpt_acpi_remove, + .driver = { + .name = "intel_catpt", + .acpi_match_table = catpt_ids, + .pm = &catpt_dev_pm, + }, +}; +module_platform_driver(catpt_acpi_driver); + +MODULE_AUTHOR("Cezary Rojewski "); +MODULE_DESCRIPTION("Intel LPT/WPT AudioDSP driver"); +MODULE_LICENSE("GPL v2"); -- cgit From 8ba1edb9c245e63c6750c4c77bfdba1230442d4d Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:41 +0200 Subject: ASoC: Intel: catpt: Event tracing Define tracing macros for easy catpt debug. These cover all IPC message types: requests, replies and notifications. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/device.c | 3 ++ sound/soc/intel/catpt/ipc.c | 11 ++++++ sound/soc/intel/catpt/trace.h | 83 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 sound/soc/intel/catpt/trace.h (limited to 'sound') diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index 96a4c1584720..0d2dd919de8d 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -25,6 +25,9 @@ #include "core.h" #include "registers.h" +#define CREATE_TRACE_POINTS +#include "trace.h" + static int __maybe_unused catpt_suspend(struct device *dev) { struct catpt_dev *cdev = dev_get_drvdata(dev); diff --git a/sound/soc/intel/catpt/ipc.c b/sound/soc/intel/catpt/ipc.c index 3cad33b1c658..5b718a846fda 100644 --- a/sound/soc/intel/catpt/ipc.c +++ b/sound/soc/intel/catpt/ipc.c @@ -9,6 +9,7 @@ #include "core.h" #include "messages.h" #include "registers.h" +#include "trace.h" #define CATPT_IPC_TIMEOUT_MS 300 @@ -56,6 +57,9 @@ static void catpt_dsp_send_tx(struct catpt_dev *cdev, { u32 header = tx->header | CATPT_IPCC_BUSY; + trace_catpt_ipc_request(header); + trace_catpt_ipc_payload(tx->data, tx->size); + memcpy_toio(catpt_outbox_addr(cdev), tx->data, tx->size); catpt_writel_shim(cdev, IPCC, header); } @@ -155,12 +159,14 @@ catpt_dsp_notify_stream(struct catpt_dev *cdev, union catpt_notify_msg msg) switch (msg.notify_reason) { case CATPT_NOTIFY_POSITION_CHANGED: memcpy_fromio(&pos, catpt_inbox_addr(cdev), sizeof(pos)); + trace_catpt_ipc_payload((u8 *)&pos, sizeof(pos)); catpt_stream_update_position(cdev, stream, &pos); break; case CATPT_NOTIFY_GLITCH_OCCURRED: memcpy_fromio(&glitch, catpt_inbox_addr(cdev), sizeof(glitch)); + trace_catpt_ipc_payload((u8 *)&glitch, sizeof(glitch)); dev_warn(cdev->dev, "glitch %d at pos: 0x%08llx, wp: 0x%08x\n", glitch.type, glitch.presentation_pos, @@ -183,6 +189,7 @@ static void catpt_dsp_copy_rx(struct catpt_dev *cdev, u32 header) return; memcpy_fromio(ipc->rx.data, catpt_outbox_addr(cdev), ipc->rx.size); + trace_catpt_ipc_payload(ipc->rx.data, ipc->rx.size); } static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header) @@ -196,6 +203,7 @@ static void catpt_dsp_process_response(struct catpt_dev *cdev, u32 header) u32 off = msg.mailbox_address << 3; memcpy_fromio(&config, cdev->lpe_ba + off, sizeof(config)); + trace_catpt_ipc_payload((u8 *)&config, sizeof(config)); catpt_ipc_arm(ipc, &config); complete(&cdev->fw_ready); @@ -236,6 +244,7 @@ irqreturn_t catpt_dsp_irq_thread(int irq, void *dev_id) u32 ipcd; ipcd = catpt_readl_shim(cdev, IPCD); + trace_catpt_ipc_notify(ipcd); /* ensure there is delayed reply or notification to process */ if (!(ipcd & CATPT_IPCD_BUSY)) @@ -259,6 +268,7 @@ irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id) u32 isc, ipcc; isc = catpt_readl_shim(cdev, ISC); + trace_catpt_irq(isc); /* immediate reply */ if (isc & CATPT_ISC_IPCCD) { @@ -266,6 +276,7 @@ irqreturn_t catpt_dsp_irq_handler(int irq, void *dev_id) catpt_updatel_shim(cdev, IMC, CATPT_IMC_IPCCD, CATPT_IMC_IPCCD); ipcc = catpt_readl_shim(cdev, IPCC); + trace_catpt_ipc_reply(ipcc); catpt_dsp_copy_rx(cdev, ipcc); complete(&cdev->ipc.done_completion); diff --git a/sound/soc/intel/catpt/trace.h b/sound/soc/intel/catpt/trace.h new file mode 100644 index 000000000000..bb3d627dbeaf --- /dev/null +++ b/sound/soc/intel/catpt/trace.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright(c) 2020 Intel Corporation. All rights reserved. + * + * Author: Cezary Rojewski + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM intel_catpt + +#if !defined(__SND_SOC_INTEL_CATPT_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __SND_SOC_INTEL_CATPT_TRACE_H + +#include +#include + +DECLARE_EVENT_CLASS(catpt_ipc_msg, + + TP_PROTO(u32 header), + + TP_ARGS(header), + + TP_STRUCT__entry( + __field(u32, header) + ), + + TP_fast_assign( + __entry->header = header; + ), + + TP_printk("0x%08x", __entry->header) +); + +DEFINE_EVENT(catpt_ipc_msg, catpt_irq, + TP_PROTO(u32 header), + TP_ARGS(header) +); + +DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_request, + TP_PROTO(u32 header), + TP_ARGS(header) +); + +DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_reply, + TP_PROTO(u32 header), + TP_ARGS(header) +); + +DEFINE_EVENT(catpt_ipc_msg, catpt_ipc_notify, + TP_PROTO(u32 header), + TP_ARGS(header) +); + +TRACE_EVENT_CONDITION(catpt_ipc_payload, + + TP_PROTO(const u8 *data, size_t size), + + TP_ARGS(data, size), + + TP_CONDITION(data && size), + + TP_STRUCT__entry( + __dynamic_array(u8, buf, size) + ), + + TP_fast_assign( + memcpy(__get_dynamic_array(buf), data, size); + ), + + TP_printk("%u byte(s)%s", + __get_dynamic_array_len(buf), + __print_hex_dump("", DUMP_PREFIX_NONE, 16, 4, + __get_dynamic_array(buf), + __get_dynamic_array_len(buf), false)) +); + +#endif /* __SND_SOC_INTEL_CATPT_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE trace +#include -- cgit From 8f80a834b909784c6e1ff7fcc819b1f8bd1651be Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:42 +0200 Subject: ASoC: Intel: catpt: Simple sysfs attributes Add sysfs entries for displaying version of FW currently in use as well as dumping full FW information including build and log-providers hashes. Signed-off-by: Cezary Rojewski Reviewed-by: Greg Kroah-Hartman Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-10-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/core.h | 2 ++ sound/soc/intel/catpt/device.c | 1 + sound/soc/intel/catpt/sysfs.c | 55 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 sound/soc/intel/catpt/sysfs.c (limited to 'sound') diff --git a/sound/soc/intel/catpt/core.h b/sound/soc/intel/catpt/core.h index a29b4c0232cb..88dc3fb6306f 100644 --- a/sound/soc/intel/catpt/core.h +++ b/sound/soc/intel/catpt/core.h @@ -15,6 +15,8 @@ struct catpt_dev; +extern const struct attribute_group *catpt_attr_groups[]; + void catpt_sram_init(struct resource *sram, u32 start, u32 size); void catpt_sram_free(struct resource *sram); struct resource * diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index 0d2dd919de8d..390ffb203de0 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -342,6 +342,7 @@ static struct platform_driver catpt_acpi_driver = { .name = "intel_catpt", .acpi_match_table = catpt_ids, .pm = &catpt_dev_pm, + .dev_groups = catpt_attr_groups, }, }; module_platform_driver(catpt_acpi_driver); diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c new file mode 100644 index 000000000000..9579e233a15d --- /dev/null +++ b/sound/soc/intel/catpt/sysfs.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2020 Intel Corporation. All rights reserved. +// +// Author: Cezary Rojewski +// + +#include +#include "core.h" + +static ssize_t fw_version_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct catpt_dev *cdev = dev_get_drvdata(dev); + struct catpt_fw_version version; + int ret; + + pm_runtime_get_sync(cdev->dev); + + ret = catpt_ipc_get_fw_version(cdev, &version); + + pm_runtime_mark_last_busy(cdev->dev); + pm_runtime_put_autosuspend(cdev->dev); + + if (ret) + return CATPT_IPC_ERROR(ret); + + return sprintf(buf, "%d.%d.%d.%d\n", version.type, version.major, + version.minor, version.build); +} +static DEVICE_ATTR_RO(fw_version); + +static ssize_t fw_info_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct catpt_dev *cdev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", cdev->ipc.config.fw_info); +} +static DEVICE_ATTR_RO(fw_info); + +static struct attribute *catpt_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_fw_info.attr, + NULL +}; + +static const struct attribute_group catpt_attr_group = { + .attrs = catpt_attrs, +}; + +const struct attribute_group *catpt_attr_groups[] = { + &catpt_attr_group, + NULL +}; -- cgit From 0ce1610578bcb3b4a6824eb12f1a02cfc34a21e0 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:43 +0200 Subject: ASoC: Intel: haswell: Remove haswell-solution specific code Remove code specific to sound/soc/intel/haswell. Update BE dai_link definition to provide seamless transition to catpt solution. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-11-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/haswell.c | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index 744b7b5b8106..c268405e5594 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -13,9 +13,6 @@ #include #include -#include "../common/sst-dsp.h" -#include "../haswell/sst-haswell-ipc.h" - #include "../../codecs/rt5640.h" /* Haswell ULT platforms have a Headphone and Mic jack */ @@ -77,25 +74,6 @@ static const struct snd_soc_ops haswell_rt5640_ops = { .hw_params = haswell_rt5640_hw_params, }; -static int haswell_rtd_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct sst_pdata *pdata = dev_get_platdata(component->dev); - struct sst_hsw *haswell = pdata->dsp; - int ret; - - /* Set ADSP SSP port settings */ - ret = sst_hsw_device_set_config(haswell, SST_HSW_DEVICE_SSP_0, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, - SST_HSW_DEVICE_CLOCK_MASTER, 9); - if (ret < 0) { - dev_err(rtd->dev, "failed to set device config\n"); - return ret; - } - - return 0; -} - SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); @@ -117,13 +95,15 @@ SND_SOC_DAILINK_DEF(codec, SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("haswell-pcm-audio"))); +SND_SOC_DAILINK_DEF(ssp0_port, + DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); + static struct snd_soc_dai_link haswell_rt5640_dais[] = { /* Front End DAI links */ { .name = "System", .stream_name = "System Playback/Capture", .dynamic = 1, - .init = haswell_rtd_init, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, .dpcm_capture = 1, @@ -167,7 +147,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { .ops = &haswell_rt5640_ops, .dpcm_playback = 1, .dpcm_capture = 1, - SND_SOC_DAILINK_REG(dummy, codec, dummy), + SND_SOC_DAILINK_REG(ssp0_port, codec, platform), }, }; -- cgit From e81a707a3935493ed04b62775943ae41ae254289 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:44 +0200 Subject: ASoC: Intel: broadwell: Remove haswell-solution specific code Remove code specific to sound/soc/intel/haswell. Update BE dai_link definition to provide seamless transition to catpt solution. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-12-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/broadwell.c | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 56972af13b6f..648b47190051 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -14,9 +14,6 @@ #include #include -#include "../common/sst-dsp.h" -#include "../haswell/sst-haswell-ipc.h" - #include "../../codecs/rt286.h" static struct snd_soc_jack broadwell_headset; @@ -122,27 +119,6 @@ static const struct snd_soc_ops broadwell_rt286_ops = { .hw_params = broadwell_rt286_hw_params, }; -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) -static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct sst_pdata *pdata = dev_get_platdata(component->dev); - struct sst_hsw *broadwell = pdata->dsp; - int ret; - - /* Set ADSP SSP port settings */ - ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, - SST_HSW_DEVICE_CLOCK_MASTER, 9); - if (ret < 0) { - dev_err(rtd->dev, "error: failed to set device config\n"); - return ret; - } - - return 0; -} -#endif - static const unsigned int channels[] = { 2, }; @@ -189,10 +165,8 @@ SND_SOC_DAILINK_DEF(platform, SND_SOC_DAILINK_DEF(codec, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-INT343A:00", "rt286-aif1"))); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); -#endif /* broadwell digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link broadwell_rt286_dais[] = { @@ -201,9 +175,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .name = "System PCM", .stream_name = "System Playback/Capture", .dynamic = 1, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) - .init = broadwell_rtd_init, -#endif .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .ops = &broadwell_fe_ops, .dpcm_playback = 1, @@ -248,11 +219,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { .ops = &broadwell_rt286_ops, .dpcm_playback = 1, .dpcm_capture = 1, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) - SND_SOC_DAILINK_REG(dummy, codec, dummy), -#else SND_SOC_DAILINK_REG(ssp0_port, codec, platform), -#endif }, }; -- cgit From 02f2442fb32a1f8bde7774bd58e1dbeabe7970bc Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:45 +0200 Subject: ASoC: Intel: bdw-5650: Remove haswell-solution specific code Remove code specific to sound/soc/intel/haswell. Update BE dai_link definition to provide seamless transition to catpt solution. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-13-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5650.c | 36 ------------------------------------ 1 file changed, 36 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index 1412a9941ed4..c44315af6a4c 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -16,9 +16,6 @@ #include #include -#include "../common/sst-dsp.h" -#include "../haswell/sst-haswell-ipc.h" - #include "../../codecs/rt5645.h" struct bdw_rt5650_priv { @@ -138,30 +135,6 @@ static struct snd_soc_ops bdw_rt5650_ops = { .hw_params = bdw_rt5650_hw_params, }; -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) -static int bdw_rt5650_rtd_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = - snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct sst_pdata *pdata = dev_get_platdata(component->dev); - struct sst_hsw *broadwell = pdata->dsp; - int ret; - - /* Set ADSP SSP port settings - * clock_divider = 4 means BCLK = MCLK/5 = 24MHz/5 = 4.8MHz - */ - ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, - SST_HSW_DEVICE_TDM_CLOCK_MASTER, 4); - if (ret < 0) { - dev_err(rtd->dev, "error: failed to set device config\n"); - return ret; - } - - return 0; -} -#endif - static const unsigned int channels[] = { 2, 4, }; @@ -251,10 +224,8 @@ SND_SOC_DAILINK_DEF(platform, SND_SOC_DAILINK_DEF(be, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1"))); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); -#endif static struct snd_soc_dai_link bdw_rt5650_dais[] = { /* Front End DAI links */ @@ -263,9 +234,6 @@ static struct snd_soc_dai_link bdw_rt5650_dais[] = { .stream_name = "System Playback", .dynamic = 1, .ops = &bdw_rt5650_fe_ops, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) - .init = bdw_rt5650_rtd_init, -#endif .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST @@ -289,11 +257,7 @@ static struct snd_soc_dai_link bdw_rt5650_dais[] = { .dpcm_playback = 1, .dpcm_capture = 1, .init = bdw_rt5650_init, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) - SND_SOC_DAILINK_REG(dummy, be, dummy), -#else SND_SOC_DAILINK_REG(ssp0_port, be, platform), -#endif }, }; -- cgit From 053743f0c49074e710401ce39dd6f7d767094f77 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:46 +0200 Subject: ASoC: Intel: bdw-5677: Remove haswell-solution specific code Remove code specific to sound/soc/intel/haswell. Update BE dai_link definition to provide seamless transition to catpt solution. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-14-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5677.c | 33 --------------------------------- 1 file changed, 33 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index 297871bcaf5d..d6584768883b 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -17,9 +17,6 @@ #include #include -#include "../common/sst-dsp.h" -#include "../haswell/sst-haswell-ipc.h" - #include "../../codecs/rt5677.h" struct bdw_rt5677_priv { @@ -201,27 +198,6 @@ static const struct snd_soc_ops bdw_rt5677_dsp_ops = { .hw_params = bdw_rt5677_dsp_hw_params, }; -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) -static int bdw_rt5677_rtd_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct sst_pdata *pdata = dev_get_platdata(component->dev); - struct sst_hsw *broadwell = pdata->dsp; - int ret; - - /* Set ADSP SSP port settings */ - ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ, - SST_HSW_DEVICE_CLOCK_MASTER, 9); - if (ret < 0) { - dev_err(rtd->dev, "error: failed to set device config\n"); - return ret; - } - - return 0; -} -#endif - static const unsigned int channels[] = { 2, }; @@ -333,10 +309,8 @@ SND_SOC_DAILINK_DEF(platform, SND_SOC_DAILINK_DEF(be, DAILINK_COMP_ARRAY(COMP_CODEC("i2c-RT5677CE:00", "rt5677-aif1"))); -#if IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) SND_SOC_DAILINK_DEF(ssp0_port, DAILINK_COMP_ARRAY(COMP_CPU("ssp0-port"))); -#endif /* Wake on voice interface */ SND_SOC_DAILINK_DEFS(dsp, @@ -350,9 +324,6 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .name = "System PCM", .stream_name = "System Playback/Capture", .dynamic = 1, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) - .init = bdw_rt5677_rtd_init, -#endif .trigger = { SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST @@ -387,11 +358,7 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { .dpcm_capture = 1, .init = bdw_rt5677_init, .exit = bdw_rt5677_exit, -#if !IS_ENABLED(CONFIG_SND_SOC_SOF_BROADWELL) - SND_SOC_DAILINK_REG(dummy, be, dummy), -#else SND_SOC_DAILINK_REG(ssp0_port, be, platform), -#endif }, }; -- cgit From 6cbfa11d2694b8a1e46d6834fb9705d5589e3ef1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 29 Sep 2020 16:12:47 +0200 Subject: ASoC: Intel: Select catpt and deprecate haswell Prevent sound/soc/intel/haswell code compile and select catpt instead as a recommended solution. Userspace-exposed members are compatible with what is exposed by deprecated solution thus no harm is done. The only visible difference is the newly added 'Loopback Mute' kcontrol. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20200929141247.8058-15-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 24 ++++++++++++------------ sound/soc/intel/Makefile | 2 +- sound/soc/intel/boards/Kconfig | 8 ++++---- sound/soc/intel/catpt/Makefile | 6 ++++++ 4 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 sound/soc/intel/catpt/Makefile (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 0e48c4f532ce..dfc20f2bb859 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -47,21 +47,21 @@ config SND_SOC_INTEL_SST_FIRMWARE # Haswell/Broadwell/Baytrail legacy and will be set # when these platforms are enabled -config SND_SOC_INTEL_HASWELL - tristate "Haswell/Broadwell Platforms" +config SND_SOC_INTEL_CATPT + tristate "Haswell and Broadwell" + depends on ACPI || COMPILE_TEST depends on SND_DMA_SGBUF - depends on DMADEVICES && ACPI - select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_ACPI - select SND_SOC_INTEL_SST_FIRMWARE + select DW_DMAC_CORE select SND_SOC_ACPI_INTEL_MATCH help - If you have a Intel Haswell or Broadwell platform connected to - an I2S codec, then enable this option by saying Y or m. This is - typically used for Chromebooks. This is a recommended option. - This option is mutually exclusive with the SOF support on - Broadwell. If you want to enable SOF on Broadwell, you need to - deselect this option first. + Enable support for Intel(R) Haswell and Broadwell platforms + with I2S codec present. This is a recommended option. + Say Y or m if you have such device. + If unsure, say N. + +config SND_SOC_INTEL_HASWELL + tristate + select SND_SOC_INTEL_CATPT config SND_SOC_INTEL_BAYTRAIL tristate "Baytrail (legacy) Platforms" diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index 04ee48204fc9..c88c615f85f7 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -3,9 +3,9 @@ obj-$(CONFIG_SND_SOC) += common/ # Platform Support -obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += haswell/ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/ +obj-$(CONFIG_SND_SOC_INTEL_CATPT) += catpt/ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/ obj-$(CONFIG_SND_SOC_INTEL_KEEMBAY) += keembay/ diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 12dd41796e82..6afdd9ac4478 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -26,7 +26,7 @@ config SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES interface. If unsure select N. -if SND_SOC_INTEL_HASWELL +if SND_SOC_INTEL_CATPT config SND_SOC_INTEL_HASWELL_MACH tristate "Haswell Lynxpoint" @@ -40,9 +40,9 @@ config SND_SOC_INTEL_HASWELL_MACH Say Y or m if you have such a device. If unsure select "N". -endif ## SND_SOC_INTEL_HASWELL +endif ## SND_SOC_INTEL_CATPT -if SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL +if SND_SOC_INTEL_CATPT || SND_SOC_SOF_BROADWELL config SND_SOC_INTEL_BDW_RT5650_MACH tristate "Broadwell with RT5650 codec" @@ -83,7 +83,7 @@ config SND_SOC_INTEL_BROADWELL_MACH Ultrabook platforms. Say Y or m if you have such a device. This is a recommended option. If unsure select "N". -endif ## SND_SOC_INTEL_HASWELL || SND_SOC_SOF_BROADWELL +endif ## SND_SOC_INTEL_CATPT || SND_SOC_SOF_BROADWELL if SND_SOC_INTEL_BAYTRAIL diff --git a/sound/soc/intel/catpt/Makefile b/sound/soc/intel/catpt/Makefile new file mode 100644 index 000000000000..c393a45795da --- /dev/null +++ b/sound/soc/intel/catpt/Makefile @@ -0,0 +1,6 @@ +snd-soc-catpt-objs := device.o dsp.o loader.o ipc.o messages.o pcm.o sysfs.o + +# tell define_trace.h where to find the trace header +CFLAGS_device.o := -I$(src) + +obj-$(CONFIG_SND_SOC_INTEL_CATPT) += snd-soc-catpt.o -- cgit From 2bc8831b135ce1a55285663505245cb79422af76 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 1 Oct 2020 11:35:37 -0700 Subject: ASoC: qcom: fix SDM845 & QDSP6 dependencies more Fix a build error and Kconfig warning in sound/soc/qcom/. ld: sound/soc/qcom/qdsp6/q6afe-clocks.o: in function `q6afe_clock_dev_probe': q6afe-clocks.c:(.text+0x182): undefined reference to `devm_clk_hw_register' ld: q6afe-clocks.c:(.text+0x19d): undefined reference to `of_clk_add_hw_provider' After adding "depends on COMMON_CLK" for SND_SOC_QDSP6, the Kconfig warning appears because "select" does not honor any "depends on" clauses, so fix the dependency for SND_SOC_SDM845 also. WARNING: unmet direct dependencies detected for SND_SOC_QDSP6 Depends on [n]: SOUND [=y] && !UML && SND [=y] && SND_SOC [=y] && SND_SOC_QCOM [=y] && QCOM_APR [=y] && COMMON_CLK [=n] Selected by [y]: - SND_SOC_SDM845 [=y] && SOUND [=y] && !UML && SND [=y] && SND_SOC [=y] && SND_SOC_QCOM [=y] && QCOM_APR [=y] && I2C [=y] && SOUNDWIRE [=y] Fixes: 520a1c396d19 ("ASoC: q6afe-clocks: add q6afe clock controller") Signed-off-by: Randy Dunlap Cc: Srinivas Kandagatla Cc: Mark Brown Cc: Jaroslav Kysela Cc: Takashi Iwai Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20201001183537.5781-1-rdunlap@infradead.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index a7ef62685b41..04258e6aa164 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -82,6 +82,7 @@ config SND_SOC_QDSP6_ASM_DAI config SND_SOC_QDSP6 tristate "SoC ALSA audio driver for QDSP6" depends on QCOM_APR + depends on COMMON_CLK select SND_SOC_QDSP6_COMMON select SND_SOC_QDSP6_CORE select SND_SOC_QDSP6_AFE @@ -110,6 +111,7 @@ config SND_SOC_MSM8996 config SND_SOC_SDM845 tristate "SoC Machine driver for SDM845 boards" depends on QCOM_APR && I2C && SOUNDWIRE + depends on COMMON_CLK select SND_SOC_QDSP6 select SND_SOC_QCOM_COMMON select SND_SOC_RT5663 -- cgit From ef265c55c1ac0f02c74a33d8e054547f7eafc81b Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 2 Oct 2020 19:03:05 +0300 Subject: ASoC: mchp-spdifrx: add driver for SPDIF RX The new SPDIF RX controller is a serial port compliant with the IEC-60958 standard. It also supports programmable User Data and Channel Status fields. This IP is embedded in Microchip's sama7g5 SoC. Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20201002160305.815523-3-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/Kconfig | 13 + sound/soc/atmel/Makefile | 2 + sound/soc/atmel/mchp-spdifrx.c | 954 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 969 insertions(+) create mode 100644 sound/soc/atmel/mchp-spdifrx.c (limited to 'sound') diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig index 93beb7d670a3..bd8854bfd2ee 100644 --- a/sound/soc/atmel/Kconfig +++ b/sound/soc/atmel/Kconfig @@ -144,4 +144,17 @@ config SND_MCHP_SOC_SPDIFTX This S/PDIF TX driver is compliant with IEC-60958 standard and includes programable User Data and Channel Status fields. + +config SND_MCHP_SOC_SPDIFRX + tristate "Microchip ASoC driver for boards using S/PDIF RX" + depends on OF && (ARCH_AT91 || COMPILE_TEST) + select SND_SOC_GENERIC_DMAENGINE_PCM + select REGMAP_MMIO + help + Say Y or M if you want to add support for Microchip S/PDIF RX ASoc + driver on the following Microchip platforms: + - sama7g5 + + This S/PDIF RX driver is compliant with IEC-60958 standard and + includes programable User Data and Channel Status fields. endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile index 3fd89a0063df..016188397210 100644 --- a/sound/soc/atmel/Makefile +++ b/sound/soc/atmel/Makefile @@ -6,6 +6,7 @@ snd-soc-atmel_ssc_dai-objs := atmel_ssc_dai.o snd-soc-atmel-i2s-objs := atmel-i2s.o snd-soc-mchp-i2s-mcc-objs := mchp-i2s-mcc.o snd-soc-mchp-spdiftx-objs := mchp-spdiftx.o +snd-soc-mchp-spdifrx-objs := mchp-spdifrx.o # pdc and dma need to both be built-in if any user of # ssc is built-in. @@ -19,6 +20,7 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o obj-$(CONFIG_SND_ATMEL_SOC_I2S) += snd-soc-atmel-i2s.o obj-$(CONFIG_SND_MCHP_SOC_I2S_MCC) += snd-soc-mchp-i2s-mcc.o obj-$(CONFIG_SND_MCHP_SOC_SPDIFTX) += snd-soc-mchp-spdiftx.o +obj-$(CONFIG_SND_MCHP_SOC_SPDIFRX) += snd-soc-mchp-spdifrx.o # AT91 Machine Support snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c new file mode 100644 index 000000000000..6776d89d56df --- /dev/null +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for Microchip S/PDIF RX Controller +// +// Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries +// +// Author: Codrin Ciubotariu + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * ---- S/PDIF Receiver Controller Register map ---- + */ +#define SPDIFRX_CR 0x00 /* Control Register */ +#define SPDIFRX_MR 0x04 /* Mode Register */ + +#define SPDIFRX_IER 0x10 /* Interrupt Enable Register */ +#define SPDIFRX_IDR 0x14 /* Interrupt Disable Register */ +#define SPDIFRX_IMR 0x18 /* Interrupt Mask Register */ +#define SPDIFRX_ISR 0x1c /* Interrupt Status Register */ +#define SPDIFRX_RSR 0x20 /* Status Register */ +#define SPDIFRX_RHR 0x24 /* Holding Register */ + +#define SPDIFRX_CHSR(channel, reg) \ + (0x30 + (channel) * 0x30 + (reg) * 4) /* Channel x Status Registers */ + +#define SPDIFRX_CHUD(channel, reg) \ + (0x48 + (channel) * 0x30 + (reg) * 4) /* Channel x User Data Registers */ + +#define SPDIFRX_WPMR 0xE4 /* Write Protection Mode Register */ +#define SPDIFRX_WPSR 0xE8 /* Write Protection Status Register */ + +#define SPDIFRX_VERSION 0xFC /* Version Register */ + +/* + * ---- Control Register (Write-only) ---- + */ +#define SPDIFRX_CR_SWRST BIT(0) /* Software Reset */ + +/* + * ---- Mode Register (Read/Write) ---- + */ +/* Receive Enable */ +#define SPDIFRX_MR_RXEN_MASK GENMASK(0, 0) +#define SPDIFRX_MR_RXEN_DISABLE (0 << 0) /* SPDIF Receiver Disabled */ +#define SPDIFRX_MR_RXEN_ENABLE (1 << 0) /* SPDIF Receiver Enabled */ + +/* Validity Bit Mode */ +#define SPDIFRX_MR_VBMODE_MASK GENAMSK(1, 1) +#define SPDIFRX_MR_VBMODE_ALWAYS_LOAD \ + (0 << 1) /* Load sample regardles of validity bit value */ +#define SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 \ + (1 << 1) /* Load sample only if validity bit is 0 */ + +/* Data Word Endian Mode */ +#define SPDIFRX_MR_ENDIAN_MASK GENMASK(2, 2) +#define SPDIFRX_MR_ENDIAN_LITTLE (0 << 2) /* Little Endian Mode */ +#define SPDIFRX_MR_ENDIAN_BIG (1 << 2) /* Big Endian Mode */ + +/* Parity Bit Mode */ +#define SPDIFRX_MR_PBMODE_MASK GENMASK(3, 3) +#define SPDIFRX_MR_PBMODE_PARCHECK (0 << 3) /* Parity Check Enabled */ +#define SPDIFRX_MR_PBMODE_NOPARCHECK (1 << 3) /* Parity Check Disabled */ + +/* Sample Data Width */ +#define SPDIFRX_MR_DATAWIDTH_MASK GENMASK(5, 4) +#define SPDIFRX_MR_DATAWIDTH(width) \ + (((6 - (width) / 4) << 4) & SPDIFRX_MR_DATAWIDTH_MASK) + +/* Packed Data Mode in Receive Holding Register */ +#define SPDIFRX_MR_PACK_MASK GENMASK(7, 7) +#define SPDIFRX_MR_PACK_DISABLED (0 << 7) +#define SPDIFRX_MR_PACK_ENABLED (1 << 7) + +/* Start of Block Bit Mode */ +#define SPDIFRX_MR_SBMODE_MASK GENMASK(8, 8) +#define SPDIFRX_MR_SBMODE_ALWAYS_LOAD (0 << 8) +#define SPDIFRX_MR_SBMODE_DISCARD (1 << 8) + +/* Consecutive Preamble Error Threshold Automatic Restart */ +#define SPDIFRX_MR_AUTORST_MASK GENMASK(24, 24) +#define SPDIFRX_MR_AUTORST_NOACTION (0 << 24) +#define SPDIFRX_MR_AUTORST_UNLOCK_ON_PRE_ERR (1 << 24) + +/* + * ---- Interrupt Enable/Disable/Mask/Status Register (Write/Read-only) ---- + */ +#define SPDIFRX_IR_RXRDY BIT(0) +#define SPDIFRX_IR_LOCKED BIT(1) +#define SPDIFRX_IR_LOSS BIT(2) +#define SPDIFRX_IR_BLOCKEND BIT(3) +#define SPDIFRX_IR_SFE BIT(4) +#define SPDIFRX_IR_PAR_ERR BIT(5) +#define SPDIFRX_IR_OVERRUN BIT(6) +#define SPDIFRX_IR_RXFULL BIT(7) +#define SPDIFRX_IR_CSC(ch) BIT((ch) + 8) +#define SPDIFRX_IR_SECE BIT(10) +#define SPDIFRX_IR_BLOCKST BIT(11) +#define SPDIFRX_IR_NRZ_ERR BIT(12) +#define SPDIFRX_IR_PRE_ERR BIT(13) +#define SPDIFRX_IR_CP_ERR BIT(14) + +/* + * ---- Receiver Status Register (Read/Write) ---- + */ +/* Enable Status */ +#define SPDIFRX_RSR_ULOCK BIT(0) +#define SPDIFRX_RSR_BADF BIT(1) +#define SPDIFRX_RSR_LOWF BIT(2) +#define SPDIFRX_RSR_NOSIGNAL BIT(3) +#define SPDIFRX_RSR_IFS_MASK GENMASK(27, 16) +#define SPDIFRX_RSR_IFS(reg) \ + (((reg) & SPDIFRX_RSR_IFS_MASK) >> 16) + +/* + * ---- Version Register (Read-only) ---- + */ +#define SPDIFRX_VERSION_MASK GENMASK(11, 0) +#define SPDIFRX_VERSION_MFN_MASK GENMASK(18, 16) +#define SPDIFRX_VERSION_MFN(reg) (((reg) & SPDIFRX_VERSION_MFN_MASK) >> 16) + +static bool mchp_spdifrx_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFRX_MR: + case SPDIFRX_IMR: + case SPDIFRX_ISR: + case SPDIFRX_RSR: + case SPDIFRX_CHSR(0, 0): + case SPDIFRX_CHSR(0, 1): + case SPDIFRX_CHSR(0, 2): + case SPDIFRX_CHSR(0, 3): + case SPDIFRX_CHSR(0, 4): + case SPDIFRX_CHSR(0, 5): + case SPDIFRX_CHUD(0, 0): + case SPDIFRX_CHUD(0, 1): + case SPDIFRX_CHUD(0, 2): + case SPDIFRX_CHUD(0, 3): + case SPDIFRX_CHUD(0, 4): + case SPDIFRX_CHUD(0, 5): + case SPDIFRX_CHSR(1, 0): + case SPDIFRX_CHSR(1, 1): + case SPDIFRX_CHSR(1, 2): + case SPDIFRX_CHSR(1, 3): + case SPDIFRX_CHSR(1, 4): + case SPDIFRX_CHSR(1, 5): + case SPDIFRX_CHUD(1, 0): + case SPDIFRX_CHUD(1, 1): + case SPDIFRX_CHUD(1, 2): + case SPDIFRX_CHUD(1, 3): + case SPDIFRX_CHUD(1, 4): + case SPDIFRX_CHUD(1, 5): + case SPDIFRX_WPMR: + case SPDIFRX_WPSR: + case SPDIFRX_VERSION: + return true; + default: + return false; + } +} + +static bool mchp_spdifrx_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFRX_CR: + case SPDIFRX_MR: + case SPDIFRX_IER: + case SPDIFRX_IDR: + case SPDIFRX_WPMR: + return true; + default: + return false; + } +} + +static bool mchp_spdifrx_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SPDIFRX_ISR: + case SPDIFRX_RHR: + return true; + default: + return false; + } +} + +static const struct regmap_config mchp_spdifrx_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = SPDIFRX_VERSION, + .readable_reg = mchp_spdifrx_readable_reg, + .writeable_reg = mchp_spdifrx_writeable_reg, + .precious_reg = mchp_spdifrx_precious_reg, +}; + +#define SPDIFRX_GCLK_RATIO_MIN (12 * 64) + +#define SPDIFRX_CS_BITS 192 +#define SPDIFRX_UD_BITS 192 + +#define SPDIFRX_CHANNELS 2 + +struct mchp_spdifrx_ch_stat { + unsigned char data[SPDIFRX_CS_BITS / 8]; + struct completion done; +}; + +struct mchp_spdifrx_user_data { + unsigned char data[SPDIFRX_UD_BITS / 8]; + struct completion done; + spinlock_t lock; /* protect access to user data */ +}; + +struct mchp_spdifrx_mixer_control { + struct mchp_spdifrx_ch_stat ch_stat[SPDIFRX_CHANNELS]; + struct mchp_spdifrx_user_data user_data[SPDIFRX_CHANNELS]; + bool ulock; + bool badf; + bool signal; +}; + +struct mchp_spdifrx_dev { + struct snd_dmaengine_dai_dma_data capture; + struct mchp_spdifrx_mixer_control control; + spinlock_t blockend_lock; /* protect access to blockend_refcount */ + int blockend_refcount; + struct device *dev; + struct regmap *regmap; + struct clk *pclk; + struct clk *gclk; + unsigned int fmt; + unsigned int gclk_enabled:1; +}; + +static void mchp_spdifrx_channel_status_read(struct mchp_spdifrx_dev *dev, + int channel) +{ + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + u8 *ch_stat = &ctrl->ch_stat[channel].data[0]; + u32 val; + int i; + + for (i = 0; i < ARRAY_SIZE(ctrl->ch_stat[channel].data) / 4; i++) { + regmap_read(dev->regmap, SPDIFRX_CHSR(channel, i), &val); + *ch_stat++ = val & 0xFF; + *ch_stat++ = (val >> 8) & 0xFF; + *ch_stat++ = (val >> 16) & 0xFF; + *ch_stat++ = (val >> 24) & 0xFF; + } +} + +static void mchp_spdifrx_channel_user_data_read(struct mchp_spdifrx_dev *dev, + int channel) +{ + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + u8 *user_data = &ctrl->user_data[channel].data[0]; + u32 val; + int i; + + for (i = 0; i < ARRAY_SIZE(ctrl->user_data[channel].data) / 4; i++) { + regmap_read(dev->regmap, SPDIFRX_CHUD(channel, i), &val); + *user_data++ = val & 0xFF; + *user_data++ = (val >> 8) & 0xFF; + *user_data++ = (val >> 16) & 0xFF; + *user_data++ = (val >> 24) & 0xFF; + } +} + +/* called from non-atomic context only */ +static void mchp_spdifrx_isr_blockend_en(struct mchp_spdifrx_dev *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->blockend_lock, flags); + dev->blockend_refcount++; + /* don't enable BLOCKEND interrupt if it's already enabled */ + if (dev->blockend_refcount == 1) + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_BLOCKEND); + spin_unlock_irqrestore(&dev->blockend_lock, flags); +} + +/* called from atomic context only */ +static void mchp_spdifrx_isr_blockend_dis(struct mchp_spdifrx_dev *dev) +{ + spin_lock(&dev->blockend_lock); + dev->blockend_refcount--; + /* don't enable BLOCKEND interrupt if it's already enabled */ + if (dev->blockend_refcount == 0) + regmap_write(dev->regmap, SPDIFRX_IDR, SPDIFRX_IR_BLOCKEND); + spin_unlock(&dev->blockend_lock); +} + +static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) +{ + struct mchp_spdifrx_dev *dev = dev_id; + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + u32 sr, imr, pending, idr = 0; + irqreturn_t ret = IRQ_NONE; + int ch; + + regmap_read(dev->regmap, SPDIFRX_ISR, &sr); + regmap_read(dev->regmap, SPDIFRX_IMR, &imr); + pending = sr & imr; + dev_dbg(dev->dev, "ISR: %#x, IMR: %#x, pending: %#x\n", sr, imr, + pending); + + if (!pending) + return IRQ_NONE; + + if (pending & SPDIFRX_IR_BLOCKEND) { + for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { + spin_lock(&ctrl->user_data[ch].lock); + mchp_spdifrx_channel_user_data_read(dev, ch); + spin_unlock(&ctrl->user_data[ch].lock); + + complete(&ctrl->user_data[ch].done); + } + mchp_spdifrx_isr_blockend_dis(dev); + ret = IRQ_HANDLED; + } + + for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { + if (pending & SPDIFRX_IR_CSC(ch)) { + mchp_spdifrx_channel_status_read(dev, ch); + complete(&ctrl->ch_stat[ch].done); + idr |= SPDIFRX_IR_CSC(ch); + ret = IRQ_HANDLED; + } + } + + if (pending & SPDIFRX_IR_OVERRUN) { + dev_warn(dev->dev, "Overrrun detected\n"); + ret = IRQ_HANDLED; + } + + regmap_write(dev->regmap, SPDIFRX_IDR, idr); + + return ret; +} + +static int mchp_spdifrx_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + u32 mr; + int running; + int ret; + + regmap_read(dev->regmap, SPDIFRX_MR, &mr); + running = !!(mr & SPDIFRX_MR_RXEN_ENABLE); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!running) { + mr &= ~SPDIFRX_MR_RXEN_MASK; + mr |= SPDIFRX_MR_RXEN_ENABLE; + /* enable overrun interrupts */ + regmap_write(dev->regmap, SPDIFRX_IER, + SPDIFRX_IR_OVERRUN); + } + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (running) { + mr &= ~SPDIFRX_MR_RXEN_MASK; + mr |= SPDIFRX_MR_RXEN_DISABLE; + /* disable overrun interrupts */ + regmap_write(dev->regmap, SPDIFRX_IDR, + SPDIFRX_IR_OVERRUN); + } + break; + default: + return -EINVAL; + } + + ret = regmap_write(dev->regmap, SPDIFRX_MR, mr); + if (ret) { + dev_err(dev->dev, "unable to enable/disable RX: %d\n", ret); + return ret; + } + + return 0; +} + +static int mchp_spdifrx_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + u32 mr; + int ret; + + dev_dbg(dev->dev, "%s() rate=%u format=%#x width=%u channels=%u\n", + __func__, params_rate(params), params_format(params), + params_width(params), params_channels(params)); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + dev_err(dev->dev, "Playback is not supported\n"); + return -EINVAL; + } + + regmap_read(dev->regmap, SPDIFRX_MR, &mr); + + if (mr & SPDIFRX_MR_RXEN_ENABLE) { + dev_err(dev->dev, "PCM already running\n"); + return -EBUSY; + } + + if (params_channels(params) != SPDIFRX_CHANNELS) { + dev_err(dev->dev, "unsupported number of channels: %d\n", + params_channels(params)); + return -EINVAL; + } + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_BE: + case SNDRV_PCM_FORMAT_S20_3BE: + case SNDRV_PCM_FORMAT_S24_3BE: + case SNDRV_PCM_FORMAT_S24_BE: + mr |= SPDIFRX_MR_ENDIAN_BIG; + fallthrough; + case SNDRV_PCM_FORMAT_S16_LE: + case SNDRV_PCM_FORMAT_S20_3LE: + case SNDRV_PCM_FORMAT_S24_3LE: + case SNDRV_PCM_FORMAT_S24_LE: + mr |= SPDIFRX_MR_DATAWIDTH(params_width(params)); + break; + default: + dev_err(dev->dev, "unsupported PCM format: %d\n", + params_format(params)); + return -EINVAL; + } + + if (dev->gclk_enabled) { + clk_disable_unprepare(dev->gclk); + dev->gclk_enabled = 0; + } + ret = clk_set_min_rate(dev->gclk, params_rate(params) * + SPDIFRX_GCLK_RATIO_MIN + 1); + if (ret) { + dev_err(dev->dev, + "unable to set gclk min rate: rate %u * ratio %u + 1\n", + params_rate(params), SPDIFRX_GCLK_RATIO_MIN); + return ret; + } + ret = clk_prepare_enable(dev->gclk); + if (ret) { + dev_err(dev->dev, "unable to enable gclk: %d\n", ret); + return ret; + } + dev->gclk_enabled = 1; + + dev_dbg(dev->dev, "GCLK range min set to %d\n", + params_rate(params) * SPDIFRX_GCLK_RATIO_MIN + 1); + + return regmap_write(dev->regmap, SPDIFRX_MR, mr); +} + +static int mchp_spdifrx_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + + if (dev->gclk_enabled) { + clk_disable_unprepare(dev->gclk); + dev->gclk_enabled = 0; + } + return 0; +} + +static const struct snd_soc_dai_ops mchp_spdifrx_dai_ops = { + .trigger = mchp_spdifrx_trigger, + .hw_params = mchp_spdifrx_hw_params, + .hw_free = mchp_spdifrx_hw_free, +}; + +#define MCHP_SPDIF_RATES SNDRV_PCM_RATE_8000_192000 + +#define MCHP_SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_BE | \ + SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S20_3BE | \ + SNDRV_PCM_FMTBIT_S24_3LE | \ + SNDRV_PCM_FMTBIT_S24_3BE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S24_BE \ + ) + +static int mchp_spdifrx_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 mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev, + int channel, + struct snd_ctl_elem_value *uvalue) +{ + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + struct mchp_spdifrx_ch_stat *ch_stat = &ctrl->ch_stat[channel]; + int ret; + + regmap_write(dev->regmap, SPDIFRX_IER, SPDIFRX_IR_CSC(channel)); + /* check for new data available */ + ret = wait_for_completion_interruptible_timeout(&ch_stat->done, + msecs_to_jiffies(100)); + /* IP might not be started or valid stream might not be prezent */ + if (ret < 0) { + dev_dbg(dev->dev, "channel status for channel %d timeout\n", + channel); + } + + memcpy(uvalue->value.iec958.status, ch_stat->data, + sizeof(ch_stat->data)); + + return 0; +} + +static int mchp_spdifrx_cs1_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + + return mchp_spdifrx_cs_get(dev, 0, uvalue); +} + +static int mchp_spdifrx_cs2_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + + return mchp_spdifrx_cs_get(dev, 1, uvalue); +} + +static int mchp_spdifrx_cs_mask(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + memset(uvalue->value.iec958.status, 0xff, + sizeof(uvalue->value.iec958.status)); + + return 0; +} + +static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev, + int channel, + struct snd_ctl_elem_value *uvalue) +{ + unsigned long flags; + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + struct mchp_spdifrx_user_data *user_data = &ctrl->user_data[channel]; + int ret; + + reinit_completion(&user_data->done); + mchp_spdifrx_isr_blockend_en(dev); + ret = wait_for_completion_interruptible_timeout(&user_data->done, + msecs_to_jiffies(100)); + /* IP might not be started or valid stream might not be prezent */ + if (ret <= 0) { + dev_dbg(dev->dev, "user data for channel %d timeout\n", + channel); + return ret; + } + + spin_lock_irqsave(&user_data->lock, flags); + memcpy(uvalue->value.iec958.subcode, user_data->data, + sizeof(user_data->data)); + spin_unlock_irqrestore(&user_data->lock, flags); + + return 0; +} + +static int mchp_spdifrx_subcode_ch1_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + + return mchp_spdifrx_subcode_ch_get(dev, 0, uvalue); +} + +static int mchp_spdifrx_subcode_ch2_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + + return mchp_spdifrx_subcode_ch_get(dev, 1, uvalue); +} + +static int mchp_spdifrx_boolean_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + u32 val; + bool ulock_old = ctrl->ulock; + + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->ulock = !(val & SPDIFRX_RSR_ULOCK); + uvalue->value.integer.value[0] = ctrl->ulock; + + return ulock_old != ctrl->ulock; +} + +static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + u32 val; + bool badf_old = ctrl->badf; + + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->badf = !!(val & SPDIFRX_RSR_BADF); + uvalue->value.integer.value[0] = ctrl->badf; + + return badf_old != ctrl->badf; +} + +static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *uvalue) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + u32 val; + bool signal_old = ctrl->signal; + + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + ctrl->signal = !(val & SPDIFRX_RSR_NOSIGNAL); + uvalue->value.integer.value[0] = ctrl->signal; + + return signal_old != ctrl->signal; +} + +static int mchp_spdifrx_rate_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 mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_dai *dai = snd_kcontrol_chip(kcontrol); + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + u32 val; + int rate; + + regmap_read(dev->regmap, SPDIFRX_RSR, &val); + + /* if the receiver is not locked, ISF data is invalid */ + if (val & SPDIFRX_RSR_ULOCK || !(val & SPDIFRX_RSR_IFS_MASK)) { + ucontrol->value.integer.value[0] = 0; + return 0; + } + + rate = clk_get_rate(dev->gclk); + + ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val)); + + return 0; +} + +static struct snd_kcontrol_new mchp_spdifrx_ctrls[] = { + /* Channel status controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT) + " Channel 1", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_info, + .get = mchp_spdifrx_cs1_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT) + " Channel 2", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_info, + .get = mchp_spdifrx_cs2_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, MASK), + .access = SNDRV_CTL_ELEM_ACCESS_READ, + .info = mchp_spdifrx_info, + .get = mchp_spdifrx_cs_mask, + }, + /* User bits controller */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Subcode Capture Default Channel 1", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_info, + .get = mchp_spdifrx_subcode_ch1_get, + }, + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = "IEC958 Subcode Capture Default Channel 2", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_info, + .get = mchp_spdifrx_subcode_ch2_get, + }, + /* Lock status */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Unlocked", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_boolean_info, + .get = mchp_spdifrx_ulock_get, + }, + /* Bad format */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE)"Bad Format", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_boolean_info, + .get = mchp_spdifrx_badf_get, + }, + /* Signal */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Signal", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_boolean_info, + .get = mchp_spdifrx_signal_get, + }, + /* Sampling rate */ + { + .iface = SNDRV_CTL_ELEM_IFACE_PCM, + .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, NONE) "Rate", + .access = SNDRV_CTL_ELEM_ACCESS_READ | + SNDRV_CTL_ELEM_ACCESS_VOLATILE, + .info = mchp_spdifrx_rate_info, + .get = mchp_spdifrx_rate_get, + }, +}; + +static int mchp_spdifrx_dai_probe(struct snd_soc_dai *dai) +{ + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + struct mchp_spdifrx_mixer_control *ctrl = &dev->control; + int ch; + int err; + + err = clk_prepare_enable(dev->pclk); + if (err) { + dev_err(dev->dev, + "failed to enable the peripheral clock: %d\n", err); + return err; + } + + snd_soc_dai_init_dma_data(dai, NULL, &dev->capture); + + /* Software reset the IP */ + regmap_write(dev->regmap, SPDIFRX_CR, SPDIFRX_CR_SWRST); + + /* Default configuration */ + regmap_write(dev->regmap, SPDIFRX_MR, + SPDIFRX_MR_VBMODE_DISCARD_IF_VB1 | + SPDIFRX_MR_SBMODE_DISCARD | + SPDIFRX_MR_AUTORST_NOACTION | + SPDIFRX_MR_PACK_DISABLED); + + dev->blockend_refcount = 0; + for (ch = 0; ch < SPDIFRX_CHANNELS; ch++) { + init_completion(&ctrl->ch_stat[ch].done); + init_completion(&ctrl->user_data[ch].done); + spin_lock_init(&ctrl->user_data[ch].lock); + } + + /* Add controls */ + snd_soc_add_dai_controls(dai, mchp_spdifrx_ctrls, + ARRAY_SIZE(mchp_spdifrx_ctrls)); + + return 0; +} + +static int mchp_spdifrx_dai_remove(struct snd_soc_dai *dai) +{ + struct mchp_spdifrx_dev *dev = snd_soc_dai_get_drvdata(dai); + + /* Disable interrupts */ + regmap_write(dev->regmap, SPDIFRX_IDR, 0xFF); + + clk_disable_unprepare(dev->pclk); + + return 0; +} + +static struct snd_soc_dai_driver mchp_spdifrx_dai = { + .name = "mchp-spdifrx", + .probe = mchp_spdifrx_dai_probe, + .remove = mchp_spdifrx_dai_remove, + .capture = { + .stream_name = "S/PDIF Capture", + .channels_min = SPDIFRX_CHANNELS, + .channels_max = SPDIFRX_CHANNELS, + .rates = MCHP_SPDIF_RATES, + .formats = MCHP_SPDIF_FORMATS, + }, + .ops = &mchp_spdifrx_dai_ops, +}; + +static const struct snd_soc_component_driver mchp_spdifrx_component = { + .name = "mchp-spdifrx", +}; + +static const struct of_device_id mchp_spdifrx_dt_ids[] = { + { + .compatible = "microchip,sama7g5-spdifrx", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mchp_spdifrx_dt_ids); + +static int mchp_spdifrx_probe(struct platform_device *pdev) +{ + struct mchp_spdifrx_dev *dev; + struct resource *mem; + struct regmap *regmap; + void __iomem *base; + int irq; + int err; + u32 vers; + + /* Get memory for driver data. */ + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + /* Map I/O registers. */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, + &mchp_spdifrx_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* Request IRQ. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + err = devm_request_irq(&pdev->dev, irq, mchp_spdif_interrupt, 0, + dev_name(&pdev->dev), dev); + if (err) + return err; + + /* Get the peripheral clock */ + dev->pclk = devm_clk_get(&pdev->dev, "pclk"); + if (IS_ERR(dev->pclk)) { + err = PTR_ERR(dev->pclk); + dev_err(&pdev->dev, "failed to get the peripheral clock: %d\n", + err); + return err; + } + + /* Get the generated clock */ + dev->gclk = devm_clk_get(&pdev->dev, "gclk"); + if (IS_ERR(dev->gclk)) { + err = PTR_ERR(dev->gclk); + dev_err(&pdev->dev, + "failed to get the PMC generated clock: %d\n", err); + return err; + } + spin_lock_init(&dev->blockend_lock); + + dev->dev = &pdev->dev; + dev->regmap = regmap; + platform_set_drvdata(pdev, dev); + + dev->capture.addr = (dma_addr_t)mem->start + SPDIFRX_RHR; + dev->capture.maxburst = 1; + + err = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); + if (err) { + dev_err(&pdev->dev, "failed to register PMC: %d\n", err); + return err; + } + + err = devm_snd_soc_register_component(&pdev->dev, + &mchp_spdifrx_component, + &mchp_spdifrx_dai, 1); + if (err) { + dev_err(&pdev->dev, "fail to register dai\n"); + return err; + } + + regmap_read(regmap, SPDIFRX_VERSION, &vers); + dev_info(&pdev->dev, "hw version: %#lx\n", vers & SPDIFRX_VERSION_MASK); + + return 0; +} + +static struct platform_driver mchp_spdifrx_driver = { + .probe = mchp_spdifrx_probe, + .driver = { + .name = "mchp_spdifrx", + .of_match_table = of_match_ptr(mchp_spdifrx_dt_ids), + }, +}; + +module_platform_driver(mchp_spdifrx_driver); + +MODULE_AUTHOR("Codrin Ciubotariu "); +MODULE_DESCRIPTION("Microchip S/PDIF RX Controller Driver"); +MODULE_LICENSE("GPL v2"); -- cgit From cec6e41ce094affad3b5f9f5e5aa1f81c66ce682 Mon Sep 17 00:00:00 2001 From: Brent Lu Date: Wed, 15 Jul 2020 21:01:50 +0800 Subject: ASoC: hdac_hdmi: remove cancel_work_sync in runtime suspend A deadlock is identified when there are three contexts running at the same time: - a HDMI jack work which is calling snd_soc_dapm_sync(). - user space is calling snd_pcm_release() to close pcm device. - pm is calling runtime suspend function of HDMI codec driver. By removing the clear_dapm_works() invocation in the hdac_hdmi_runtime_suspend() function, the snd_pcm_release() could always returns from dapm_power_widgets() function call without blocking the hdac_hdmi_jack_dapm_work() work thread or being blocked by the hdac_hdmi_runtime_suspend() function. The purpose of the jack work is to enable/disable the dapm jack pin so it's not necessary to cancel the work in runtime suspend function which is usually called when pcm device is closed. Signed-off-by: Brent Lu Link: https://lore.kernel.org/r/1594818110-786-1-git-send-email-brent.lu@intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/hdac_hdmi.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index c61cce53f513..2c1305bf0572 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -2235,8 +2235,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev) if (!bus) return 0; - clear_dapm_works(hdev); - /* * Power down afg. * codec_read is preferred over codec_write to set the power state. -- cgit From 1c71497bb5b819bf3a32cfc35e293060b6590c39 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 2 Oct 2020 18:28:41 +0100 Subject: ASoC: fsl_spdif: Remove unused np Reported-by: Stephen Rothwell Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20201002172841.37344-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/fsl/fsl_spdif.c | 1 - 1 file changed, 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c index 9c5607780590..b0f643fefe1e 100644 --- a/sound/soc/fsl/fsl_spdif.c +++ b/sound/soc/fsl/fsl_spdif.c @@ -1252,7 +1252,6 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv, static int fsl_spdif_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct fsl_spdif_priv *spdif_priv; struct spdif_mixer_control *ctrl; struct resource *res; -- cgit From 98bd2b506a309faca2f5a8388dadfc983123e14a Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 2 Oct 2020 18:59:08 +0200 Subject: ASoC: wm8523: Fix a typo in a comment It is likely that this header file is about the WM8523. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/20201002165908.637809-1-christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- sound/soc/codecs/wm8523.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/codecs/wm8523.h b/sound/soc/codecs/wm8523.h index 79afbf1e4f1a..5f9bb3df1866 100644 --- a/sound/soc/codecs/wm8523.h +++ b/sound/soc/codecs/wm8523.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * wm8523.h -- WM8423 ASoC driver + * wm8523.h -- WM8523 ASoC driver * * Copyright 2009 Wolfson Microelectronics, plc * -- cgit From d8f006825ac57e34f7ed5e63a1e16d889dc1508e Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:24 -0500 Subject: ASoC: sun8i-codec: Set up clock tree at probe time The sun8i codec is effectively an on-die variant of the X-Powers AC100 codec. The AC100 can derive its clocks from either of two I2S master clocks or an internal PLL. For the on-die variant, Allwinner replaced the codec's own PLL with a connection to SoC's existing PLL_AUDIO, and they connected both I2S MCLK inputs to the same source -- which happens to be an integer divider from the same PLL_AUDIO. So there's actually no clocking flexibility. To run SYSCLK at the required rate, it must be run straight from the PLL. The only choice is whether it goes through AIF1CLK or AIF2CLK. Since both run at the same rate, the only effect of that choice is which field in SYS_SR_CTRL (AIF1_FS or AIF2_FS) controls the system sample rate. Since AIFnCLK is required to bring up the corresponding DAI, and AIF1 (connected to the CPU) is used most often, let's use AIF1CLK as the SYSCLK parent. That means we no longer need to set AIF2_FS. Since this clock tree never changes, we can program it from the component probe function, instead of using DAPM widgets. The DAPM widgets unnecessarily change clock parents when the codec goes in/out of idle and the supply widgets are powered up/down. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-2-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 54 ++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 19 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 178f6fb31fd4..407f0fedc4ed 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -24,10 +24,13 @@ #define SUN8I_SYSCLK_CTL 0x00c #define SUN8I_SYSCLK_CTL_AIF1CLK_ENA 11 -#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL 9 -#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC 8 +#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL (0x2 << 8) +#define SUN8I_SYSCLK_CTL_AIF2CLK_ENA 7 +#define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL (0x2 << 4) #define SUN8I_SYSCLK_CTL_SYSCLK_ENA 3 #define SUN8I_SYSCLK_CTL_SYSCLK_SRC 0 +#define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK (0x0 << 0) +#define SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF2CLK (0x1 << 0) #define SUN8I_MOD_CLK_ENA 0x010 #define SUN8I_MOD_CLK_ENA_AIF1 15 #define SUN8I_MOD_CLK_ENA_ADC 3 @@ -79,6 +82,8 @@ #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_AIF2DACR 9 #define SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR 8 +#define SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK GENMASK(9, 8) +#define SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK GENMASK(5, 4) #define SUN8I_SYS_SR_CTRL_AIF1_FS_MASK GENMASK(15, 12) #define SUN8I_SYS_SR_CTRL_AIF2_FS_MASK GENMASK(11, 8) #define SUN8I_AIF1CLK_CTRL_AIF1_BCLK_DIV_MASK GENMASK(12, 9) @@ -323,9 +328,6 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL, SUN8I_SYS_SR_CTRL_AIF1_FS_MASK, sample_rate << SUN8I_SYS_SR_CTRL_AIF1_FS); - regmap_update_bits(scodec->regmap, SUN8I_SYS_SR_CTRL, - SUN8I_SYS_SR_CTRL_AIF2_FS_MASK, - sample_rate << SUN8I_SYS_SR_CTRL_AIF2_FS); return 0; } @@ -366,8 +368,16 @@ static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = { }; static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { + /* System Clocks */ SND_SOC_DAPM_CLOCK_SUPPLY("mod"), + SND_SOC_DAPM_SUPPLY("AIF1CLK", + SUN8I_SYSCLK_CTL, + SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SYSCLK", + SUN8I_SYSCLK_CTL, + SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0), + /* Digital parts of the DACs and ADC */ SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), @@ -415,16 +425,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA, SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("AIF1", SUN8I_SYSCLK_CTL, - SUN8I_SYSCLK_CTL_AIF1CLK_ENA, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("SYSCLK", SUN8I_SYSCLK_CTL, - SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0), - - SND_SOC_DAPM_SUPPLY("AIF1 PLL", SUN8I_SYSCLK_CTL, - SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL, 0, NULL, 0), - /* Inversion as 0=AIF1, 1=AIF2 */ - SND_SOC_DAPM_SUPPLY("SYSCLK AIF1", SUN8I_SYSCLK_CTL, - SUN8I_SYSCLK_CTL_SYSCLK_SRC, 1, NULL, 0), /* Module reset */ SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL, @@ -437,12 +437,11 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { /* Clock Routes */ - { "AIF1", NULL, "mod" }, + { "AIF1CLK", NULL, "mod" }, - { "AIF1", NULL, "SYSCLK AIF1" }, - { "AIF1 PLL", NULL, "AIF1" }, - { "SYSCLK", NULL, "AIF1 PLL" }, + { "SYSCLK", NULL, "AIF1CLK" }, + { "RST AIF1", NULL, "AIF1CLK" }, { "RST AIF1", NULL, "SYSCLK" }, { "MODCLK AIF1", NULL, "RST AIF1" }, { "AIF1 AD0L", NULL, "MODCLK AIF1" }, @@ -524,6 +523,23 @@ static int sun8i_codec_component_probe(struct snd_soc_component *component) return ret; } + /* + * AIF1CLK and AIF2CLK share a pair of clock parents: PLL_AUDIO ("mod") + * and MCLK (from the CPU DAI connected to AIF1). MCLK's parent is also + * PLL_AUDIO, so using it adds no additional flexibility. Use PLL_AUDIO + * directly to simplify the clock tree. + */ + regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL, + SUN8I_SYSCLK_CTL_AIF1CLK_SRC_MASK | + SUN8I_SYSCLK_CTL_AIF2CLK_SRC_MASK, + SUN8I_SYSCLK_CTL_AIF1CLK_SRC_PLL | + SUN8I_SYSCLK_CTL_AIF2CLK_SRC_PLL); + + /* Use AIF1CLK as the SYSCLK parent since AIF1 is used most often. */ + regmap_update_bits(scodec->regmap, SUN8I_SYSCLK_CTL, + BIT(SUN8I_SYSCLK_CTL_SYSCLK_SRC), + SUN8I_SYSCLK_CTL_SYSCLK_SRC_AIF1CLK); + return 0; } -- cgit From ed3caa3bd44c9ae1cebeb32d787adc5ed35e29fa Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:25 -0500 Subject: ASoC: sun8i-codec: Swap module clock/reset dependencies This matches the module power-up/down sequence from the vendor's driver. While updating these widgets/routes, reorder them to match the register and bit layout of the hardware. This puts them in the same place in the widget and route arrays (previously they were at opposite ends), and it makes it easier to track which parts of which registers are implemented. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-3-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 72 +++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 33 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 407f0fedc4ed..6887a2e897f4 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -378,6 +378,28 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_SYSCLK_CTL, SUN8I_SYSCLK_CTL_SYSCLK_ENA, 0, NULL, 0), + /* Module Clocks */ + SND_SOC_DAPM_SUPPLY("CLK AIF1", + SUN8I_MOD_CLK_ENA, + SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK ADC", + SUN8I_MOD_CLK_ENA, + SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CLK DAC", + SUN8I_MOD_CLK_ENA, + SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), + + /* Module Resets */ + SND_SOC_DAPM_SUPPLY("RST AIF1", + SUN8I_MOD_RST_CTL, + SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RST ADC", + SUN8I_MOD_RST_CTL, + SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("RST DAC", + SUN8I_MOD_RST_CTL, + SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), + /* Digital parts of the DACs and ADC */ SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), @@ -417,22 +439,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { sun8i_input_mixer_controls), SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0, sun8i_input_mixer_controls), - - /* Clocks */ - SND_SOC_DAPM_SUPPLY("MODCLK AIF1", SUN8I_MOD_CLK_ENA, - SUN8I_MOD_CLK_ENA_AIF1, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("MODCLK DAC", SUN8I_MOD_CLK_ENA, - SUN8I_MOD_CLK_ENA_DAC, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("MODCLK ADC", SUN8I_MOD_CLK_ENA, - SUN8I_MOD_CLK_ENA_ADC, 0, NULL, 0), - - /* Module reset */ - SND_SOC_DAPM_SUPPLY("RST AIF1", SUN8I_MOD_RST_CTL, - SUN8I_MOD_RST_CTL_AIF1, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("RST DAC", SUN8I_MOD_RST_CTL, - SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("RST ADC", SUN8I_MOD_RST_CTL, - SUN8I_MOD_RST_CTL_ADC, 0, NULL, 0), }; static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { @@ -441,26 +447,26 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "SYSCLK", NULL, "AIF1CLK" }, - { "RST AIF1", NULL, "AIF1CLK" }, - { "RST AIF1", NULL, "SYSCLK" }, - { "MODCLK AIF1", NULL, "RST AIF1" }, - { "AIF1 AD0L", NULL, "MODCLK AIF1" }, - { "AIF1 AD0R", NULL, "MODCLK AIF1" }, - { "AIF1 DA0L", NULL, "MODCLK AIF1" }, - { "AIF1 DA0R", NULL, "MODCLK AIF1" }, - - { "RST DAC", NULL, "SYSCLK" }, - { "MODCLK DAC", NULL, "RST DAC" }, - { "DAC", NULL, "MODCLK DAC" }, - { "DACL", NULL, "DAC" }, - { "DACR", NULL, "DAC" }, - - { "RST ADC", NULL, "SYSCLK" }, - { "MODCLK ADC", NULL, "RST ADC" }, - { "ADC", NULL, "MODCLK ADC" }, + { "CLK AIF1", NULL, "AIF1CLK" }, + { "CLK AIF1", NULL, "SYSCLK" }, + { "RST AIF1", NULL, "CLK AIF1" }, + { "AIF1 AD0L", NULL, "RST AIF1" }, + { "AIF1 AD0R", NULL, "RST AIF1" }, + { "AIF1 DA0L", NULL, "RST AIF1" }, + { "AIF1 DA0R", NULL, "RST AIF1" }, + + { "CLK ADC", NULL, "SYSCLK" }, + { "RST ADC", NULL, "CLK ADC" }, + { "ADC", NULL, "RST ADC" }, { "ADCL", NULL, "ADC" }, { "ADCR", NULL, "ADC" }, + { "CLK DAC", NULL, "SYSCLK" }, + { "RST DAC", NULL, "CLK DAC" }, + { "DAC", NULL, "RST DAC" }, + { "DACL", NULL, "DAC" }, + { "DACR", NULL, "DAC" }, + /* DAC Routes */ { "DACL", NULL, "Left Digital DAC Mixer" }, { "DACR", NULL, "Right Digital DAC Mixer" }, -- cgit From d58b7247087900414aa3e988e70ecba85e06f412 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:26 -0500 Subject: ASoC: sun8i-codec: Sort DAPM controls, widgets, and routes Sort the remaining pieces of the DAPM driver so that they are all in the same order among controls/widgets/routes, and so they roughly match the register word and bit order of the hardware. This nicely separates the AIF-related widgets from the ADC/DAC widgets, which allows the AIF widgets to stay in a logical order as more AIFs are added to the driver. No widgets are renamed, to ease verification that this commit makes no functional change. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-4-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 101 ++++++++++++++++++++++-------------------- 1 file changed, 53 insertions(+), 48 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 6887a2e897f4..d14243c434f9 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -332,6 +332,25 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, return 0; } +static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = { + SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), + SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0), + SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), + SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch", + SUN8I_AIF1_MXR_SRC, + SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR, + SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), +}; + static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC, @@ -349,24 +368,6 @@ static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { SUN8I_DAC_MXR_SRC_DACR_MXR_SRC_ADCR, 1, 0), }; -static const struct snd_kcontrol_new sun8i_input_mixer_controls[] = { - SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", - SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L, - SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF1DA0R, 1, 0), - SOC_DAPM_DOUBLE("AIF2 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL, - SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACR, 1, 0), - SOC_DAPM_DOUBLE("AIF1 Data Digital ADC Capture Switch", - SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_ADCL, - SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_ADCR, 1, 0), - SOC_DAPM_DOUBLE("AIF2 Inv Digital ADC Capture Switch", - SUN8I_AIF1_MXR_SRC, - SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACR, - SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), -}; - static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { /* System Clocks */ SND_SOC_DAPM_CLOCK_SUPPLY("mod"), @@ -400,19 +401,13 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_MOD_RST_CTL, SUN8I_MOD_RST_CTL_DAC, 0, NULL, 0), - /* Digital parts of the DACs and ADC */ - SND_SOC_DAPM_SUPPLY("DAC", SUN8I_DAC_DIG_CTRL, SUN8I_DAC_DIG_CTRL_ENDA, - 0, NULL, 0), - SND_SOC_DAPM_SUPPLY("ADC", SUN8I_ADC_DIG_CTRL, SUN8I_ADC_DIG_CTRL_ENAD, - 0, NULL, 0), - - /* AIF "DAC" Inputs */ - SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, - SUN8I_AIF1_DACDAT_CTRL, - SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), - SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0, - SUN8I_AIF1_DACDAT_CTRL, - SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), + /* Module Supplies */ + SND_SOC_DAPM_SUPPLY("ADC", + SUN8I_ADC_DIG_CTRL, + SUN8I_ADC_DIG_CTRL_ENAD, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("DAC", + SUN8I_DAC_DIG_CTRL, + SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), /* AIF "ADC" Outputs */ SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0, @@ -422,6 +417,20 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), + /* AIF "ADC" Mixers */ + SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0, + sun8i_aif1_ad0_mixer_controls), + SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0, + sun8i_aif1_ad0_mixer_controls), + + /* AIF "DAC" Inputs */ + SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, + SUN8I_AIF1_DACDAT_CTRL, + SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), + SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0, + SUN8I_AIF1_DACDAT_CTRL, + SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), + /* ADC Inputs (connected to analog codec DAPM context) */ SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0), @@ -430,15 +439,11 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0), SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), - /* DAC and ADC Mixers */ + /* DAC Mixers */ SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), - SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0, - sun8i_input_mixer_controls), - SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0, - sun8i_input_mixer_controls), }; static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { @@ -467,7 +472,18 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "DACL", NULL, "DAC" }, { "DACR", NULL, "DAC" }, - /* DAC Routes */ + /* AIF "ADC" Output Routes */ + { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" }, + { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" }, + + /* AIF "ADC" Mixer Routes */ + { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, + { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, + + { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, + { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, + + /* DAC Output Routes */ { "DACL", NULL, "Left Digital DAC Mixer" }, { "DACR", NULL, "Right Digital DAC Mixer" }, @@ -477,17 +493,6 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, { "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, - - /* ADC Routes */ - { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" }, - { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" }, - - /* ADC Mixer Routes */ - { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, - { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, - - { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, - { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, }; static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = { -- cgit From 7b51f3c7029fab706e7d9ac99f67cbcf8f29beca Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:27 -0500 Subject: ASoC: sun8i-codec: Consistently name DAPM widgets and routes This cleans up the mixer widget names. The AIF1 AD0 Mixer names were previously wrong -- they do not control the digital side of the ADC. The DAC mixer widgets were not wrong, but they were verbose and did not match the naming scheme of the other widgets. The mixer controls are not renamed because they are exposed to userspace. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-5-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index d14243c434f9..30fe27648254 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -418,9 +418,9 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), /* AIF "ADC" Mixers */ - SOC_MIXER_ARRAY("Left Digital ADC Mixer", SND_SOC_NOPM, 0, 0, + SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0, sun8i_aif1_ad0_mixer_controls), - SOC_MIXER_ARRAY("Right Digital ADC Mixer", SND_SOC_NOPM, 0, 0, + SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0, sun8i_aif1_ad0_mixer_controls), /* AIF "DAC" Inputs */ @@ -440,9 +440,9 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0), /* DAC Mixers */ - SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0, + SOC_MIXER_ARRAY("DACL Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), - SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0, + SOC_MIXER_ARRAY("DACR Mixer", SND_SOC_NOPM, 0, 0, sun8i_dac_mixer_controls), }; @@ -473,26 +473,26 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "DACR", NULL, "DAC" }, /* AIF "ADC" Output Routes */ - { "AIF1 AD0L", NULL, "Left Digital ADC Mixer" }, - { "AIF1 AD0R", NULL, "Right Digital ADC Mixer" }, + { "AIF1 AD0L", NULL, "AIF1 AD0L Mixer" }, + { "AIF1 AD0R", NULL, "AIF1 AD0R Mixer" }, /* AIF "ADC" Mixer Routes */ - { "Left Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, - { "Left Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, + { "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, + { "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, - { "Right Digital ADC Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, - { "Right Digital ADC Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, + { "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, + { "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, /* DAC Output Routes */ - { "DACL", NULL, "Left Digital DAC Mixer" }, - { "DACR", NULL, "Right Digital DAC Mixer" }, + { "DACL", NULL, "DACL Mixer" }, + { "DACR", NULL, "DACR Mixer" }, /* DAC Mixer Routes */ - { "Left Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, - { "Left Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCL" }, + { "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, + { "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" }, - { "Right Digital DAC Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, - { "Right Digital DAC Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, + { "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, + { "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, }; static const struct snd_soc_dapm_widget sun8i_codec_legacy_widgets[] = { -- cgit From fc5668f62d089ba69b343f0e80146f5a3bc6fa71 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:28 -0500 Subject: ASoC: sun8i-codec: Correct DAPM widget types Whie the aif_in and aif_out widget types are handled exactly the same in the core DAPM code, a future widget event hook will need the correct widget type to derive the associated substream. Clean up the widget type for that reason, and so these widgets will match newly-added widgets for the other AIFs. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-6-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 30fe27648254..d0028883950c 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -410,12 +410,12 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_DAC_DIG_CTRL_ENDA, 0, NULL, 0), /* AIF "ADC" Outputs */ - SND_SOC_DAPM_AIF_IN("AIF1 AD0L", "Capture", 0, - SUN8I_AIF1_ADCDAT_CTRL, - SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0), - SND_SOC_DAPM_AIF_IN("AIF1 AD0R", "Capture", 0, - SUN8I_AIF1_ADCDAT_CTRL, - SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), + SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0, + SUN8I_AIF1_ADCDAT_CTRL, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0), + SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 0, + SUN8I_AIF1_ADCDAT_CTRL, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), /* AIF "ADC" Mixers */ SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0, -- cgit From 4ab60cef3149d57fe56add8c60ee7e6d45816f27 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:29 -0500 Subject: ASoC: sun8i-codec: Fix AIF widget channel references Both the left and right side widgets referenced channel 0. This would unnecessarily power on the right side widget (and its associated path) when a mono stream was active. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-7-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index d0028883950c..2c89974243e1 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -413,7 +413,7 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SND_SOC_DAPM_AIF_OUT("AIF1 AD0L", "Capture", 0, SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA, 0), - SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 0, + SND_SOC_DAPM_AIF_OUT("AIF1 AD0R", "Capture", 1, SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), @@ -427,7 +427,7 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA, 0), - SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 0, + SND_SOC_DAPM_AIF_IN("AIF1 DA0R", "Playback", 1, SUN8I_AIF1_DACDAT_CTRL, SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0), -- cgit From 18ebd62c30f0380da11d6c86e20b56c771ac1e18 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:30 -0500 Subject: ASoC: sun8i-codec: Enable AIF mono/stereo control Each left/right pair of AIF input/output channels can be swapped or combined. This is useful for sending a mono audio source to both sides of a stereo sink, or for creating complex mixing scenarios. Add the support to control this feature from userspace. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-8-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 82 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 2c89974243e1..578c0c0e6330 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -54,9 +54,13 @@ #define SUN8I_AIF1_ADCDAT_CTRL 0x044 #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_ENA 15 #define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA 14 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC 10 +#define SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC 8 #define SUN8I_AIF1_DACDAT_CTRL 0x048 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_ENA 15 #define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA 14 +#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC 10 +#define SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC 8 #define SUN8I_AIF1_MXR_SRC 0x04c #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF1DA0L 15 #define SUN8I_AIF1_MXR_SRC_AD0L_MXR_SRC_AIF2DACL 14 @@ -332,6 +336,20 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, return 0; } +static const char *const sun8i_aif_stereo_mux_enum_values[] = { + "Stereo", "Reverse Stereo", "Sum Mono", "Mix Mono" +}; + +static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_ad0_stereo_mux_enum, + SUN8I_AIF1_ADCDAT_CTRL, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0L_SRC, + SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_SRC, + sun8i_aif_stereo_mux_enum_values); + +static const struct snd_kcontrol_new sun8i_aif1_ad0_stereo_mux_control = + SOC_DAPM_ENUM("AIF1 AD0 Stereo Capture Route", + sun8i_aif1_ad0_stereo_mux_enum); + static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = { SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital ADC Capture Switch", SUN8I_AIF1_MXR_SRC, @@ -351,6 +369,16 @@ static const struct snd_kcontrol_new sun8i_aif1_ad0_mixer_controls[] = { SUN8I_AIF1_MXR_SRC_AD0R_MXR_SRC_AIF2DACL, 1, 0), }; +static SOC_ENUM_DOUBLE_DECL(sun8i_aif1_da0_stereo_mux_enum, + SUN8I_AIF1_DACDAT_CTRL, + SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0L_SRC, + SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_SRC, + sun8i_aif_stereo_mux_enum_values); + +static const struct snd_kcontrol_new sun8i_aif1_da0_stereo_mux_control = + SOC_DAPM_ENUM("AIF1 DA0 Stereo Playback Route", + sun8i_aif1_da0_stereo_mux_enum); + static const struct snd_kcontrol_new sun8i_dac_mixer_controls[] = { SOC_DAPM_DOUBLE("AIF1 Slot 0 Digital DAC Playback Switch", SUN8I_DAC_MXR_SRC, @@ -417,12 +445,24 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = { SUN8I_AIF1_ADCDAT_CTRL, SUN8I_AIF1_ADCDAT_CTRL_AIF1_AD0R_ENA, 0), + /* AIF "ADC" Mono/Stereo Muxes */ + SND_SOC_DAPM_MUX("AIF1 AD0L Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_ad0_stereo_mux_control), + SND_SOC_DAPM_MUX("AIF1 AD0R Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_ad0_stereo_mux_control), + /* AIF "ADC" Mixers */ SOC_MIXER_ARRAY("AIF1 AD0L Mixer", SND_SOC_NOPM, 0, 0, sun8i_aif1_ad0_mixer_controls), SOC_MIXER_ARRAY("AIF1 AD0R Mixer", SND_SOC_NOPM, 0, 0, sun8i_aif1_ad0_mixer_controls), + /* AIF "DAC" Mono/Stereo Muxes */ + SND_SOC_DAPM_MUX("AIF1 DA0L Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_da0_stereo_mux_control), + SND_SOC_DAPM_MUX("AIF1 DA0R Stereo Mux", SND_SOC_NOPM, 0, 0, + &sun8i_aif1_da0_stereo_mux_control), + /* AIF "DAC" Inputs */ SND_SOC_DAPM_AIF_IN("AIF1 DA0L", "Playback", 0, SUN8I_AIF1_DACDAT_CTRL, @@ -473,25 +513,55 @@ static const struct snd_soc_dapm_route sun8i_codec_dapm_routes[] = { { "DACR", NULL, "DAC" }, /* AIF "ADC" Output Routes */ - { "AIF1 AD0L", NULL, "AIF1 AD0L Mixer" }, - { "AIF1 AD0R", NULL, "AIF1 AD0R Mixer" }, + { "AIF1 AD0L", NULL, "AIF1 AD0L Stereo Mux" }, + { "AIF1 AD0R", NULL, "AIF1 AD0R Stereo Mux" }, + + /* AIF "ADC" Mono/Stereo Mux Routes */ + { "AIF1 AD0L Stereo Mux", "Stereo", "AIF1 AD0L Mixer" }, + { "AIF1 AD0L Stereo Mux", "Reverse Stereo", "AIF1 AD0R Mixer" }, + { "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0L Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" }, + { "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0L Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" }, + + { "AIF1 AD0R Stereo Mux", "Stereo", "AIF1 AD0R Mixer" }, + { "AIF1 AD0R Stereo Mux", "Reverse Stereo", "AIF1 AD0L Mixer" }, + { "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0R Stereo Mux", "Sum Mono", "AIF1 AD0R Mixer" }, + { "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0L Mixer" }, + { "AIF1 AD0R Stereo Mux", "Mix Mono", "AIF1 AD0R Mixer" }, /* AIF "ADC" Mixer Routes */ - { "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L" }, + { "AIF1 AD0L Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0L Stereo Mux" }, { "AIF1 AD0L Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCL" }, - { "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R" }, + { "AIF1 AD0R Mixer", "AIF1 Slot 0 Digital ADC Capture Switch", "AIF1 DA0R Stereo Mux" }, { "AIF1 AD0R Mixer", "AIF1 Data Digital ADC Capture Switch", "ADCR" }, + /* AIF "DAC" Mono/Stereo Mux Routes */ + { "AIF1 DA0L Stereo Mux", "Stereo", "AIF1 DA0L" }, + { "AIF1 DA0L Stereo Mux", "Reverse Stereo", "AIF1 DA0R" }, + { "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0L" }, + { "AIF1 DA0L Stereo Mux", "Sum Mono", "AIF1 DA0R" }, + { "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0L" }, + { "AIF1 DA0L Stereo Mux", "Mix Mono", "AIF1 DA0R" }, + + { "AIF1 DA0R Stereo Mux", "Stereo", "AIF1 DA0R" }, + { "AIF1 DA0R Stereo Mux", "Reverse Stereo", "AIF1 DA0L" }, + { "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0L" }, + { "AIF1 DA0R Stereo Mux", "Sum Mono", "AIF1 DA0R" }, + { "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0L" }, + { "AIF1 DA0R Stereo Mux", "Mix Mono", "AIF1 DA0R" }, + /* DAC Output Routes */ { "DACL", NULL, "DACL Mixer" }, { "DACR", NULL, "DACR Mixer" }, /* DAC Mixer Routes */ - { "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L" }, + { "DACL Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0L Stereo Mux" }, { "DACL Mixer", "ADC Digital DAC Playback Switch", "ADCL" }, - { "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R" }, + { "DACR Mixer", "AIF1 Slot 0 Digital DAC Playback Switch", "AIF1 DA0R Stereo Mux" }, { "DACR Mixer", "ADC Digital DAC Playback Switch", "ADCR" }, }; -- cgit From a886990c9525e83146829c7711ce444ff652c98a Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Wed, 30 Sep 2020 21:11:31 -0500 Subject: ASoC: sun8i-codec: Use snd_soc_dai_get_drvdata Remove a level of indirection by getting the device directly from the passed-in struct snd_soc_dai, instead of going through its component. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20201001021148.15852-9-samuel@sholland.org Signed-off-by: Mark Brown --- sound/soc/sunxi/sun8i-codec.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c index 578c0c0e6330..7590c4b04d14 100644 --- a/sound/soc/sunxi/sun8i-codec.c +++ b/sound/soc/sunxi/sun8i-codec.c @@ -167,7 +167,7 @@ static int sun8i_codec_get_hw_rate(struct snd_pcm_hw_params *params) static int sun8i_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component); + struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); u32 value; /* clock masters */ @@ -299,7 +299,7 @@ static int sun8i_codec_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct sun8i_codec *scodec = snd_soc_component_get_drvdata(dai->component); + struct sun8i_codec *scodec = snd_soc_dai_get_drvdata(dai); int sample_rate, lrck_div; u8 bclk_div; -- cgit From 044eb2d13a2147a7ac15a400c2a4f020a7857c36 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 2 Oct 2020 16:18:59 -0500 Subject: ASoC: Intel: sof_sdw_rt1308: add extra check on init Apply same test as for other amplifiers - in case we enable feedback one day. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20201002211902.287692-2-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt1308.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt1308.c index dba2fd28d77f..0d476f6f6313 100644 --- a/sound/soc/intel/boards/sof_sdw_rt1308.c +++ b/sound/soc/intel/boards/sof_sdw_rt1308.c @@ -132,6 +132,10 @@ int sof_sdw_rt1308_init(const struct snd_soc_acpi_link_adr *link, struct sof_sdw_codec_info *info, bool playback) { + /* Count amp number and do init on playback link only. */ + if (!playback) + return 0; + info->amp_num++; if (info->amp_num == 1) dai_links->init = first_spk_init; -- cgit From 8cc8945da74284809c8a4a575e55e91d5557b651 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 2 Oct 2020 16:19:00 -0500 Subject: ASoC: Intel: sof_sdw_rt1316: add missing component string Without this string UCM cannot fetch the relevant configurations. Fixes: b75bea4b8834c ('ASoC: intel: sof_sdw: add rt711 rt1316 rt714 SDCA codec support') Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Link: https://lore.kernel.org/r/20201002211902.287692-3-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw_rt1316.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw_rt1316.c b/sound/soc/intel/boards/sof_sdw_rt1316.c index 2c566330f236..d6e1ebf18d57 100644 --- a/sound/soc/intel/boards/sof_sdw_rt1316.c +++ b/sound/soc/intel/boards/sof_sdw_rt1316.c @@ -39,6 +39,12 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd) struct snd_soc_card *card = rtd->card; int ret; + card->components = devm_kasprintf(card->dev, GFP_KERNEL, + "%s spk:rt1316", + card->components); + if (!card->components) + return -ENOMEM; + ret = snd_soc_add_card_controls(card, rt1316_controls, ARRAY_SIZE(rt1316_controls)); if (ret) { -- cgit From 4c652df83ba42fadc884541bfec856c836822302 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 2 Oct 2020 16:19:01 -0500 Subject: ASoC: rt715-sdw: probe with RT714 Device ID RT715 and RT714 are essentially the same chips but with different SoundWire Dev_ID registers, make sure we can probe the same driver if RT714 is used. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Jack Yu Reviewed-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20201002211902.287692-4-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/codecs/rt715-sdw.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index dbb8895150a6..a5fd31c98221 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -541,6 +541,7 @@ static int rt715_sdw_probe(struct sdw_slave *slave, } static const struct sdw_device_id rt715_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x2, 0, 0), SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0), {}, }; -- cgit From df64b9882b35671c4916574a783b614c6164980b Mon Sep 17 00:00:00 2001 From: Pierre-Louis Bossart Date: Fri, 2 Oct 2020 16:19:02 -0500 Subject: ASoC: Intel: sof_sdw: add version_id to avoid rt714/rt715 confusion RT715 and RT714 are essentially the same chip. In addition, there are two versions, one supporting SoundWire 1.1 and one supporting SoundWire 1.2 (SDCA). The previous configurations assumed that RT714 was SDCA-only, which isn't correct. Add support for the 4 possible combinations to avoid confusions. Signed-off-by: Pierre-Louis Bossart Reviewed-by: Bard Liao Reviewed-by: Jack Yu Reviewed-by: Guennadi Liakhovetski Link: https://lore.kernel.org/r/20201002211902.287692-5-pierre-louis.bossart@linux.intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/sof_sdw.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b56df04775c2..b29946eb4355 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -295,12 +295,28 @@ static struct sof_sdw_codec_info codec_info_list[] = { }, { .part_id = 0x714, + .version_id = 3, + .direction = {false, true}, + .dai_name = "rt715-aif2", + .init = sof_sdw_rt715_sdca_init, + }, + { + .part_id = 0x715, + .version_id = 3, .direction = {false, true}, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_sdca_init, }, + { + .part_id = 0x714, + .version_id = 2, + .direction = {false, true}, + .dai_name = "rt715-aif2", + .init = sof_sdw_rt715_init, + }, { .part_id = 0x715, + .version_id = 2, .direction = {false, true}, .dai_name = "rt715-aif2", .init = sof_sdw_rt715_init, -- cgit From 8031b93efa8d393b7e38fa66b836ac157a2f354d Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Sun, 4 Oct 2020 12:45:05 +0300 Subject: ASoC: mchp-spdifrx: convert to devm_platform_get_and_ioremap_resource Use the helper function that wraps the calls to platform_get_resource() and devm_ioremap_resource() together. Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20201004094505.1041898-1-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdifrx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index 6776d89d56df..726e4951d9a5 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -873,8 +873,7 @@ static int mchp_spdifrx_probe(struct platform_device *pdev) return -ENOMEM; /* Map I/O registers. */ - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, mem); + base = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); if (IS_ERR(base)) return PTR_ERR(base); -- cgit From 859ffd0af117e7ebc4d744a648551443cc90f150 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Mon, 5 Oct 2020 15:47:48 +0800 Subject: ASoC: mediatek: mt8183-da7219: support jack detection for LINEOUT Supports jack detection for LINEOUT. Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20201005074748.3394630-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 15e48cde4921..4d69ea31bfe4 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -549,7 +549,8 @@ mt8183_da7219_max98357_headset_init(struct snd_soc_component *component) "Headset Jack", SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | - SND_JACK_BTN_2 | SND_JACK_BTN_3, + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_LINEOUT, &priv->headset_jack, NULL, 0); if (ret) -- cgit From 4cc62da459aeee438a1fcf009e6101292025476f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Sun, 4 Oct 2020 11:06:06 +0200 Subject: ASoC: Intel: bdw-rt5650: Mark FE DAIs as nonatomic PCM operations for DAI links connected with DSP platform component involve communication with DSP firmware by IPCs. As IPC protocol may cause thread to sleep while waiting for a response from DSP, propagate that information to ALSA core by marking all FE DAIs as nonatomic. Signed-off-by: Cezary Rojewski Acked-by: Pierre-Louis Bossart Link: https://lore.kernel.org/r/20201004090609.29066-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5650.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/bdw-rt5650.c b/sound/soc/intel/boards/bdw-rt5650.c index c44315af6a4c..aa420b201848 100644 --- a/sound/soc/intel/boards/bdw-rt5650.c +++ b/sound/soc/intel/boards/bdw-rt5650.c @@ -232,6 +232,7 @@ static struct snd_soc_dai_link bdw_rt5650_dais[] = { { .name = "System PCM", .stream_name = "System Playback", + .nonatomic = 1, .dynamic = 1, .ops = &bdw_rt5650_fe_ops, .trigger = { -- cgit From fc5c8729c1ef78d54432d68216c1b13791248bb1 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Sun, 4 Oct 2020 11:06:07 +0200 Subject: ASoC: Intel: bdw-rt5677: Mark FE DAIs as nonatomic PCM operations for DAI links connected with DSP platform component involve communication with DSP firmware by IPCs. As IPC protocol may cause thread to sleep while waiting for a response from DSP, propagate that information to ALSA core by marking all FE DAIs as nonatomic. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201004090609.29066-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/bdw-rt5677.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c index d6584768883b..7a3e773d0a1c 100644 --- a/sound/soc/intel/boards/bdw-rt5677.c +++ b/sound/soc/intel/boards/bdw-rt5677.c @@ -323,6 +323,7 @@ static struct snd_soc_dai_link bdw_rt5677_dais[] = { { .name = "System PCM", .stream_name = "System Playback/Capture", + .nonatomic = 1, .dynamic = 1, .trigger = { SND_SOC_DPCM_TRIGGER_POST, -- cgit From 727d7d84f74744a6f8d583eb5034e926aecc78e7 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Sun, 4 Oct 2020 11:06:08 +0200 Subject: ASoC: Intel: broadwell: Mark FE DAIs as nonatomic PCM operations for DAI links connected with DSP platform component involve communication with DSP firmware by IPCs. As IPC protocol may cause thread to sleep while waiting for a response from DSP, propagate that information to ALSA core by marking all FE DAIs as nonatomic. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201004090609.29066-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/broadwell.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c index 648b47190051..77c85f17aca6 100644 --- a/sound/soc/intel/boards/broadwell.c +++ b/sound/soc/intel/boards/broadwell.c @@ -174,6 +174,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { { .name = "System PCM", .stream_name = "System Playback/Capture", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .ops = &broadwell_fe_ops, @@ -184,6 +185,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { { .name = "Offload0", .stream_name = "Offload0 Playback", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, @@ -192,6 +194,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { { .name = "Offload1", .stream_name = "Offload1 Playback", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, @@ -200,6 +203,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { { .name = "Loopback PCM", .stream_name = "Loopback", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, -- cgit From dc155ad5fa6ef7d48fb3c3cc30497b492da0749e Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Sun, 4 Oct 2020 11:06:09 +0200 Subject: ASoC: Intel: haswell: Mark FE DAIs as nonatomic PCM operations for DAI links connected with DSP platform component involve communication with DSP firmware by IPCs. As IPC protocol may cause thread to sleep while waiting for a response from DSP, propagate that information to ALSA core by marking all FE DAIs as nonatomic. Signed-off-by: Cezary Rojewski Link: https://lore.kernel.org/r/20201004090609.29066-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/haswell.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'sound') diff --git a/sound/soc/intel/boards/haswell.c b/sound/soc/intel/boards/haswell.c index c268405e5594..c55d1239e705 100644 --- a/sound/soc/intel/boards/haswell.c +++ b/sound/soc/intel/boards/haswell.c @@ -103,6 +103,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { { .name = "System", .stream_name = "System Playback/Capture", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, @@ -112,6 +113,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { { .name = "Offload0", .stream_name = "Offload0 Playback", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, @@ -120,6 +122,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { { .name = "Offload1", .stream_name = "Offload1 Playback", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, @@ -128,6 +131,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = { { .name = "Loopback", .stream_name = "Loopback", + .nonatomic = 1, .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_capture = 1, -- cgit From ebb11d1d9fe2d6b4a47755f7f09b2b631046e308 Mon Sep 17 00:00:00 2001 From: Tzung-Bi Shih Date: Tue, 6 Oct 2020 18:12:52 +0800 Subject: ASoC: mediatek: mt8183-da7219: fix wrong ops for I2S3 DA7219 uses I2S2 and I2S3 for input and output respectively. Commit 9e30251fb22e ("ASoC: mediatek: mt8183-da7219: support machine driver with rt1015") introduces a bug that: - If using I2S2 solely, MCLK to DA7219 is 256FS. - If using I2S3 solely, MCLK to DA7219 is 128FS. - If using I2S3 first and then I2S2, the MCLK changes from 128FS to 256FS. As a result, no sound output to the headset. Also no sound input from the headset microphone. Both I2S2 and I2S3 should set MCLK to 256FS. Fixes the wrong ops for I2S3. Fixes: 9e30251fb22e ("ASoC: mediatek: mt8183-da7219: support machine driver with rt1015") Signed-off-by: Tzung-Bi Shih Link: https://lore.kernel.org/r/20201006101252.1890385-1-tzungbi@google.com Signed-off-by: Mark Brown --- sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c index 06d0a4f80fc1..a6c690c5308d 100644 --- a/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-da7219-max98357.c @@ -673,7 +673,7 @@ static int mt8183_da7219_max98357_dev_probe(struct platform_device *pdev) if (card == &mt8183_da7219_max98357_card) { dai_link->be_hw_params_fixup = mt8183_i2s_hw_params_fixup; - dai_link->ops = &mt8183_mt6358_i2s_ops; + dai_link->ops = &mt8183_da7219_i2s_ops; dai_link->cpus = i2s3_max98357a_cpus; dai_link->num_cpus = ARRAY_SIZE(i2s3_max98357a_cpus); -- cgit From ca756120d4bcf28dfde5e3df8882153303d4010f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:48:55 +0200 Subject: ASoC: Intel: Remove haswell solution Newly added catpt solution found in sound/soc/intel/catpt is a direct replacement to sound/soc/intel/haswell. It covers all features supported by it and more - by aligning to recommended flows and requirement list based on Windows driver equivalent. No harm is done to userspace as catpt - similarly to haswell - loads no extenal topology files while sharing the exact same ADSP firmware binary. Given the above, existing haswell code is redundant so remove it. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/haswell/Makefile | 5 - sound/soc/intel/haswell/sst-haswell-dsp.c | 705 --------- sound/soc/intel/haswell/sst-haswell-ipc.c | 2222 ----------------------------- sound/soc/intel/haswell/sst-haswell-ipc.h | 527 ------- sound/soc/intel/haswell/sst-haswell-pcm.c | 1369 ------------------ 5 files changed, 4828 deletions(-) delete mode 100644 sound/soc/intel/haswell/Makefile delete mode 100644 sound/soc/intel/haswell/sst-haswell-dsp.c delete mode 100644 sound/soc/intel/haswell/sst-haswell-ipc.c delete mode 100644 sound/soc/intel/haswell/sst-haswell-ipc.h delete mode 100644 sound/soc/intel/haswell/sst-haswell-pcm.c (limited to 'sound') diff --git a/sound/soc/intel/haswell/Makefile b/sound/soc/intel/haswell/Makefile deleted file mode 100644 index ad2341aea8ae..000000000000 --- a/sound/soc/intel/haswell/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -snd-soc-sst-haswell-pcm-objs := \ - sst-haswell-ipc.o sst-haswell-pcm.o sst-haswell-dsp.o - -obj-$(CONFIG_SND_SOC_INTEL_HASWELL) += snd-soc-sst-haswell-pcm.o diff --git a/sound/soc/intel/haswell/sst-haswell-dsp.c b/sound/soc/intel/haswell/sst-haswell-dsp.c deleted file mode 100644 index 88c3f63bded9..000000000000 --- a/sound/soc/intel/haswell/sst-haswell-dsp.c +++ /dev/null @@ -1,705 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Haswell SST DSP driver - * - * Copyright (C) 2013, Intel Corporation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../common/sst-dsp.h" -#include "../common/sst-dsp-priv.h" -#include "../haswell/sst-haswell-ipc.h" - -#include - -#define SST_HSW_FW_SIGNATURE_SIZE 4 -#define SST_HSW_FW_SIGN "$SST" -#define SST_HSW_FW_LIB_SIGN "$LIB" - -#define SST_WPT_SHIM_OFFSET 0xFB000 -#define SST_LP_SHIM_OFFSET 0xE7000 -#define SST_WPT_IRAM_OFFSET 0xA0000 -#define SST_LP_IRAM_OFFSET 0x80000 -#define SST_WPT_DSP_DRAM_OFFSET 0x400000 -#define SST_WPT_DSP_IRAM_OFFSET 0x00000 -#define SST_LPT_DSP_DRAM_OFFSET 0x400000 -#define SST_LPT_DSP_IRAM_OFFSET 0x00000 - -#define SST_SHIM_PM_REG 0x84 - -#define SST_HSW_IRAM 1 -#define SST_HSW_DRAM 2 -#define SST_HSW_REGS 3 - -struct dma_block_info { - __le32 type; /* IRAM/DRAM */ - __le32 size; /* Bytes */ - __le32 ram_offset; /* Offset in I/DRAM */ - __le32 rsvd; /* Reserved field */ -} __attribute__((packed)); - -struct fw_module_info { - __le32 persistent_size; - __le32 scratch_size; -} __attribute__((packed)); - -struct fw_header { - unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* FW signature */ - __le32 file_size; /* size of fw minus this header */ - __le32 modules; /* # of modules */ - __le32 file_format; /* version of header format */ - __le32 reserved[4]; -} __attribute__((packed)); - -struct fw_module_header { - unsigned char signature[SST_HSW_FW_SIGNATURE_SIZE]; /* module signature */ - __le32 mod_size; /* size of module */ - __le32 blocks; /* # of blocks */ - __le16 padding; - __le16 type; /* codec type, pp lib */ - __le32 entry_point; - struct fw_module_info info; -} __attribute__((packed)); - -static void hsw_free(struct sst_dsp *sst); - -static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, - struct fw_module_header *module) -{ - struct dma_block_info *block; - struct sst_module *mod; - struct sst_module_template template; - int count, ret; - void __iomem *ram; - int type = le16_to_cpu(module->type); - int entry_point = le32_to_cpu(module->entry_point); - - /* TODO: allowed module types need to be configurable */ - if (type != SST_HSW_MODULE_BASE_FW && - type != SST_HSW_MODULE_PCM_SYSTEM && - type != SST_HSW_MODULE_PCM && - type != SST_HSW_MODULE_PCM_REFERENCE && - type != SST_HSW_MODULE_PCM_CAPTURE && - type != SST_HSW_MODULE_WAVES && - type != SST_HSW_MODULE_LPAL) - return 0; - - dev_dbg(dsp->dev, "new module sign 0x%s size 0x%x blocks 0x%x type 0x%x\n", - module->signature, module->mod_size, - module->blocks, type); - dev_dbg(dsp->dev, " entrypoint 0x%x\n", entry_point); - dev_dbg(dsp->dev, " persistent 0x%x scratch 0x%x\n", - module->info.persistent_size, module->info.scratch_size); - - memset(&template, 0, sizeof(template)); - template.id = type; - template.entry = entry_point - 4; - template.persistent_size = le32_to_cpu(module->info.persistent_size); - template.scratch_size = le32_to_cpu(module->info.scratch_size); - - mod = sst_module_new(fw, &template, NULL); - if (mod == NULL) - return -ENOMEM; - - block = (void *)module + sizeof(*module); - - for (count = 0; count < le32_to_cpu(module->blocks); count++) { - - if (le32_to_cpu(block->size) <= 0) { - dev_err(dsp->dev, - "error: block %d size invalid\n", count); - sst_module_free(mod); - return -EINVAL; - } - - switch (le32_to_cpu(block->type)) { - case SST_HSW_IRAM: - ram = dsp->addr.lpe; - mod->offset = le32_to_cpu(block->ram_offset) + - dsp->addr.iram_offset; - mod->type = SST_MEM_IRAM; - break; - case SST_HSW_DRAM: - case SST_HSW_REGS: - ram = dsp->addr.lpe; - mod->offset = le32_to_cpu(block->ram_offset); - mod->type = SST_MEM_DRAM; - break; - default: - dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n", - block->type, count); - sst_module_free(mod); - return -EINVAL; - } - - mod->size = le32_to_cpu(block->size); - mod->data = (void *)block + sizeof(*block); - mod->data_offset = mod->data - fw->dma_buf; - - dev_dbg(dsp->dev, "module block %d type 0x%x " - "size 0x%x ==> ram %p offset 0x%x\n", - count, mod->type, block->size, ram, - block->ram_offset); - - ret = sst_module_alloc_blocks(mod); - if (ret < 0) { - dev_err(dsp->dev, "error: could not allocate blocks for module %d\n", - count); - sst_module_free(mod); - return ret; - } - - block = (void *)block + sizeof(*block) + - le32_to_cpu(block->size); - } - mod->state = SST_MODULE_STATE_LOADED; - - return 0; -} - -static int hsw_parse_fw_image(struct sst_fw *sst_fw) -{ - struct fw_header *header; - struct fw_module_header *module; - struct sst_dsp *dsp = sst_fw->dsp; - int ret, count; - - /* Read the header information from the data pointer */ - header = (struct fw_header *)sst_fw->dma_buf; - - /* verify FW */ - if ((strncmp(header->signature, SST_HSW_FW_SIGN, 4) != 0) || - (sst_fw->size != - le32_to_cpu(header->file_size) + sizeof(*header))) { - dev_err(dsp->dev, "error: invalid fw sign/filesize mismatch\n"); - return -EINVAL; - } - - dev_dbg(dsp->dev, "header size=0x%x modules=0x%x fmt=0x%x size=%zu\n", - header->file_size, header->modules, - header->file_format, sizeof(*header)); - - /* parse each module */ - module = (void *)sst_fw->dma_buf + sizeof(*header); - for (count = 0; count < le32_to_cpu(header->modules); count++) { - - /* module */ - ret = hsw_parse_module(dsp, sst_fw, module); - if (ret < 0) { - dev_err(dsp->dev, "error: invalid module %d\n", count); - return ret; - } - module = (void *)module + sizeof(*module) + - le32_to_cpu(module->mod_size); - } - - return 0; -} - -static irqreturn_t hsw_irq(int irq, void *context) -{ - struct sst_dsp *sst = (struct sst_dsp *) context; - u32 isr; - int ret = IRQ_NONE; - - spin_lock(&sst->spinlock); - - /* Interrupt arrived, check src */ - isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); - if (isr & SST_ISRX_DONE) { - trace_sst_irq_done(isr, - sst_dsp_shim_read_unlocked(sst, SST_IMRX)); - - /* Mask Done interrupt before return */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, - SST_IMRX_DONE, SST_IMRX_DONE); - ret = IRQ_WAKE_THREAD; - } - - if (isr & SST_ISRX_BUSY) { - trace_sst_irq_busy(isr, - sst_dsp_shim_read_unlocked(sst, SST_IMRX)); - - /* Mask Busy interrupt before return */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, - SST_IMRX_BUSY, SST_IMRX_BUSY); - ret = IRQ_WAKE_THREAD; - } - - spin_unlock(&sst->spinlock); - return ret; -} - -static void hsw_set_dsp_D3(struct sst_dsp *sst) -{ - u32 val; - u32 reg; - - /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE); - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); - - /* enable power gating and switch off DRAM & IRAM blocks */ - val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - val |= SST_VDRTCL0_DSRAMPGE_MASK | - SST_VDRTCL0_ISRAMPGE_MASK; - val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD); - writel(val, sst->addr.pci_cfg + SST_VDRTCTL0); - - /* switch off audio PLL */ - val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - val |= SST_VDRTCL2_APLLSE_MASK; - writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); - - /* disable MCLK(clkctl.smos = 0) */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL, - SST_CLKCTL_MASK, 0); - - /* Set D3 state, delay 50 us */ - val = readl(sst->addr.pci_cfg + SST_PMCS); - val |= SST_PMCS_PS_MASK; - writel(val, sst->addr.pci_cfg + SST_PMCS); - udelay(50); - - /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE; - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); - - udelay(50); - -} - -static void hsw_reset(struct sst_dsp *sst) -{ - /* put DSP into reset and stall */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, - SST_CSR_RST | SST_CSR_STALL, - SST_CSR_RST | SST_CSR_STALL); - - /* keep in reset for 10ms */ - mdelay(10); - - /* take DSP out of reset and keep stalled for FW loading */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, - SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL); -} - -static int hsw_set_dsp_D0(struct sst_dsp *sst) -{ - int tries = 10; - u32 reg, fw_dump_bit; - - /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE); - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); - - /* Disable D3PG (VDRTCTL0.D3PGD = 1) */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - reg |= SST_VDRTCL0_D3PGD; - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0); - - /* Set D0 state */ - reg = readl(sst->addr.pci_cfg + SST_PMCS); - reg &= ~SST_PMCS_PS_MASK; - writel(reg, sst->addr.pci_cfg + SST_PMCS); - - /* check that ADSP shim is enabled */ - while (tries--) { - reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK; - if (reg == 0) - goto finish; - - msleep(1); - } - - return -ENODEV; - -finish: - /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, - SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0); - - /* stall DSP core, set clk to 192/96Mhz */ - sst_dsp_shim_update_bits_unlocked(sst, - SST_CSR, SST_CSR_STALL | SST_CSR_DCS_MASK, - SST_CSR_STALL | SST_CSR_DCS(4)); - - /* Set 24MHz MCLK, prevent local clock gating, enable SSP0 clock */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL, - SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0, - SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0); - - /* Stall and reset core, set CSR */ - hsw_reset(sst); - - /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE; - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); - - udelay(50); - - /* switch on audio PLL */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - reg &= ~SST_VDRTCL2_APLLSE_MASK; - writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2); - - /* set default power gating control, enable power gating control for all blocks. that is, - can't be accessed, please enable each block before accessing. */ - reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK; - /* for D0, always enable the block(DSRAM[0]) used for FW dump */ - fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT; - writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0); - - - /* disable DMA finish function for SSP0 & SSP1 */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1, - SST_CSR2_SDFD_SSP1); - - /* set on-demond mode on engine 0,1 for all channels */ - sst_dsp_shim_update_bits(sst, SST_HMDC, - SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, - SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH); - - /* Enable Interrupt from both sides */ - sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE), - 0x0); - sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY | - SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0); - - /* clear IPC registers */ - sst_dsp_shim_write(sst, SST_IPCX, 0x0); - sst_dsp_shim_write(sst, SST_IPCD, 0x0); - sst_dsp_shim_write(sst, 0x80, 0x6); - sst_dsp_shim_write(sst, 0xe0, 0x300a); - - return 0; -} - -static void hsw_boot(struct sst_dsp *sst) -{ - /* set oportunistic mode on engine 0,1 for all channels */ - sst_dsp_shim_update_bits(sst, SST_HMDC, - SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0); - - /* set DSP to RUN */ - sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0); -} - -static void hsw_stall(struct sst_dsp *sst) -{ - /* stall DSP */ - sst_dsp_shim_update_bits(sst, SST_CSR, - SST_CSR_24MHZ_LPCS | SST_CSR_STALL, - SST_CSR_STALL | SST_CSR_24MHZ_LPCS); -} - -static void hsw_sleep(struct sst_dsp *sst) -{ - dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n"); - - /* put DSP into reset and stall */ - sst_dsp_shim_update_bits(sst, SST_CSR, - SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL, - SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS); - - hsw_set_dsp_D3(sst); - dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n"); -} - -static int hsw_wake(struct sst_dsp *sst) -{ - int ret; - - dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n"); - - ret = hsw_set_dsp_D0(sst); - if (ret < 0) - return ret; - - dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n"); - - return 0; -} - -struct sst_adsp_memregion { - u32 start; - u32 end; - int blocks; - enum sst_mem_type type; -}; - -/* lynx point ADSP mem regions */ -static const struct sst_adsp_memregion lp_region[] = { - {0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */ - {0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */ - {0x80000, 0xE0000, 12, SST_MEM_IRAM}, /* I-SRAM - 12 * 32kB */ -}; - -/* wild cat point ADSP mem regions */ -static const struct sst_adsp_memregion wpt_region[] = { - {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */ - {0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */ -}; - -static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata) -{ - /* ADSP DRAM & IRAM */ - sst->addr.lpe_base = pdata->lpe_base; - sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size); - if (!sst->addr.lpe) - return -ENODEV; - - /* ADSP PCI MMIO config space */ - sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size); - if (!sst->addr.pci_cfg) { - iounmap(sst->addr.lpe); - return -ENODEV; - } - - /* SST Shim */ - sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset; - return 0; -} - -struct sst_sram_shift { - u32 dev_id; /* SST Device IDs */ - u32 iram_shift; - u32 dram_shift; -}; - -static const struct sst_sram_shift sram_shift[] = { - {SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */ - {SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */ -}; - -static u32 hsw_block_get_bit(struct sst_mem_block *block) -{ - u32 bit = 0, shift = 0, index; - struct sst_dsp *sst = block->dsp; - - for (index = 0; index < ARRAY_SIZE(sram_shift); index++) { - if (sram_shift[index].dev_id == sst->id) - break; - } - - if (index < ARRAY_SIZE(sram_shift)) { - switch (block->type) { - case SST_MEM_DRAM: - shift = sram_shift[index].dram_shift; - break; - case SST_MEM_IRAM: - shift = sram_shift[index].iram_shift; - break; - default: - shift = 0; - } - } else - shift = 0; - - bit = 1 << (block->index + shift); - - return bit; -} - -/*dummy read a SRAM block.*/ -static void sst_mem_block_dummy_read(struct sst_mem_block *block) -{ - u32 size; - u8 tmp_buf[4]; - struct sst_dsp *sst = block->dsp; - - size = block->size > 4 ? 4 : block->size; - memcpy_fromio(tmp_buf, sst->addr.lpe + block->offset, size); -} - -/* enable 32kB memory block - locks held by caller */ -static int hsw_block_enable(struct sst_mem_block *block) -{ - struct sst_dsp *sst = block->dsp; - u32 bit, val; - - if (block->users++ > 0) - return 0; - - dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n", - block->type, block->index, block->offset); - - /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ - val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - val &= ~SST_VDRTCL2_DCLCGE; - writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); - - val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - bit = hsw_block_get_bit(block); - writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0); - - /* wait 18 DSP clock ticks */ - udelay(10); - - /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ - val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - val |= SST_VDRTCL2_DCLCGE; - writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); - - udelay(50); - - /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/ - sst_mem_block_dummy_read(block); - return 0; -} - -/* disable 32kB memory block - locks held by caller */ -static int hsw_block_disable(struct sst_mem_block *block) -{ - struct sst_dsp *sst = block->dsp; - u32 bit, val; - - if (--block->users > 0) - return 0; - - dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n", - block->type, block->index, block->offset); - - /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */ - val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - val &= ~SST_VDRTCL2_DCLCGE; - writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); - - - val = readl(sst->addr.pci_cfg + SST_VDRTCTL0); - bit = hsw_block_get_bit(block); - /* don't disable DSRAM[0], keep it always enable for FW dump*/ - if (bit != (1 << SST_VDRTCL0_DSRAMPGE_SHIFT)) - writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0); - - /* wait 18 DSP clock ticks */ - udelay(10); - - /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */ - val = readl(sst->addr.pci_cfg + SST_VDRTCTL2); - val |= SST_VDRTCL2_DCLCGE; - writel(val, sst->addr.pci_cfg + SST_VDRTCTL2); - - udelay(50); - - return 0; -} - -static const struct sst_block_ops sst_hsw_ops = { - .enable = hsw_block_enable, - .disable = hsw_block_disable, -}; - -static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) -{ - const struct sst_adsp_memregion *region; - struct device *dev; - int ret = -ENODEV, i, j, region_count; - u32 offset, size, fw_dump_bit; - - dev = sst->dma_dev; - - switch (sst->id) { - case SST_DEV_ID_LYNX_POINT: - region = lp_region; - region_count = ARRAY_SIZE(lp_region); - sst->addr.iram_offset = SST_LP_IRAM_OFFSET; - sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET; - sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET; - sst->addr.shim_offset = SST_LP_SHIM_OFFSET; - break; - case SST_DEV_ID_WILDCAT_POINT: - region = wpt_region; - region_count = ARRAY_SIZE(wpt_region); - sst->addr.iram_offset = SST_WPT_IRAM_OFFSET; - sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET; - sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET; - sst->addr.shim_offset = SST_WPT_SHIM_OFFSET; - break; - default: - dev_err(dev, "error: failed to get mem resources\n"); - return ret; - } - - ret = hsw_acpi_resource_map(sst, pdata); - if (ret < 0) { - dev_err(dev, "error: failed to map resources\n"); - return ret; - } - - /* enable the DSP SHIM */ - ret = hsw_set_dsp_D0(sst); - if (ret < 0) { - dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n"); - return ret; - } - - ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31)); - if (ret) - return ret; - - - /* register DSP memory blocks - ideally we should get this from ACPI */ - for (i = 0; i < region_count; i++) { - offset = region[i].start; - size = (region[i].end - region[i].start) / region[i].blocks; - - /* register individual memory blocks */ - for (j = 0; j < region[i].blocks; j++) { - sst_mem_block_register(sst, offset, size, - region[i].type, &sst_hsw_ops, j, sst); - offset += size; - } - } - - /* always enable the block(DSRAM[0]) used for FW dump */ - fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT; - /* set default power gating control, enable power gating control for all blocks. that is, - can't be accessed, please enable each block before accessing. */ - writel(0xffffffff & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0); - - return 0; -} - -static void hsw_free(struct sst_dsp *sst) -{ - sst_mem_block_unregister_all(sst); - iounmap(sst->addr.lpe); - iounmap(sst->addr.pci_cfg); -} - -struct sst_ops haswell_ops = { - .reset = hsw_reset, - .boot = hsw_boot, - .stall = hsw_stall, - .wake = hsw_wake, - .sleep = hsw_sleep, - .write = sst_shim32_write, - .read = sst_shim32_read, - .write64 = sst_shim32_write64, - .read64 = sst_shim32_read64, - .ram_read = sst_memcpy_fromio_32, - .ram_write = sst_memcpy_toio_32, - .irq_handler = hsw_irq, - .init = hsw_init, - .free = hsw_free, - .parse_fw = hsw_parse_fw_image, -}; diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c deleted file mode 100644 index 95a50265dff6..000000000000 --- a/sound/soc/intel/haswell/sst-haswell-ipc.c +++ /dev/null @@ -1,2222 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel SST Haswell/Broadwell IPC Support - * - * Copyright (C) 2013, Intel Corporation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sst-haswell-ipc.h" -#include "../common/sst-dsp.h" -#include "../common/sst-dsp-priv.h" -#include "../common/sst-ipc.h" - -/* Global Message - Generic */ -#define IPC_GLB_TYPE_SHIFT 24 -#define IPC_GLB_TYPE_MASK (0x1f << IPC_GLB_TYPE_SHIFT) -#define IPC_GLB_TYPE(x) (x << IPC_GLB_TYPE_SHIFT) - -/* Global Message - Reply */ -#define IPC_GLB_REPLY_SHIFT 0 -#define IPC_GLB_REPLY_MASK (0x1f << IPC_GLB_REPLY_SHIFT) -#define IPC_GLB_REPLY_TYPE(x) (x << IPC_GLB_REPLY_TYPE_SHIFT) - -/* Stream Message - Generic */ -#define IPC_STR_TYPE_SHIFT 20 -#define IPC_STR_TYPE_MASK (0xf << IPC_STR_TYPE_SHIFT) -#define IPC_STR_TYPE(x) (x << IPC_STR_TYPE_SHIFT) -#define IPC_STR_ID_SHIFT 16 -#define IPC_STR_ID_MASK (0xf << IPC_STR_ID_SHIFT) -#define IPC_STR_ID(x) (x << IPC_STR_ID_SHIFT) - -/* Stream Message - Reply */ -#define IPC_STR_REPLY_SHIFT 0 -#define IPC_STR_REPLY_MASK (0x1f << IPC_STR_REPLY_SHIFT) - -/* Stream Stage Message - Generic */ -#define IPC_STG_TYPE_SHIFT 12 -#define IPC_STG_TYPE_MASK (0xf << IPC_STG_TYPE_SHIFT) -#define IPC_STG_TYPE(x) (x << IPC_STG_TYPE_SHIFT) -#define IPC_STG_ID_SHIFT 10 -#define IPC_STG_ID_MASK (0x3 << IPC_STG_ID_SHIFT) -#define IPC_STG_ID(x) (x << IPC_STG_ID_SHIFT) - -/* Stream Stage Message - Reply */ -#define IPC_STG_REPLY_SHIFT 0 -#define IPC_STG_REPLY_MASK (0x1f << IPC_STG_REPLY_SHIFT) - -/* Debug Log Message - Generic */ -#define IPC_LOG_OP_SHIFT 20 -#define IPC_LOG_OP_MASK (0xf << IPC_LOG_OP_SHIFT) -#define IPC_LOG_OP_TYPE(x) (x << IPC_LOG_OP_SHIFT) -#define IPC_LOG_ID_SHIFT 16 -#define IPC_LOG_ID_MASK (0xf << IPC_LOG_ID_SHIFT) -#define IPC_LOG_ID(x) (x << IPC_LOG_ID_SHIFT) - -/* Module Message */ -#define IPC_MODULE_OPERATION_SHIFT 20 -#define IPC_MODULE_OPERATION_MASK (0xf << IPC_MODULE_OPERATION_SHIFT) -#define IPC_MODULE_OPERATION(x) (x << IPC_MODULE_OPERATION_SHIFT) - -#define IPC_MODULE_ID_SHIFT 16 -#define IPC_MODULE_ID_MASK (0xf << IPC_MODULE_ID_SHIFT) -#define IPC_MODULE_ID(x) (x << IPC_MODULE_ID_SHIFT) - -/* IPC message timeout (msecs) */ -#define IPC_TIMEOUT_MSECS 300 -#define IPC_BOOT_MSECS 200 -#define IPC_MSG_WAIT 0 -#define IPC_MSG_NOWAIT 1 - -/* Firmware Ready Message */ -#define IPC_FW_READY (0x1 << 29) -#define IPC_STATUS_MASK (0x3 << 30) - -#define IPC_EMPTY_LIST_SIZE 8 -#define IPC_MAX_STREAMS 4 - -/* Mailbox */ -#define IPC_MAX_MAILBOX_BYTES 256 - -#define INVALID_STREAM_HW_ID 0xffffffff - -/* Global Message - Types and Replies */ -enum ipc_glb_type { - IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */ - IPC_GLB_PERFORMANCE_MONITOR = 1, /* Performance monitoring actions */ - IPC_GLB_ALLOCATE_STREAM = 3, /* Request to allocate new stream */ - IPC_GLB_FREE_STREAM = 4, /* Request to free stream */ - IPC_GLB_GET_FW_CAPABILITIES = 5, /* Retrieves firmware capabilities */ - IPC_GLB_STREAM_MESSAGE = 6, /* Message directed to stream or its stages */ - /* Request to store firmware context during D0->D3 transition */ - IPC_GLB_REQUEST_DUMP = 7, - /* Request to restore firmware context during D3->D0 transition */ - IPC_GLB_RESTORE_CONTEXT = 8, - IPC_GLB_GET_DEVICE_FORMATS = 9, /* Set device format */ - IPC_GLB_SET_DEVICE_FORMATS = 10, /* Get device format */ - IPC_GLB_SHORT_REPLY = 11, - IPC_GLB_ENTER_DX_STATE = 12, - IPC_GLB_GET_MIXER_STREAM_INFO = 13, /* Request mixer stream params */ - IPC_GLB_DEBUG_LOG_MESSAGE = 14, /* Message to or from the debug logger. */ - IPC_GLB_MODULE_OPERATION = 15, /* Message to loadable fw module */ - IPC_GLB_REQUEST_TRANSFER = 16, /* < Request Transfer for host */ - IPC_GLB_MAX_IPC_MESSAGE_TYPE = 17, /* Maximum message number */ -}; - -enum ipc_glb_reply { - IPC_GLB_REPLY_SUCCESS = 0, /* The operation was successful. */ - IPC_GLB_REPLY_ERROR_INVALID_PARAM = 1, /* Invalid parameter was passed. */ - IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE = 2, /* Uknown message type was resceived. */ - IPC_GLB_REPLY_OUT_OF_RESOURCES = 3, /* No resources to satisfy the request. */ - IPC_GLB_REPLY_BUSY = 4, /* The system or resource is busy. */ - IPC_GLB_REPLY_PENDING = 5, /* The action was scheduled for processing. */ - IPC_GLB_REPLY_FAILURE = 6, /* Critical error happened. */ - IPC_GLB_REPLY_INVALID_REQUEST = 7, /* Request can not be completed. */ - IPC_GLB_REPLY_STAGE_UNINITIALIZED = 8, /* Processing stage was uninitialized. */ - IPC_GLB_REPLY_NOT_FOUND = 9, /* Required resource can not be found. */ - IPC_GLB_REPLY_SOURCE_NOT_STARTED = 10, /* Source was not started. */ -}; - -enum ipc_module_operation { - IPC_MODULE_NOTIFICATION = 0, - IPC_MODULE_ENABLE = 1, - IPC_MODULE_DISABLE = 2, - IPC_MODULE_GET_PARAMETER = 3, - IPC_MODULE_SET_PARAMETER = 4, - IPC_MODULE_GET_INFO = 5, - IPC_MODULE_MAX_MESSAGE -}; - -/* Stream Message - Types */ -enum ipc_str_operation { - IPC_STR_RESET = 0, - IPC_STR_PAUSE = 1, - IPC_STR_RESUME = 2, - IPC_STR_STAGE_MESSAGE = 3, - IPC_STR_NOTIFICATION = 4, - IPC_STR_MAX_MESSAGE -}; - -/* Stream Stage Message Types */ -enum ipc_stg_operation { - IPC_STG_GET_VOLUME = 0, - IPC_STG_SET_VOLUME, - IPC_STG_SET_WRITE_POSITION, - IPC_STG_SET_FX_ENABLE, - IPC_STG_SET_FX_DISABLE, - IPC_STG_SET_FX_GET_PARAM, - IPC_STG_SET_FX_SET_PARAM, - IPC_STG_SET_FX_GET_INFO, - IPC_STG_MUTE_LOOPBACK, - IPC_STG_MAX_MESSAGE -}; - -/* Stream Stage Message Types For Notification*/ -enum ipc_stg_operation_notify { - IPC_POSITION_CHANGED = 0, - IPC_STG_GLITCH, - IPC_STG_MAX_NOTIFY -}; - -enum ipc_glitch_type { - IPC_GLITCH_UNDERRUN = 1, - IPC_GLITCH_DECODER_ERROR, - IPC_GLITCH_DOUBLED_WRITE_POS, - IPC_GLITCH_MAX -}; - -/* Debug Control */ -enum ipc_debug_operation { - IPC_DEBUG_ENABLE_LOG = 0, - IPC_DEBUG_DISABLE_LOG = 1, - IPC_DEBUG_REQUEST_LOG_DUMP = 2, - IPC_DEBUG_NOTIFY_LOG_DUMP = 3, - IPC_DEBUG_MAX_DEBUG_LOG -}; - -/* Firmware Ready */ -struct sst_hsw_ipc_fw_ready { - u32 inbox_offset; - u32 outbox_offset; - u32 inbox_size; - u32 outbox_size; - u32 fw_info_size; - u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)]; -} __attribute__((packed)); - -struct sst_hsw_stream; -struct sst_hsw; - -/* Stream infomation */ -struct sst_hsw_stream { - /* configuration */ - struct sst_hsw_ipc_stream_alloc_req request; - struct sst_hsw_ipc_stream_alloc_reply reply; - struct sst_hsw_ipc_stream_free_req free_req; - - /* Mixer info */ - u32 mute_volume[SST_HSW_NO_CHANNELS]; - u32 mute[SST_HSW_NO_CHANNELS]; - - /* runtime info */ - struct sst_hsw *hsw; - int host_id; - bool commited; - bool running; - - /* Notification work */ - struct work_struct notify_work; - u32 header; - - /* Position info from DSP */ - struct sst_hsw_ipc_stream_set_position wpos; - struct sst_hsw_ipc_stream_get_position rpos; - struct sst_hsw_ipc_stream_glitch_position glitch; - - /* Volume info */ - struct sst_hsw_ipc_volume_req vol_req; - - /* driver callback */ - u32 (*notify_position)(struct sst_hsw_stream *stream, void *data); - void *pdata; - - /* record the fw read position when playback */ - snd_pcm_uframes_t old_position; - bool play_silence; - struct list_head node; -}; - -/* FW log ring information */ -struct sst_hsw_log_stream { - dma_addr_t dma_addr; - unsigned char *dma_area; - unsigned char *ring_descr; - int pages; - int size; - - /* Notification work */ - struct work_struct notify_work; - wait_queue_head_t readers_wait_q; - struct mutex rw_mutex; - - u32 last_pos; - u32 curr_pos; - u32 reader_pos; - - /* fw log config */ - u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS]; - - struct sst_hsw *hsw; -}; - -/* SST Haswell IPC data */ -struct sst_hsw { - struct device *dev; - struct sst_dsp *dsp; - struct platform_device *pdev_pcm; - - /* FW config */ - struct sst_hsw_ipc_fw_ready fw_ready; - struct sst_hsw_ipc_fw_version version; - bool fw_done; - struct sst_fw *sst_fw; - - /* stream */ - struct list_head stream_list; - - /* global mixer */ - struct sst_hsw_ipc_stream_info_reply mixer_info; - enum sst_hsw_volume_curve curve_type; - u32 curve_duration; - u32 mute[SST_HSW_NO_CHANNELS]; - u32 mute_volume[SST_HSW_NO_CHANNELS]; - - /* DX */ - struct sst_hsw_ipc_dx_reply dx; - void *dx_context; - dma_addr_t dx_context_paddr; - enum sst_hsw_device_id dx_dev; - enum sst_hsw_device_mclk dx_mclk; - enum sst_hsw_device_mode dx_mode; - u32 dx_clock_divider; - - /* boot */ - wait_queue_head_t boot_wait; - bool boot_complete; - bool shutdown; - - /* IPC messaging */ - struct sst_generic_ipc ipc; - - /* FW log stream */ - struct sst_hsw_log_stream log_stream; - - /* flags bit field to track module state when resume from RTD3, - * each bit represent state (enabled/disabled) of single module */ - u32 enabled_modules_rtd3; - - /* buffer to store parameter lines */ - u32 param_idx_w; /* write index */ - u32 param_idx_r; /* read index */ - u8 param_buf[WAVES_PARAM_LINES][WAVES_PARAM_COUNT]; -}; - -#define CREATE_TRACE_POINTS -#include - -static inline u32 msg_get_global_type(u32 msg) -{ - return (msg & IPC_GLB_TYPE_MASK) >> IPC_GLB_TYPE_SHIFT; -} - -static inline u32 msg_get_global_reply(u32 msg) -{ - return (msg & IPC_GLB_REPLY_MASK) >> IPC_GLB_REPLY_SHIFT; -} - -static inline u32 msg_get_stream_type(u32 msg) -{ - return (msg & IPC_STR_TYPE_MASK) >> IPC_STR_TYPE_SHIFT; -} - -static inline u32 msg_get_stream_id(u32 msg) -{ - return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT; -} - -static inline u32 msg_get_notify_reason(u32 msg) -{ - return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT; -} - -static inline u32 msg_get_module_operation(u32 msg) -{ - return (msg & IPC_MODULE_OPERATION_MASK) >> IPC_MODULE_OPERATION_SHIFT; -} - -static inline u32 msg_get_module_id(u32 msg) -{ - return (msg & IPC_MODULE_ID_MASK) >> IPC_MODULE_ID_SHIFT; -} - -u32 create_channel_map(enum sst_hsw_channel_config config) -{ - switch (config) { - case SST_HSW_CHANNEL_CONFIG_MONO: - return (0xFFFFFFF0 | SST_HSW_CHANNEL_CENTER); - case SST_HSW_CHANNEL_CONFIG_STEREO: - return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_RIGHT << 4)); - case SST_HSW_CHANNEL_CONFIG_2_POINT_1: - return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_RIGHT << 4) - | (SST_HSW_CHANNEL_LFE << 8 )); - case SST_HSW_CHANNEL_CONFIG_3_POINT_0: - return (0xFFFFF000 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_CENTER << 4) - | (SST_HSW_CHANNEL_RIGHT << 8)); - case SST_HSW_CHANNEL_CONFIG_3_POINT_1: - return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_CENTER << 4) - | (SST_HSW_CHANNEL_RIGHT << 8) - | (SST_HSW_CHANNEL_LFE << 12)); - case SST_HSW_CHANNEL_CONFIG_QUATRO: - return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_RIGHT << 4) - | (SST_HSW_CHANNEL_LEFT_SURROUND << 8) - | (SST_HSW_CHANNEL_RIGHT_SURROUND << 12)); - case SST_HSW_CHANNEL_CONFIG_4_POINT_0: - return (0xFFFF0000 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_CENTER << 4) - | (SST_HSW_CHANNEL_RIGHT << 8) - | (SST_HSW_CHANNEL_CENTER_SURROUND << 12)); - case SST_HSW_CHANNEL_CONFIG_5_POINT_0: - return (0xFFF00000 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_CENTER << 4) - | (SST_HSW_CHANNEL_RIGHT << 8) - | (SST_HSW_CHANNEL_LEFT_SURROUND << 12) - | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16)); - case SST_HSW_CHANNEL_CONFIG_5_POINT_1: - return (0xFF000000 | SST_HSW_CHANNEL_CENTER - | (SST_HSW_CHANNEL_LEFT << 4) - | (SST_HSW_CHANNEL_RIGHT << 8) - | (SST_HSW_CHANNEL_LEFT_SURROUND << 12) - | (SST_HSW_CHANNEL_RIGHT_SURROUND << 16) - | (SST_HSW_CHANNEL_LFE << 20)); - case SST_HSW_CHANNEL_CONFIG_DUAL_MONO: - return (0xFFFFFF00 | SST_HSW_CHANNEL_LEFT - | (SST_HSW_CHANNEL_LEFT << 4)); - default: - return 0xFFFFFFFF; - } -} - -static struct sst_hsw_stream *get_stream_by_id(struct sst_hsw *hsw, - int stream_id) -{ - struct sst_hsw_stream *stream; - - list_for_each_entry(stream, &hsw->stream_list, node) { - if (stream->reply.stream_hw_id == stream_id) - return stream; - } - - return NULL; -} - -static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) -{ - struct sst_hsw_ipc_fw_ready fw_ready; - u32 offset; - u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)]; - char *tmp[5], *pinfo; - int i; - - offset = (header & 0x1FFFFFFF) << 3; - - dev_dbg(hsw->dev, "ipc: DSP is ready 0x%8.8x offset %d\n", - header, offset); - - /* copy data from the DSP FW ready offset */ - sst_dsp_read(hsw->dsp, &fw_ready, offset, sizeof(fw_ready)); - - sst_dsp_mailbox_init(hsw->dsp, fw_ready.inbox_offset, - fw_ready.inbox_size, fw_ready.outbox_offset, - fw_ready.outbox_size); - - hsw->boot_complete = true; - wake_up(&hsw->boot_wait); - - dev_dbg(hsw->dev, " mailbox upstream 0x%x - size 0x%x\n", - fw_ready.inbox_offset, fw_ready.inbox_size); - dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n", - fw_ready.outbox_offset, fw_ready.outbox_size); - if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) { - fw_ready.fw_info[fw_ready.fw_info_size] = 0; - dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info); - - /* log the FW version info got from the mailbox here. */ - memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size); - pinfo = &fw_info[0]; - for (i = 0; i < ARRAY_SIZE(tmp); i++) - tmp[i] = strsep(&pinfo, " "); - dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - " - "version: %s.%s, build %s, source commit id: %s\n", - tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]); - } -} - -static void hsw_notification_work(struct work_struct *work) -{ - struct sst_hsw_stream *stream = container_of(work, - struct sst_hsw_stream, notify_work); - struct sst_hsw_ipc_stream_glitch_position *glitch = &stream->glitch; - struct sst_hsw_ipc_stream_get_position *pos = &stream->rpos; - struct sst_hsw *hsw = stream->hsw; - u32 reason; - - reason = msg_get_notify_reason(stream->header); - - switch (reason) { - case IPC_STG_GLITCH: - trace_ipc_notification("DSP stream under/overrun", - stream->reply.stream_hw_id); - sst_dsp_inbox_read(hsw->dsp, glitch, sizeof(*glitch)); - - dev_err(hsw->dev, "glitch %d pos 0x%x write pos 0x%x\n", - glitch->glitch_type, glitch->present_pos, - glitch->write_pos); - break; - - case IPC_POSITION_CHANGED: - trace_ipc_notification("DSP stream position changed for", - stream->reply.stream_hw_id); - sst_dsp_inbox_read(hsw->dsp, pos, sizeof(*pos)); - - if (stream->notify_position) - stream->notify_position(stream, stream->pdata); - - break; - default: - dev_err(hsw->dev, "error: unknown notification 0x%x\n", - stream->header); - break; - } - - /* tell DSP that notification has been handled */ - sst_dsp_shim_update_bits(hsw->dsp, SST_IPCD, - SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); - - /* unmask busy interrupt */ - sst_dsp_shim_update_bits(hsw->dsp, SST_IMRX, SST_IMRX_BUSY, 0); -} - -static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg) -{ - struct sst_hsw_stream *stream; - u32 header = msg->tx.header & ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); - u32 stream_id = msg_get_stream_id(header); - u32 stream_msg = msg_get_stream_type(header); - - stream = get_stream_by_id(hsw, stream_id); - if (stream == NULL) - return; - - switch (stream_msg) { - case IPC_STR_STAGE_MESSAGE: - case IPC_STR_NOTIFICATION: - break; - case IPC_STR_RESET: - trace_ipc_notification("stream reset", stream->reply.stream_hw_id); - break; - case IPC_STR_PAUSE: - stream->running = false; - trace_ipc_notification("stream paused", - stream->reply.stream_hw_id); - break; - case IPC_STR_RESUME: - stream->running = true; - trace_ipc_notification("stream running", - stream->reply.stream_hw_id); - break; - } -} - -static int hsw_process_reply(struct sst_hsw *hsw, u32 header) -{ - struct ipc_message *msg; - u32 reply = msg_get_global_reply(header); - - trace_ipc_reply("processing -->", header); - - msg = sst_ipc_reply_find_msg(&hsw->ipc, header); - if (msg == NULL) { - trace_ipc_error("error: can't find message header", header); - return -EIO; - } - - msg->rx.header = header; - /* first process the header */ - switch (reply) { - case IPC_GLB_REPLY_PENDING: - trace_ipc_pending_reply("received", header); - msg->pending = true; - hsw->ipc.pending = true; - return 1; - case IPC_GLB_REPLY_SUCCESS: - if (msg->pending) { - trace_ipc_pending_reply("completed", header); - sst_dsp_inbox_read(hsw->dsp, msg->rx.data, - msg->rx.size); - hsw->ipc.pending = false; - } else { - /* copy data from the DSP */ - sst_dsp_outbox_read(hsw->dsp, msg->rx.data, - msg->rx.size); - } - break; - /* these will be rare - but useful for debug */ - case IPC_GLB_REPLY_UNKNOWN_MESSAGE_TYPE: - trace_ipc_error("error: unknown message type", header); - msg->errno = -EBADMSG; - break; - case IPC_GLB_REPLY_OUT_OF_RESOURCES: - trace_ipc_error("error: out of resources", header); - msg->errno = -ENOMEM; - break; - case IPC_GLB_REPLY_BUSY: - trace_ipc_error("error: reply busy", header); - msg->errno = -EBUSY; - break; - case IPC_GLB_REPLY_FAILURE: - trace_ipc_error("error: reply failure", header); - msg->errno = -EINVAL; - break; - case IPC_GLB_REPLY_STAGE_UNINITIALIZED: - trace_ipc_error("error: stage uninitialized", header); - msg->errno = -EINVAL; - break; - case IPC_GLB_REPLY_NOT_FOUND: - trace_ipc_error("error: reply not found", header); - msg->errno = -EINVAL; - break; - case IPC_GLB_REPLY_SOURCE_NOT_STARTED: - trace_ipc_error("error: source not started", header); - msg->errno = -EINVAL; - break; - case IPC_GLB_REPLY_INVALID_REQUEST: - trace_ipc_error("error: invalid request", header); - msg->errno = -EINVAL; - break; - case IPC_GLB_REPLY_ERROR_INVALID_PARAM: - trace_ipc_error("error: invalid parameter", header); - msg->errno = -EINVAL; - break; - default: - trace_ipc_error("error: unknown reply", header); - msg->errno = -EINVAL; - break; - } - - /* update any stream states */ - if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE) - hsw_stream_update(hsw, msg); - - /* wake up and return the error if we have waiters on this message ? */ - list_del(&msg->list); - sst_ipc_tx_msg_reply_complete(&hsw->ipc, msg); - - return 1; -} - -static int hsw_module_message(struct sst_hsw *hsw, u32 header) -{ - u32 operation, module_id; - int handled = 0; - - operation = msg_get_module_operation(header); - module_id = msg_get_module_id(header); - dev_dbg(hsw->dev, "received module message header: 0x%8.8x\n", - header); - dev_dbg(hsw->dev, "operation: 0x%8.8x module_id: 0x%8.8x\n", - operation, module_id); - - switch (operation) { - case IPC_MODULE_NOTIFICATION: - dev_dbg(hsw->dev, "module notification received"); - handled = 1; - break; - default: - handled = hsw_process_reply(hsw, header); - break; - } - - return handled; -} - -static int hsw_stream_message(struct sst_hsw *hsw, u32 header) -{ - u32 stream_msg, stream_id; - struct sst_hsw_stream *stream; - int handled = 0; - - stream_msg = msg_get_stream_type(header); - stream_id = msg_get_stream_id(header); - - stream = get_stream_by_id(hsw, stream_id); - if (stream == NULL) - return handled; - - stream->header = header; - - switch (stream_msg) { - case IPC_STR_STAGE_MESSAGE: - dev_err(hsw->dev, "error: stage msg not implemented 0x%8.8x\n", - header); - break; - case IPC_STR_NOTIFICATION: - schedule_work(&stream->notify_work); - break; - default: - /* handle pending message complete request */ - handled = hsw_process_reply(hsw, header); - break; - } - - return handled; -} - -static int hsw_log_message(struct sst_hsw *hsw, u32 header) -{ - u32 operation = (header & IPC_LOG_OP_MASK) >> IPC_LOG_OP_SHIFT; - struct sst_hsw_log_stream *stream = &hsw->log_stream; - int ret = 1; - - if (operation != IPC_DEBUG_REQUEST_LOG_DUMP) { - dev_err(hsw->dev, - "error: log msg not implemented 0x%8.8x\n", header); - return 0; - } - - mutex_lock(&stream->rw_mutex); - stream->last_pos = stream->curr_pos; - sst_dsp_inbox_read( - hsw->dsp, &stream->curr_pos, sizeof(stream->curr_pos)); - mutex_unlock(&stream->rw_mutex); - - schedule_work(&stream->notify_work); - - return ret; -} - -static int hsw_process_notification(struct sst_hsw *hsw) -{ - struct sst_dsp *sst = hsw->dsp; - u32 type, header; - int handled = 1; - - header = sst_dsp_shim_read_unlocked(sst, SST_IPCD); - type = msg_get_global_type(header); - - trace_ipc_request("processing -->", header); - - /* FW Ready is a special case */ - if (!hsw->boot_complete && header & IPC_FW_READY) { - hsw_fw_ready(hsw, header); - return handled; - } - - switch (type) { - case IPC_GLB_GET_FW_VERSION: - case IPC_GLB_ALLOCATE_STREAM: - case IPC_GLB_FREE_STREAM: - case IPC_GLB_GET_FW_CAPABILITIES: - case IPC_GLB_REQUEST_DUMP: - case IPC_GLB_GET_DEVICE_FORMATS: - case IPC_GLB_SET_DEVICE_FORMATS: - case IPC_GLB_ENTER_DX_STATE: - case IPC_GLB_GET_MIXER_STREAM_INFO: - case IPC_GLB_MAX_IPC_MESSAGE_TYPE: - case IPC_GLB_RESTORE_CONTEXT: - case IPC_GLB_SHORT_REPLY: - dev_err(hsw->dev, "error: message type %d header 0x%x\n", - type, header); - break; - case IPC_GLB_STREAM_MESSAGE: - handled = hsw_stream_message(hsw, header); - break; - case IPC_GLB_DEBUG_LOG_MESSAGE: - handled = hsw_log_message(hsw, header); - break; - case IPC_GLB_MODULE_OPERATION: - handled = hsw_module_message(hsw, header); - break; - default: - dev_err(hsw->dev, "error: unexpected type %d hdr 0x%8.8x\n", - type, header); - break; - } - - return handled; -} - -static irqreturn_t hsw_irq_thread(int irq, void *context) -{ - struct sst_dsp *sst = (struct sst_dsp *) context; - struct sst_hsw *hsw = sst_dsp_get_thread_context(sst); - struct sst_generic_ipc *ipc = &hsw->ipc; - u32 ipcx, ipcd; - unsigned long flags; - - spin_lock_irqsave(&sst->spinlock, flags); - - ipcx = sst_dsp_ipc_msg_rx(hsw->dsp); - ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); - - /* reply message from DSP */ - if (ipcx & SST_IPCX_DONE) { - - /* Handle Immediate reply from DSP Core */ - hsw_process_reply(hsw, ipcx); - - /* clear DONE bit - tell DSP we have completed */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IPCX, - SST_IPCX_DONE, 0); - - /* unmask Done interrupt */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, - SST_IMRX_DONE, 0); - } - - /* new message from DSP */ - if (ipcd & SST_IPCD_BUSY) { - - /* Handle Notification and Delayed reply from DSP Core */ - hsw_process_notification(hsw); - - /* clear BUSY bit and set DONE bit - accept new messages */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IPCD, - SST_IPCD_BUSY | SST_IPCD_DONE, SST_IPCD_DONE); - - /* unmask busy interrupt */ - sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, - SST_IMRX_BUSY, 0); - } - - spin_unlock_irqrestore(&sst->spinlock, flags); - - /* continue to send any remaining messages... */ - schedule_work(&ipc->kwork); - - return IRQ_HANDLED; -} - -int sst_hsw_fw_get_version(struct sst_hsw *hsw, - struct sst_hsw_ipc_fw_version *version) -{ - struct sst_ipc_message request = {0}, reply = {0}; - int ret; - - request.header = IPC_GLB_TYPE(IPC_GLB_GET_FW_VERSION); - reply.data = version; - reply.size = sizeof(*version); - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, &reply); - if (ret < 0) - dev_err(hsw->dev, "error: get version failed\n"); - - return ret; -} - -/* Mixer Controls */ -int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - u32 stage_id, u32 channel, u32 *volume) -{ - if (channel > 1) - return -EINVAL; - - sst_dsp_read(hsw->dsp, volume, - stream->reply.volume_register_address[channel], - sizeof(*volume)); - - return 0; -} - -/* stream volume */ -int sst_hsw_stream_set_volume(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume) -{ - struct sst_hsw_ipc_volume_req *req; - struct sst_ipc_message request; - int ret; - - trace_ipc_request("set stream volume", stream->reply.stream_hw_id); - - if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL) - return -EINVAL; - - request.header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | - IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); - request.header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT); - request.header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT); - request.header |= (stage_id << IPC_STG_ID_SHIFT); - - req = &stream->vol_req; - req->target_volume = volume; - - /* set both at same time ? */ - if (channel == SST_HSW_CHANNELS_ALL) { - if (hsw->mute[0] && hsw->mute[1]) { - hsw->mute_volume[0] = hsw->mute_volume[1] = volume; - return 0; - } else if (hsw->mute[0]) - req->channel = 1; - else if (hsw->mute[1]) - req->channel = 0; - else - req->channel = SST_HSW_CHANNELS_ALL; - } else { - /* set only 1 channel */ - if (hsw->mute[channel]) { - hsw->mute_volume[channel] = volume; - return 0; - } - req->channel = channel; - } - - request.data = req; - request.size = sizeof(*req); - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) { - dev_err(hsw->dev, "error: set stream volume failed\n"); - return ret; - } - - return 0; -} - -int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, - u32 *volume) -{ - if (channel > 1) - return -EINVAL; - - sst_dsp_read(hsw->dsp, volume, - hsw->mixer_info.volume_register_address[channel], - sizeof(*volume)); - - return 0; -} - -/* global mixer volume */ -int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, - u32 volume) -{ - struct sst_hsw_ipc_volume_req req; - struct sst_ipc_message request; - int ret; - - trace_ipc_request("set mixer volume", volume); - - if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL) - return -EINVAL; - - /* set both at same time ? */ - if (channel == SST_HSW_CHANNELS_ALL) { - if (hsw->mute[0] && hsw->mute[1]) { - hsw->mute_volume[0] = hsw->mute_volume[1] = volume; - return 0; - } else if (hsw->mute[0]) - req.channel = 1; - else if (hsw->mute[1]) - req.channel = 0; - else - req.channel = SST_HSW_CHANNELS_ALL; - } else { - /* set only 1 channel */ - if (hsw->mute[channel]) { - hsw->mute_volume[channel] = volume; - return 0; - } - req.channel = channel; - } - - request.header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) | - IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE); - request.header |= (hsw->mixer_info.mixer_hw_id << IPC_STR_ID_SHIFT); - request.header |= (IPC_STG_SET_VOLUME << IPC_STG_TYPE_SHIFT); - request.header |= (stage_id << IPC_STG_ID_SHIFT); - - req.curve_duration = hsw->curve_duration; - req.curve_type = hsw->curve_type; - req.target_volume = volume; - - request.data = &req; - request.size = sizeof(req); - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) { - dev_err(hsw->dev, "error: set mixer volume failed\n"); - return ret; - } - - return 0; -} - -/* Stream API */ -struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, - u32 (*notify_position)(struct sst_hsw_stream *stream, void *data), - void *data) -{ - struct sst_hsw_stream *stream; - struct sst_dsp *sst = hsw->dsp; - unsigned long flags; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (stream == NULL) - return NULL; - - spin_lock_irqsave(&sst->spinlock, flags); - stream->reply.stream_hw_id = INVALID_STREAM_HW_ID; - list_add(&stream->node, &hsw->stream_list); - stream->notify_position = notify_position; - stream->pdata = data; - stream->hsw = hsw; - stream->host_id = id; - - /* work to process notification messages */ - INIT_WORK(&stream->notify_work, hsw_notification_work); - spin_unlock_irqrestore(&sst->spinlock, flags); - - return stream; -} - -int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream) -{ - struct sst_ipc_message request; - int ret = 0; - struct sst_dsp *sst = hsw->dsp; - unsigned long flags; - - if (!stream) { - dev_warn(hsw->dev, "warning: stream is NULL, no stream to free, ignore it.\n"); - return 0; - } - - /* dont free DSP streams that are not commited */ - if (!stream->commited) - goto out; - - trace_ipc_request("stream free", stream->host_id); - - stream->free_req.stream_id = stream->reply.stream_hw_id; - request.header = IPC_GLB_TYPE(IPC_GLB_FREE_STREAM); - request.data = &stream->free_req; - request.size = sizeof(stream->free_req); - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) { - dev_err(hsw->dev, "error: free stream %d failed\n", - stream->free_req.stream_id); - return -EAGAIN; - } - - trace_hsw_stream_free_req(stream, &stream->free_req); - -out: - cancel_work_sync(&stream->notify_work); - spin_lock_irqsave(&sst->spinlock, flags); - list_del(&stream->node); - kfree(stream); - spin_unlock_irqrestore(&sst->spinlock, flags); - - return ret; -} - -int sst_hsw_stream_set_bits(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, enum sst_hsw_bitdepth bits) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set bits\n"); - return -EINVAL; - } - - stream->request.format.bitdepth = bits; - return 0; -} - -int sst_hsw_stream_set_channels(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, int channels) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set channels\n"); - return -EINVAL; - } - - stream->request.format.ch_num = channels; - return 0; -} - -int sst_hsw_stream_set_rate(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, int rate) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set rate\n"); - return -EINVAL; - } - - stream->request.format.frequency = rate; - return 0; -} - -int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 map, - enum sst_hsw_channel_config config) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set map\n"); - return -EINVAL; - } - - stream->request.format.map = map; - stream->request.format.config = config; - return 0; -} - -int sst_hsw_stream_set_style(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, enum sst_hsw_interleaving style) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set style\n"); - return -EINVAL; - } - - stream->request.format.style = style; - return 0; -} - -int sst_hsw_stream_set_valid(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 bits) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set valid bits\n"); - return -EINVAL; - } - - stream->request.format.valid_bit = bits; - return 0; -} - -/* Stream Configuration */ -int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - enum sst_hsw_stream_path_id path_id, - enum sst_hsw_stream_type stream_type, - enum sst_hsw_stream_format format_id) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set format\n"); - return -EINVAL; - } - - stream->request.path_id = path_id; - stream->request.stream_type = stream_type; - stream->request.format_id = format_id; - - trace_hsw_stream_alloc_request(stream, &stream->request); - - return 0; -} - -int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - u32 ring_pt_address, u32 num_pages, - u32 ring_size, u32 ring_offset, u32 ring_first_pfn) -{ - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for buffer\n"); - return -EINVAL; - } - - stream->request.ringinfo.ring_pt_address = ring_pt_address; - stream->request.ringinfo.num_pages = num_pages; - stream->request.ringinfo.ring_size = ring_size; - stream->request.ringinfo.ring_offset = ring_offset; - stream->request.ringinfo.ring_first_pfn = ring_first_pfn; - - trace_hsw_stream_buffer(stream); - - return 0; -} - -int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, struct sst_module_runtime *runtime) -{ - struct sst_hsw_module_map *map = &stream->request.map; - struct sst_dsp *dsp = sst_hsw_get_dsp(hsw); - struct sst_module *module = runtime->module; - - if (stream->commited) { - dev_err(hsw->dev, "error: stream committed for set module\n"); - return -EINVAL; - } - - /* only support initial module atm */ - map->module_entries_count = 1; - map->module_entries[0].module_id = module->id; - map->module_entries[0].entry_point = module->entry; - - stream->request.persistent_mem.offset = - sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM); - stream->request.persistent_mem.size = module->persistent_size; - - stream->request.scratch_mem.offset = - sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM); - stream->request.scratch_mem.size = dsp->scratch_size; - - dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id, - runtime->id); - dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n", - stream->request.persistent_mem.offset, - stream->request.persistent_mem.size); - dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n", - stream->request.scratch_mem.offset, - stream->request.scratch_mem.size); - - return 0; -} - -int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream) -{ - struct sst_ipc_message request, reply = {0}; - int ret; - - if (!stream) { - dev_warn(hsw->dev, "warning: stream is NULL, no stream to commit, ignore it.\n"); - return 0; - } - - if (stream->commited) { - dev_warn(hsw->dev, "warning: stream is already committed, ignore it.\n"); - return 0; - } - - trace_ipc_request("stream alloc", stream->host_id); - - request.header = IPC_GLB_TYPE(IPC_GLB_ALLOCATE_STREAM); - request.data = &stream->request; - request.size = sizeof(stream->request); - reply.data = &stream->reply; - reply.size = sizeof(stream->reply); - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, &reply); - if (ret < 0) { - dev_err(hsw->dev, "error: stream commit failed\n"); - return ret; - } - - stream->commited = true; - trace_hsw_stream_alloc_reply(stream); - - return 0; -} - -snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream) -{ - return stream->old_position; -} - -void sst_hsw_stream_set_old_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, snd_pcm_uframes_t val) -{ - stream->old_position = val; -} - -bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw, - struct sst_hsw_stream *stream) -{ - return stream->play_silence; -} - -void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, bool val) -{ - stream->play_silence = val; -} - -/* Stream Information - these calls could be inline but we want the IPC - ABI to be opaque to client PCM drivers to cope with any future ABI changes */ -int sst_hsw_mixer_get_info(struct sst_hsw *hsw) -{ - struct sst_ipc_message request = {0}, reply = {0}; - int ret; - - request.header = IPC_GLB_TYPE(IPC_GLB_GET_MIXER_STREAM_INFO); - reply.data = &hsw->mixer_info; - reply.size = sizeof(hsw->mixer_info); - - trace_ipc_request("get global mixer info", 0); - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, &reply); - if (ret < 0) { - dev_err(hsw->dev, "error: get stream info failed\n"); - return ret; - } - - trace_hsw_mixer_info_reply(&hsw->mixer_info); - - return 0; -} - -/* Send stream command */ -static int sst_hsw_stream_operations(struct sst_hsw *hsw, int type, - int stream_id, int wait) -{ - struct sst_ipc_message request = {0}; - - request.header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE); - request.header |= IPC_STR_TYPE(type) | (stream_id << IPC_STR_ID_SHIFT); - - if (wait) - return sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - else - return sst_ipc_tx_message_nowait(&hsw->ipc, request); -} - -/* Stream ALSA trigger operations */ -int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - int wait) -{ - int ret; - - if (!stream) { - dev_warn(hsw->dev, "warning: stream is NULL, no stream to pause, ignore it.\n"); - return 0; - } - - trace_ipc_request("stream pause", stream->reply.stream_hw_id); - - ret = sst_hsw_stream_operations(hsw, IPC_STR_PAUSE, - stream->reply.stream_hw_id, wait); - if (ret < 0) - dev_err(hsw->dev, "error: failed to pause stream %d\n", - stream->reply.stream_hw_id); - - return ret; -} - -int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - int wait) -{ - int ret; - - if (!stream) { - dev_warn(hsw->dev, "warning: stream is NULL, no stream to resume, ignore it.\n"); - return 0; - } - - trace_ipc_request("stream resume", stream->reply.stream_hw_id); - - ret = sst_hsw_stream_operations(hsw, IPC_STR_RESUME, - stream->reply.stream_hw_id, wait); - if (ret < 0) - dev_err(hsw->dev, "error: failed to resume stream %d\n", - stream->reply.stream_hw_id); - - return ret; -} - -int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream) -{ - int ret, tries = 10; - - if (!stream) { - dev_warn(hsw->dev, "warning: stream is NULL, no stream to reset, ignore it.\n"); - return 0; - } - - /* dont reset streams that are not commited */ - if (!stream->commited) - return 0; - - /* wait for pause to complete before we reset the stream */ - while (stream->running && --tries) - msleep(1); - if (!tries) { - dev_err(hsw->dev, "error: reset stream %d still running\n", - stream->reply.stream_hw_id); - return -EINVAL; - } - - trace_ipc_request("stream reset", stream->reply.stream_hw_id); - - ret = sst_hsw_stream_operations(hsw, IPC_STR_RESET, - stream->reply.stream_hw_id, 1); - if (ret < 0) - dev_err(hsw->dev, "error: failed to reset stream %d\n", - stream->reply.stream_hw_id); - return ret; -} - -/* Stream pointer positions */ -u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream) -{ - u32 rpos; - - sst_dsp_read(hsw->dsp, &rpos, - stream->reply.read_position_register_address, sizeof(rpos)); - - return rpos; -} - -/* Stream presentation (monotonic) positions */ -u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream) -{ - u64 ppos; - - sst_dsp_read(hsw->dsp, &ppos, - stream->reply.presentation_position_register_address, - sizeof(ppos)); - - return ppos; -} - -/* physical BE config */ -int sst_hsw_device_set_config(struct sst_hsw *hsw, - enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk, - enum sst_hsw_device_mode mode, u32 clock_divider) -{ - struct sst_ipc_message request; - struct sst_hsw_ipc_device_config_req config; - int ret; - - trace_ipc_request("set device config", dev); - - hsw->dx_dev = config.ssp_interface = dev; - hsw->dx_mclk = config.clock_frequency = mclk; - hsw->dx_mode = config.mode = mode; - hsw->dx_clock_divider = config.clock_divider = clock_divider; - if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER) - config.channels = 4; - else - config.channels = 2; - - trace_hsw_device_config_req(&config); - - request.header = IPC_GLB_TYPE(IPC_GLB_SET_DEVICE_FORMATS); - request.data = &config; - request.size = sizeof(config); - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) - dev_err(hsw->dev, "error: set device formats failed\n"); - - return ret; -} -EXPORT_SYMBOL_GPL(sst_hsw_device_set_config); - -/* DX Config */ -int sst_hsw_dx_set_state(struct sst_hsw *hsw, - enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx) -{ - struct sst_ipc_message request, reply = {0}; - u32 state_; - int ret, item; - - state_ = state; - request.header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE); - request.data = &state_; - request.size = sizeof(state_); - reply.data = dx; - reply.size = sizeof(*dx); - - trace_ipc_request("PM enter Dx state", state); - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, &reply); - if (ret < 0) { - dev_err(hsw->dev, "ipc: error set dx state %d failed\n", state); - return ret; - } - - for (item = 0; item < dx->entries_no; item++) { - dev_dbg(hsw->dev, - "Item[%d] offset[%x] - size[%x] - source[%x]\n", - item, dx->mem_info[item].offset, - dx->mem_info[item].size, - dx->mem_info[item].source); - } - dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n", - dx->entries_no, state); - - return ret; -} - -struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, - int mod_id, int offset) -{ - struct sst_dsp *dsp = hsw->dsp; - struct sst_module *module; - struct sst_module_runtime *runtime; - int err; - - module = sst_module_get_from_id(dsp, mod_id); - if (module == NULL) { - dev_err(dsp->dev, "error: failed to get module %d for pcm\n", - mod_id); - return NULL; - } - - runtime = sst_module_runtime_new(module, mod_id, NULL); - if (runtime == NULL) { - dev_err(dsp->dev, "error: failed to create module %d runtime\n", - mod_id); - return NULL; - } - - err = sst_module_runtime_alloc_blocks(runtime, offset); - if (err < 0) { - dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n", - mod_id); - sst_module_runtime_free(runtime); - return NULL; - } - - dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id, - mod_id); - return runtime; -} - -void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime) -{ - sst_module_runtime_free_blocks(runtime); - sst_module_runtime_free(runtime); -} - -#ifdef CONFIG_PM -static int sst_hsw_dx_state_dump(struct sst_hsw *hsw) -{ - struct sst_dsp *sst = hsw->dsp; - u32 item, offset, size; - int ret = 0; - - trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS); - - if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) { - dev_err(hsw->dev, - "error: number of FW context regions greater than %d\n", - SST_HSW_MAX_DX_REGIONS); - memset(&hsw->dx, 0, sizeof(hsw->dx)); - return -EINVAL; - } - - ret = sst_dsp_dma_get_channel(sst, 0); - if (ret < 0) { - dev_err(hsw->dev, "error: can't allocate dma channel %d\n", ret); - return ret; - } - - /* set on-demond mode on engine 0 channel 3 */ - sst_dsp_shim_update_bits(sst, SST_HMDC, - SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, - SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH); - - for (item = 0; item < hsw->dx.entries_no; item++) { - if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP - && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET - && hsw->dx.mem_info[item].offset < - DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) { - - offset = hsw->dx.mem_info[item].offset - - DSP_DRAM_ADDR_OFFSET; - size = (hsw->dx.mem_info[item].size + 3) & (~3); - - ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset, - sst->addr.lpe_base + offset, size); - if (ret < 0) { - dev_err(hsw->dev, - "error: FW context dump failed\n"); - memset(&hsw->dx, 0, sizeof(hsw->dx)); - goto out; - } - } - } - -out: - sst_dsp_dma_put_channel(sst); - return ret; -} - -static int sst_hsw_dx_state_restore(struct sst_hsw *hsw) -{ - struct sst_dsp *sst = hsw->dsp; - u32 item, offset, size; - int ret; - - for (item = 0; item < hsw->dx.entries_no; item++) { - if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP - && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET - && hsw->dx.mem_info[item].offset < - DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) { - - offset = hsw->dx.mem_info[item].offset - - DSP_DRAM_ADDR_OFFSET; - size = (hsw->dx.mem_info[item].size + 3) & (~3); - - ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset, - hsw->dx_context_paddr + offset, size); - if (ret < 0) { - dev_err(hsw->dev, - "error: FW context restore failed\n"); - return ret; - } - } - } - - return 0; -} - -int sst_hsw_dsp_load(struct sst_hsw *hsw) -{ - struct sst_dsp *dsp = hsw->dsp; - struct sst_fw *sst_fw, *t; - int ret; - - dev_dbg(hsw->dev, "loading audio DSP...."); - - ret = sst_dsp_wake(dsp); - if (ret < 0) { - dev_err(hsw->dev, "error: failed to wake audio DSP\n"); - return -ENODEV; - } - - ret = sst_dsp_dma_get_channel(dsp, 0); - if (ret < 0) { - dev_err(hsw->dev, "error: can't allocate dma channel %d\n", ret); - return ret; - } - - list_for_each_entry_safe_reverse(sst_fw, t, &dsp->fw_list, list) { - ret = sst_fw_reload(sst_fw); - if (ret < 0) { - dev_err(hsw->dev, "error: SST FW reload failed\n"); - sst_dsp_dma_put_channel(dsp); - return -ENOMEM; - } - } - ret = sst_block_alloc_scratch(hsw->dsp); - if (ret < 0) - return -EINVAL; - - sst_dsp_dma_put_channel(dsp); - return 0; -} - -static int sst_hsw_dsp_restore(struct sst_hsw *hsw) -{ - struct sst_dsp *dsp = hsw->dsp; - int ret; - - dev_dbg(hsw->dev, "restoring audio DSP...."); - - ret = sst_dsp_dma_get_channel(dsp, 0); - if (ret < 0) { - dev_err(hsw->dev, "error: can't allocate dma channel %d\n", ret); - return ret; - } - - ret = sst_hsw_dx_state_restore(hsw); - if (ret < 0) { - dev_err(hsw->dev, "error: SST FW context restore failed\n"); - sst_dsp_dma_put_channel(dsp); - return -ENOMEM; - } - sst_dsp_dma_put_channel(dsp); - - /* wait for DSP boot completion */ - sst_dsp_boot(dsp); - - return ret; -} - -int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw) -{ - int ret; - - dev_dbg(hsw->dev, "audio dsp runtime suspend\n"); - - ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx); - if (ret < 0) - return ret; - - sst_dsp_stall(hsw->dsp); - - ret = sst_hsw_dx_state_dump(hsw); - if (ret < 0) - return ret; - - sst_ipc_drop_all(&hsw->ipc); - - return 0; -} - -int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw) -{ - struct sst_fw *sst_fw, *t; - struct sst_dsp *dsp = hsw->dsp; - - list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) { - sst_fw_unload(sst_fw); - } - sst_block_free_scratch(dsp); - - hsw->boot_complete = false; - - sst_dsp_sleep(dsp); - - return 0; -} - -int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw) -{ - struct device *dev = hsw->dev; - int ret; - - dev_dbg(dev, "audio dsp runtime resume\n"); - - if (hsw->boot_complete) - return 1; /* tell caller no action is required */ - - ret = sst_hsw_dsp_restore(hsw); - if (ret < 0) - dev_err(dev, "error: audio DSP boot failure\n"); - - sst_hsw_init_module_state(hsw); - - ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, - msecs_to_jiffies(IPC_BOOT_MSECS)); - if (ret == 0) { - dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n", - sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD), - sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX)); - return -EIO; - } - - /* Set ADSP SSP port settings - sadly the FW does not store SSP port - settings as part of the PM context. */ - ret = sst_hsw_device_set_config(hsw, hsw->dx_dev, hsw->dx_mclk, - hsw->dx_mode, hsw->dx_clock_divider); - if (ret < 0) - dev_err(dev, "error: SSP re-initialization failed\n"); - - return ret; -} -#endif - -struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw) -{ - return hsw->dsp; -} - -void sst_hsw_init_module_state(struct sst_hsw *hsw) -{ - struct sst_module *module; - enum sst_hsw_module_id id; - - /* the base fw contains several modules */ - for (id = SST_HSW_MODULE_BASE_FW; id < SST_HSW_MAX_MODULE_ID; id++) { - module = sst_module_get_from_id(hsw->dsp, id); - if (module) { - /* module waves is active only after being enabled */ - if (id == SST_HSW_MODULE_WAVES) - module->state = SST_MODULE_STATE_INITIALIZED; - else - module->state = SST_MODULE_STATE_ACTIVE; - } - } -} - -bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id) -{ - struct sst_module *module; - - module = sst_module_get_from_id(hsw->dsp, module_id); - if (module == NULL || module->state == SST_MODULE_STATE_UNLOADED) - return false; - else - return true; -} - -bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id) -{ - struct sst_module *module; - - module = sst_module_get_from_id(hsw->dsp, module_id); - if (module != NULL && module->state == SST_MODULE_STATE_ACTIVE) - return true; - else - return false; -} - -void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) -{ - hsw->enabled_modules_rtd3 |= (1 << module_id); -} - -void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id) -{ - hsw->enabled_modules_rtd3 &= ~(1 << module_id); -} - -bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id) -{ - return hsw->enabled_modules_rtd3 & (1 << module_id); -} - -void sst_hsw_reset_param_buf(struct sst_hsw *hsw) -{ - hsw->param_idx_w = 0; - hsw->param_idx_r = 0; - memset((void *)hsw->param_buf, 0, sizeof(hsw->param_buf)); -} - -int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf) -{ - /* save line to the first available position of param buffer */ - if (hsw->param_idx_w > WAVES_PARAM_LINES - 1) { - dev_warn(hsw->dev, "warning: param buffer overflow!\n"); - return -EPERM; - } - memcpy(hsw->param_buf[hsw->param_idx_w], buf, WAVES_PARAM_COUNT); - hsw->param_idx_w++; - return 0; -} - -int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf) -{ - u8 id; - - /* read the first matching line from param buffer */ - while (hsw->param_idx_r < WAVES_PARAM_LINES) { - id = hsw->param_buf[hsw->param_idx_r][0]; - hsw->param_idx_r++; - if (buf[0] == id) { - memcpy(buf, hsw->param_buf[hsw->param_idx_r], - WAVES_PARAM_COUNT); - break; - } - } - if (hsw->param_idx_r > WAVES_PARAM_LINES - 1) { - dev_dbg(hsw->dev, "end of buffer, roll to the beginning\n"); - hsw->param_idx_r = 0; - return 0; - } - return 0; -} - -int sst_hsw_launch_param_buf(struct sst_hsw *hsw) -{ - int ret, idx; - - if (!sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { - dev_dbg(hsw->dev, "module waves is not active\n"); - return 0; - } - - /* put all param lines to DSP through ipc */ - for (idx = 0; idx < hsw->param_idx_w; idx++) { - ret = sst_hsw_module_set_param(hsw, - SST_HSW_MODULE_WAVES, 0, hsw->param_buf[idx][0], - WAVES_PARAM_COUNT, hsw->param_buf[idx]); - if (ret < 0) - return ret; - } - return 0; -} - -int sst_hsw_module_load(struct sst_hsw *hsw, - u32 module_id, u32 instance_id, char *name) -{ - int ret = 0; - const struct firmware *fw = NULL; - struct sst_fw *hsw_sst_fw; - struct sst_module *module; - struct device *dev = hsw->dev; - struct sst_dsp *dsp = hsw->dsp; - - dev_dbg(dev, "sst_hsw_module_load id=%d, name='%s'", module_id, name); - - module = sst_module_get_from_id(dsp, module_id); - if (module == NULL) { - /* loading for the first time */ - if (module_id == SST_HSW_MODULE_BASE_FW) { - /* for base module: use fw requested in acpi probe */ - fw = dsp->pdata->fw; - if (!fw) { - dev_err(dev, "request Base fw failed\n"); - return -ENODEV; - } - } else { - /* try and load any other optional modules if they are - * available. Use dev_info instead of dev_err in case - * request firmware failed */ - ret = request_firmware(&fw, name, dev); - if (ret) { - dev_info(dev, "fw image %s not available(%d)\n", - name, ret); - return ret; - } - } - hsw_sst_fw = sst_fw_new(dsp, fw, hsw); - if (hsw_sst_fw == NULL) { - dev_err(dev, "error: failed to load firmware\n"); - ret = -ENOMEM; - goto out; - } - module = sst_module_get_from_id(dsp, module_id); - if (module == NULL) { - dev_err(dev, "error: no module %d in firmware %s\n", - module_id, name); - } - } else - dev_info(dev, "module %d (%s) already loaded\n", - module_id, name); -out: - /* release fw, but base fw should be released by acpi driver */ - if (fw && module_id != SST_HSW_MODULE_BASE_FW) - release_firmware(fw); - - return ret; -} - -int sst_hsw_module_enable(struct sst_hsw *hsw, - u32 module_id, u32 instance_id) -{ - int ret; - struct sst_ipc_message request; - struct sst_hsw_ipc_module_config config; - struct sst_module *module; - struct sst_module_runtime *runtime; - struct device *dev = hsw->dev; - struct sst_dsp *dsp = hsw->dsp; - - if (!sst_hsw_is_module_loaded(hsw, module_id)) { - dev_dbg(dev, "module %d not loaded\n", module_id); - return 0; - } - - if (sst_hsw_is_module_active(hsw, module_id)) { - dev_info(dev, "module %d already enabled\n", module_id); - return 0; - } - - module = sst_module_get_from_id(dsp, module_id); - if (module == NULL) { - dev_err(dev, "module %d not valid\n", module_id); - return -ENXIO; - } - - runtime = sst_module_runtime_get_from_id(module, module_id); - if (runtime == NULL) { - dev_err(dev, "runtime %d not valid", module_id); - return -ENXIO; - } - - request.header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | - IPC_MODULE_OPERATION(IPC_MODULE_ENABLE) | - IPC_MODULE_ID(module_id); - dev_dbg(dev, "module enable header: %x\n", (u32)request.header); - - config.map.module_entries_count = 1; - config.map.module_entries[0].module_id = module->id; - config.map.module_entries[0].entry_point = module->entry; - - config.persistent_mem.offset = - sst_dsp_get_offset(dsp, - runtime->persistent_offset, SST_MEM_DRAM); - config.persistent_mem.size = module->persistent_size; - - config.scratch_mem.offset = - sst_dsp_get_offset(dsp, - dsp->scratch_offset, SST_MEM_DRAM); - config.scratch_mem.size = module->scratch_size; - dev_dbg(dev, "mod %d enable p:%d @ %x, s:%d @ %x, ep: %x", - config.map.module_entries[0].module_id, - config.persistent_mem.size, - config.persistent_mem.offset, - config.scratch_mem.size, config.scratch_mem.offset, - config.map.module_entries[0].entry_point); - - request.data = &config; - request.size = sizeof(config); - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) - dev_err(dev, "ipc: module enable failed - %d\n", ret); - else - module->state = SST_MODULE_STATE_ACTIVE; - - return ret; -} - -int sst_hsw_module_disable(struct sst_hsw *hsw, - u32 module_id, u32 instance_id) -{ - int ret; - struct sst_ipc_message request = {0}; - struct sst_module *module; - struct device *dev = hsw->dev; - struct sst_dsp *dsp = hsw->dsp; - - if (!sst_hsw_is_module_loaded(hsw, module_id)) { - dev_dbg(dev, "module %d not loaded\n", module_id); - return 0; - } - - if (!sst_hsw_is_module_active(hsw, module_id)) { - dev_info(dev, "module %d already disabled\n", module_id); - return 0; - } - - module = sst_module_get_from_id(dsp, module_id); - if (module == NULL) { - dev_err(dev, "module %d not valid\n", module_id); - return -ENXIO; - } - - request.header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | - IPC_MODULE_OPERATION(IPC_MODULE_DISABLE) | - IPC_MODULE_ID(module_id); - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) - dev_err(dev, "module disable failed - %d\n", ret); - else - module->state = SST_MODULE_STATE_INITIALIZED; - - return ret; -} - -int sst_hsw_module_set_param(struct sst_hsw *hsw, - u32 module_id, u32 instance_id, u32 parameter_id, - u32 param_size, char *param) -{ - int ret; - struct sst_ipc_message request = {0}; - u32 payload_size = 0; - struct sst_hsw_transfer_parameter *parameter; - struct device *dev = hsw->dev; - - request.header = IPC_GLB_TYPE(IPC_GLB_MODULE_OPERATION) | - IPC_MODULE_OPERATION(IPC_MODULE_SET_PARAMETER) | - IPC_MODULE_ID(module_id); - dev_dbg(dev, "sst_hsw_module_set_param header=%x\n", - (u32)request.header); - - payload_size = param_size + - sizeof(struct sst_hsw_transfer_parameter) - - sizeof(struct sst_hsw_transfer_list); - dev_dbg(dev, "parameter size : %d\n", param_size); - dev_dbg(dev, "payload size : %d\n", payload_size); - - if (payload_size <= SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE) { - /* short parameter, mailbox can contain data */ - dev_dbg(dev, "transfer parameter size : %zu\n", - request.size); - - request.size = ALIGN(payload_size, 4); - dev_dbg(dev, "transfer parameter aligned size : %zu\n", - request.size); - - parameter = kzalloc(request.size, GFP_KERNEL); - if (parameter == NULL) - return -ENOMEM; - - memcpy(parameter->data, param, param_size); - } else { - dev_warn(dev, "transfer parameter size too large!"); - return 0; - } - - parameter->parameter_id = parameter_id; - parameter->data_size = param_size; - request.data = parameter; - - ret = sst_ipc_tx_message_wait(&hsw->ipc, request, NULL); - if (ret < 0) - dev_err(dev, "ipc: module set parameter failed - %d\n", ret); - - kfree(parameter); - - return ret; -} - -static struct sst_dsp_device hsw_dev = { - .thread = hsw_irq_thread, - .ops = &haswell_ops, -}; - -static void hsw_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) -{ - /* send the message */ - sst_dsp_outbox_write(ipc->dsp, msg->tx.data, msg->tx.size); - sst_dsp_ipc_msg_tx(ipc->dsp, msg->tx.header); -} - -static void hsw_shim_dbg(struct sst_generic_ipc *ipc, const char *text) -{ - struct sst_dsp *sst = ipc->dsp; - u32 isr, ipcd, imrx, ipcx; - - ipcx = sst_dsp_shim_read_unlocked(sst, SST_IPCX); - isr = sst_dsp_shim_read_unlocked(sst, SST_ISRX); - ipcd = sst_dsp_shim_read_unlocked(sst, SST_IPCD); - imrx = sst_dsp_shim_read_unlocked(sst, SST_IMRX); - - dev_err(ipc->dev, - "ipc: --%s-- ipcx 0x%8.8x isr 0x%8.8x ipcd 0x%8.8x imrx 0x%8.8x\n", - text, ipcx, isr, ipcd, imrx); -} - -static void hsw_tx_data_copy(struct ipc_message *msg, char *tx_data, - size_t tx_size) -{ - memcpy(msg->tx.data, tx_data, tx_size); -} - -static u64 hsw_reply_msg_match(u64 header, u64 *mask) -{ - /* clear reply bits & status bits */ - header &= ~(IPC_STATUS_MASK | IPC_GLB_REPLY_MASK); - *mask = (u64)-1; - - return header; -} - -static bool hsw_is_dsp_busy(struct sst_dsp *dsp) -{ - u64 ipcx; - - ipcx = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); - return (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)); -} - -int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_hsw_ipc_fw_version version; - struct sst_hsw *hsw; - struct sst_generic_ipc *ipc; - int ret; - - dev_dbg(dev, "initialising Audio DSP IPC\n"); - - hsw = devm_kzalloc(dev, sizeof(*hsw), GFP_KERNEL); - if (hsw == NULL) - return -ENOMEM; - - hsw->dev = dev; - - ipc = &hsw->ipc; - ipc->dev = dev; - ipc->ops.tx_msg = hsw_tx_msg; - ipc->ops.shim_dbg = hsw_shim_dbg; - ipc->ops.tx_data_copy = hsw_tx_data_copy; - ipc->ops.reply_msg_match = hsw_reply_msg_match; - ipc->ops.is_dsp_busy = hsw_is_dsp_busy; - - ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES; - ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES; - - ret = sst_ipc_init(ipc); - if (ret != 0) - goto ipc_init_err; - - INIT_LIST_HEAD(&hsw->stream_list); - init_waitqueue_head(&hsw->boot_wait); - hsw_dev.thread_context = hsw; - - /* init SST shim */ - hsw->dsp = sst_dsp_new(dev, &hsw_dev, pdata); - if (hsw->dsp == NULL) { - ret = -ENODEV; - goto dsp_new_err; - } - - ipc->dsp = hsw->dsp; - - /* allocate DMA buffer for context storage */ - hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev, - SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL); - if (hsw->dx_context == NULL) { - ret = -ENOMEM; - goto dma_err; - } - - /* keep the DSP in reset state for base FW loading */ - sst_dsp_reset(hsw->dsp); - - /* load base module and other modules in base firmware image */ - ret = sst_hsw_module_load(hsw, SST_HSW_MODULE_BASE_FW, 0, "Base"); - if (ret < 0) - goto fw_err; - - /* try to load module waves */ - sst_hsw_module_load(hsw, SST_HSW_MODULE_WAVES, 0, "intel/IntcPP01.bin"); - - /* allocate scratch mem regions */ - ret = sst_block_alloc_scratch(hsw->dsp); - if (ret < 0) - goto boot_err; - - /* init param buffer */ - sst_hsw_reset_param_buf(hsw); - - /* wait for DSP boot completion */ - sst_dsp_boot(hsw->dsp); - ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete, - msecs_to_jiffies(IPC_BOOT_MSECS)); - if (ret == 0) { - ret = -EIO; - dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n", - sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD), - sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX)); - goto boot_err; - } - - /* init module state after boot */ - sst_hsw_init_module_state(hsw); - - /* get the FW version */ - sst_hsw_fw_get_version(hsw, &version); - - /* get the globalmixer */ - ret = sst_hsw_mixer_get_info(hsw); - if (ret < 0) { - dev_err(hsw->dev, "error: failed to get stream info\n"); - goto boot_err; - } - - pdata->dsp = hsw; - return 0; - -boot_err: - sst_dsp_reset(hsw->dsp); - sst_fw_free_all(hsw->dsp); -fw_err: - dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, - hsw->dx_context, hsw->dx_context_paddr); -dma_err: - sst_dsp_free(hsw->dsp); -dsp_new_err: - sst_ipc_fini(ipc); -ipc_init_err: - return ret; -} -EXPORT_SYMBOL_GPL(sst_hsw_dsp_init); - -void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_hsw *hsw = pdata->dsp; - - sst_dsp_reset(hsw->dsp); - sst_fw_free_all(hsw->dsp); - dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE, - hsw->dx_context, hsw->dx_context_paddr); - sst_dsp_free(hsw->dsp); - sst_ipc_fini(&hsw->ipc); -} -EXPORT_SYMBOL_GPL(sst_hsw_dsp_free); diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.h b/sound/soc/intel/haswell/sst-haswell-ipc.h deleted file mode 100644 index 646dbbba327b..000000000000 --- a/sound/soc/intel/haswell/sst-haswell-ipc.h +++ /dev/null @@ -1,527 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel SST Haswell/Broadwell IPC Support - * - * Copyright (C) 2013, Intel Corporation. All rights reserved. - */ - -#ifndef __SST_HASWELL_IPC_H -#define __SST_HASWELL_IPC_H - -#include -#include -#include -#include - -#define DRV_NAME "haswell-dai" - -#define SST_HSW_NO_CHANNELS 4 -#define SST_HSW_MAX_DX_REGIONS 14 -#define SST_HSW_DX_CONTEXT_SIZE (640 * 1024) -#define SST_HSW_CHANNELS_ALL 0xffffffff - -#define SST_HSW_FW_LOG_CONFIG_DWORDS 12 -#define SST_HSW_GLOBAL_LOG 15 - -/** - * Upfront defined maximum message size that is - * expected by the in/out communication pipes in FW. - */ -#define SST_HSW_IPC_MAX_PAYLOAD_SIZE 400 -#define SST_HSW_MAX_INFO_SIZE 64 -#define SST_HSW_BUILD_HASH_LENGTH 40 -#define SST_HSW_IPC_MAX_SHORT_PARAMETER_SIZE 500 -#define WAVES_PARAM_COUNT 128 -#define WAVES_PARAM_LINES 160 - -struct sst_hsw; -struct sst_hsw_stream; -struct sst_hsw_log_stream; -struct sst_pdata; -struct sst_module; -struct sst_module_runtime; -extern struct sst_ops haswell_ops; - -/* Stream Allocate Path ID */ -enum sst_hsw_stream_path_id { - SST_HSW_STREAM_PATH_SSP0_OUT = 0, - SST_HSW_STREAM_PATH_SSP0_IN = 1, - SST_HSW_STREAM_PATH_MAX_PATH_ID = 2, -}; - -/* Stream Allocate Stream Type */ -enum sst_hsw_stream_type { - SST_HSW_STREAM_TYPE_RENDER = 0, - SST_HSW_STREAM_TYPE_SYSTEM = 1, - SST_HSW_STREAM_TYPE_CAPTURE = 2, - SST_HSW_STREAM_TYPE_LOOPBACK = 3, - SST_HSW_STREAM_TYPE_MAX_STREAM_TYPE = 4, -}; - -/* Stream Allocate Stream Format */ -enum sst_hsw_stream_format { - SST_HSW_STREAM_FORMAT_PCM_FORMAT = 0, - SST_HSW_STREAM_FORMAT_MP3_FORMAT = 1, - SST_HSW_STREAM_FORMAT_AAC_FORMAT = 2, - SST_HSW_STREAM_FORMAT_MAX_FORMAT_ID = 3, -}; - -/* Device ID */ -enum sst_hsw_device_id { - SST_HSW_DEVICE_SSP_0 = 0, - SST_HSW_DEVICE_SSP_1 = 1, -}; - -/* Device Master Clock Frequency */ -enum sst_hsw_device_mclk { - SST_HSW_DEVICE_MCLK_OFF = 0, - SST_HSW_DEVICE_MCLK_FREQ_6_MHZ = 1, - SST_HSW_DEVICE_MCLK_FREQ_12_MHZ = 2, - SST_HSW_DEVICE_MCLK_FREQ_24_MHZ = 3, -}; - -/* Device Clock Master */ -enum sst_hsw_device_mode { - SST_HSW_DEVICE_CLOCK_SLAVE = 0, - SST_HSW_DEVICE_CLOCK_MASTER = 1, - SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2, -}; - -/* DX Power State */ -enum sst_hsw_dx_state { - SST_HSW_DX_STATE_D0 = 0, - SST_HSW_DX_STATE_D1 = 1, - SST_HSW_DX_STATE_D3 = 3, - SST_HSW_DX_STATE_MAX = 3, -}; - -/* Audio stream stage IDs */ -enum sst_hsw_fx_stage_id { - SST_HSW_STAGE_ID_WAVES = 0, - SST_HSW_STAGE_ID_DTS = 1, - SST_HSW_STAGE_ID_DOLBY = 2, - SST_HSW_STAGE_ID_BOOST = 3, - SST_HSW_STAGE_ID_MAX_FX_ID -}; - -/* DX State Type */ -enum sst_hsw_dx_type { - SST_HSW_DX_TYPE_FW_IMAGE = 0, - SST_HSW_DX_TYPE_MEMORY_DUMP = 1 -}; - -/* Volume Curve Type*/ -enum sst_hsw_volume_curve { - SST_HSW_VOLUME_CURVE_NONE = 0, - SST_HSW_VOLUME_CURVE_FADE = 1 -}; - -/* Sample ordering */ -enum sst_hsw_interleaving { - SST_HSW_INTERLEAVING_PER_CHANNEL = 0, - SST_HSW_INTERLEAVING_PER_SAMPLE = 1, -}; - -/* Channel indices */ -enum sst_hsw_channel_index { - SST_HSW_CHANNEL_LEFT = 0, - SST_HSW_CHANNEL_CENTER = 1, - SST_HSW_CHANNEL_RIGHT = 2, - SST_HSW_CHANNEL_LEFT_SURROUND = 3, - SST_HSW_CHANNEL_CENTER_SURROUND = 3, - SST_HSW_CHANNEL_RIGHT_SURROUND = 4, - SST_HSW_CHANNEL_LFE = 7, - SST_HSW_CHANNEL_INVALID = 0xF, -}; - -/* List of supported channel maps. */ -enum sst_hsw_channel_config { - SST_HSW_CHANNEL_CONFIG_MONO = 0, /* mono only. */ - SST_HSW_CHANNEL_CONFIG_STEREO = 1, /* L & R. */ - SST_HSW_CHANNEL_CONFIG_2_POINT_1 = 2, /* L, R & LFE; PCM only. */ - SST_HSW_CHANNEL_CONFIG_3_POINT_0 = 3, /* L, C & R; MP3 & AAC only. */ - SST_HSW_CHANNEL_CONFIG_3_POINT_1 = 4, /* L, C, R & LFE; PCM only. */ - SST_HSW_CHANNEL_CONFIG_QUATRO = 5, /* L, R, Ls & Rs; PCM only. */ - SST_HSW_CHANNEL_CONFIG_4_POINT_0 = 6, /* L, C, R & Cs; MP3 & AAC only. */ - SST_HSW_CHANNEL_CONFIG_5_POINT_0 = 7, /* L, C, R, Ls & Rs. */ - SST_HSW_CHANNEL_CONFIG_5_POINT_1 = 8, /* L, C, R, Ls, Rs & LFE. */ - SST_HSW_CHANNEL_CONFIG_DUAL_MONO = 9, /* One channel replicated in two. */ - SST_HSW_CHANNEL_CONFIG_INVALID, -}; - -/* List of supported bit depths. */ -enum sst_hsw_bitdepth { - SST_HSW_DEPTH_8BIT = 8, - SST_HSW_DEPTH_16BIT = 16, - SST_HSW_DEPTH_24BIT = 24, /* Default. */ - SST_HSW_DEPTH_32BIT = 32, - SST_HSW_DEPTH_INVALID = 33, -}; - -enum sst_hsw_module_id { - SST_HSW_MODULE_BASE_FW = 0x0, - SST_HSW_MODULE_MP3 = 0x1, - SST_HSW_MODULE_AAC_5_1 = 0x2, - SST_HSW_MODULE_AAC_2_0 = 0x3, - SST_HSW_MODULE_SRC = 0x4, - SST_HSW_MODULE_WAVES = 0x5, - SST_HSW_MODULE_DOLBY = 0x6, - SST_HSW_MODULE_BOOST = 0x7, - SST_HSW_MODULE_LPAL = 0x8, - SST_HSW_MODULE_DTS = 0x9, - SST_HSW_MODULE_PCM_CAPTURE = 0xA, - SST_HSW_MODULE_PCM_SYSTEM = 0xB, - SST_HSW_MODULE_PCM_REFERENCE = 0xC, - SST_HSW_MODULE_PCM = 0xD, - SST_HSW_MODULE_BLUETOOTH_RENDER_MODULE = 0xE, - SST_HSW_MODULE_BLUETOOTH_CAPTURE_MODULE = 0xF, - SST_HSW_MAX_MODULE_ID, -}; - -enum sst_hsw_performance_action { - SST_HSW_PERF_START = 0, - SST_HSW_PERF_STOP = 1, -}; - -struct sst_hsw_transfer_info { - uint32_t destination; /* destination address */ - uint32_t reverse:1; /* if 1 data flows from destination */ - uint32_t size:31; /* transfer size in bytes.*/ - uint16_t first_page_offset; /* offset to data in the first page. */ - uint8_t packed_pages; /* page addresses. Each occupies 20 bits */ -} __attribute__((packed)); - -struct sst_hsw_transfer_list { - uint32_t transfers_count; - struct sst_hsw_transfer_info transfers; -} __attribute__((packed)); - -struct sst_hsw_transfer_parameter { - uint32_t parameter_id; - uint32_t data_size; - union { - uint8_t data[1]; - struct sst_hsw_transfer_list transfer_list; - }; -} __attribute__((packed)); - -/* SST firmware module info */ -struct sst_hsw_module_info { - u8 name[SST_HSW_MAX_INFO_SIZE]; - u8 version[SST_HSW_MAX_INFO_SIZE]; -} __attribute__((packed)); - -/* Module entry point */ -struct sst_hsw_module_entry { - enum sst_hsw_module_id module_id; - u32 entry_point; -} __attribute__((packed)); - -/* Module map - alignement matches DSP */ -struct sst_hsw_module_map { - u8 module_entries_count; - struct sst_hsw_module_entry module_entries[1]; -} __attribute__((packed)); - -struct sst_hsw_memory_info { - u32 offset; - u32 size; -} __attribute__((packed)); - -struct sst_hsw_fx_enable { - struct sst_hsw_module_map module_map; - struct sst_hsw_memory_info persistent_mem; -} __attribute__((packed)); - -struct sst_hsw_ipc_module_config { - struct sst_hsw_module_map map; - struct sst_hsw_memory_info persistent_mem; - struct sst_hsw_memory_info scratch_mem; -} __attribute__((packed)); - -struct sst_hsw_get_fx_param { - u32 parameter_id; - u32 param_size; -} __attribute__((packed)); - -struct sst_hsw_perf_action { - u32 action; -} __attribute__((packed)); - -struct sst_hsw_perf_data { - u64 timestamp; - u64 cycles; - u64 datatime; -} __attribute__((packed)); - -/* FW version */ -struct sst_hsw_ipc_fw_version { - u8 build; - u8 minor; - u8 major; - u8 type; - u8 fw_build_hash[SST_HSW_BUILD_HASH_LENGTH]; - u32 fw_log_providers_hash; -} __attribute__((packed)); - -/* Stream ring info */ -struct sst_hsw_ipc_stream_ring { - u32 ring_pt_address; - u32 num_pages; - u32 ring_size; - u32 ring_offset; - u32 ring_first_pfn; -} __attribute__((packed)); - -/* Debug Dump Log Enable Request */ -struct sst_hsw_ipc_debug_log_enable_req { - struct sst_hsw_ipc_stream_ring ringinfo; - u32 config[SST_HSW_FW_LOG_CONFIG_DWORDS]; -} __attribute__((packed)); - -/* Debug Dump Log Reply */ -struct sst_hsw_ipc_debug_log_reply { - u32 log_buffer_begining; - u32 log_buffer_size; -} __attribute__((packed)); - -/* Stream glitch position */ -struct sst_hsw_ipc_stream_glitch_position { - u32 glitch_type; - u32 present_pos; - u32 write_pos; -} __attribute__((packed)); - -/* Stream get position */ -struct sst_hsw_ipc_stream_get_position { - u32 position; - u32 fw_cycle_count; -} __attribute__((packed)); - -/* Stream set position */ -struct sst_hsw_ipc_stream_set_position { - u32 position; - u32 end_of_buffer; -} __attribute__((packed)); - -/* Stream Free Request */ -struct sst_hsw_ipc_stream_free_req { - u8 stream_id; - u8 reserved[3]; -} __attribute__((packed)); - -/* Set Volume Request */ -struct sst_hsw_ipc_volume_req { - u32 channel; - u32 target_volume; - u64 curve_duration; - u32 curve_type; -} __attribute__((packed)); - -/* Device Configuration Request */ -struct sst_hsw_ipc_device_config_req { - u32 ssp_interface; - u32 clock_frequency; - u32 mode; - u16 clock_divider; - u8 channels; - u8 reserved; -} __attribute__((packed)); - -/* Audio Data formats */ -struct sst_hsw_audio_data_format_ipc { - u32 frequency; - u32 bitdepth; - u32 map; - u32 config; - u32 style; - u8 ch_num; - u8 valid_bit; - u8 reserved[2]; -} __attribute__((packed)); - -/* Stream Allocate Request */ -struct sst_hsw_ipc_stream_alloc_req { - u8 path_id; - u8 stream_type; - u8 format_id; - u8 reserved; - struct sst_hsw_audio_data_format_ipc format; - struct sst_hsw_ipc_stream_ring ringinfo; - struct sst_hsw_module_map map; - struct sst_hsw_memory_info persistent_mem; - struct sst_hsw_memory_info scratch_mem; - u32 number_of_notifications; -} __attribute__((packed)); - -/* Stream Allocate Reply */ -struct sst_hsw_ipc_stream_alloc_reply { - u32 stream_hw_id; - u32 mixer_hw_id; // returns rate ???? - u32 read_position_register_address; - u32 presentation_position_register_address; - u32 peak_meter_register_address[SST_HSW_NO_CHANNELS]; - u32 volume_register_address[SST_HSW_NO_CHANNELS]; -} __attribute__((packed)); - -/* Get Mixer Stream Info */ -struct sst_hsw_ipc_stream_info_reply { - u32 mixer_hw_id; - u32 peak_meter_register_address[SST_HSW_NO_CHANNELS]; - u32 volume_register_address[SST_HSW_NO_CHANNELS]; -} __attribute__((packed)); - -/* DX State Request */ -struct sst_hsw_ipc_dx_req { - u8 state; - u8 reserved[3]; -} __attribute__((packed)); - -/* DX State Reply Memory Info Item */ -struct sst_hsw_ipc_dx_memory_item { - u32 offset; - u32 size; - u32 source; -} __attribute__((packed)); - -/* DX State Reply */ -struct sst_hsw_ipc_dx_reply { - u32 entries_no; - struct sst_hsw_ipc_dx_memory_item mem_info[SST_HSW_MAX_DX_REGIONS]; -} __attribute__((packed)); - -struct sst_hsw_ipc_fw_version; - -/* SST Init & Free */ -struct sst_hsw *sst_hsw_new(struct device *dev, const u8 *fw, size_t fw_length, - u32 fw_offset); -void sst_hsw_free(struct sst_hsw *hsw); -int sst_hsw_fw_get_version(struct sst_hsw *hsw, - struct sst_hsw_ipc_fw_version *version); -u32 create_channel_map(enum sst_hsw_channel_config config); - -/* Stream Mixer Controls - */ -int sst_hsw_stream_set_volume(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume); -int sst_hsw_stream_get_volume(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume); - -/* Global Mixer Controls - */ -int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, - u32 volume); -int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel, - u32 *volume); - -/* Stream API */ -struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id, - u32 (*notify_position)(struct sst_hsw_stream *stream, void *data), - void *data); - -int sst_hsw_stream_free(struct sst_hsw *hsw, struct sst_hsw_stream *stream); - -/* Stream Configuration */ -int sst_hsw_stream_format(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - enum sst_hsw_stream_path_id path_id, - enum sst_hsw_stream_type stream_type, - enum sst_hsw_stream_format format_id); - -int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - u32 ring_pt_address, u32 num_pages, - u32 ring_size, u32 ring_offset, u32 ring_first_pfn); - -int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream); - -int sst_hsw_stream_set_valid(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - u32 bits); -int sst_hsw_stream_set_rate(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - int rate); -int sst_hsw_stream_set_bits(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - enum sst_hsw_bitdepth bits); -int sst_hsw_stream_set_channels(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, int channels); -int sst_hsw_stream_set_map_config(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 map, - enum sst_hsw_channel_config config); -int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - enum sst_hsw_interleaving style); -int sst_hsw_stream_set_module_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, struct sst_module_runtime *runtime); -int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 offset, u32 size); -int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 offset, u32 size); -snd_pcm_uframes_t sst_hsw_stream_get_old_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream); -void sst_hsw_stream_set_old_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, snd_pcm_uframes_t val); -bool sst_hsw_stream_get_silence_start(struct sst_hsw *hsw, - struct sst_hsw_stream *stream); -void sst_hsw_stream_set_silence_start(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, bool val); -int sst_hsw_mixer_get_info(struct sst_hsw *hsw); - -/* Stream ALSA trigger operations */ -int sst_hsw_stream_pause(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - int wait); -int sst_hsw_stream_resume(struct sst_hsw *hsw, struct sst_hsw_stream *stream, - int wait); -int sst_hsw_stream_reset(struct sst_hsw *hsw, struct sst_hsw_stream *stream); - -/* Stream pointer positions */ -int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 *position); -int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw, - struct sst_hsw_stream *stream, u32 *position); -u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream); -u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw, - struct sst_hsw_stream *stream); - -/* HW port config */ -int sst_hsw_device_set_config(struct sst_hsw *hsw, - enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk, - enum sst_hsw_device_mode mode, u32 clock_divider); - -/* DX Config */ -int sst_hsw_dx_set_state(struct sst_hsw *hsw, - enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx); - -/* init */ -int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata); -void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata); -struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw); - -/* fw module function */ -void sst_hsw_init_module_state(struct sst_hsw *hsw); -bool sst_hsw_is_module_loaded(struct sst_hsw *hsw, u32 module_id); -bool sst_hsw_is_module_active(struct sst_hsw *hsw, u32 module_id); -void sst_hsw_set_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); -void sst_hsw_set_module_disabled_rtd3(struct sst_hsw *hsw, u32 module_id); -bool sst_hsw_is_module_enabled_rtd3(struct sst_hsw *hsw, u32 module_id); -void sst_hsw_reset_param_buf(struct sst_hsw *hsw); -int sst_hsw_store_param_line(struct sst_hsw *hsw, u8 *buf); -int sst_hsw_load_param_line(struct sst_hsw *hsw, u8 *buf); -int sst_hsw_launch_param_buf(struct sst_hsw *hsw); - -int sst_hsw_module_load(struct sst_hsw *hsw, - u32 module_id, u32 instance_id, char *name); -int sst_hsw_module_enable(struct sst_hsw *hsw, - u32 module_id, u32 instance_id); -int sst_hsw_module_disable(struct sst_hsw *hsw, - u32 module_id, u32 instance_id); -int sst_hsw_module_set_param(struct sst_hsw *hsw, - u32 module_id, u32 instance_id, u32 parameter_id, - u32 param_size, char *param); - -/* runtime module management */ -struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw, - int mod_id, int offset); -void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime); - -/* PM */ -int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw); -int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw); -int sst_hsw_dsp_load(struct sst_hsw *hsw); -int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw); - -#endif diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c deleted file mode 100644 index b8d86c74c53d..000000000000 --- a/sound/soc/intel/haswell/sst-haswell-pcm.c +++ /dev/null @@ -1,1369 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel SST Haswell/Broadwell PCM Support - * - * Copyright (C) 2013, Intel Corporation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../haswell/sst-haswell-ipc.h" -#include "../common/sst-dsp-priv.h" -#include "../common/sst-dsp.h" - -#define HSW_PCM_COUNT 6 -#define HSW_VOLUME_MAX 0x7FFFFFFF /* 0dB */ - -#define SST_OLD_POSITION(d, r, o) ((d) + \ - frames_to_bytes(r, o)) -#define SST_SAMPLES(r, x) (bytes_to_samples(r, \ - frames_to_bytes(r, (x)))) - -/* simple volume table */ -static const u32 volume_map[] = { - HSW_VOLUME_MAX >> 30, - HSW_VOLUME_MAX >> 29, - HSW_VOLUME_MAX >> 28, - HSW_VOLUME_MAX >> 27, - HSW_VOLUME_MAX >> 26, - HSW_VOLUME_MAX >> 25, - HSW_VOLUME_MAX >> 24, - HSW_VOLUME_MAX >> 23, - HSW_VOLUME_MAX >> 22, - HSW_VOLUME_MAX >> 21, - HSW_VOLUME_MAX >> 20, - HSW_VOLUME_MAX >> 19, - HSW_VOLUME_MAX >> 18, - HSW_VOLUME_MAX >> 17, - HSW_VOLUME_MAX >> 16, - HSW_VOLUME_MAX >> 15, - HSW_VOLUME_MAX >> 14, - HSW_VOLUME_MAX >> 13, - HSW_VOLUME_MAX >> 12, - HSW_VOLUME_MAX >> 11, - HSW_VOLUME_MAX >> 10, - HSW_VOLUME_MAX >> 9, - HSW_VOLUME_MAX >> 8, - HSW_VOLUME_MAX >> 7, - HSW_VOLUME_MAX >> 6, - HSW_VOLUME_MAX >> 5, - HSW_VOLUME_MAX >> 4, - HSW_VOLUME_MAX >> 3, - HSW_VOLUME_MAX >> 2, - HSW_VOLUME_MAX >> 1, - HSW_VOLUME_MAX >> 0, -}; - -#define HSW_PCM_PERIODS_MAX 64 -#define HSW_PCM_PERIODS_MIN 2 - -#define HSW_PCM_DAI_ID_SYSTEM 0 -#define HSW_PCM_DAI_ID_OFFLOAD0 1 -#define HSW_PCM_DAI_ID_OFFLOAD1 2 -#define HSW_PCM_DAI_ID_LOOPBACK 3 - - -static const struct snd_pcm_hardware hsw_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME | - SNDRV_PCM_INFO_NO_PERIOD_WAKEUP | - SNDRV_PCM_INFO_DRAIN_TRIGGER, - .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | - SNDRV_PCM_FMTBIT_S32_LE, - .period_bytes_min = PAGE_SIZE, - .period_bytes_max = (HSW_PCM_PERIODS_MAX / HSW_PCM_PERIODS_MIN) * PAGE_SIZE, - .periods_min = HSW_PCM_PERIODS_MIN, - .periods_max = HSW_PCM_PERIODS_MAX, - .buffer_bytes_max = HSW_PCM_PERIODS_MAX * PAGE_SIZE, -}; - -struct hsw_pcm_module_map { - int dai_id; - int stream; - enum sst_hsw_module_id mod_id; -}; - -/* private data for each PCM DSP stream */ -struct hsw_pcm_data { - int dai_id; - struct sst_hsw_stream *stream; - struct sst_module_runtime *runtime; - struct sst_module_runtime_context context; - struct snd_pcm *hsw_pcm; - u32 volume[2]; - struct snd_pcm_substream *substream; - struct snd_compr_stream *cstream; - unsigned int wpos; - struct mutex mutex; - bool allocated; - int persistent_offset; -}; - -enum hsw_pm_state { - HSW_PM_STATE_D0 = 0, - HSW_PM_STATE_RTD3 = 1, - HSW_PM_STATE_D3 = 2, -}; - -/* private data for the driver */ -struct hsw_priv_data { - /* runtime DSP */ - struct sst_hsw *hsw; - struct device *dev; - enum hsw_pm_state pm_state; - struct snd_soc_card *soc_card; - struct sst_module_runtime *runtime_waves; /* sound effect module */ - - /* page tables */ - struct snd_dma_buffer dmab[HSW_PCM_COUNT][2]; - - /* DAI data */ - struct hsw_pcm_data pcm[HSW_PCM_COUNT][2]; -}; - - -/* static mappings between PCMs and modules - may be dynamic in future */ -static struct hsw_pcm_module_map mod_map[] = { - {HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM}, - {HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM}, - {HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM}, - {HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE}, - {HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE}, -}; - -static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data); - -static inline u32 hsw_mixer_to_ipc(unsigned int value) -{ - if (value >= ARRAY_SIZE(volume_map)) - return volume_map[0]; - else - return volume_map[value]; -} - -static inline unsigned int hsw_ipc_to_mixer(u32 value) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(volume_map); i++) { - if (volume_map[i] >= value) - return i; - } - - return i - 1; -} - -static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct hsw_priv_data *pdata = - snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw *hsw = pdata->hsw; - u32 volume; - int dai, stream; - - dai = mod_map[mc->reg].dai_id; - stream = mod_map[mc->reg].stream; - pcm_data = &pdata->pcm[dai][stream]; - - mutex_lock(&pcm_data->mutex); - pm_runtime_get_sync(pdata->dev); - - if (!pcm_data->stream) { - pcm_data->volume[0] = - hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - pcm_data->volume[1] = - hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - mutex_unlock(&pcm_data->mutex); - return 0; - } - - if (ucontrol->value.integer.value[0] == - ucontrol->value.integer.value[1]) { - volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - /* apply volume value to all channels */ - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume); - } else { - volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume); - volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume); - } - - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - mutex_unlock(&pcm_data->mutex); - return 0; -} - -static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct hsw_priv_data *pdata = - snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw *hsw = pdata->hsw; - u32 volume; - int dai, stream; - - dai = mod_map[mc->reg].dai_id; - stream = mod_map[mc->reg].stream; - pcm_data = &pdata->pcm[dai][stream]; - - mutex_lock(&pcm_data->mutex); - pm_runtime_get_sync(pdata->dev); - - if (!pcm_data->stream) { - ucontrol->value.integer.value[0] = - hsw_ipc_to_mixer(pcm_data->volume[0]); - ucontrol->value.integer.value[1] = - hsw_ipc_to_mixer(pcm_data->volume[1]); - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - mutex_unlock(&pcm_data->mutex); - return 0; - } - - sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 0, &volume); - ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); - sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume); - ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); - - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - mutex_unlock(&pcm_data->mutex); - - return 0; -} - -static int hsw_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - u32 volume; - - pm_runtime_get_sync(pdata->dev); - - if (ucontrol->value.integer.value[0] == - ucontrol->value.integer.value[1]) { - - volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume); - - } else { - volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]); - sst_hsw_mixer_set_volume(hsw, 0, 0, volume); - - volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[1]); - sst_hsw_mixer_set_volume(hsw, 0, 1, volume); - } - - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - return 0; -} - -static int hsw_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - unsigned int volume = 0; - - pm_runtime_get_sync(pdata->dev); - sst_hsw_mixer_get_volume(hsw, 0, 0, &volume); - ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume); - - sst_hsw_mixer_get_volume(hsw, 0, 1, &volume); - ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume); - - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - return 0; -} - -static int hsw_waves_switch_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; - - ucontrol->value.integer.value[0] = - (sst_hsw_is_module_active(hsw, id) || - sst_hsw_is_module_enabled_rtd3(hsw, id)); - return 0; -} - -static int hsw_waves_switch_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - int ret = 0; - enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; - bool switch_on = (bool)ucontrol->value.integer.value[0]; - - /* if module is in RAM on the DSP, apply user settings to module through - * ipc. If module is not in RAM on the DSP, store user setting for - * track */ - if (sst_hsw_is_module_loaded(hsw, id)) { - if (switch_on == sst_hsw_is_module_active(hsw, id)) - return 0; - - if (switch_on) - ret = sst_hsw_module_enable(hsw, id, 0); - else - ret = sst_hsw_module_disable(hsw, id, 0); - } else { - if (switch_on == sst_hsw_is_module_enabled_rtd3(hsw, id)) - return 0; - - if (switch_on) - sst_hsw_set_module_enabled_rtd3(hsw, id); - else - sst_hsw_set_module_disabled_rtd3(hsw, id); - } - - return ret; -} - -static int hsw_waves_param_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - - /* return a matching line from param buffer */ - return sst_hsw_load_param_line(hsw, ucontrol->value.bytes.data); -} - -static int hsw_waves_param_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - int ret; - enum sst_hsw_module_id id = SST_HSW_MODULE_WAVES; - int param_id = ucontrol->value.bytes.data[0]; - int param_size = WAVES_PARAM_COUNT; - - /* clear param buffer and reset buffer index */ - if (param_id == 0xFF) { - sst_hsw_reset_param_buf(hsw); - return 0; - } - - /* store params into buffer */ - ret = sst_hsw_store_param_line(hsw, ucontrol->value.bytes.data); - if (ret < 0) - return ret; - - if (sst_hsw_is_module_active(hsw, id)) - ret = sst_hsw_module_set_param(hsw, id, 0, param_id, - param_size, ucontrol->value.bytes.data); - return ret; -} - -/* TLV used by both global and stream volumes */ -static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1); - -/* System Pin has no volume control */ -static const struct snd_kcontrol_new hsw_volume_controls[] = { - /* Global DSP volume */ - SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8, - ARRAY_SIZE(volume_map) - 1, 0, - hsw_volume_get, hsw_volume_put, hsw_vol_tlv), - /* Offload 0 volume */ - SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8, - ARRAY_SIZE(volume_map) - 1, 0, - hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), - /* Offload 1 volume */ - SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8, - ARRAY_SIZE(volume_map) - 1, 0, - hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), - /* Mic Capture volume */ - SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8, - ARRAY_SIZE(volume_map) - 1, 0, - hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv), - /* enable/disable module waves */ - SOC_SINGLE_BOOL_EXT("Waves Switch", 0, - hsw_waves_switch_get, hsw_waves_switch_put), - /* set parameters to module waves */ - SND_SOC_BYTES_EXT("Waves Set Param", WAVES_PARAM_COUNT, - hsw_waves_param_get, hsw_waves_param_put), -}; - -/* Create DMA buffer page table for DSP */ -static int create_adsp_page_table(struct snd_pcm_substream *substream, - struct hsw_priv_data *pdata, struct snd_soc_pcm_runtime *rtd, - unsigned char *dma_area, size_t size, int pcm) -{ - struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream); - int i, pages, stream = substream->stream; - - pages = snd_sgbuf_aligned_pages(size); - - dev_dbg(rtd->dev, "generating page table for %p size 0x%zx pages %d\n", - dma_area, size, pages); - - for (i = 0; i < pages; i++) { - u32 idx = (((i << 2) + i)) >> 1; - u32 pfn = snd_sgbuf_get_addr(dmab, i * PAGE_SIZE) >> PAGE_SHIFT; - u32 *pg_table; - - dev_dbg(rtd->dev, "pfn i %i idx %d pfn %x\n", i, idx, pfn); - - pg_table = (u32 *)(pdata->dmab[pcm][stream].area + idx); - - if (i & 1) - *pg_table |= (pfn << 4); - else - *pg_table |= pfn; - } - - return 0; -} - -/* this may get called several times by oss emulation */ -static int hsw_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw *hsw = pdata->hsw; - struct sst_module *module_data; - struct sst_dsp *dsp; - struct snd_dma_buffer *dmab; - enum sst_hsw_stream_type stream_type; - enum sst_hsw_stream_path_id path_id; - u32 rate, bits, map, pages, module_id; - u8 channels; - int ret, dai; - - dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; - pcm_data = &pdata->pcm[dai][substream->stream]; - - /* check if we are being called a subsequent time */ - if (pcm_data->allocated) { - ret = sst_hsw_stream_reset(hsw, pcm_data->stream); - if (ret < 0) - dev_dbg(rtd->dev, "error: reset stream failed %d\n", - ret); - - ret = sst_hsw_stream_free(hsw, pcm_data->stream); - if (ret < 0) { - dev_dbg(rtd->dev, "error: free stream failed %d\n", - ret); - return ret; - } - pcm_data->allocated = false; - - pcm_data->stream = sst_hsw_stream_new(hsw, asoc_rtd_to_cpu(rtd, 0)->id, - hsw_notify_pointer, pcm_data); - if (pcm_data->stream == NULL) { - dev_err(rtd->dev, "error: failed to create stream\n"); - return -EINVAL; - } - } - - /* stream direction */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - path_id = SST_HSW_STREAM_PATH_SSP0_OUT; - else - path_id = SST_HSW_STREAM_PATH_SSP0_IN; - - /* DSP stream type depends on DAI ID */ - switch (asoc_rtd_to_cpu(rtd, 0)->id) { - case 0: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - stream_type = SST_HSW_STREAM_TYPE_SYSTEM; - module_id = SST_HSW_MODULE_PCM_SYSTEM; - } - else { - stream_type = SST_HSW_STREAM_TYPE_CAPTURE; - module_id = SST_HSW_MODULE_PCM_CAPTURE; - } - break; - case 1: - case 2: - stream_type = SST_HSW_STREAM_TYPE_RENDER; - module_id = SST_HSW_MODULE_PCM; - break; - case 3: - /* path ID needs to be OUT for loopback */ - stream_type = SST_HSW_STREAM_TYPE_LOOPBACK; - path_id = SST_HSW_STREAM_PATH_SSP0_OUT; - module_id = SST_HSW_MODULE_PCM_REFERENCE; - break; - default: - dev_err(rtd->dev, "error: invalid DAI ID %d\n", - asoc_rtd_to_cpu(rtd, 0)->id); - return -EINVAL; - } - - ret = sst_hsw_stream_format(hsw, pcm_data->stream, - path_id, stream_type, SST_HSW_STREAM_FORMAT_PCM_FORMAT); - if (ret < 0) { - dev_err(rtd->dev, "error: failed to set format %d\n", ret); - return ret; - } - - rate = params_rate(params); - ret = sst_hsw_stream_set_rate(hsw, pcm_data->stream, rate); - if (ret < 0) { - dev_err(rtd->dev, "error: could not set rate %d\n", rate); - return ret; - } - - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - bits = SST_HSW_DEPTH_16BIT; - sst_hsw_stream_set_valid(hsw, pcm_data->stream, 16); - break; - case SNDRV_PCM_FORMAT_S24_LE: - bits = SST_HSW_DEPTH_32BIT; - sst_hsw_stream_set_valid(hsw, pcm_data->stream, 24); - break; - case SNDRV_PCM_FORMAT_S8: - bits = SST_HSW_DEPTH_8BIT; - sst_hsw_stream_set_valid(hsw, pcm_data->stream, 8); - break; - case SNDRV_PCM_FORMAT_S32_LE: - bits = SST_HSW_DEPTH_32BIT; - sst_hsw_stream_set_valid(hsw, pcm_data->stream, 32); - break; - default: - dev_err(rtd->dev, "error: invalid format %d\n", - params_format(params)); - return -EINVAL; - } - - ret = sst_hsw_stream_set_bits(hsw, pcm_data->stream, bits); - if (ret < 0) { - dev_err(rtd->dev, "error: could not set bits %d\n", bits); - return ret; - } - - channels = params_channels(params); - map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO); - sst_hsw_stream_set_map_config(hsw, pcm_data->stream, - map, SST_HSW_CHANNEL_CONFIG_STEREO); - - ret = sst_hsw_stream_set_channels(hsw, pcm_data->stream, channels); - if (ret < 0) { - dev_err(rtd->dev, "error: could not set channels %d\n", - channels); - return ret; - } - - dmab = snd_pcm_get_dma_buf(substream); - - ret = create_adsp_page_table(substream, pdata, rtd, runtime->dma_area, - runtime->dma_bytes, asoc_rtd_to_cpu(rtd, 0)->id); - if (ret < 0) - return ret; - - sst_hsw_stream_set_style(hsw, pcm_data->stream, - SST_HSW_INTERLEAVING_PER_CHANNEL); - - if (runtime->dma_bytes % PAGE_SIZE) - pages = (runtime->dma_bytes / PAGE_SIZE) + 1; - else - pages = runtime->dma_bytes / PAGE_SIZE; - - ret = sst_hsw_stream_buffer(hsw, pcm_data->stream, - pdata->dmab[asoc_rtd_to_cpu(rtd, 0)->id][substream->stream].addr, - pages, runtime->dma_bytes, 0, - snd_sgbuf_get_addr(dmab, 0) >> PAGE_SHIFT); - if (ret < 0) { - dev_err(rtd->dev, "error: failed to set DMA buffer %d\n", ret); - return ret; - } - - dsp = sst_hsw_get_dsp(hsw); - - module_data = sst_module_get_from_id(dsp, module_id); - if (module_data == NULL) { - dev_err(rtd->dev, "error: failed to get module config\n"); - return -EINVAL; - } - - sst_hsw_stream_set_module_info(hsw, pcm_data->stream, - pcm_data->runtime); - - ret = sst_hsw_stream_commit(hsw, pcm_data->stream); - if (ret < 0) { - dev_err(rtd->dev, "error: failed to commit stream %d\n", ret); - return ret; - } - - if (!pcm_data->allocated) { - /* Set previous saved volume */ - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, - 0, pcm_data->volume[0]); - sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, - 1, pcm_data->volume[1]); - pcm_data->allocated = true; - } - - ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1); - if (ret < 0) - dev_err(rtd->dev, "error: failed to pause %d\n", ret); - - return 0; -} - -static int hsw_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int cmd) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw_stream *sst_stream; - struct sst_hsw *hsw = pdata->hsw; - struct snd_pcm_runtime *runtime = substream->runtime; - snd_pcm_uframes_t pos; - int dai; - - dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; - pcm_data = &pdata->pcm[dai][substream->stream]; - sst_stream = pcm_data->stream; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - sst_hsw_stream_set_silence_start(hsw, sst_stream, false); - sst_hsw_stream_resume(hsw, pcm_data->stream, 0); - break; - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - sst_hsw_stream_set_silence_start(hsw, sst_stream, false); - sst_hsw_stream_pause(hsw, pcm_data->stream, 0); - break; - case SNDRV_PCM_TRIGGER_DRAIN: - pos = runtime->control->appl_ptr % runtime->buffer_size; - sst_hsw_stream_set_old_position(hsw, pcm_data->stream, pos); - sst_hsw_stream_set_silence_start(hsw, sst_stream, true); - break; - default: - break; - } - - return 0; -} - -static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data) -{ - struct hsw_pcm_data *pcm_data = data; - struct snd_pcm_substream *substream = pcm_data->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_hsw *hsw = pdata->hsw; - u32 pos; - snd_pcm_uframes_t position = bytes_to_frames(runtime, - sst_hsw_get_dsp_position(hsw, pcm_data->stream)); - unsigned char *dma_area = runtime->dma_area; - snd_pcm_uframes_t dma_frames = - bytes_to_frames(runtime, runtime->dma_bytes); - snd_pcm_uframes_t old_position; - ssize_t samples; - - pos = frames_to_bytes(runtime, - (runtime->control->appl_ptr % runtime->buffer_size)); - - dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); - - /* SST fw don't know where to stop dma - * So, SST driver need to clean the data which has been consumed - */ - if (dma_area == NULL || dma_frames <= 0 - || (substream->stream == SNDRV_PCM_STREAM_CAPTURE) - || !sst_hsw_stream_get_silence_start(hsw, stream)) { - snd_pcm_period_elapsed(substream); - return pos; - } - - old_position = sst_hsw_stream_get_old_position(hsw, stream); - if (position > old_position) { - if (position < dma_frames) { - samples = SST_SAMPLES(runtime, position - old_position); - snd_pcm_format_set_silence(runtime->format, - SST_OLD_POSITION(dma_area, - runtime, old_position), - samples); - } else - dev_err(rtd->dev, "PCM: position is wrong\n"); - } else { - if (old_position < dma_frames) { - samples = SST_SAMPLES(runtime, - dma_frames - old_position); - snd_pcm_format_set_silence(runtime->format, - SST_OLD_POSITION(dma_area, - runtime, old_position), - samples); - } else - dev_err(rtd->dev, "PCM: dma_bytes is wrong\n"); - if (position < dma_frames) { - samples = SST_SAMPLES(runtime, position); - snd_pcm_format_set_silence(runtime->format, - dma_area, samples); - } else - dev_err(rtd->dev, "PCM: position is wrong\n"); - } - sst_hsw_stream_set_old_position(hsw, stream, position); - - /* let alsa know we have play a period */ - snd_pcm_period_elapsed(substream); - return pos; -} - -static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw *hsw = pdata->hsw; - snd_pcm_uframes_t offset; - uint64_t ppos; - u32 position; - int dai; - - dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; - pcm_data = &pdata->pcm[dai][substream->stream]; - position = sst_hsw_get_dsp_position(hsw, pcm_data->stream); - - offset = bytes_to_frames(runtime, position); - ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream); - - dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n", - position, ppos); - return offset; -} - -static int hsw_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw *hsw = pdata->hsw; - int dai; - - dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; - pcm_data = &pdata->pcm[dai][substream->stream]; - - mutex_lock(&pcm_data->mutex); - pm_runtime_get_sync(pdata->dev); - - pcm_data->substream = substream; - - snd_soc_set_runtime_hwparams(substream, &hsw_pcm_hardware); - - pcm_data->stream = sst_hsw_stream_new(hsw, asoc_rtd_to_cpu(rtd, 0)->id, - hsw_notify_pointer, pcm_data); - if (pcm_data->stream == NULL) { - dev_err(rtd->dev, "error: failed to create stream\n"); - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - mutex_unlock(&pcm_data->mutex); - return -EINVAL; - } - - mutex_unlock(&pcm_data->mutex); - return 0; -} - -static int hsw_pcm_close(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct hsw_pcm_data *pcm_data; - struct sst_hsw *hsw = pdata->hsw; - int ret, dai; - - dai = mod_map[asoc_rtd_to_cpu(rtd, 0)->id].dai_id; - pcm_data = &pdata->pcm[dai][substream->stream]; - - mutex_lock(&pcm_data->mutex); - ret = sst_hsw_stream_reset(hsw, pcm_data->stream); - if (ret < 0) { - dev_dbg(rtd->dev, "error: reset stream failed %d\n", ret); - goto out; - } - - ret = sst_hsw_stream_free(hsw, pcm_data->stream); - if (ret < 0) { - dev_dbg(rtd->dev, "error: free stream failed %d\n", ret); - goto out; - } - pcm_data->allocated = false; - pcm_data->stream = NULL; - -out: - pm_runtime_mark_last_busy(pdata->dev); - pm_runtime_put_autosuspend(pdata->dev); - mutex_unlock(&pcm_data->mutex); - return ret; -} - -static int hsw_pcm_create_modules(struct hsw_priv_data *pdata) -{ - struct sst_hsw *hsw = pdata->hsw; - struct hsw_pcm_data *pcm_data; - int i; - - for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - - /* create new runtime module, use same offset if recreated */ - pcm_data->runtime = sst_hsw_runtime_module_create(hsw, - mod_map[i].mod_id, pcm_data->persistent_offset); - if (pcm_data->runtime == NULL) - goto err; - pcm_data->persistent_offset = - pcm_data->runtime->persistent_offset; - } - - /* create runtime blocks for module waves */ - if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES)) { - pdata->runtime_waves = sst_hsw_runtime_module_create(hsw, - SST_HSW_MODULE_WAVES, 0); - if (pdata->runtime_waves == NULL) - goto err; - } - - return 0; - -err: - for (--i; i >= 0; i--) { - pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - sst_hsw_runtime_module_free(pcm_data->runtime); - } - - return -ENODEV; -} - -static void hsw_pcm_free_modules(struct hsw_priv_data *pdata) -{ - struct sst_hsw *hsw = pdata->hsw; - struct hsw_pcm_data *pcm_data; - int i; - - for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - if (pcm_data->runtime){ - sst_hsw_runtime_module_free(pcm_data->runtime); - pcm_data->runtime = NULL; - } - } - if (sst_hsw_is_module_loaded(hsw, SST_HSW_MODULE_WAVES) && - pdata->runtime_waves) { - sst_hsw_runtime_module_free(pdata->runtime_waves); - pdata->runtime_waves = NULL; - } -} - -static int hsw_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_pcm *pcm = rtd->pcm; - struct sst_pdata *pdata = dev_get_platdata(component->dev); - struct hsw_priv_data *priv_data = dev_get_drvdata(component->dev); - struct device *dev = pdata->dma_dev; - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || - pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - snd_pcm_set_managed_buffer_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, - dev, - hsw_pcm_hardware.buffer_bytes_max, - hsw_pcm_hardware.buffer_bytes_max); - } - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) - priv_data->pcm[asoc_rtd_to_cpu(rtd, 0)->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm; - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) - priv_data->pcm[asoc_rtd_to_cpu(rtd, 0)->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm; - - return 0; -} - -#define HSW_FORMATS \ - (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) - -static struct snd_soc_dai_driver hsw_dais[] = { - { - .name = "System Pin", - .id = HSW_PCM_DAI_ID_SYSTEM, - .playback = { - .stream_name = "System Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "Analog Capture", - .channels_min = 2, - .channels_max = 4, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, - }, - }, - { - /* PCM */ - .name = "Offload0 Pin", - .id = HSW_PCM_DAI_ID_OFFLOAD0, - .playback = { - .stream_name = "Offload0 Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, - .formats = HSW_FORMATS, - }, - }, - { - /* PCM */ - .name = "Offload1 Pin", - .id = HSW_PCM_DAI_ID_OFFLOAD1, - .playback = { - .stream_name = "Offload1 Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_192000, - .formats = HSW_FORMATS, - }, - }, - { - .name = "Loopback Pin", - .id = HSW_PCM_DAI_ID_LOOPBACK, - .capture = { - .stream_name = "Loopback Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE, - }, - }, -}; - -static const struct snd_soc_dapm_widget widgets[] = { - - /* Backend DAIs */ - SND_SOC_DAPM_AIF_IN("SSP0 CODEC IN", NULL, 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("SSP0 CODEC OUT", NULL, 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_IN("SSP1 BT IN", NULL, 0, SND_SOC_NOPM, 0, 0), - SND_SOC_DAPM_AIF_OUT("SSP1 BT OUT", NULL, 0, SND_SOC_NOPM, 0, 0), - - /* Global Playback Mixer */ - SND_SOC_DAPM_MIXER("Playback VMixer", SND_SOC_NOPM, 0, 0, NULL, 0), -}; - -static const struct snd_soc_dapm_route graph[] = { - - /* Playback Mixer */ - {"Playback VMixer", NULL, "System Playback"}, - {"Playback VMixer", NULL, "Offload0 Playback"}, - {"Playback VMixer", NULL, "Offload1 Playback"}, - - {"SSP0 CODEC OUT", NULL, "Playback VMixer"}, - - {"Analog Capture", NULL, "SSP0 CODEC IN"}, -}; - -static int hsw_pcm_probe(struct snd_soc_component *component) -{ - struct hsw_priv_data *priv_data = snd_soc_component_get_drvdata(component); - struct sst_pdata *pdata = dev_get_platdata(component->dev); - struct device *dma_dev, *dev; - int i, ret = 0; - - if (!pdata) - return -ENODEV; - - dev = component->dev; - dma_dev = pdata->dma_dev; - - priv_data->hsw = pdata->dsp; - priv_data->dev = dev; - priv_data->pm_state = HSW_PM_STATE_D0; - priv_data->soc_card = component->card; - - /* allocate DSP buffer page tables */ - for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { - - /* playback */ - if (hsw_dais[i].playback.channels_min) { - mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex); - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, - PAGE_SIZE, &priv_data->dmab[i][0]); - if (ret < 0) - goto err; - } - - /* capture */ - if (hsw_dais[i].capture.channels_min) { - mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex); - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev, - PAGE_SIZE, &priv_data->dmab[i][1]); - if (ret < 0) - goto err; - } - } - - /* allocate runtime modules */ - ret = hsw_pcm_create_modules(priv_data); - if (ret < 0) - goto err; - - /* enable runtime PM with auto suspend */ - pm_runtime_set_autosuspend_delay(dev, SST_RUNTIME_SUSPEND_DELAY); - pm_runtime_use_autosuspend(dev); - pm_runtime_enable(dev); - pm_runtime_idle(dev); - - return 0; - -err: - for (--i; i >= 0; i--) { - if (hsw_dais[i].playback.channels_min) - snd_dma_free_pages(&priv_data->dmab[i][0]); - if (hsw_dais[i].capture.channels_min) - snd_dma_free_pages(&priv_data->dmab[i][1]); - } - return ret; -} - -static void hsw_pcm_remove(struct snd_soc_component *component) -{ - struct hsw_priv_data *priv_data = - snd_soc_component_get_drvdata(component); - int i; - - pm_runtime_disable(component->dev); - hsw_pcm_free_modules(priv_data); - - for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) { - if (hsw_dais[i].playback.channels_min) - snd_dma_free_pages(&priv_data->dmab[i][0]); - if (hsw_dais[i].capture.channels_min) - snd_dma_free_pages(&priv_data->dmab[i][1]); - } -} - -static const struct snd_soc_component_driver hsw_dai_component = { - .name = DRV_NAME, - .probe = hsw_pcm_probe, - .remove = hsw_pcm_remove, - .open = hsw_pcm_open, - .close = hsw_pcm_close, - .hw_params = hsw_pcm_hw_params, - .trigger = hsw_pcm_trigger, - .pointer = hsw_pcm_pointer, - .pcm_construct = hsw_pcm_new, - .controls = hsw_volume_controls, - .num_controls = ARRAY_SIZE(hsw_volume_controls), - .dapm_widgets = widgets, - .num_dapm_widgets = ARRAY_SIZE(widgets), - .dapm_routes = graph, - .num_dapm_routes = ARRAY_SIZE(graph), -}; - -static int hsw_pcm_dev_probe(struct platform_device *pdev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); - struct hsw_priv_data *priv_data; - int ret; - - if (!sst_pdata) - return -EINVAL; - - priv_data = devm_kzalloc(&pdev->dev, sizeof(*priv_data), GFP_KERNEL); - if (!priv_data) - return -ENOMEM; - - ret = sst_hsw_dsp_init(&pdev->dev, sst_pdata); - if (ret < 0) - return -ENODEV; - - priv_data->hsw = sst_pdata->dsp; - platform_set_drvdata(pdev, priv_data); - - ret = devm_snd_soc_register_component(&pdev->dev, &hsw_dai_component, - hsw_dais, ARRAY_SIZE(hsw_dais)); - if (ret < 0) - goto err_plat; - - return 0; - -err_plat: - sst_hsw_dsp_free(&pdev->dev, sst_pdata); - return 0; -} - -static int hsw_pcm_dev_remove(struct platform_device *pdev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); - - sst_hsw_dsp_free(&pdev->dev, sst_pdata); - - return 0; -} - -#ifdef CONFIG_PM - -static int hsw_pcm_runtime_idle(struct device *dev) -{ - return 0; -} - -static int hsw_pcm_suspend(struct device *dev) -{ - struct hsw_priv_data *pdata = dev_get_drvdata(dev); - struct sst_hsw *hsw = pdata->hsw; - - /* enter D3 state and stall */ - sst_hsw_dsp_runtime_suspend(hsw); - /* free all runtime modules */ - hsw_pcm_free_modules(pdata); - /* put the DSP to sleep, fw unloaded after runtime modules freed */ - sst_hsw_dsp_runtime_sleep(hsw); - return 0; -} - -static int hsw_pcm_runtime_suspend(struct device *dev) -{ - struct hsw_priv_data *pdata = dev_get_drvdata(dev); - struct sst_hsw *hsw = pdata->hsw; - int ret; - - if (pdata->pm_state >= HSW_PM_STATE_RTD3) - return 0; - - /* fw modules will be unloaded on RTD3, set flag to track */ - if (sst_hsw_is_module_active(hsw, SST_HSW_MODULE_WAVES)) { - ret = sst_hsw_module_disable(hsw, SST_HSW_MODULE_WAVES, 0); - if (ret < 0) - return ret; - sst_hsw_set_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES); - } - hsw_pcm_suspend(dev); - pdata->pm_state = HSW_PM_STATE_RTD3; - - return 0; -} - -static int hsw_pcm_runtime_resume(struct device *dev) -{ - struct hsw_priv_data *pdata = dev_get_drvdata(dev); - struct sst_hsw *hsw = pdata->hsw; - int ret; - - if (pdata->pm_state != HSW_PM_STATE_RTD3) - return 0; - - ret = sst_hsw_dsp_load(hsw); - if (ret < 0) { - dev_err(dev, "failed to reload %d\n", ret); - return ret; - } - - ret = hsw_pcm_create_modules(pdata); - if (ret < 0) { - dev_err(dev, "failed to create modules %d\n", ret); - return ret; - } - - ret = sst_hsw_dsp_runtime_resume(hsw); - if (ret < 0) - return ret; - else if (ret == 1) /* no action required */ - return 0; - - /* check flag when resume */ - if (sst_hsw_is_module_enabled_rtd3(hsw, SST_HSW_MODULE_WAVES)) { - ret = sst_hsw_module_enable(hsw, SST_HSW_MODULE_WAVES, 0); - if (ret < 0) - return ret; - /* put parameters from buffer to dsp */ - ret = sst_hsw_launch_param_buf(hsw); - if (ret < 0) - return ret; - /* unset flag */ - sst_hsw_set_module_disabled_rtd3(hsw, SST_HSW_MODULE_WAVES); - } - - pdata->pm_state = HSW_PM_STATE_D0; - return ret; -} - -#else -#define hsw_pcm_runtime_idle NULL -#define hsw_pcm_runtime_suspend NULL -#define hsw_pcm_runtime_resume NULL -#endif - -#ifdef CONFIG_PM - -static void hsw_pcm_complete(struct device *dev) -{ - struct hsw_priv_data *pdata = dev_get_drvdata(dev); - struct sst_hsw *hsw = pdata->hsw; - struct hsw_pcm_data *pcm_data; - int i, err; - - if (pdata->pm_state != HSW_PM_STATE_D3) - return; - - err = sst_hsw_dsp_load(hsw); - if (err < 0) { - dev_err(dev, "failed to reload %d\n", err); - return; - } - - err = hsw_pcm_create_modules(pdata); - if (err < 0) { - dev_err(dev, "failed to create modules %d\n", err); - return; - } - - for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - - if (!pcm_data->substream) - continue; - - err = sst_module_runtime_restore(pcm_data->runtime, - &pcm_data->context); - if (err < 0) - dev_err(dev, "failed to restore context for PCM %d\n", i); - } - - snd_soc_resume(pdata->soc_card->dev); - - err = sst_hsw_dsp_runtime_resume(hsw); - if (err < 0) - return; - else if (err == 1) /* no action required */ - return; - - pdata->pm_state = HSW_PM_STATE_D0; - return; -} - -static int hsw_pcm_prepare(struct device *dev) -{ - struct hsw_priv_data *pdata = dev_get_drvdata(dev); - struct hsw_pcm_data *pcm_data; - int i, err; - - if (pdata->pm_state == HSW_PM_STATE_D3) - return 0; - else if (pdata->pm_state == HSW_PM_STATE_D0) { - /* suspend all active streams */ - for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - - if (!pcm_data->substream) - continue; - dev_dbg(dev, "suspending pcm %d\n", i); - snd_pcm_suspend_all(pcm_data->hsw_pcm); - - /* We need to wait until the DSP FW stops the streams */ - msleep(2); - } - - /* preserve persistent memory */ - for (i = 0; i < ARRAY_SIZE(mod_map); i++) { - pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream]; - - if (!pcm_data->substream) - continue; - - dev_dbg(dev, "saving context pcm %d\n", i); - err = sst_module_runtime_save(pcm_data->runtime, - &pcm_data->context); - if (err < 0) - dev_err(dev, "failed to save context for PCM %d\n", i); - } - hsw_pcm_suspend(dev); - } - - snd_soc_suspend(pdata->soc_card->dev); - snd_soc_poweroff(pdata->soc_card->dev); - - pdata->pm_state = HSW_PM_STATE_D3; - - return 0; -} - -#else -#define hsw_pcm_prepare NULL -#define hsw_pcm_complete NULL -#endif - -static const struct dev_pm_ops hsw_pcm_pm = { - .runtime_idle = hsw_pcm_runtime_idle, - .runtime_suspend = hsw_pcm_runtime_suspend, - .runtime_resume = hsw_pcm_runtime_resume, - .prepare = hsw_pcm_prepare, - .complete = hsw_pcm_complete, -}; - -static struct platform_driver hsw_pcm_driver = { - .driver = { - .name = "haswell-pcm-audio", - .pm = &hsw_pcm_pm, - }, - - .probe = hsw_pcm_dev_probe, - .remove = hsw_pcm_dev_remove, -}; -module_platform_driver(hsw_pcm_driver); - -MODULE_AUTHOR("Liam Girdwood, Xingchao Wang"); -MODULE_DESCRIPTION("Haswell/Lynxpoint + Broadwell/Wildcatpoint PCM"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:haswell-pcm-audio"); -- cgit From 5f3941b63c25d8123ebe4406a714c603525b1b90 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:48:56 +0200 Subject: ASoC: Intel: Remove max98090 support for baytrail solution byt-max98090 is deprecated in favor of cht-bsw-max98090 used by sound/soc/intel/atom and SOF solutions both. Remove redundant machine board and all related code. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-3-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 11 -- sound/soc/intel/boards/Makefile | 2 - sound/soc/intel/boards/byt-max98090.c | 182 ---------------------- sound/soc/intel/common/soc-acpi-intel-byt-match.c | 5 - 4 files changed, 200 deletions(-) delete mode 100644 sound/soc/intel/boards/byt-max98090.c (limited to 'sound') diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index 6afdd9ac4478..a8da892441be 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -87,17 +87,6 @@ endif ## SND_SOC_INTEL_CATPT || SND_SOC_SOF_BROADWELL if SND_SOC_INTEL_BAYTRAIL -config SND_SOC_INTEL_BYT_MAX98090_MACH - tristate "Baytrail with MAX98090 codec" - depends on I2C - depends on X86_INTEL_LPSS || COMPILE_TEST - select SND_SOC_MAX98090 - help - This adds audio driver for Intel Baytrail platform based boards - with the MAX98090 audio codec. This driver is deprecated, use - SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH instead for better - functionality. - config SND_SOC_INTEL_BYT_RT5640_MACH tristate "Baytrail with RT5640 codec" depends on I2C diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index de7cc9b86354..b2e5709a097b 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-sst-haswell-objs := haswell.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o -snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o snd-soc-sst-bdw-rt5650-mach-objs := bdw-rt5650.o snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o @@ -44,7 +43,6 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o -obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_SOF_PCM512x_MACH) += snd-soc-sst-sof-pcm512x.o diff --git a/sound/soc/intel/boards/byt-max98090.c b/sound/soc/intel/boards/byt-max98090.c deleted file mode 100644 index f5097da28828..000000000000 --- a/sound/soc/intel/boards/byt-max98090.c +++ /dev/null @@ -1,182 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Baytrail SST MAX98090 machine driver - * Copyright (c) 2014, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../codecs/max98090.h" - -struct byt_max98090_private { - struct snd_soc_jack jack; -}; - -static const struct snd_soc_dapm_widget byt_max98090_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Int Mic", NULL), - SND_SOC_DAPM_SPK("Ext Spk", NULL), -}; - -static const struct snd_soc_dapm_route byt_max98090_audio_map[] = { - {"IN34", NULL, "Headset Mic"}, - {"Headset Mic", NULL, "MICBIAS"}, - {"DMICL", NULL, "Int Mic"}, - {"Headphone", NULL, "HPL"}, - {"Headphone", NULL, "HPR"}, - {"Ext Spk", NULL, "SPKL"}, - {"Ext Spk", NULL, "SPKR"}, -}; - -static const struct snd_kcontrol_new byt_max98090_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Int Mic"), - SOC_DAPM_PIN_SWITCH("Ext Spk"), -}; - -static struct snd_soc_jack_pin hs_jack_pins[] = { - { - .pin = "Headphone", - .mask = SND_JACK_HEADPHONE, - }, - { - .pin = "Headset Mic", - .mask = SND_JACK_MICROPHONE, - }, -}; - -static struct snd_soc_jack_gpio hs_jack_gpios[] = { - { - .name = "hp", - .report = SND_JACK_HEADPHONE | SND_JACK_LINEOUT, - .debounce_time = 200, - }, - { - .name = "mic", - .invert = 1, - .report = SND_JACK_MICROPHONE, - .debounce_time = 200, - }, -}; - -static const struct acpi_gpio_params hp_gpios = { 0, 0, false }; -static const struct acpi_gpio_params mic_gpios = { 1, 0, false }; - -static const struct acpi_gpio_mapping acpi_byt_max98090_gpios[] = { - { "hp-gpios", &hp_gpios, 1 }, - { "mic-gpios", &mic_gpios, 1 }, - {}, -}; - -static int byt_max98090_init(struct snd_soc_pcm_runtime *runtime) -{ - int ret; - struct snd_soc_card *card = runtime->card; - struct byt_max98090_private *drv = snd_soc_card_get_drvdata(card); - struct snd_soc_jack *jack = &drv->jack; - - card->dapm.idle_bias_off = true; - - ret = snd_soc_dai_set_sysclk(asoc_rtd_to_codec(runtime, 0), - M98090_REG_SYSTEM_CLOCK, - 25000000, SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(card->dev, "Can't set codec clock %d\n", ret); - return ret; - } - - /* Enable jack detection */ - ret = snd_soc_card_jack_new(runtime->card, "Headset", - SND_JACK_LINEOUT | SND_JACK_HEADSET, jack, - hs_jack_pins, ARRAY_SIZE(hs_jack_pins)); - if (ret) - return ret; - - return snd_soc_jack_add_gpiods(card->dev->parent, jack, - ARRAY_SIZE(hs_jack_gpios), - hs_jack_gpios); -} - -SND_SOC_DAILINK_DEFS(baytrail, - DAILINK_COMP_ARRAY(COMP_CPU("baytrail-pcm-audio")), - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-193C9890:00", "HiFi")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("baytrail-pcm-audio"))); - -static struct snd_soc_dai_link byt_max98090_dais[] = { - { - .name = "Baytrail Audio", - .stream_name = "Audio", - .init = byt_max98090_init, - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - SND_SOC_DAILINK_REG(baytrail), - }, -}; - -static struct snd_soc_card byt_max98090_card = { - .name = "byt-max98090", - .owner = THIS_MODULE, - .dai_link = byt_max98090_dais, - .num_links = ARRAY_SIZE(byt_max98090_dais), - .dapm_widgets = byt_max98090_widgets, - .num_dapm_widgets = ARRAY_SIZE(byt_max98090_widgets), - .dapm_routes = byt_max98090_audio_map, - .num_dapm_routes = ARRAY_SIZE(byt_max98090_audio_map), - .controls = byt_max98090_controls, - .num_controls = ARRAY_SIZE(byt_max98090_controls), - .fully_routed = true, -}; - -static int byt_max98090_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct byt_max98090_private *priv; - int ret_val; - - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "allocation failed\n"); - return -ENOMEM; - } - - ret_val = devm_acpi_dev_add_driver_gpios(dev->parent, acpi_byt_max98090_gpios); - if (ret_val) - dev_dbg(dev, "Unable to add GPIO mapping table\n"); - - byt_max98090_card.dev = &pdev->dev; - snd_soc_card_set_drvdata(&byt_max98090_card, priv); - ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_max98090_card); - if (ret_val) { - dev_err(&pdev->dev, - "snd_soc_register_card failed %d\n", ret_val); - return ret_val; - } - - return 0; -} - -static struct platform_driver byt_max98090_driver = { - .probe = byt_max98090_probe, - .driver = { - .name = "byt-max98090", - .pm = &snd_soc_pm_ops, - }, -}; -module_platform_driver(byt_max98090_driver) - -MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver"); -MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:byt-max98090"); diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 1cc801ba92eb..4043a1ab44b3 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -126,11 +126,6 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[] = { .drv_name = "byt-rt5640", .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", }, - { - .id = "193C9890", - .drv_name = "byt-max98090", - .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", - }, {} }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_legacy_machines); -- cgit From 3056cb0082feccee9a0012440ee5e4ca6a6e80ac Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:48:57 +0200 Subject: ASoC: Intel: Remove rt5640 support for baytrail solution byt-rt5640 is deprecated in favor of bytcr_rt5640 used by sound/soc/intel/atom and SOF solutions both. Remove redundant machine board and all related code. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-4-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/boards/Kconfig | 14 -- sound/soc/intel/boards/Makefile | 2 - sound/soc/intel/boards/byt-rt5640.c | 224 ---------------------- sound/soc/intel/common/soc-acpi-intel-byt-match.c | 10 - 4 files changed, 250 deletions(-) delete mode 100644 sound/soc/intel/boards/byt-rt5640.c (limited to 'sound') diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig index a8da892441be..c10c37803c67 100644 --- a/sound/soc/intel/boards/Kconfig +++ b/sound/soc/intel/boards/Kconfig @@ -85,20 +85,6 @@ config SND_SOC_INTEL_BROADWELL_MACH If unsure select "N". endif ## SND_SOC_INTEL_CATPT || SND_SOC_SOF_BROADWELL -if SND_SOC_INTEL_BAYTRAIL - -config SND_SOC_INTEL_BYT_RT5640_MACH - tristate "Baytrail with RT5640 codec" - depends on I2C - depends on X86_INTEL_LPSS || COMPILE_TEST - select SND_SOC_RT5640 - help - This adds audio driver for Intel Baytrail platform based boards - with the RT5640 audio codec. This driver is deprecated, use - SND_SOC_INTEL_BYTCR_RT5640_MACH instead for better functionality. - -endif ## SND_SOC_INTEL_BAYTRAIL - if SND_SST_ATOM_HIFI2_PLATFORM || SND_SOC_SOF_BAYTRAIL config SND_SOC_INTEL_BYTCR_RT5640_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index b2e5709a097b..a58e4d22e9c8 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-sst-haswell-objs := haswell.o -snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-bdw-rt5650-mach-objs := bdw-rt5650.o snd-soc-sst-bdw-rt5677-mach-objs := bdw-rt5677.o snd-soc-sst-broadwell-objs := broadwell.o @@ -42,7 +41,6 @@ snd-soc-sof-sdw-objs += sof_sdw.o \ sof_sdw_dmic.o sof_sdw_hdmi.o hda_dsp_common.o obj-$(CONFIG_SND_SOC_INTEL_SOF_RT5682_MACH) += snd-soc-sof_rt5682.o obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o -obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o obj-$(CONFIG_SND_SOC_INTEL_BXT_DA7219_MAX98357A_COMMON) += snd-soc-sst-bxt-da7219_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o obj-$(CONFIG_SND_SOC_INTEL_SOF_PCM512x_MACH) += snd-soc-sst-sof-pcm512x.o diff --git a/sound/soc/intel/boards/byt-rt5640.c b/sound/soc/intel/boards/byt-rt5640.c deleted file mode 100644 index 8851949f38e2..000000000000 --- a/sound/soc/intel/boards/byt-rt5640.c +++ /dev/null @@ -1,224 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Baytrail SST RT5640 machine driver - * Copyright (c) 2014, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../../codecs/rt5640.h" - -#include "../common/sst-dsp.h" - -static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { - SND_SOC_DAPM_HP("Headphone", NULL), - SND_SOC_DAPM_MIC("Headset Mic", NULL), - SND_SOC_DAPM_MIC("Internal Mic", NULL), - SND_SOC_DAPM_SPK("Speaker", NULL), -}; - -static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = { - {"Headset Mic", NULL, "MICBIAS1"}, - {"IN2P", NULL, "Headset Mic"}, - {"Headphone", NULL, "HPOL"}, - {"Headphone", NULL, "HPOR"}, - {"Speaker", NULL, "SPOLP"}, - {"Speaker", NULL, "SPOLN"}, - {"Speaker", NULL, "SPORP"}, - {"Speaker", NULL, "SPORN"}, -}; - -static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic1_map[] = { - {"DMIC1", NULL, "Internal Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5640_intmic_dmic2_map[] = { - {"DMIC2", NULL, "Internal Mic"}, -}; - -static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = { - {"Internal Mic", NULL, "MICBIAS1"}, - {"IN1P", NULL, "Internal Mic"}, -}; - -enum { - BYT_RT5640_DMIC1_MAP, - BYT_RT5640_DMIC2_MAP, - BYT_RT5640_IN1_MAP, -}; - -#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff) -#define BYT_RT5640_DMIC_EN BIT(16) - -static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP | - BYT_RT5640_DMIC_EN; - -static const struct snd_kcontrol_new byt_rt5640_controls[] = { - SOC_DAPM_PIN_SWITCH("Headphone"), - SOC_DAPM_PIN_SWITCH("Headset Mic"), - SOC_DAPM_PIN_SWITCH("Internal Mic"), - SOC_DAPM_PIN_SWITCH("Speaker"), -}; - -static int byt_rt5640_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); - int ret; - - ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, - params_rate(params) * 256, - SND_SOC_CLOCK_IN); - if (ret < 0) { - dev_err(codec_dai->dev, "can't set codec clock %d\n", ret); - return ret; - } - ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1, - params_rate(params) * 64, - params_rate(params) * 256); - if (ret < 0) { - dev_err(codec_dai->dev, "can't set codec pll: %d\n", ret); - return ret; - } - return 0; -} - -static int byt_rt5640_quirk_cb(const struct dmi_system_id *id) -{ - byt_rt5640_quirk = (unsigned long)id->driver_data; - return 1; -} - -static const struct dmi_system_id byt_rt5640_quirk_table[] = { - { - .callback = byt_rt5640_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "T100TA"), - }, - .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP, - }, - { - .callback = byt_rt5640_quirk_cb, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "DellInc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"), - }, - .driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP | - BYT_RT5640_DMIC_EN), - }, - {} -}; - -static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime) -{ - int ret; - struct snd_soc_component *component = asoc_rtd_to_codec(runtime, 0)->component; - struct snd_soc_card *card = runtime->card; - const struct snd_soc_dapm_route *custom_map; - int num_routes; - - card->dapm.idle_bias_off = true; - - ret = snd_soc_add_card_controls(card, byt_rt5640_controls, - ARRAY_SIZE(byt_rt5640_controls)); - if (ret) { - dev_err(card->dev, "unable to add card controls\n"); - return ret; - } - - dmi_check_system(byt_rt5640_quirk_table); - switch (BYT_RT5640_MAP(byt_rt5640_quirk)) { - case BYT_RT5640_IN1_MAP: - custom_map = byt_rt5640_intmic_in1_map; - num_routes = ARRAY_SIZE(byt_rt5640_intmic_in1_map); - break; - case BYT_RT5640_DMIC2_MAP: - custom_map = byt_rt5640_intmic_dmic2_map; - num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map); - break; - default: - custom_map = byt_rt5640_intmic_dmic1_map; - num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map); - } - - ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes); - if (ret) - return ret; - - if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) { - ret = rt5640_dmic_enable(component, 0, 0); - if (ret) - return ret; - } - - snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone"); - snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker"); - - return ret; -} - -static struct snd_soc_ops byt_rt5640_ops = { - .hw_params = byt_rt5640_hw_params, -}; - -SND_SOC_DAILINK_DEFS(audio, - DAILINK_COMP_ARRAY(COMP_CPU("baytrail-pcm-audio")), - DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5640:00", "rt5640-aif1")), - DAILINK_COMP_ARRAY(COMP_PLATFORM("baytrail-pcm-audio"))); - -static struct snd_soc_dai_link byt_rt5640_dais[] = { - { - .name = "Baytrail Audio", - .stream_name = "Audio", - .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBS_CFS, - .init = byt_rt5640_init, - .ops = &byt_rt5640_ops, - SND_SOC_DAILINK_REG(audio), - }, -}; - -static struct snd_soc_card byt_rt5640_card = { - .name = "byt-rt5640", - .owner = THIS_MODULE, - .dai_link = byt_rt5640_dais, - .num_links = ARRAY_SIZE(byt_rt5640_dais), - .dapm_widgets = byt_rt5640_widgets, - .num_dapm_widgets = ARRAY_SIZE(byt_rt5640_widgets), - .dapm_routes = byt_rt5640_audio_map, - .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), - .fully_routed = true, -}; - -static int byt_rt5640_probe(struct platform_device *pdev) -{ - struct snd_soc_card *card = &byt_rt5640_card; - - card->dev = &pdev->dev; - return devm_snd_soc_register_card(&pdev->dev, card); -} - -static struct platform_driver byt_rt5640_audio = { - .probe = byt_rt5640_probe, - .driver = { - .name = "byt-rt5640", - .pm = &snd_soc_pm_ops, - }, -}; -module_platform_driver(byt_rt5640_audio) - -MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Machine driver"); -MODULE_AUTHOR("Omair Md Abdullah, Jarkko Nikula"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:byt-rt5640"); diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c index 4043a1ab44b3..c348607b49a5 100644 --- a/sound/soc/intel/common/soc-acpi-intel-byt-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -120,16 +120,6 @@ static struct snd_soc_acpi_mach *byt_quirk(void *arg) } } -struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_legacy_machines[] = { - { - .id = "10EC5640", - .drv_name = "byt-rt5640", - .fw_filename = "intel/fw_sst_0f28.bin-48kHz_i2s_master", - }, - {} -}; -EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_baytrail_legacy_machines); - struct snd_soc_acpi_mach snd_soc_acpi_intel_baytrail_machines[] = { { .id = "10EC5640", -- cgit From 07833cd0569bb73cc9f82814cdab921abb3dfb4a Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:48:58 +0200 Subject: ASoC: Intel: Remove baytrail solution sound/soc/intel/baytrail is a niche solution which supports limited number of BYT products - as described by snd_soc_acpi_intel_baytrail_legacy_machines table. For a long time it's deprecated in favor of sound/soc/intel/atom solution with SOF providing support for some products too effectively rendering /baytrail/ redundant. Remove deprecated code from ASoC tree. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-5-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 13 - sound/soc/intel/Makefile | 1 - sound/soc/intel/baytrail/Makefile | 5 - sound/soc/intel/baytrail/sst-baytrail-dsp.c | 358 ------------- sound/soc/intel/baytrail/sst-baytrail-ipc.c | 772 ---------------------------- sound/soc/intel/baytrail/sst-baytrail-ipc.h | 64 --- sound/soc/intel/baytrail/sst-baytrail-pcm.c | 459 ----------------- 7 files changed, 1672 deletions(-) delete mode 100644 sound/soc/intel/baytrail/Makefile delete mode 100644 sound/soc/intel/baytrail/sst-baytrail-dsp.c delete mode 100644 sound/soc/intel/baytrail/sst-baytrail-ipc.c delete mode 100644 sound/soc/intel/baytrail/sst-baytrail-ipc.h delete mode 100644 sound/soc/intel/baytrail/sst-baytrail-pcm.c (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index dfc20f2bb859..41f9c286b34c 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -63,19 +63,6 @@ config SND_SOC_INTEL_HASWELL tristate select SND_SOC_INTEL_CATPT -config SND_SOC_INTEL_BAYTRAIL - tristate "Baytrail (legacy) Platforms" - depends on DMADEVICES && ACPI && SND_SST_ATOM_HIFI2_PLATFORM=n && SND_SOC_SOF_BAYTRAIL=n - select SND_SOC_INTEL_SST - select SND_SOC_INTEL_SST_ACPI - select SND_SOC_INTEL_SST_FIRMWARE - select SND_SOC_ACPI_INTEL_MATCH - help - If you have a Intel Baytrail platform connected to an I2S codec, - then enable this option by saying Y or m. This was typically used - for Baytrail Chromebooks but this option is now deprecated and is - not recommended, use SND_SST_ATOM_HIFI2_PLATFORM instead. - config SND_SST_ATOM_HIFI2_PLATFORM tristate select SND_SOC_COMPRESS diff --git a/sound/soc/intel/Makefile b/sound/soc/intel/Makefile index c88c615f85f7..4e0248d2accc 100644 --- a/sound/soc/intel/Makefile +++ b/sound/soc/intel/Makefile @@ -3,7 +3,6 @@ obj-$(CONFIG_SND_SOC) += common/ # Platform Support -obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += baytrail/ obj-$(CONFIG_SND_SST_ATOM_HIFI2_PLATFORM) += atom/ obj-$(CONFIG_SND_SOC_INTEL_CATPT) += catpt/ obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += skylake/ diff --git a/sound/soc/intel/baytrail/Makefile b/sound/soc/intel/baytrail/Makefile deleted file mode 100644 index 4d0806aac6bd..000000000000 --- a/sound/soc/intel/baytrail/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -snd-soc-sst-baytrail-pcm-objs := \ - sst-baytrail-ipc.o sst-baytrail-pcm.o sst-baytrail-dsp.o - -obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o diff --git a/sound/soc/intel/baytrail/sst-baytrail-dsp.c b/sound/soc/intel/baytrail/sst-baytrail-dsp.c deleted file mode 100644 index 4116ba66a4c2..000000000000 --- a/sound/soc/intel/baytrail/sst-baytrail-dsp.c +++ /dev/null @@ -1,358 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Baytrail SST DSP driver - * Copyright (c) 2014, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../common/sst-dsp.h" -#include "../common/sst-dsp-priv.h" -#include "sst-baytrail-ipc.h" - -#define SST_BYT_FW_SIGNATURE_SIZE 4 -#define SST_BYT_FW_SIGN "$SST" - -#define SST_BYT_IRAM_OFFSET 0xC0000 -#define SST_BYT_DRAM_OFFSET 0x100000 -#define SST_BYT_SHIM_OFFSET 0x140000 - -enum sst_ram_type { - SST_BYT_IRAM = 1, - SST_BYT_DRAM = 2, - SST_BYT_CACHE = 3, -}; - -struct dma_block_info { - enum sst_ram_type type; /* IRAM/DRAM */ - u32 size; /* Bytes */ - u32 ram_offset; /* Offset in I/DRAM */ - u32 rsvd; /* Reserved field */ -}; - -struct fw_header { - unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE]; - u32 file_size; /* size of fw minus this header */ - u32 modules; /* # of modules */ - u32 file_format; /* version of header format */ - u32 reserved[4]; -}; - -struct sst_byt_fw_module_header { - unsigned char signature[SST_BYT_FW_SIGNATURE_SIZE]; - u32 mod_size; /* size of module */ - u32 blocks; /* # of blocks */ - u32 type; /* codec type, pp lib */ - u32 entry_point; -}; - -static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw, - struct sst_byt_fw_module_header *module) -{ - struct dma_block_info *block; - struct sst_module *mod; - struct sst_module_template template; - int count; - - memset(&template, 0, sizeof(template)); - template.id = module->type; - template.entry = module->entry_point; - - mod = sst_module_new(fw, &template, NULL); - if (mod == NULL) - return -ENOMEM; - - block = (void *)module + sizeof(*module); - - for (count = 0; count < module->blocks; count++) { - - if (block->size <= 0) { - dev_err(dsp->dev, "block %d size invalid\n", count); - return -EINVAL; - } - - switch (block->type) { - case SST_BYT_IRAM: - mod->offset = block->ram_offset + - dsp->addr.iram_offset; - mod->type = SST_MEM_IRAM; - break; - case SST_BYT_DRAM: - mod->offset = block->ram_offset + - dsp->addr.dram_offset; - mod->type = SST_MEM_DRAM; - break; - case SST_BYT_CACHE: - mod->offset = block->ram_offset + - (dsp->addr.fw_ext - dsp->addr.lpe); - mod->type = SST_MEM_CACHE; - break; - default: - dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n", - block->type, count); - return -EINVAL; - } - - mod->size = block->size; - mod->data = (void *)block + sizeof(*block); - - sst_module_alloc_blocks(mod); - - block = (void *)block + sizeof(*block) + block->size; - } - return 0; -} - -static int sst_byt_parse_fw_image(struct sst_fw *sst_fw) -{ - struct fw_header *header; - struct sst_byt_fw_module_header *module; - struct sst_dsp *dsp = sst_fw->dsp; - int ret, count; - - /* Read the header information from the data pointer */ - header = (struct fw_header *)sst_fw->dma_buf; - - /* verify FW */ - if ((strncmp(header->signature, SST_BYT_FW_SIGN, 4) != 0) || - (sst_fw->size != header->file_size + sizeof(*header))) { - /* Invalid FW signature */ - dev_err(dsp->dev, "Invalid FW sign/filesize mismatch\n"); - return -EINVAL; - } - - dev_dbg(dsp->dev, - "header sign=%4s size=0x%x modules=0x%x fmt=0x%x size=%zu\n", - header->signature, header->file_size, header->modules, - header->file_format, sizeof(*header)); - - module = (void *)sst_fw->dma_buf + sizeof(*header); - for (count = 0; count < header->modules; count++) { - /* module */ - ret = sst_byt_parse_module(dsp, sst_fw, module); - if (ret < 0) { - dev_err(dsp->dev, "invalid module %d\n", count); - return ret; - } - module = (void *)module + sizeof(*module) + module->mod_size; - } - - return 0; -} - -static void sst_byt_dump_shim(struct sst_dsp *sst) -{ - int i; - u64 reg; - - for (i = 0; i <= 0xF0; i += 8) { - reg = sst_dsp_shim_read64_unlocked(sst, i); - if (reg) - dev_dbg(sst->dev, "shim 0x%2.2x value 0x%16.16llx\n", - i, reg); - } - - for (i = 0x00; i <= 0xff; i += 4) { - reg = readl(sst->addr.pci_cfg + i); - if (reg) - dev_dbg(sst->dev, "pci 0x%2.2x value 0x%8.8x\n", - i, (u32)reg); - } -} - -static irqreturn_t sst_byt_irq(int irq, void *context) -{ - struct sst_dsp *sst = (struct sst_dsp *) context; - u64 isrx; - irqreturn_t ret = IRQ_NONE; - - spin_lock(&sst->spinlock); - - isrx = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); - if (isrx & SST_ISRX_DONE) { - /* ADSP has processed the message request from IA */ - sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCX, - SST_BYT_IPCX_DONE, 0); - ret = IRQ_WAKE_THREAD; - } - if (isrx & SST_BYT_ISRX_REQUEST) { - /* mask message request from ADSP and do processing later */ - sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX, - SST_BYT_IMRX_REQUEST, - SST_BYT_IMRX_REQUEST); - ret = IRQ_WAKE_THREAD; - } - - spin_unlock(&sst->spinlock); - - return ret; -} - -static void sst_byt_boot(struct sst_dsp *sst) -{ - int tries = 10; - - /* - * save the physical address of extended firmware block in the first - * 4 bytes of the mailbox - */ - memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET, - &sst->pdata->fw_base, sizeof(u32)); - - /* release stall and wait to unstall */ - sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0); - while (tries--) { - if (!(sst_dsp_shim_read64(sst, SST_CSR) & - SST_BYT_CSR_PWAITMODE)) - break; - msleep(100); - } - if (tries < 0) { - dev_err(sst->dev, "unable to start DSP\n"); - sst_byt_dump_shim(sst); - } -} - -static void sst_byt_reset(struct sst_dsp *sst) -{ - /* put DSP into reset, set reset vector and stall */ - sst_dsp_shim_update_bits64(sst, SST_CSR, - SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL, - SST_BYT_CSR_RST | SST_BYT_CSR_VECTOR_SEL | SST_BYT_CSR_STALL); - - udelay(10); - - /* take DSP out of reset and keep stalled for FW loading */ - sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_RST, 0); -} - -struct sst_adsp_memregion { - u32 start; - u32 end; - int blocks; - enum sst_mem_type type; -}; - -/* BYT test stuff */ -static const struct sst_adsp_memregion byt_region[] = { - {0xC0000, 0x100000, 8, SST_MEM_IRAM}, /* I-SRAM - 8 * 32kB */ - {0x100000, 0x140000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */ -}; - -static int sst_byt_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata) -{ - sst->addr.lpe_base = pdata->lpe_base; - sst->addr.lpe = ioremap(pdata->lpe_base, pdata->lpe_size); - if (!sst->addr.lpe) - return -ENODEV; - - /* ADSP PCI MMIO config space */ - sst->addr.pci_cfg = ioremap(pdata->pcicfg_base, pdata->pcicfg_size); - if (!sst->addr.pci_cfg) { - iounmap(sst->addr.lpe); - return -ENODEV; - } - - /* SST Extended FW allocation */ - sst->addr.fw_ext = ioremap(pdata->fw_base, pdata->fw_size); - if (!sst->addr.fw_ext) { - iounmap(sst->addr.pci_cfg); - iounmap(sst->addr.lpe); - return -ENODEV; - } - - /* SST Shim */ - sst->addr.shim = sst->addr.lpe + sst->addr.shim_offset; - - sst_dsp_mailbox_init(sst, SST_BYT_MAILBOX_OFFSET + 0x204, - SST_BYT_IPC_MAX_PAYLOAD_SIZE, - SST_BYT_MAILBOX_OFFSET, - SST_BYT_IPC_MAX_PAYLOAD_SIZE); - - sst->irq = pdata->irq; - - return 0; -} - -static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata) -{ - const struct sst_adsp_memregion *region; - struct device *dev; - int ret = -ENODEV, i, j, region_count; - u32 offset, size; - - dev = sst->dev; - - switch (sst->id) { - case SST_DEV_ID_BYT: - region = byt_region; - region_count = ARRAY_SIZE(byt_region); - sst->addr.iram_offset = SST_BYT_IRAM_OFFSET; - sst->addr.dram_offset = SST_BYT_DRAM_OFFSET; - sst->addr.shim_offset = SST_BYT_SHIM_OFFSET; - break; - default: - dev_err(dev, "failed to get mem resources\n"); - return ret; - } - - ret = sst_byt_resource_map(sst, pdata); - if (ret < 0) { - dev_err(dev, "failed to map resources\n"); - return ret; - } - - ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - - /* enable Interrupt from both sides */ - sst_dsp_shim_update_bits64(sst, SST_IMRX, 0x3, 0x0); - sst_dsp_shim_update_bits64(sst, SST_IMRD, 0x3, 0x0); - - /* register DSP memory blocks - ideally we should get this from ACPI */ - for (i = 0; i < region_count; i++) { - offset = region[i].start; - size = (region[i].end - region[i].start) / region[i].blocks; - - /* register individual memory blocks */ - for (j = 0; j < region[i].blocks; j++) { - sst_mem_block_register(sst, offset, size, - region[i].type, NULL, j, sst); - offset += size; - } - } - - return 0; -} - -static void sst_byt_free(struct sst_dsp *sst) -{ - sst_mem_block_unregister_all(sst); - iounmap(sst->addr.lpe); - iounmap(sst->addr.pci_cfg); - iounmap(sst->addr.fw_ext); -} - -struct sst_ops sst_byt_ops = { - .reset = sst_byt_reset, - .boot = sst_byt_boot, - .write = sst_shim32_write, - .read = sst_shim32_read, - .write64 = sst_shim32_write64, - .read64 = sst_shim32_read64, - .ram_read = sst_memcpy_fromio_32, - .ram_write = sst_memcpy_toio_32, - .irq_handler = sst_byt_irq, - .init = sst_byt_init, - .free = sst_byt_free, - .parse_fw = sst_byt_parse_fw_image, -}; diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c deleted file mode 100644 index 34746fd871b0..000000000000 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c +++ /dev/null @@ -1,772 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Baytrail SST IPC Support - * Copyright (c) 2014, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "sst-baytrail-ipc.h" -#include "../common/sst-dsp.h" -#include "../common/sst-dsp-priv.h" -#include "../common/sst-ipc.h" - -/* IPC message timeout */ -#define IPC_TIMEOUT_MSECS 300 -#define IPC_BOOT_MSECS 200 - -#define IPC_EMPTY_LIST_SIZE 8 - -/* IPC header bits */ -#define IPC_HEADER_MSG_ID_MASK 0xff -#define IPC_HEADER_MSG_ID(x) ((x) & IPC_HEADER_MSG_ID_MASK) -#define IPC_HEADER_STR_ID_SHIFT 8 -#define IPC_HEADER_STR_ID_MASK 0x1f -#define IPC_HEADER_STR_ID(x) (((x) & 0x1f) << IPC_HEADER_STR_ID_SHIFT) -#define IPC_HEADER_LARGE_SHIFT 13 -#define IPC_HEADER_LARGE(x) (((x) & 0x1) << IPC_HEADER_LARGE_SHIFT) -#define IPC_HEADER_DATA_SHIFT 16 -#define IPC_HEADER_DATA_MASK 0x3fff -#define IPC_HEADER_DATA(x) (((x) & 0x3fff) << IPC_HEADER_DATA_SHIFT) - -/* mask for differentiating between notification and reply message */ -#define IPC_NOTIFICATION (0x1 << 7) - -/* I2L Stream config/control msgs */ -#define IPC_IA_ALLOC_STREAM 0x20 -#define IPC_IA_FREE_STREAM 0x21 -#define IPC_IA_PAUSE_STREAM 0x24 -#define IPC_IA_RESUME_STREAM 0x25 -#define IPC_IA_DROP_STREAM 0x26 -#define IPC_IA_START_STREAM 0x30 - -/* notification messages */ -#define IPC_IA_FW_INIT_CMPLT 0x81 -#define IPC_SST_PERIOD_ELAPSED 0x97 - -/* IPC messages between host and ADSP */ -struct sst_byt_address_info { - u32 addr; - u32 size; -} __packed; - -struct sst_byt_str_type { - u8 codec_type; - u8 str_type; - u8 operation; - u8 protected_str; - u8 time_slots; - u8 reserved; - u16 result; -} __packed; - -struct sst_byt_pcm_params { - u8 num_chan; - u8 pcm_wd_sz; - u8 use_offload_path; - u8 reserved; - u32 sfreq; - u8 channel_map[8]; -} __packed; - -struct sst_byt_frames_info { - u16 num_entries; - u16 rsrvd; - u32 frag_size; - struct sst_byt_address_info ring_buf_info[8]; -} __packed; - -struct sst_byt_alloc_params { - struct sst_byt_str_type str_type; - struct sst_byt_pcm_params pcm_params; - struct sst_byt_frames_info frame_info; -} __packed; - -struct sst_byt_alloc_response { - struct sst_byt_str_type str_type; - u8 reserved[88]; -} __packed; - -struct sst_byt_start_stream_params { - u32 byte_offset; -} __packed; - -struct sst_byt_tstamp { - u64 ring_buffer_counter; - u64 hardware_counter; - u64 frames_decoded; - u64 bytes_decoded; - u64 bytes_copied; - u32 sampling_frequency; - u32 channel_peak[8]; -} __packed; - -struct sst_byt_fw_version { - u8 build; - u8 minor; - u8 major; - u8 type; -} __packed; - -struct sst_byt_fw_build_info { - u8 date[16]; - u8 time[16]; -} __packed; - -struct sst_byt_fw_init { - struct sst_byt_fw_version fw_version; - struct sst_byt_fw_build_info build_info; - u16 result; - u8 module_id; - u8 debug_info; -} __packed; - -struct sst_byt_stream; -struct sst_byt; - -/* stream infomation */ -struct sst_byt_stream { - struct list_head node; - - /* configuration */ - struct sst_byt_alloc_params request; - struct sst_byt_alloc_response reply; - - /* runtime info */ - struct sst_byt *byt; - int str_id; - bool commited; - bool running; - - /* driver callback */ - u32 (*notify_position)(struct sst_byt_stream *stream, void *data); - void *pdata; -}; - -/* SST Baytrail IPC data */ -struct sst_byt { - struct device *dev; - struct sst_dsp *dsp; - - /* stream */ - struct list_head stream_list; - - /* boot */ - wait_queue_head_t boot_wait; - bool boot_complete; - struct sst_fw *fw; - - /* IPC messaging */ - struct sst_generic_ipc ipc; -}; - -static inline u64 sst_byt_header(int msg_id, int data, bool large, int str_id) -{ - return IPC_HEADER_MSG_ID(msg_id) | IPC_HEADER_STR_ID(str_id) | - IPC_HEADER_LARGE(large) | IPC_HEADER_DATA(data) | - SST_BYT_IPCX_BUSY; -} - -static inline u16 sst_byt_header_msg_id(u64 header) -{ - return header & IPC_HEADER_MSG_ID_MASK; -} - -static inline u8 sst_byt_header_str_id(u64 header) -{ - return (header >> IPC_HEADER_STR_ID_SHIFT) & IPC_HEADER_STR_ID_MASK; -} - -static inline u16 sst_byt_header_data(u64 header) -{ - return (header >> IPC_HEADER_DATA_SHIFT) & IPC_HEADER_DATA_MASK; -} - -static struct sst_byt_stream *sst_byt_get_stream(struct sst_byt *byt, - int stream_id) -{ - struct sst_byt_stream *stream; - - list_for_each_entry(stream, &byt->stream_list, node) { - if (stream->str_id == stream_id) - return stream; - } - - return NULL; -} - -static void sst_byt_stream_update(struct sst_byt *byt, struct ipc_message *msg) -{ - struct sst_byt_stream *stream; - u64 header = msg->tx.header; - u8 stream_id = sst_byt_header_str_id(header); - u8 stream_msg = sst_byt_header_msg_id(header); - - stream = sst_byt_get_stream(byt, stream_id); - if (stream == NULL) - return; - - switch (stream_msg) { - case IPC_IA_DROP_STREAM: - case IPC_IA_PAUSE_STREAM: - case IPC_IA_FREE_STREAM: - stream->running = false; - break; - case IPC_IA_START_STREAM: - case IPC_IA_RESUME_STREAM: - stream->running = true; - break; - } -} - -static int sst_byt_process_reply(struct sst_byt *byt, u64 header) -{ - struct ipc_message *msg; - - msg = sst_ipc_reply_find_msg(&byt->ipc, header); - if (msg == NULL) - return 1; - - msg->rx.header = header; - if (header & IPC_HEADER_LARGE(true)) { - msg->rx.size = sst_byt_header_data(header); - sst_dsp_inbox_read(byt->dsp, msg->rx.data, msg->rx.size); - } - - /* update any stream states */ - sst_byt_stream_update(byt, msg); - - list_del(&msg->list); - /* wake up */ - sst_ipc_tx_msg_reply_complete(&byt->ipc, msg); - - return 1; -} - -static void sst_byt_fw_ready(struct sst_byt *byt, u64 header) -{ - dev_dbg(byt->dev, "ipc: DSP is ready 0x%llX\n", header); - - byt->boot_complete = true; - wake_up(&byt->boot_wait); -} - -static int sst_byt_process_notification(struct sst_byt *byt, - unsigned long *flags) -{ - struct sst_dsp *sst = byt->dsp; - struct sst_byt_stream *stream; - u64 header; - u8 msg_id, stream_id; - - header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); - msg_id = sst_byt_header_msg_id(header); - - switch (msg_id) { - case IPC_SST_PERIOD_ELAPSED: - stream_id = sst_byt_header_str_id(header); - stream = sst_byt_get_stream(byt, stream_id); - if (stream && stream->running && stream->notify_position) { - spin_unlock_irqrestore(&sst->spinlock, *flags); - stream->notify_position(stream, stream->pdata); - spin_lock_irqsave(&sst->spinlock, *flags); - } - break; - case IPC_IA_FW_INIT_CMPLT: - sst_byt_fw_ready(byt, header); - break; - } - - return 1; -} - -static irqreturn_t sst_byt_irq_thread(int irq, void *context) -{ - struct sst_dsp *sst = (struct sst_dsp *) context; - struct sst_byt *byt = sst_dsp_get_thread_context(sst); - struct sst_generic_ipc *ipc = &byt->ipc; - u64 header; - unsigned long flags; - - spin_lock_irqsave(&sst->spinlock, flags); - - header = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); - if (header & SST_BYT_IPCD_BUSY) { - if (header & IPC_NOTIFICATION) { - /* message from ADSP */ - sst_byt_process_notification(byt, &flags); - } else { - /* reply from ADSP */ - sst_byt_process_reply(byt, header); - } - /* - * clear IPCD BUSY bit and set DONE bit. Tell DSP we have - * processed the message and can accept new. Clear data part - * of the header - */ - sst_dsp_shim_update_bits64_unlocked(sst, SST_IPCD, - SST_BYT_IPCD_DONE | SST_BYT_IPCD_BUSY | - IPC_HEADER_DATA(IPC_HEADER_DATA_MASK), - SST_BYT_IPCD_DONE); - /* unmask message request interrupts */ - sst_dsp_shim_update_bits64_unlocked(sst, SST_IMRX, - SST_BYT_IMRX_REQUEST, 0); - } - - spin_unlock_irqrestore(&sst->spinlock, flags); - - /* continue to send any remaining messages... */ - schedule_work(&ipc->kwork); - - return IRQ_HANDLED; -} - -/* stream API */ -struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, - u32 (*notify_position)(struct sst_byt_stream *stream, void *data), - void *data) -{ - struct sst_byt_stream *stream; - struct sst_dsp *sst = byt->dsp; - unsigned long flags; - - stream = kzalloc(sizeof(*stream), GFP_KERNEL); - if (stream == NULL) - return NULL; - - spin_lock_irqsave(&sst->spinlock, flags); - list_add(&stream->node, &byt->stream_list); - stream->notify_position = notify_position; - stream->pdata = data; - stream->byt = byt; - stream->str_id = id; - spin_unlock_irqrestore(&sst->spinlock, flags); - - return stream; -} - -int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream, - int bits) -{ - stream->request.pcm_params.pcm_wd_sz = bits; - return 0; -} - -int sst_byt_stream_set_channels(struct sst_byt *byt, - struct sst_byt_stream *stream, u8 channels) -{ - stream->request.pcm_params.num_chan = channels; - return 0; -} - -int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream, - unsigned int rate) -{ - stream->request.pcm_params.sfreq = rate; - return 0; -} - -/* stream sonfiguration */ -int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream, - int codec_type, int stream_type, int operation) -{ - stream->request.str_type.codec_type = codec_type; - stream->request.str_type.str_type = stream_type; - stream->request.str_type.operation = operation; - stream->request.str_type.time_slots = 0xc; - - return 0; -} - -int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream, - uint32_t buffer_addr, uint32_t buffer_size) -{ - stream->request.frame_info.num_entries = 1; - stream->request.frame_info.ring_buf_info[0].addr = buffer_addr; - stream->request.frame_info.ring_buf_info[0].size = buffer_size; - /* calculate bytes per 4 ms fragment */ - stream->request.frame_info.frag_size = - stream->request.pcm_params.sfreq * - stream->request.pcm_params.num_chan * - stream->request.pcm_params.pcm_wd_sz / 8 * - 4 / 1000; - return 0; -} - -int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream) -{ - struct sst_ipc_message request, reply = {0}; - int ret; - - request.header = sst_byt_header(IPC_IA_ALLOC_STREAM, - sizeof(stream->request) + sizeof(u32), - true, stream->str_id); - request.data = &stream->request; - request.size = sizeof(stream->request); - reply.data = &stream->reply; - reply.size = sizeof(stream->reply); - - ret = sst_ipc_tx_message_wait(&byt->ipc, request, &reply); - if (ret < 0) { - dev_err(byt->dev, "ipc: error stream commit failed\n"); - return ret; - } - - stream->commited = true; - - return 0; -} - -int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream) -{ - struct sst_ipc_message request = {0}; - int ret = 0; - struct sst_dsp *sst = byt->dsp; - unsigned long flags; - - if (!stream->commited) - goto out; - - request.header = sst_byt_header(IPC_IA_FREE_STREAM, - 0, false, stream->str_id); - ret = sst_ipc_tx_message_wait(&byt->ipc, request, NULL); - if (ret < 0) { - dev_err(byt->dev, "ipc: free stream %d failed\n", - stream->str_id); - return -EAGAIN; - } - - stream->commited = false; -out: - spin_lock_irqsave(&sst->spinlock, flags); - list_del(&stream->node); - kfree(stream); - spin_unlock_irqrestore(&sst->spinlock, flags); - - return ret; -} - -static int sst_byt_stream_operations(struct sst_byt *byt, int type, - int stream_id, int wait) -{ - struct sst_ipc_message request = {0}; - - request.header = sst_byt_header(type, 0, false, stream_id); - if (wait) - return sst_ipc_tx_message_wait(&byt->ipc, request, NULL); - else - return sst_ipc_tx_message_nowait(&byt->ipc, request); -} - -/* stream ALSA trigger operations */ -int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, - u32 start_offset) -{ - struct sst_byt_start_stream_params start_stream; - struct sst_ipc_message request; - int ret; - - start_stream.byte_offset = start_offset; - request.header = sst_byt_header(IPC_IA_START_STREAM, - sizeof(start_stream) + sizeof(u32), - true, stream->str_id); - request.data = &start_stream; - request.size = sizeof(start_stream); - - ret = sst_ipc_tx_message_nowait(&byt->ipc, request); - if (ret < 0) - dev_err(byt->dev, "ipc: error failed to start stream %d\n", - stream->str_id); - - return ret; -} - -int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream) -{ - int ret; - - /* don't stop streams that are not commited */ - if (!stream->commited) - return 0; - - ret = sst_byt_stream_operations(byt, IPC_IA_DROP_STREAM, - stream->str_id, 0); - if (ret < 0) - dev_err(byt->dev, "ipc: error failed to stop stream %d\n", - stream->str_id); - return ret; -} - -int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream) -{ - int ret; - - ret = sst_byt_stream_operations(byt, IPC_IA_PAUSE_STREAM, - stream->str_id, 0); - if (ret < 0) - dev_err(byt->dev, "ipc: error failed to pause stream %d\n", - stream->str_id); - - return ret; -} - -int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream) -{ - int ret; - - ret = sst_byt_stream_operations(byt, IPC_IA_RESUME_STREAM, - stream->str_id, 0); - if (ret < 0) - dev_err(byt->dev, "ipc: error failed to resume stream %d\n", - stream->str_id); - - return ret; -} - -int sst_byt_get_dsp_position(struct sst_byt *byt, - struct sst_byt_stream *stream, int buffer_size) -{ - struct sst_dsp *sst = byt->dsp; - struct sst_byt_tstamp fw_tstamp; - u8 str_id = stream->str_id; - u32 tstamp_offset; - - tstamp_offset = SST_BYT_TIMESTAMP_OFFSET + str_id * sizeof(fw_tstamp); - memcpy_fromio(&fw_tstamp, - sst->addr.lpe + tstamp_offset, sizeof(fw_tstamp)); - - return do_div(fw_tstamp.ring_buffer_counter, buffer_size); -} - -struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt) -{ - return byt->dsp; -} - -static struct sst_dsp_device byt_dev = { - .thread = sst_byt_irq_thread, - .ops = &sst_byt_ops, -}; - -int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt = pdata->dsp; - - dev_dbg(byt->dev, "dsp reset\n"); - sst_dsp_reset(byt->dsp); - sst_ipc_drop_all(&byt->ipc); - dev_dbg(byt->dev, "dsp in reset\n"); - - dev_dbg(byt->dev, "free all blocks and unload fw\n"); - sst_fw_unload(byt->fw); - - return 0; -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late); - -int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt = pdata->dsp; - int ret; - - dev_dbg(byt->dev, "reload dsp fw\n"); - - sst_dsp_reset(byt->dsp); - - ret = sst_fw_reload(byt->fw); - if (ret < 0) { - dev_err(dev, "error: failed to reload firmware\n"); - return ret; - } - - /* wait for DSP boot completion */ - byt->boot_complete = false; - sst_dsp_boot(byt->dsp); - dev_dbg(byt->dev, "dsp booting...\n"); - - return 0; -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_boot); - -int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt = pdata->dsp; - int err; - - dev_dbg(byt->dev, "wait for dsp reboot\n"); - - err = wait_event_timeout(byt->boot_wait, byt->boot_complete, - msecs_to_jiffies(IPC_BOOT_MSECS)); - if (err == 0) { - dev_err(byt->dev, "ipc: error DSP boot timeout\n"); - return -EIO; - } - - dev_dbg(byt->dev, "dsp rebooted\n"); - return 0; -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready); - -static void byt_tx_msg(struct sst_generic_ipc *ipc, struct ipc_message *msg) -{ - if (msg->tx.header & IPC_HEADER_LARGE(true)) - sst_dsp_outbox_write(ipc->dsp, msg->tx.data, msg->tx.size); - - sst_dsp_shim_write64_unlocked(ipc->dsp, SST_IPCX, msg->tx.header); -} - -static void byt_shim_dbg(struct sst_generic_ipc *ipc, const char *text) -{ - struct sst_dsp *sst = ipc->dsp; - u64 isr, ipcd, imrx, ipcx; - - ipcx = sst_dsp_shim_read64_unlocked(sst, SST_IPCX); - isr = sst_dsp_shim_read64_unlocked(sst, SST_ISRX); - ipcd = sst_dsp_shim_read64_unlocked(sst, SST_IPCD); - imrx = sst_dsp_shim_read64_unlocked(sst, SST_IMRX); - - dev_err(ipc->dev, - "ipc: --%s-- ipcx 0x%llx isr 0x%llx ipcd 0x%llx imrx 0x%llx\n", - text, ipcx, isr, ipcd, imrx); -} - -static void byt_tx_data_copy(struct ipc_message *msg, char *tx_data, - size_t tx_size) -{ - /* msg content = lower 32-bit of the header + data */ - *(u32 *)msg->tx.data = (u32)(msg->tx.header & (u32)-1); - memcpy(msg->tx.data + sizeof(u32), tx_data, tx_size); - msg->tx.size += sizeof(u32); -} - -static u64 byt_reply_msg_match(u64 header, u64 *mask) -{ - /* match reply to message sent based on msg and stream IDs */ - *mask = IPC_HEADER_MSG_ID_MASK | - IPC_HEADER_STR_ID_MASK << IPC_HEADER_STR_ID_SHIFT; - header &= *mask; - - return header; -} - -static bool byt_is_dsp_busy(struct sst_dsp *dsp) -{ - u64 ipcx; - - ipcx = sst_dsp_shim_read64_unlocked(dsp, SST_IPCX); - return (ipcx & (SST_BYT_IPCX_BUSY | SST_BYT_IPCX_DONE)); -} - -int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt; - struct sst_generic_ipc *ipc; - struct sst_fw *byt_sst_fw; - struct sst_byt_fw_init init; - int err; - - dev_dbg(dev, "initialising Byt DSP IPC\n"); - - byt = devm_kzalloc(dev, sizeof(*byt), GFP_KERNEL); - if (byt == NULL) - return -ENOMEM; - - byt->dev = dev; - - ipc = &byt->ipc; - ipc->dev = dev; - ipc->ops.tx_msg = byt_tx_msg; - ipc->ops.shim_dbg = byt_shim_dbg; - ipc->ops.tx_data_copy = byt_tx_data_copy; - ipc->ops.reply_msg_match = byt_reply_msg_match; - ipc->ops.is_dsp_busy = byt_is_dsp_busy; - ipc->tx_data_max_size = IPC_MAX_MAILBOX_BYTES; - ipc->rx_data_max_size = IPC_MAX_MAILBOX_BYTES; - - err = sst_ipc_init(ipc); - if (err != 0) - goto ipc_init_err; - - INIT_LIST_HEAD(&byt->stream_list); - init_waitqueue_head(&byt->boot_wait); - byt_dev.thread_context = byt; - - /* init SST shim */ - byt->dsp = sst_dsp_new(dev, &byt_dev, pdata); - if (byt->dsp == NULL) { - err = -ENODEV; - goto dsp_new_err; - } - - ipc->dsp = byt->dsp; - - /* keep the DSP in reset state for base FW loading */ - sst_dsp_reset(byt->dsp); - - byt_sst_fw = sst_fw_new(byt->dsp, pdata->fw, byt); - if (byt_sst_fw == NULL) { - err = -ENODEV; - dev_err(dev, "error: failed to load firmware\n"); - goto fw_err; - } - - /* wait for DSP boot completion */ - sst_dsp_boot(byt->dsp); - err = wait_event_timeout(byt->boot_wait, byt->boot_complete, - msecs_to_jiffies(IPC_BOOT_MSECS)); - if (err == 0) { - err = -EIO; - dev_err(byt->dev, "ipc: error DSP boot timeout\n"); - goto boot_err; - } - - /* show firmware information */ - sst_dsp_inbox_read(byt->dsp, &init, sizeof(init)); - dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n", - init.fw_version.major, init.fw_version.minor, - init.fw_version.build, init.fw_version.type); - dev_info(byt->dev, "Build type: %x\n", init.fw_version.type); - dev_info(byt->dev, "Build date: %s %s\n", - init.build_info.date, init.build_info.time); - - pdata->dsp = byt; - byt->fw = byt_sst_fw; - - return 0; - -boot_err: - sst_dsp_reset(byt->dsp); - sst_fw_free(byt_sst_fw); -fw_err: - sst_dsp_free(byt->dsp); -dsp_new_err: - sst_ipc_fini(ipc); -ipc_init_err: - - return err; -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_init); - -void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata) -{ - struct sst_byt *byt = pdata->dsp; - - sst_dsp_reset(byt->dsp); - sst_fw_free_all(byt->dsp); - sst_dsp_free(byt->dsp); - sst_ipc_fini(&byt->ipc); -} -EXPORT_SYMBOL_GPL(sst_byt_dsp_free); diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.h b/sound/soc/intel/baytrail/sst-baytrail-ipc.h deleted file mode 100644 index cc960b79e9e1..000000000000 --- a/sound/soc/intel/baytrail/sst-baytrail-ipc.h +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Intel Baytrail SST IPC Support - * Copyright (c) 2014, Intel Corporation. - */ - -#ifndef __SST_BYT_IPC_H -#define __SST_BYT_IPC_H - -#include - -struct sst_byt; -struct sst_byt_stream; -struct sst_pdata; -extern struct sst_ops sst_byt_ops; - - -#define SST_BYT_MAILBOX_OFFSET 0x144000 -#define SST_BYT_TIMESTAMP_OFFSET (SST_BYT_MAILBOX_OFFSET + 0x800) - -/** - * Upfront defined maximum message size that is - * expected by the in/out communication pipes in FW. - */ -#define SST_BYT_IPC_MAX_PAYLOAD_SIZE 200 - -/* stream API */ -struct sst_byt_stream *sst_byt_stream_new(struct sst_byt *byt, int id, - uint32_t (*notify_position)(struct sst_byt_stream *stream, void *data), - void *data); - -/* stream configuration */ -int sst_byt_stream_set_bits(struct sst_byt *byt, struct sst_byt_stream *stream, - int bits); -int sst_byt_stream_set_channels(struct sst_byt *byt, - struct sst_byt_stream *stream, u8 channels); -int sst_byt_stream_set_rate(struct sst_byt *byt, struct sst_byt_stream *stream, - unsigned int rate); -int sst_byt_stream_type(struct sst_byt *byt, struct sst_byt_stream *stream, - int codec_type, int stream_type, int operation); -int sst_byt_stream_buffer(struct sst_byt *byt, struct sst_byt_stream *stream, - uint32_t buffer_addr, uint32_t buffer_size); -int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream); -int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream); - -/* stream ALSA trigger operations */ -int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream, - u32 start_offset); -int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream); -int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream); -int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream); - -int sst_byt_get_dsp_position(struct sst_byt *byt, - struct sst_byt_stream *stream, int buffer_size); - -/* init */ -int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata); -void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata); -struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt); -int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata); -int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata); -int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata); - -#endif diff --git a/sound/soc/intel/baytrail/sst-baytrail-pcm.c b/sound/soc/intel/baytrail/sst-baytrail-pcm.c deleted file mode 100644 index 54a66cc6db89..000000000000 --- a/sound/soc/intel/baytrail/sst-baytrail-pcm.c +++ /dev/null @@ -1,459 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel Baytrail SST PCM Support - * Copyright (c) 2014, Intel Corporation. - */ - -#include -#include -#include -#include -#include -#include -#include -#include "sst-baytrail-ipc.h" -#include "../common/sst-dsp-priv.h" -#include "../common/sst-dsp.h" - -#define DRV_NAME "byt-dai" -#define BYT_PCM_COUNT 2 - -static const struct snd_pcm_hardware sst_byt_pcm_hardware = { - .info = SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE | - SNDRV_PCM_FMTBIT_S24_LE, - .period_bytes_min = 384, - .period_bytes_max = 48000, - .periods_min = 2, - .periods_max = 250, - .buffer_bytes_max = 96000, -}; - -/* private data for each PCM DSP stream */ -struct sst_byt_pcm_data { - struct sst_byt_stream *stream; - struct snd_pcm_substream *substream; - struct mutex mutex; - - /* latest DSP DMA hw pointer */ - u32 hw_ptr; - - struct work_struct work; -}; - -/* private data for the driver */ -struct sst_byt_priv_data { - /* runtime DSP */ - struct sst_byt *byt; - - /* DAI data */ - struct sst_byt_pcm_data pcm[BYT_PCM_COUNT]; - - /* flag indicating is stream context restore needed after suspend */ - bool restore_stream; -}; - -/* this may get called several times by oss emulation */ -static int sst_byt_pcm_hw_params(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; - struct sst_byt *byt = pdata->byt; - u32 rate, bits; - u8 channels; - int ret, playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK); - - dev_dbg(rtd->dev, "PCM: hw_params, pcm_data %p\n", pcm_data); - - ret = sst_byt_stream_type(byt, pcm_data->stream, - 1, 1, !playback); - if (ret < 0) { - dev_err(rtd->dev, "failed to set stream format %d\n", ret); - return ret; - } - - rate = params_rate(params); - ret = sst_byt_stream_set_rate(byt, pcm_data->stream, rate); - if (ret < 0) { - dev_err(rtd->dev, "could not set rate %d\n", rate); - return ret; - } - - bits = snd_pcm_format_width(params_format(params)); - ret = sst_byt_stream_set_bits(byt, pcm_data->stream, bits); - if (ret < 0) { - dev_err(rtd->dev, "could not set formats %d\n", - params_rate(params)); - return ret; - } - - channels = (u8)(params_channels(params) & 0xF); - ret = sst_byt_stream_set_channels(byt, pcm_data->stream, channels); - if (ret < 0) { - dev_err(rtd->dev, "could not set channels %d\n", - params_rate(params)); - return ret; - } - - ret = sst_byt_stream_buffer(byt, pcm_data->stream, - substream->dma_buffer.addr, - params_buffer_bytes(params)); - if (ret < 0) { - dev_err(rtd->dev, "PCM: failed to set DMA buffer %d\n", ret); - return ret; - } - - ret = sst_byt_stream_commit(byt, pcm_data->stream); - if (ret < 0) { - dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret); - return ret; - } - - return 0; -} - -static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; - struct sst_byt *byt = pdata->byt; - int ret; - - /* commit stream using existing stream params */ - ret = sst_byt_stream_commit(byt, pcm_data->stream); - if (ret < 0) { - dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret); - return ret; - } - - sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr); - - dev_dbg(rtd->dev, "stream context restored at offset %d\n", - pcm_data->hw_ptr); - - return 0; -} - -static void sst_byt_pcm_work(struct work_struct *work) -{ - struct sst_byt_pcm_data *pcm_data = - container_of(work, struct sst_byt_pcm_data, work); - - if (snd_pcm_running(pcm_data->substream)) - sst_byt_pcm_restore_stream_context(pcm_data->substream); -} - -static int sst_byt_pcm_trigger(struct snd_soc_component *component, - struct snd_pcm_substream *substream, int cmd) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; - struct sst_byt *byt = pdata->byt; - - dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - pcm_data->hw_ptr = 0; - sst_byt_stream_start(byt, pcm_data->stream, 0); - break; - case SNDRV_PCM_TRIGGER_RESUME: - if (pdata->restore_stream) - schedule_work(&pcm_data->work); - else - sst_byt_stream_resume(byt, pcm_data->stream); - break; - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - sst_byt_stream_resume(byt, pcm_data->stream); - break; - case SNDRV_PCM_TRIGGER_STOP: - sst_byt_stream_stop(byt, pcm_data->stream); - break; - case SNDRV_PCM_TRIGGER_SUSPEND: - pdata->restore_stream = false; - /* fallthrough */ - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - sst_byt_stream_pause(byt, pcm_data->stream); - break; - default: - break; - } - - return 0; -} - -static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data) -{ - struct sst_byt_pcm_data *pcm_data = data; - struct snd_pcm_substream *substream = pcm_data->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt *byt = pdata->byt; - u32 pos, hw_pos; - - hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream, - snd_pcm_lib_buffer_bytes(substream)); - pcm_data->hw_ptr = hw_pos; - pos = frames_to_bytes(runtime, - (runtime->control->appl_ptr % - runtime->buffer_size)); - - dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos); - - snd_pcm_period_elapsed(substream); - return pos; -} - -static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; - - dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr); - - return bytes_to_frames(runtime, pcm_data->hw_ptr); -} - -static int sst_byt_pcm_open(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; - struct sst_byt *byt = pdata->byt; - - dev_dbg(rtd->dev, "PCM: open\n"); - - mutex_lock(&pcm_data->mutex); - - pcm_data->substream = substream; - - snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware); - - pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1, - byt_notify_pointer, pcm_data); - if (pcm_data->stream == NULL) { - dev_err(rtd->dev, "failed to create stream\n"); - mutex_unlock(&pcm_data->mutex); - return -EINVAL; - } - - mutex_unlock(&pcm_data->mutex); - return 0; -} - -static int sst_byt_pcm_close(struct snd_soc_component *component, - struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct sst_byt_priv_data *pdata = snd_soc_component_get_drvdata(component); - struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream]; - struct sst_byt *byt = pdata->byt; - int ret; - - dev_dbg(rtd->dev, "PCM: close\n"); - - cancel_work_sync(&pcm_data->work); - mutex_lock(&pcm_data->mutex); - ret = sst_byt_stream_free(byt, pcm_data->stream); - if (ret < 0) { - dev_dbg(rtd->dev, "Free stream fail\n"); - goto out; - } - pcm_data->stream = NULL; - -out: - mutex_unlock(&pcm_data->mutex); - return ret; -} - -static int sst_byt_pcm_mmap(struct snd_soc_component *component, - struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - - dev_dbg(rtd->dev, "PCM: mmap\n"); - return snd_pcm_lib_default_mmap(substream, vma); -} - -static int sst_byt_pcm_new(struct snd_soc_component *component, - struct snd_soc_pcm_runtime *rtd) -{ - struct snd_pcm *pcm = rtd->pcm; - size_t size; - struct sst_pdata *pdata = dev_get_platdata(component->dev); - - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || - pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - size = sst_byt_pcm_hardware.buffer_bytes_max; - snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, - pdata->dma_dev, size, size); - } - - return 0; -} - -static struct snd_soc_dai_driver byt_dais[] = { - { - .name = "Baytrail PCM", - .playback = { - .stream_name = "System Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S24_3LE | - SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "Analog Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - }, -}; - -static int sst_byt_pcm_probe(struct snd_soc_component *component) -{ - struct sst_pdata *plat_data = dev_get_platdata(component->dev); - struct sst_byt_priv_data *priv_data; - int i; - - if (!plat_data) - return -ENODEV; - - priv_data = devm_kzalloc(component->dev, sizeof(*priv_data), - GFP_KERNEL); - if (!priv_data) - return -ENOMEM; - priv_data->byt = plat_data->dsp; - snd_soc_component_set_drvdata(component, priv_data); - - for (i = 0; i < BYT_PCM_COUNT; i++) { - mutex_init(&priv_data->pcm[i].mutex); - INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work); - } - - return 0; -} - -static const struct snd_soc_component_driver byt_dai_component = { - .name = DRV_NAME, - .probe = sst_byt_pcm_probe, - .open = sst_byt_pcm_open, - .close = sst_byt_pcm_close, - .hw_params = sst_byt_pcm_hw_params, - .trigger = sst_byt_pcm_trigger, - .pointer = sst_byt_pcm_pointer, - .mmap = sst_byt_pcm_mmap, - .pcm_construct = sst_byt_pcm_new, -}; - -#ifdef CONFIG_PM -static int sst_byt_pcm_dev_suspend_late(struct device *dev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(dev); - struct sst_byt_priv_data *priv_data = dev_get_drvdata(dev); - int ret; - - dev_dbg(dev, "suspending late\n"); - - ret = sst_byt_dsp_suspend_late(dev, sst_pdata); - if (ret < 0) { - dev_err(dev, "failed to suspend %d\n", ret); - return ret; - } - - priv_data->restore_stream = true; - - return ret; -} - -static int sst_byt_pcm_dev_resume_early(struct device *dev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(dev); - int ret; - - dev_dbg(dev, "resume early\n"); - - /* load fw and boot DSP */ - ret = sst_byt_dsp_boot(dev, sst_pdata); - if (ret) - return ret; - - /* wait for FW to finish booting */ - return sst_byt_dsp_wait_for_ready(dev, sst_pdata); -} - -static const struct dev_pm_ops sst_byt_pm_ops = { - .suspend_late = sst_byt_pcm_dev_suspend_late, - .resume_early = sst_byt_pcm_dev_resume_early, -}; - -#define SST_BYT_PM_OPS (&sst_byt_pm_ops) -#else -#define SST_BYT_PM_OPS NULL -#endif - -static int sst_byt_pcm_dev_probe(struct platform_device *pdev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); - int ret; - - ret = sst_byt_dsp_init(&pdev->dev, sst_pdata); - if (ret < 0) - return -ENODEV; - - ret = devm_snd_soc_register_component(&pdev->dev, &byt_dai_component, - byt_dais, ARRAY_SIZE(byt_dais)); - if (ret < 0) - goto err_plat; - - return 0; - -err_plat: - sst_byt_dsp_free(&pdev->dev, sst_pdata); - return ret; -} - -static int sst_byt_pcm_dev_remove(struct platform_device *pdev) -{ - struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); - - sst_byt_dsp_free(&pdev->dev, sst_pdata); - - return 0; -} - -static struct platform_driver sst_byt_pcm_driver = { - .driver = { - .name = "baytrail-pcm-audio", - .pm = SST_BYT_PM_OPS, - }, - - .probe = sst_byt_pcm_dev_probe, - .remove = sst_byt_pcm_dev_remove, -}; -module_platform_driver(sst_byt_pcm_driver); - -MODULE_AUTHOR("Jarkko Nikula"); -MODULE_DESCRIPTION("Baytrail PCM"); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:baytrail-pcm-audio"); -- cgit From 05668be1b3644f9bd25b22f62e79ad7a5adbd3e2 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:48:59 +0200 Subject: ASoC: Intel: Remove SST ACPI component baytrail and haswell solutions present within sound/soc/intel are the only users of sst-acpi componenent and with them removed it becomes redundant so remove it too. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-6-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 6 - sound/soc/intel/common/Makefile | 2 - sound/soc/intel/common/sst-acpi.c | 236 -------------------------------------- 3 files changed, 244 deletions(-) delete mode 100644 sound/soc/intel/common/sst-acpi.c (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index 41f9c286b34c..a9940e81c5cd 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -31,12 +31,6 @@ config SND_SST_IPC_ACPI # This option controls the ACPI-based IPC for HiFi2 platforms # (Baytrail, Cherrytrail) -config SND_SOC_INTEL_SST_ACPI - tristate - # This option controls ACPI-based probing on - # Haswell/Broadwell/Baytrail legacy and will be set - # when these platforms are enabled - config SND_SOC_INTEL_SST tristate diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index 2674c9790fa1..f4d41d284e67 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,6 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-sst-dsp-objs := sst-dsp.o -snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-ipc-objs := sst-ipc.o snd-soc-sst-firmware-objs := sst-firmware.o snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ @@ -14,6 +13,5 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o -obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c deleted file mode 100644 index 5854868650b9..000000000000 --- a/sound/soc/intel/common/sst-acpi.c +++ /dev/null @@ -1,236 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel SST loader on ACPI systems - * - * Copyright (C) 2013, Intel Corporation. All rights reserved. - */ - -#include -#include -#include -#include -#include - -#include "sst-dsp.h" -#include -#include - -#define SST_LPT_DSP_DMA_ADDR_OFFSET 0x0F0000 -#define SST_WPT_DSP_DMA_ADDR_OFFSET 0x0FE000 -#define SST_LPT_DSP_DMA_SIZE (1024 - 1) - -/* Descriptor for setting up SST platform data */ -struct sst_acpi_desc { - const char *drv_name; - struct snd_soc_acpi_mach *machines; - /* Platform resource indexes. Must set to -1 if not used */ - int resindex_lpe_base; - int resindex_pcicfg_base; - int resindex_fw_base; - int irqindex_host_ipc; - int resindex_dma_base; - /* Unique number identifying the SST core on platform */ - int sst_id; - /* DMA only valid when resindex_dma_base != -1*/ - int dma_engine; - int dma_size; -}; - -struct sst_acpi_priv { - struct platform_device *pdev_mach; - struct platform_device *pdev_pcm; - struct sst_pdata sst_pdata; - struct sst_acpi_desc *desc; - struct snd_soc_acpi_mach *mach; -}; - -static void sst_acpi_fw_cb(const struct firmware *fw, void *context) -{ - struct platform_device *pdev = context; - struct device *dev = &pdev->dev; - struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); - struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; - struct sst_acpi_desc *desc = sst_acpi->desc; - struct snd_soc_acpi_mach *mach = sst_acpi->mach; - - sst_pdata->fw = fw; - if (!fw) { - dev_err(dev, "Cannot load firmware %s\n", mach->fw_filename); - return; - } - - /* register PCM and DAI driver */ - sst_acpi->pdev_pcm = - platform_device_register_data(dev, desc->drv_name, -1, - sst_pdata, sizeof(*sst_pdata)); - if (IS_ERR(sst_acpi->pdev_pcm)) { - dev_err(dev, "Cannot register device %s. Error %d\n", - desc->drv_name, (int)PTR_ERR(sst_acpi->pdev_pcm)); - } - - return; -} - -static int sst_acpi_probe(struct platform_device *pdev) -{ - const struct acpi_device_id *id; - struct device *dev = &pdev->dev; - struct sst_acpi_priv *sst_acpi; - struct sst_pdata *sst_pdata; - struct snd_soc_acpi_mach *mach; - struct sst_acpi_desc *desc; - struct resource *mmio; - int ret = 0; - - sst_acpi = devm_kzalloc(dev, sizeof(*sst_acpi), GFP_KERNEL); - if (sst_acpi == NULL) - return -ENOMEM; - - id = acpi_match_device(dev->driver->acpi_match_table, dev); - if (!id) - return -ENODEV; - - desc = (struct sst_acpi_desc *)id->driver_data; - mach = snd_soc_acpi_find_machine(desc->machines); - if (mach == NULL) { - dev_err(dev, "No matching ASoC machine driver found\n"); - return -ENODEV; - } - - sst_pdata = &sst_acpi->sst_pdata; - sst_pdata->id = desc->sst_id; - sst_pdata->dma_dev = dev; - sst_acpi->desc = desc; - sst_acpi->mach = mach; - - sst_pdata->resindex_dma_base = desc->resindex_dma_base; - if (desc->resindex_dma_base >= 0) { - sst_pdata->dma_engine = desc->dma_engine; - sst_pdata->dma_base = desc->resindex_dma_base; - sst_pdata->dma_size = desc->dma_size; - } - - if (desc->irqindex_host_ipc >= 0) - sst_pdata->irq = platform_get_irq(pdev, desc->irqindex_host_ipc); - - if (desc->resindex_lpe_base >= 0) { - mmio = platform_get_resource(pdev, IORESOURCE_MEM, - desc->resindex_lpe_base); - if (mmio) { - sst_pdata->lpe_base = mmio->start; - sst_pdata->lpe_size = resource_size(mmio); - } - } - - if (desc->resindex_pcicfg_base >= 0) { - mmio = platform_get_resource(pdev, IORESOURCE_MEM, - desc->resindex_pcicfg_base); - if (mmio) { - sst_pdata->pcicfg_base = mmio->start; - sst_pdata->pcicfg_size = resource_size(mmio); - } - } - - if (desc->resindex_fw_base >= 0) { - mmio = platform_get_resource(pdev, IORESOURCE_MEM, - desc->resindex_fw_base); - if (mmio) { - sst_pdata->fw_base = mmio->start; - sst_pdata->fw_size = resource_size(mmio); - } - } - - platform_set_drvdata(pdev, sst_acpi); - mach->pdata = sst_pdata; - - /* register machine driver */ - sst_acpi->pdev_mach = - platform_device_register_data(dev, mach->drv_name, -1, - mach, sizeof(*mach)); - if (IS_ERR(sst_acpi->pdev_mach)) - return PTR_ERR(sst_acpi->pdev_mach); - - /* continue SST probing after firmware is loaded */ - ret = request_firmware_nowait(THIS_MODULE, true, mach->fw_filename, - dev, GFP_KERNEL, pdev, sst_acpi_fw_cb); - if (ret) - platform_device_unregister(sst_acpi->pdev_mach); - - return ret; -} - -static int sst_acpi_remove(struct platform_device *pdev) -{ - struct sst_acpi_priv *sst_acpi = platform_get_drvdata(pdev); - struct sst_pdata *sst_pdata = &sst_acpi->sst_pdata; - - platform_device_unregister(sst_acpi->pdev_mach); - if (!IS_ERR_OR_NULL(sst_acpi->pdev_pcm)) - platform_device_unregister(sst_acpi->pdev_pcm); - release_firmware(sst_pdata->fw); - - return 0; -} - -static struct sst_acpi_desc sst_acpi_haswell_desc = { - .drv_name = "haswell-pcm-audio", - .machines = snd_soc_acpi_intel_haswell_machines, - .resindex_lpe_base = 0, - .resindex_pcicfg_base = 1, - .resindex_fw_base = -1, - .irqindex_host_ipc = 0, - .sst_id = SST_DEV_ID_LYNX_POINT, - .dma_engine = SST_DMA_TYPE_DW, - .resindex_dma_base = SST_LPT_DSP_DMA_ADDR_OFFSET, - .dma_size = SST_LPT_DSP_DMA_SIZE, -}; - -static struct sst_acpi_desc sst_acpi_broadwell_desc = { - .drv_name = "haswell-pcm-audio", - .machines = snd_soc_acpi_intel_broadwell_machines, - .resindex_lpe_base = 0, - .resindex_pcicfg_base = 1, - .resindex_fw_base = -1, - .irqindex_host_ipc = 0, - .sst_id = SST_DEV_ID_WILDCAT_POINT, - .dma_engine = SST_DMA_TYPE_DW, - .resindex_dma_base = SST_WPT_DSP_DMA_ADDR_OFFSET, - .dma_size = SST_LPT_DSP_DMA_SIZE, -}; - -#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) -static struct sst_acpi_desc sst_acpi_baytrail_desc = { - .drv_name = "baytrail-pcm-audio", - .machines = snd_soc_acpi_intel_baytrail_legacy_machines, - .resindex_lpe_base = 0, - .resindex_pcicfg_base = 1, - .resindex_fw_base = 2, - .irqindex_host_ipc = 5, - .sst_id = SST_DEV_ID_BYT, - .resindex_dma_base = -1, -}; -#endif - -static const struct acpi_device_id sst_acpi_match[] = { - { "INT33C8", (unsigned long)&sst_acpi_haswell_desc }, - { "INT3438", (unsigned long)&sst_acpi_broadwell_desc }, -#if !IS_ENABLED(CONFIG_SND_SST_IPC_ACPI) - { "80860F28", (unsigned long)&sst_acpi_baytrail_desc }, -#endif - { } -}; -MODULE_DEVICE_TABLE(acpi, sst_acpi_match); - -static struct platform_driver sst_acpi_driver = { - .probe = sst_acpi_probe, - .remove = sst_acpi_remove, - .driver = { - .name = "sst-acpi", - .acpi_match_table = ACPI_PTR(sst_acpi_match), - }, -}; -module_platform_driver(sst_acpi_driver); - -MODULE_AUTHOR("Jarkko Nikula "); -MODULE_DESCRIPTION("Intel SST loader on ACPI systems"); -MODULE_LICENSE("GPL v2"); -- cgit From fb94b7b11c6a20b786c6a8aec3d701ced8854419 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:00 +0200 Subject: ASoC: Intel: Remove SST firmware components sst-firmware is host to many image loading over DMA operations. Majority of code targets sound/soc/intel/haswell solution as /baytrail/ never switched to DMA-based firmware loading. With /haswell/ removed this code serves no purpose. Address this redundancy. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-7-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 7 - sound/soc/intel/common/Makefile | 2 - sound/soc/intel/common/sst-dsp-priv.h | 216 ------ sound/soc/intel/common/sst-dsp.h | 15 - sound/soc/intel/common/sst-firmware.c | 1273 --------------------------------- 5 files changed, 1513 deletions(-) delete mode 100644 sound/soc/intel/common/sst-firmware.c (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index a9940e81c5cd..e53e9ffc5e2f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -34,13 +34,6 @@ config SND_SST_IPC_ACPI config SND_SOC_INTEL_SST tristate -config SND_SOC_INTEL_SST_FIRMWARE - tristate - select DW_DMAC_CORE - # This option controls firmware download on - # Haswell/Broadwell/Baytrail legacy and will be set - # when these platforms are enabled - config SND_SOC_INTEL_CATPT tristate "Haswell and Broadwell" depends on ACPI || COMPILE_TEST diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile index f4d41d284e67..64468fe9c513 100644 --- a/sound/soc/intel/common/Makefile +++ b/sound/soc/intel/common/Makefile @@ -1,7 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only snd-soc-sst-dsp-objs := sst-dsp.o snd-soc-sst-ipc-objs := sst-ipc.o -snd-soc-sst-firmware-objs := sst-firmware.o snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-match.o \ soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ @@ -13,5 +12,4 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o -obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o obj-$(CONFIG_SND_SOC_ACPI_INTEL_MATCH) += snd-soc-acpi-intel-match.o diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 9a760123b46f..6b6738a289dc 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -15,10 +15,6 @@ #include "../skylake/skl-sst-dsp.h" -struct sst_mem_block; -struct sst_module; -struct sst_fw; - /* do we need to remove or keep */ #define DSP_DRAM_ADDR_OFFSET 0x400000 @@ -53,9 +49,6 @@ struct sst_ops { /* SST init and free */ int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata); void (*free)(struct sst_dsp *sst); - - /* FW module parser/loader */ - int (*parse_fw)(struct sst_fw *sst_fw); }; /* @@ -88,168 +81,6 @@ struct sst_mailbox { size_t out_size; }; -/* - * Audio DSP memory block types. - */ -enum sst_mem_type { - SST_MEM_IRAM = 0, - SST_MEM_DRAM = 1, - SST_MEM_ANY = 2, - SST_MEM_CACHE= 3, -}; - -/* - * Audio DSP Generic Firmware File. - * - * SST Firmware files can consist of 1..N modules. This generic structure is - * used to manage each firmware file and it's modules regardless of SST firmware - * type. A SST driver may load multiple FW files. - */ -struct sst_fw { - struct sst_dsp *dsp; - - /* base addresses of FW file data */ - dma_addr_t dmable_fw_paddr; /* physical address of fw data */ - void *dma_buf; /* virtual address of fw data */ - u32 size; /* size of fw data */ - - /* lists */ - struct list_head list; /* DSP list of FW */ - struct list_head module_list; /* FW list of modules */ - - void *private; /* core doesn't touch this */ -}; - -/* - * Audio DSP Generic Module Template. - * - * Used to define and register a new FW module. This data is extracted from - * FW module header information. - */ -struct sst_module_template { - u32 id; - u32 entry; /* entry point */ - u32 scratch_size; - u32 persistent_size; -}; - -/* - * Block Allocator - Used to allocate blocks of DSP memory. - */ -struct sst_block_allocator { - u32 id; - u32 offset; - int size; - enum sst_mem_type type; -}; - -/* - * Runtime Module Instance - A module object can be instantiated multiple - * times within the DSP FW. - */ -struct sst_module_runtime { - struct sst_dsp *dsp; - int id; - struct sst_module *module; /* parent module we belong too */ - - u32 persistent_offset; /* private memory offset */ - void *private; - - struct list_head list; - struct list_head block_list; /* list of blocks used */ -}; - -/* - * Runtime Module Context - The runtime context must be manually stored by the - * driver prior to enter S3 and restored after leaving S3. This should really be - * part of the memory context saved by the enter D3 message IPC ??? - */ -struct sst_module_runtime_context { - dma_addr_t dma_buffer; - u32 *buffer; -}; - -/* - * Audio DSP Module State - */ -enum sst_module_state { - SST_MODULE_STATE_UNLOADED = 0, /* default state */ - SST_MODULE_STATE_LOADED, - SST_MODULE_STATE_INITIALIZED, /* and inactive */ - SST_MODULE_STATE_ACTIVE, -}; - -/* - * Audio DSP Generic Module. - * - * Each Firmware file can consist of 1..N modules. A module can span multiple - * ADSP memory blocks. The simplest FW will be a file with 1 module. A module - * can be instantiated multiple times in the DSP. - */ -struct sst_module { - struct sst_dsp *dsp; - struct sst_fw *sst_fw; /* parent FW we belong too */ - - /* module configuration */ - u32 id; - u32 entry; /* module entry point */ - s32 offset; /* module offset in firmware file */ - u32 size; /* module size */ - u32 scratch_size; /* global scratch memory required */ - u32 persistent_size; /* private memory required */ - enum sst_mem_type type; /* destination memory type */ - u32 data_offset; /* offset in ADSP memory space */ - void *data; /* module data */ - - /* runtime */ - u32 usage_count; /* can be unloaded if count == 0 */ - void *private; /* core doesn't touch this */ - - /* lists */ - struct list_head block_list; /* Module list of blocks in use */ - struct list_head list; /* DSP list of modules */ - struct list_head list_fw; /* FW list of modules */ - struct list_head runtime_list; /* list of runtime module objects*/ - - /* state */ - enum sst_module_state state; -}; - -/* - * SST Memory Block operations. - */ -struct sst_block_ops { - int (*enable)(struct sst_mem_block *block); - int (*disable)(struct sst_mem_block *block); -}; - -/* - * SST Generic Memory Block. - * - * SST ADP memory has multiple IRAM and DRAM blocks. Some ADSP blocks can be - * power gated. - */ -struct sst_mem_block { - struct sst_dsp *dsp; - struct sst_module *module; /* module that uses this block */ - - /* block config */ - u32 offset; /* offset from base */ - u32 size; /* block size */ - u32 index; /* block index 0..N */ - enum sst_mem_type type; /* block memory type IRAM/DRAM */ - const struct sst_block_ops *ops;/* block operations, if any */ - - /* block status */ - u32 bytes_used; /* bytes in use by modules */ - void *private; /* generic core does not touch this */ - int users; /* number of modules using this block */ - - /* block lists */ - struct list_head module_list; /* Module list of blocks */ - struct list_head list; /* Map list of free/used blocks */ -}; - /* * Generic SST Shim Interface. */ @@ -333,51 +164,4 @@ static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst) return sst->thread_context; } -/* Create/Free FW files - can contain multiple modules */ -struct sst_fw *sst_fw_new(struct sst_dsp *dsp, - const struct firmware *fw, void *private); -void sst_fw_free(struct sst_fw *sst_fw); -void sst_fw_free_all(struct sst_dsp *dsp); -int sst_fw_reload(struct sst_fw *sst_fw); -void sst_fw_unload(struct sst_fw *sst_fw); - -/* Create/Free firmware modules */ -struct sst_module *sst_module_new(struct sst_fw *sst_fw, - struct sst_module_template *template, void *private); -void sst_module_free(struct sst_module *sst_module); -struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id); -int sst_module_alloc_blocks(struct sst_module *module); -int sst_module_free_blocks(struct sst_module *module); - -/* Create/Free firmware module runtime instances */ -struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module, - int id, void *private); -void sst_module_runtime_free(struct sst_module_runtime *runtime); -struct sst_module_runtime *sst_module_runtime_get_from_id( - struct sst_module *module, u32 id); -int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime, - int offset); -int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime); -int sst_module_runtime_save(struct sst_module_runtime *runtime, - struct sst_module_runtime_context *context); -int sst_module_runtime_restore(struct sst_module_runtime *runtime, - struct sst_module_runtime_context *context); - -/* generic block allocation */ -int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba, - struct list_head *block_list); -int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list); - -/* scratch allocation */ -int sst_block_alloc_scratch(struct sst_dsp *dsp); -void sst_block_free_scratch(struct sst_dsp *dsp); - -/* Register the DSPs memory blocks - would be nice to read from ACPI */ -struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, - u32 size, enum sst_mem_type type, const struct sst_block_ops *ops, - u32 index, void *private); -void sst_mem_block_unregister_all(struct sst_dsp *dsp); - -u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, - enum sst_mem_type type); #endif diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index d55014587415..ebd0dc5706a0 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -207,13 +207,6 @@ struct sst_pdata { void *dsp; }; -#if IS_ENABLED(CONFIG_DW_DMAC_CORE) -/* Initialization */ -struct sst_dsp *sst_dsp_new(struct device *dev, - struct sst_dsp_device *sst_dev, struct sst_pdata *pdata); -void sst_dsp_free(struct sst_dsp *sst); -#endif - /* SHIM Read / Write */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); @@ -255,14 +248,6 @@ int sst_dsp_wake(struct sst_dsp *sst); void sst_dsp_sleep(struct sst_dsp *sst); void sst_dsp_stall(struct sst_dsp *sst); -/* DMA */ -int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id); -void sst_dsp_dma_put_channel(struct sst_dsp *dsp); -int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr, - dma_addr_t src_addr, size_t size); -int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr, - dma_addr_t src_addr, size_t size); - /* Msg IO */ void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c deleted file mode 100644 index 1189ec37134e..000000000000 --- a/sound/soc/intel/common/sst-firmware.c +++ /dev/null @@ -1,1273 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Intel SST Firmware Loader - * - * Copyright (C) 2013, Intel Corporation. All rights reserved. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* supported DMA engine drivers */ -#include - -#include - -#include "sst-dsp.h" -#include "sst-dsp-priv.h" - -#define SST_DMA_RESOURCES 2 -#define SST_DSP_DMA_MAX_BURST 0x3 -#define SST_HSW_BLOCK_ANY 0xffffffff - -#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000 - -struct sst_dma { - struct sst_dsp *sst; - - struct dw_dma_chip *chip; - - struct dma_async_tx_descriptor *desc; - struct dma_chan *ch; -}; - -static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) -{ - u32 tmp = 0; - int i, m, n; - const u8 *src_byte = src; - - m = bytes / 4; - n = bytes % 4; - - /* __iowrite32_copy use 32bit size values so divide by 4 */ - __iowrite32_copy((void *)dest, src, m); - - if (n) { - for (i = 0; i < n; i++) - tmp |= (u32)*(src_byte + m * 4 + i) << (i * 8); - __iowrite32_copy((void *)(dest + m * 4), &tmp, 1); - } - -} - -static void sst_dma_transfer_complete(void *arg) -{ - struct sst_dsp *sst = (struct sst_dsp *)arg; - - dev_dbg(sst->dev, "DMA: callback\n"); -} - -static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr, - dma_addr_t src_addr, size_t size) -{ - struct dma_async_tx_descriptor *desc; - struct sst_dma *dma = sst->dma; - - if (dma->ch == NULL) { - dev_err(sst->dev, "error: no DMA channel\n"); - return -ENODEV; - } - - dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n", - (unsigned long)src_addr, (unsigned long)dest_addr, size); - - desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr, - src_addr, size, DMA_CTRL_ACK); - if (!desc){ - dev_err(sst->dev, "error: dma prep memcpy failed\n"); - return -EINVAL; - } - - desc->callback = sst_dma_transfer_complete; - desc->callback_param = sst; - - desc->tx_submit(desc); - dma_wait_for_async_tx(desc); - - return 0; -} - -/* copy to DSP */ -int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr, - dma_addr_t src_addr, size_t size) -{ - return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP, - src_addr, size); -} -EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto); - -/* copy from DSP */ -int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr, - dma_addr_t src_addr, size_t size) -{ - return sst_dsp_dma_copy(sst, dest_addr, - src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size); -} -EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom); - -/* remove module from memory - callers hold locks */ -static void block_list_remove(struct sst_dsp *dsp, - struct list_head *block_list) -{ - struct sst_mem_block *block, *tmp; - int err; - - /* disable each block */ - list_for_each_entry(block, block_list, module_list) { - - if (block->ops && block->ops->disable) { - err = block->ops->disable(block); - if (err < 0) - dev_err(dsp->dev, - "error: can't disable block %d:%d\n", - block->type, block->index); - } - } - - /* mark each block as free */ - list_for_each_entry_safe(block, tmp, block_list, module_list) { - list_del(&block->module_list); - list_move(&block->list, &dsp->free_block_list); - dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n", - block->type, block->index, block->offset); - } -} - -/* prepare the memory block to receive data from host - callers hold locks */ -static int block_list_prepare(struct sst_dsp *dsp, - struct list_head *block_list) -{ - struct sst_mem_block *block; - int ret = 0; - - /* enable each block so that's it'e ready for data */ - list_for_each_entry(block, block_list, module_list) { - - if (block->ops && block->ops->enable && !block->users) { - ret = block->ops->enable(block); - if (ret < 0) { - dev_err(dsp->dev, - "error: can't disable block %d:%d\n", - block->type, block->index); - goto err; - } - } - } - return ret; - -err: - list_for_each_entry(block, block_list, module_list) { - if (block->ops && block->ops->disable) - block->ops->disable(block); - } - return ret; -} - -static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem, - int irq) -{ - struct dw_dma_chip *chip; - int err; - - chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); - if (!chip) - return ERR_PTR(-ENOMEM); - - chip->irq = irq; - chip->regs = devm_ioremap_resource(dev, mem); - if (IS_ERR(chip->regs)) - return ERR_CAST(chip->regs); - - err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31)); - if (err) - return ERR_PTR(err); - - chip->dev = dev; - - err = dw_dma_probe(chip); - if (err) - return ERR_PTR(err); - - return chip; -} - -static void dw_remove(struct dw_dma_chip *chip) -{ - dw_dma_remove(chip); -} - -static bool dma_chan_filter(struct dma_chan *chan, void *param) -{ - struct sst_dsp *dsp = (struct sst_dsp *)param; - - return chan->device->dev == dsp->dma_dev; -} - -int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id) -{ - struct sst_dma *dma = dsp->dma; - struct dma_slave_config slave; - dma_cap_mask_t mask; - int ret; - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_MEMCPY, mask); - - dma->ch = dma_request_channel(mask, dma_chan_filter, dsp); - if (dma->ch == NULL) { - dev_err(dsp->dev, "error: DMA request channel failed\n"); - return -EIO; - } - - memset(&slave, 0, sizeof(slave)); - slave.direction = DMA_MEM_TO_DEV; - slave.src_addr_width = - slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; - slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST; - - ret = dmaengine_slave_config(dma->ch, &slave); - if (ret) { - dev_err(dsp->dev, "error: unable to set DMA slave config %d\n", - ret); - dma_release_channel(dma->ch); - dma->ch = NULL; - } - - return ret; -} -EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel); - -void sst_dsp_dma_put_channel(struct sst_dsp *dsp) -{ - struct sst_dma *dma = dsp->dma; - - if (!dma->ch) - return; - - dma_release_channel(dma->ch); - dma->ch = NULL; -} -EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel); - -static int sst_dma_new(struct sst_dsp *sst) -{ - struct sst_pdata *sst_pdata = sst->pdata; - struct sst_dma *dma; - struct resource mem; - int ret = 0; - - if (sst->pdata->resindex_dma_base == -1) - /* DMA is not used, return and squelsh error messages */ - return 0; - - /* configure the correct platform data for whatever DMA engine - * is attached to the ADSP IP. */ - switch (sst->pdata->dma_engine) { - case SST_DMA_TYPE_DW: - break; - default: - dev_err(sst->dev, "error: invalid DMA engine %d\n", - sst->pdata->dma_engine); - return -EINVAL; - } - - dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL); - if (!dma) - return -ENOMEM; - - dma->sst = sst; - - memset(&mem, 0, sizeof(mem)); - - mem.start = sst->addr.lpe_base + sst_pdata->dma_base; - mem.end = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1; - mem.flags = IORESOURCE_MEM; - - /* now register DMA engine device */ - dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq); - if (IS_ERR(dma->chip)) { - dev_err(sst->dev, "error: DMA device register failed\n"); - ret = PTR_ERR(dma->chip); - goto err_dma_dev; - } - - sst->dma = dma; - sst->fw_use_dma = true; - return 0; - -err_dma_dev: - devm_kfree(sst->dev, dma); - return ret; -} - -static void sst_dma_free(struct sst_dma *dma) -{ - - if (dma == NULL) - return; - - if (dma->ch) - dma_release_channel(dma->ch); - - if (dma->chip) - dw_remove(dma->chip); - -} - -/* create new generic firmware object */ -struct sst_fw *sst_fw_new(struct sst_dsp *dsp, - const struct firmware *fw, void *private) -{ - struct sst_fw *sst_fw; - int err; - - if (!dsp->ops->parse_fw) - return NULL; - - sst_fw = kzalloc(sizeof(*sst_fw), GFP_KERNEL); - if (sst_fw == NULL) - return NULL; - - sst_fw->dsp = dsp; - sst_fw->private = private; - sst_fw->size = fw->size; - - /* allocate DMA buffer to store FW data */ - sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size, - &sst_fw->dmable_fw_paddr, GFP_KERNEL); - if (!sst_fw->dma_buf) { - dev_err(dsp->dev, "error: DMA alloc failed\n"); - kfree(sst_fw); - return NULL; - } - - /* copy FW data to DMA-able memory */ - memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size); - - if (dsp->fw_use_dma) { - err = sst_dsp_dma_get_channel(dsp, 0); - if (err < 0) - goto chan_err; - } - - /* call core specific FW paser to load FW data into DSP */ - err = dsp->ops->parse_fw(sst_fw); - if (err < 0) { - dev_err(dsp->dev, "error: parse fw failed %d\n", err); - goto parse_err; - } - - if (dsp->fw_use_dma) - sst_dsp_dma_put_channel(dsp); - - mutex_lock(&dsp->mutex); - list_add(&sst_fw->list, &dsp->fw_list); - mutex_unlock(&dsp->mutex); - - return sst_fw; - -parse_err: - if (dsp->fw_use_dma) - sst_dsp_dma_put_channel(dsp); -chan_err: - dma_free_coherent(dsp->dma_dev, sst_fw->size, - sst_fw->dma_buf, - sst_fw->dmable_fw_paddr); - sst_fw->dma_buf = NULL; - kfree(sst_fw); - return NULL; -} -EXPORT_SYMBOL_GPL(sst_fw_new); - -int sst_fw_reload(struct sst_fw *sst_fw) -{ - struct sst_dsp *dsp = sst_fw->dsp; - int ret; - - dev_dbg(dsp->dev, "reloading firmware\n"); - - /* call core specific FW paser to load FW data into DSP */ - ret = dsp->ops->parse_fw(sst_fw); - if (ret < 0) - dev_err(dsp->dev, "error: parse fw failed %d\n", ret); - - return ret; -} -EXPORT_SYMBOL_GPL(sst_fw_reload); - -void sst_fw_unload(struct sst_fw *sst_fw) -{ - struct sst_dsp *dsp = sst_fw->dsp; - struct sst_module *module, *mtmp; - struct sst_module_runtime *runtime, *rtmp; - - dev_dbg(dsp->dev, "unloading firmware\n"); - - mutex_lock(&dsp->mutex); - - /* check module by module */ - list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) { - if (module->sst_fw == sst_fw) { - - /* remove runtime modules */ - list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) { - - block_list_remove(dsp, &runtime->block_list); - list_del(&runtime->list); - kfree(runtime); - } - - /* now remove the module */ - block_list_remove(dsp, &module->block_list); - list_del(&module->list); - kfree(module); - } - } - - /* remove all scratch blocks */ - block_list_remove(dsp, &dsp->scratch_block_list); - - mutex_unlock(&dsp->mutex); -} -EXPORT_SYMBOL_GPL(sst_fw_unload); - -/* free single firmware object */ -void sst_fw_free(struct sst_fw *sst_fw) -{ - struct sst_dsp *dsp = sst_fw->dsp; - - mutex_lock(&dsp->mutex); - list_del(&sst_fw->list); - mutex_unlock(&dsp->mutex); - - if (sst_fw->dma_buf) - dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf, - sst_fw->dmable_fw_paddr); - kfree(sst_fw); -} -EXPORT_SYMBOL_GPL(sst_fw_free); - -/* free all firmware objects */ -void sst_fw_free_all(struct sst_dsp *dsp) -{ - struct sst_fw *sst_fw, *t; - - mutex_lock(&dsp->mutex); - list_for_each_entry_safe(sst_fw, t, &dsp->fw_list, list) { - - list_del(&sst_fw->list); - dma_free_coherent(dsp->dev, sst_fw->size, sst_fw->dma_buf, - sst_fw->dmable_fw_paddr); - kfree(sst_fw); - } - mutex_unlock(&dsp->mutex); -} -EXPORT_SYMBOL_GPL(sst_fw_free_all); - -/* create a new SST generic module from FW template */ -struct sst_module *sst_module_new(struct sst_fw *sst_fw, - struct sst_module_template *template, void *private) -{ - struct sst_dsp *dsp = sst_fw->dsp; - struct sst_module *sst_module; - - sst_module = kzalloc(sizeof(*sst_module), GFP_KERNEL); - if (sst_module == NULL) - return NULL; - - sst_module->id = template->id; - sst_module->dsp = dsp; - sst_module->sst_fw = sst_fw; - sst_module->scratch_size = template->scratch_size; - sst_module->persistent_size = template->persistent_size; - sst_module->entry = template->entry; - sst_module->state = SST_MODULE_STATE_UNLOADED; - - INIT_LIST_HEAD(&sst_module->block_list); - INIT_LIST_HEAD(&sst_module->runtime_list); - - mutex_lock(&dsp->mutex); - list_add(&sst_module->list, &dsp->module_list); - mutex_unlock(&dsp->mutex); - - return sst_module; -} -EXPORT_SYMBOL_GPL(sst_module_new); - -/* free firmware module and remove from available list */ -void sst_module_free(struct sst_module *sst_module) -{ - struct sst_dsp *dsp = sst_module->dsp; - - mutex_lock(&dsp->mutex); - list_del(&sst_module->list); - mutex_unlock(&dsp->mutex); - - kfree(sst_module); -} -EXPORT_SYMBOL_GPL(sst_module_free); - -struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module, - int id, void *private) -{ - struct sst_dsp *dsp = module->dsp; - struct sst_module_runtime *runtime; - - runtime = kzalloc(sizeof(*runtime), GFP_KERNEL); - if (runtime == NULL) - return NULL; - - runtime->id = id; - runtime->dsp = dsp; - runtime->module = module; - INIT_LIST_HEAD(&runtime->block_list); - - mutex_lock(&dsp->mutex); - list_add(&runtime->list, &module->runtime_list); - mutex_unlock(&dsp->mutex); - - return runtime; -} -EXPORT_SYMBOL_GPL(sst_module_runtime_new); - -void sst_module_runtime_free(struct sst_module_runtime *runtime) -{ - struct sst_dsp *dsp = runtime->dsp; - - mutex_lock(&dsp->mutex); - list_del(&runtime->list); - mutex_unlock(&dsp->mutex); - - kfree(runtime); -} -EXPORT_SYMBOL_GPL(sst_module_runtime_free); - -static struct sst_mem_block *find_block(struct sst_dsp *dsp, - struct sst_block_allocator *ba) -{ - struct sst_mem_block *block; - - list_for_each_entry(block, &dsp->free_block_list, list) { - if (block->type == ba->type && block->offset == ba->offset) - return block; - } - - return NULL; -} - -/* Block allocator must be on block boundary */ -static int block_alloc_contiguous(struct sst_dsp *dsp, - struct sst_block_allocator *ba, struct list_head *block_list) -{ - struct list_head tmp = LIST_HEAD_INIT(tmp); - struct sst_mem_block *block; - u32 block_start = SST_HSW_BLOCK_ANY; - int size = ba->size, offset = ba->offset; - - while (ba->size > 0) { - - block = find_block(dsp, ba); - if (!block) { - list_splice(&tmp, &dsp->free_block_list); - - ba->size = size; - ba->offset = offset; - return -ENOMEM; - } - - list_move_tail(&block->list, &tmp); - ba->offset += block->size; - ba->size -= block->size; - } - ba->size = size; - ba->offset = offset; - - list_for_each_entry(block, &tmp, list) { - - if (block->offset < block_start) - block_start = block->offset; - - list_add(&block->module_list, block_list); - - dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", - block->type, block->index, block->offset); - } - - list_splice(&tmp, &dsp->used_block_list); - return 0; -} - -/* allocate first free DSP blocks for data - callers hold locks */ -static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba, - struct list_head *block_list) -{ - struct sst_mem_block *block, *tmp; - int ret = 0; - - if (ba->size == 0) - return 0; - - /* find first free whole blocks that can hold module */ - list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { - - /* ignore blocks with wrong type */ - if (block->type != ba->type) - continue; - - if (ba->size > block->size) - continue; - - ba->offset = block->offset; - block->bytes_used = ba->size % block->size; - list_add(&block->module_list, block_list); - list_move(&block->list, &dsp->used_block_list); - dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", - block->type, block->index, block->offset); - return 0; - } - - /* then find free multiple blocks that can hold module */ - list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { - - /* ignore blocks with wrong type */ - if (block->type != ba->type) - continue; - - /* do we span > 1 blocks */ - if (ba->size > block->size) { - - /* align ba to block boundary */ - ba->offset = block->offset; - - ret = block_alloc_contiguous(dsp, ba, block_list); - if (ret == 0) - return ret; - - } - } - - /* not enough free block space */ - return -ENOMEM; -} - -int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba, - struct list_head *block_list) -{ - int ret; - - dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n", - ba->size, ba->offset, ba->type); - - mutex_lock(&dsp->mutex); - - ret = block_alloc(dsp, ba, block_list); - if (ret < 0) { - dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret); - goto out; - } - - /* prepare DSP blocks for module usage */ - ret = block_list_prepare(dsp, block_list); - if (ret < 0) - dev_err(dsp->dev, "error: prepare failed\n"); - -out: - mutex_unlock(&dsp->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(sst_alloc_blocks); - -int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list) -{ - mutex_lock(&dsp->mutex); - block_list_remove(dsp, block_list); - mutex_unlock(&dsp->mutex); - return 0; -} -EXPORT_SYMBOL_GPL(sst_free_blocks); - -/* allocate memory blocks for static module addresses - callers hold locks */ -static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba, - struct list_head *block_list) -{ - struct sst_mem_block *block, *tmp; - struct sst_block_allocator ba_tmp = *ba; - u32 end = ba->offset + ba->size, block_end; - int err; - - /* only IRAM/DRAM blocks are managed */ - if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM) - return 0; - - /* are blocks already attached to this module */ - list_for_each_entry_safe(block, tmp, block_list, module_list) { - - /* ignore blocks with wrong type */ - if (block->type != ba->type) - continue; - - block_end = block->offset + block->size; - - /* find block that holds section */ - if (ba->offset >= block->offset && end <= block_end) - return 0; - - /* does block span more than 1 section */ - if (ba->offset >= block->offset && ba->offset < block_end) { - - /* align ba to block boundary */ - ba_tmp.size -= block_end - ba->offset; - ba_tmp.offset = block_end; - err = block_alloc_contiguous(dsp, &ba_tmp, block_list); - if (err < 0) - return -ENOMEM; - - /* module already owns blocks */ - return 0; - } - } - - /* find first free blocks that can hold section in free list */ - list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { - block_end = block->offset + block->size; - - /* ignore blocks with wrong type */ - if (block->type != ba->type) - continue; - - /* find block that holds section */ - if (ba->offset >= block->offset && end <= block_end) { - - /* add block */ - list_move(&block->list, &dsp->used_block_list); - list_add(&block->module_list, block_list); - dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n", - block->type, block->index, block->offset); - return 0; - } - - /* does block span more than 1 section */ - if (ba->offset >= block->offset && ba->offset < block_end) { - - /* add block */ - list_move(&block->list, &dsp->used_block_list); - list_add(&block->module_list, block_list); - /* align ba to block boundary */ - ba_tmp.size -= block_end - ba->offset; - ba_tmp.offset = block_end; - - err = block_alloc_contiguous(dsp, &ba_tmp, block_list); - if (err < 0) - return -ENOMEM; - - return 0; - } - } - - return -ENOMEM; -} - -/* Load fixed module data into DSP memory blocks */ -int sst_module_alloc_blocks(struct sst_module *module) -{ - struct sst_dsp *dsp = module->dsp; - struct sst_fw *sst_fw = module->sst_fw; - struct sst_block_allocator ba; - int ret; - - memset(&ba, 0, sizeof(ba)); - ba.size = module->size; - ba.type = module->type; - ba.offset = module->offset; - - dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n", - ba.size, ba.offset, ba.type); - - mutex_lock(&dsp->mutex); - - /* alloc blocks that includes this section */ - ret = block_alloc_fixed(dsp, &ba, &module->block_list); - if (ret < 0) { - dev_err(dsp->dev, - "error: no free blocks for section at offset 0x%x size 0x%x\n", - module->offset, module->size); - mutex_unlock(&dsp->mutex); - return -ENOMEM; - } - - /* prepare DSP blocks for module copy */ - ret = block_list_prepare(dsp, &module->block_list); - if (ret < 0) { - dev_err(dsp->dev, "error: fw module prepare failed\n"); - goto err; - } - - /* copy partial module data to blocks */ - if (dsp->fw_use_dma) { - ret = sst_dsp_dma_copyto(dsp, - dsp->addr.lpe_base + module->offset, - sst_fw->dmable_fw_paddr + module->data_offset, - module->size); - if (ret < 0) { - dev_err(dsp->dev, "error: module copy failed\n"); - goto err; - } - } else - sst_memcpy32(dsp->addr.lpe + module->offset, module->data, - module->size); - - mutex_unlock(&dsp->mutex); - return ret; - -err: - block_list_remove(dsp, &module->block_list); - mutex_unlock(&dsp->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(sst_module_alloc_blocks); - -/* Unload entire module from DSP memory */ -int sst_module_free_blocks(struct sst_module *module) -{ - struct sst_dsp *dsp = module->dsp; - - mutex_lock(&dsp->mutex); - block_list_remove(dsp, &module->block_list); - mutex_unlock(&dsp->mutex); - return 0; -} -EXPORT_SYMBOL_GPL(sst_module_free_blocks); - -int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime, - int offset) -{ - struct sst_dsp *dsp = runtime->dsp; - struct sst_module *module = runtime->module; - struct sst_block_allocator ba; - int ret; - - if (module->persistent_size == 0) - return 0; - - memset(&ba, 0, sizeof(ba)); - ba.size = module->persistent_size; - ba.type = SST_MEM_DRAM; - - mutex_lock(&dsp->mutex); - - /* do we need to allocate at a fixed address ? */ - if (offset != 0) { - - ba.offset = offset; - - dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n", - ba.size, ba.type, ba.offset); - - /* alloc blocks that includes this section */ - ret = block_alloc_fixed(dsp, &ba, &runtime->block_list); - - } else { - dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n", - ba.size, ba.type); - - /* alloc blocks that includes this section */ - ret = block_alloc(dsp, &ba, &runtime->block_list); - } - if (ret < 0) { - dev_err(dsp->dev, - "error: no free blocks for runtime module size 0x%x\n", - module->persistent_size); - mutex_unlock(&dsp->mutex); - return -ENOMEM; - } - runtime->persistent_offset = ba.offset; - - /* prepare DSP blocks for module copy */ - ret = block_list_prepare(dsp, &runtime->block_list); - if (ret < 0) { - dev_err(dsp->dev, "error: runtime block prepare failed\n"); - goto err; - } - - mutex_unlock(&dsp->mutex); - return ret; - -err: - block_list_remove(dsp, &module->block_list); - mutex_unlock(&dsp->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks); - -int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime) -{ - struct sst_dsp *dsp = runtime->dsp; - - mutex_lock(&dsp->mutex); - block_list_remove(dsp, &runtime->block_list); - mutex_unlock(&dsp->mutex); - return 0; -} -EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks); - -int sst_module_runtime_save(struct sst_module_runtime *runtime, - struct sst_module_runtime_context *context) -{ - struct sst_dsp *dsp = runtime->dsp; - struct sst_module *module = runtime->module; - int ret = 0; - - dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n", - runtime->id, runtime->persistent_offset, - module->persistent_size); - - context->buffer = dma_alloc_coherent(dsp->dma_dev, - module->persistent_size, - &context->dma_buffer, GFP_DMA | GFP_KERNEL); - if (!context->buffer) { - dev_err(dsp->dev, "error: DMA context alloc failed\n"); - return -ENOMEM; - } - - mutex_lock(&dsp->mutex); - - if (dsp->fw_use_dma) { - - ret = sst_dsp_dma_get_channel(dsp, 0); - if (ret < 0) - goto err; - - ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer, - dsp->addr.lpe_base + runtime->persistent_offset, - module->persistent_size); - sst_dsp_dma_put_channel(dsp); - if (ret < 0) { - dev_err(dsp->dev, "error: context copy failed\n"); - goto err; - } - } else - sst_memcpy32(context->buffer, dsp->addr.lpe + - runtime->persistent_offset, - module->persistent_size); - -err: - mutex_unlock(&dsp->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(sst_module_runtime_save); - -int sst_module_runtime_restore(struct sst_module_runtime *runtime, - struct sst_module_runtime_context *context) -{ - struct sst_dsp *dsp = runtime->dsp; - struct sst_module *module = runtime->module; - int ret = 0; - - dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n", - runtime->id, runtime->persistent_offset, - module->persistent_size); - - mutex_lock(&dsp->mutex); - - if (!context->buffer) { - dev_info(dsp->dev, "no context buffer need to restore!\n"); - goto err; - } - - if (dsp->fw_use_dma) { - - ret = sst_dsp_dma_get_channel(dsp, 0); - if (ret < 0) - goto err; - - ret = sst_dsp_dma_copyto(dsp, - dsp->addr.lpe_base + runtime->persistent_offset, - context->dma_buffer, module->persistent_size); - sst_dsp_dma_put_channel(dsp); - if (ret < 0) { - dev_err(dsp->dev, "error: module copy failed\n"); - goto err; - } - } else - sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset, - context->buffer, module->persistent_size); - - dma_free_coherent(dsp->dma_dev, module->persistent_size, - context->buffer, context->dma_buffer); - context->buffer = NULL; - -err: - mutex_unlock(&dsp->mutex); - return ret; -} -EXPORT_SYMBOL_GPL(sst_module_runtime_restore); - -/* register a DSP memory block for use with FW based modules */ -struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset, - u32 size, enum sst_mem_type type, const struct sst_block_ops *ops, - u32 index, void *private) -{ - struct sst_mem_block *block; - - block = kzalloc(sizeof(*block), GFP_KERNEL); - if (block == NULL) - return NULL; - - block->offset = offset; - block->size = size; - block->index = index; - block->type = type; - block->dsp = dsp; - block->private = private; - block->ops = ops; - - mutex_lock(&dsp->mutex); - list_add(&block->list, &dsp->free_block_list); - mutex_unlock(&dsp->mutex); - - return block; -} -EXPORT_SYMBOL_GPL(sst_mem_block_register); - -/* unregister all DSP memory blocks */ -void sst_mem_block_unregister_all(struct sst_dsp *dsp) -{ - struct sst_mem_block *block, *tmp; - - mutex_lock(&dsp->mutex); - - /* unregister used blocks */ - list_for_each_entry_safe(block, tmp, &dsp->used_block_list, list) { - list_del(&block->list); - kfree(block); - } - - /* unregister free blocks */ - list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) { - list_del(&block->list); - kfree(block); - } - - mutex_unlock(&dsp->mutex); -} -EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all); - -/* allocate scratch buffer blocks */ -int sst_block_alloc_scratch(struct sst_dsp *dsp) -{ - struct sst_module *module; - struct sst_block_allocator ba; - int ret; - - mutex_lock(&dsp->mutex); - - /* calculate required scratch size */ - dsp->scratch_size = 0; - list_for_each_entry(module, &dsp->module_list, list) { - dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n", - module->id, module->scratch_size); - if (dsp->scratch_size < module->scratch_size) - dsp->scratch_size = module->scratch_size; - } - - dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n", - dsp->scratch_size); - - if (dsp->scratch_size == 0) { - dev_info(dsp->dev, "no modules need scratch buffer\n"); - mutex_unlock(&dsp->mutex); - return 0; - } - - /* allocate blocks for module scratch buffers */ - dev_dbg(dsp->dev, "allocating scratch blocks\n"); - - ba.size = dsp->scratch_size; - ba.type = SST_MEM_DRAM; - - /* do we need to allocate at fixed offset */ - if (dsp->scratch_offset != 0) { - - dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n", - ba.size, ba.type, ba.offset); - - ba.offset = dsp->scratch_offset; - - /* alloc blocks that includes this section */ - ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list); - - } else { - dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n", - ba.size, ba.type); - - ba.offset = 0; - ret = block_alloc(dsp, &ba, &dsp->scratch_block_list); - } - if (ret < 0) { - dev_err(dsp->dev, "error: can't alloc scratch blocks\n"); - mutex_unlock(&dsp->mutex); - return ret; - } - - ret = block_list_prepare(dsp, &dsp->scratch_block_list); - if (ret < 0) { - dev_err(dsp->dev, "error: scratch block prepare failed\n"); - mutex_unlock(&dsp->mutex); - return ret; - } - - /* assign the same offset of scratch to each module */ - dsp->scratch_offset = ba.offset; - mutex_unlock(&dsp->mutex); - return dsp->scratch_size; -} -EXPORT_SYMBOL_GPL(sst_block_alloc_scratch); - -/* free all scratch blocks */ -void sst_block_free_scratch(struct sst_dsp *dsp) -{ - mutex_lock(&dsp->mutex); - block_list_remove(dsp, &dsp->scratch_block_list); - mutex_unlock(&dsp->mutex); -} -EXPORT_SYMBOL_GPL(sst_block_free_scratch); - -/* get a module from it's unique ID */ -struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id) -{ - struct sst_module *module; - - mutex_lock(&dsp->mutex); - - list_for_each_entry(module, &dsp->module_list, list) { - if (module->id == id) { - mutex_unlock(&dsp->mutex); - return module; - } - } - - mutex_unlock(&dsp->mutex); - return NULL; -} -EXPORT_SYMBOL_GPL(sst_module_get_from_id); - -struct sst_module_runtime *sst_module_runtime_get_from_id( - struct sst_module *module, u32 id) -{ - struct sst_module_runtime *runtime; - struct sst_dsp *dsp = module->dsp; - - mutex_lock(&dsp->mutex); - - list_for_each_entry(runtime, &module->runtime_list, list) { - if (runtime->id == id) { - mutex_unlock(&dsp->mutex); - return runtime; - } - } - - mutex_unlock(&dsp->mutex); - return NULL; -} -EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id); - -/* returns block address in DSP address space */ -u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset, - enum sst_mem_type type) -{ - switch (type) { - case SST_MEM_IRAM: - return offset - dsp->addr.iram_offset + - dsp->addr.dsp_iram_offset; - case SST_MEM_DRAM: - return offset - dsp->addr.dram_offset + - dsp->addr.dsp_dram_offset; - default: - return 0; - } -} -EXPORT_SYMBOL_GPL(sst_dsp_get_offset); - -struct sst_dsp *sst_dsp_new(struct device *dev, - struct sst_dsp_device *sst_dev, struct sst_pdata *pdata) -{ - struct sst_dsp *sst; - int err; - - dev_dbg(dev, "initialising audio DSP id 0x%x\n", pdata->id); - - sst = devm_kzalloc(dev, sizeof(*sst), GFP_KERNEL); - if (sst == NULL) - return NULL; - - spin_lock_init(&sst->spinlock); - mutex_init(&sst->mutex); - sst->dev = dev; - sst->dma_dev = pdata->dma_dev; - sst->thread_context = sst_dev->thread_context; - sst->sst_dev = sst_dev; - sst->id = pdata->id; - sst->irq = pdata->irq; - sst->ops = sst_dev->ops; - sst->pdata = pdata; - INIT_LIST_HEAD(&sst->used_block_list); - INIT_LIST_HEAD(&sst->free_block_list); - INIT_LIST_HEAD(&sst->module_list); - INIT_LIST_HEAD(&sst->fw_list); - INIT_LIST_HEAD(&sst->scratch_block_list); - - /* Initialise SST Audio DSP */ - if (sst->ops->init) { - err = sst->ops->init(sst, pdata); - if (err < 0) - return NULL; - } - - /* Register the ISR */ - err = request_threaded_irq(sst->irq, sst->ops->irq_handler, - sst_dev->thread, IRQF_SHARED, "AudioDSP", sst); - if (err) - goto irq_err; - - err = sst_dma_new(sst); - if (err) { - dev_err(dev, "sst_dma_new failed %d\n", err); - goto dma_err; - } - - return sst; - -dma_err: - free_irq(sst->irq, sst); -irq_err: - if (sst->ops->free) - sst->ops->free(sst); - - return NULL; -} -EXPORT_SYMBOL_GPL(sst_dsp_new); - -void sst_dsp_free(struct sst_dsp *sst) -{ - free_irq(sst->irq, sst); - if (sst->ops->free) - sst->ops->free(sst); - - sst_dma_free(sst->dma); -} -EXPORT_SYMBOL_GPL(sst_dsp_free); - -MODULE_DESCRIPTION("Intel SST Firmware Loader"); -MODULE_LICENSE("GPL v2"); -- cgit From a4bebce26d560a4a1dff557ad7822bab90dd1c3f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:01 +0200 Subject: ASoC: Intel: Skylake: Unassign ram_read and read_write ops Skylake driver makes no use of ram_read or ram_write operation so remove the assignments. This prepares sound/soc/common/sst-dsp* for following removal of unused DSP operations. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-8-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/skylake/bxt-sst.c | 2 -- sound/soc/intel/skylake/cnl-sst.c | 2 -- sound/soc/intel/skylake/skl-sst.c | 2 -- 3 files changed, 6 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c index 38b9d7494083..fd4fdcb95224 100644 --- a/sound/soc/intel/skylake/bxt-sst.c +++ b/sound/soc/intel/skylake/bxt-sst.c @@ -533,8 +533,6 @@ static struct sst_ops skl_ops = { .irq_handler = skl_dsp_sst_interrupt, .write = sst_shim32_write, .read = sst_shim32_read, - .ram_read = sst_memcpy_fromio_32, - .ram_write = sst_memcpy_toio_32, .free = skl_dsp_free, }; diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index c6abcd5aa67b..355a7b116886 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -300,8 +300,6 @@ static struct sst_ops cnl_ops = { .irq_handler = cnl_dsp_sst_interrupt, .write = sst_shim32_write, .read = sst_shim32_read, - .ram_read = sst_memcpy_fromio_32, - .ram_write = sst_memcpy_toio_32, .free = cnl_dsp_free, }; diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c index 00a97cea58b4..39d027ac16ec 100644 --- a/sound/soc/intel/skylake/skl-sst.c +++ b/sound/soc/intel/skylake/skl-sst.c @@ -506,8 +506,6 @@ static struct sst_ops skl_ops = { .irq_handler = skl_dsp_sst_interrupt, .write = sst_shim32_write, .read = sst_shim32_read, - .ram_read = sst_memcpy_fromio_32, - .ram_write = sst_memcpy_toio_32, .free = skl_dsp_free, }; -- cgit From 37465972015cf7aeb586a9245da2a87d3b531959 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:02 +0200 Subject: ASoC: Intel: Remove unused DSP operations sound/soc/intel/common/ declares several helper functions for /intel/ solutions. In practice, differences between these - /haswell/ and /skylake/ especially - led to many of the helpers being used only by a single solution. As /skylake/ makes no use of these and /haswell/ and /baytail/ are no more, remove the unused functions. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-9-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 13 --- sound/soc/intel/common/sst-dsp.c | 162 ---------------------------------- sound/soc/intel/common/sst-dsp.h | 26 ------ sound/soc/intel/common/sst-ipc.c | 27 ------ sound/soc/intel/common/sst-ipc.h | 1 - 5 files changed, 229 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 6b6738a289dc..3787ef07b840 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -146,19 +146,6 @@ struct sst_dsp { struct snd_dma_buffer dmab; }; -/* Size optimised DRAM/IRAM memcpy */ -static inline void sst_dsp_write(struct sst_dsp *sst, void *src, - u32 dest_offset, size_t bytes) -{ - sst->ops->ram_write(sst, sst->addr.lpe + dest_offset, src, bytes); -} - -static inline void sst_dsp_read(struct sst_dsp *sst, void *dest, - u32 src_offset, size_t bytes) -{ - sst->ops->ram_read(sst, dest, sst->addr.lpe + src_offset, bytes); -} - static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst) { return sst->thread_context; diff --git a/sound/soc/intel/common/sst-dsp.c b/sound/soc/intel/common/sst-dsp.c index 36c077aa386e..229d09d61afb 100644 --- a/sound/soc/intel/common/sst-dsp.c +++ b/sound/soc/intel/common/sst-dsp.c @@ -44,38 +44,6 @@ u64 sst_shim32_read64(void __iomem *addr, u32 offset) } EXPORT_SYMBOL_GPL(sst_shim32_read64); -static inline void _sst_memcpy_toio_32(volatile u32 __iomem *dest, - u32 *src, size_t bytes) -{ - int i, words = bytes >> 2; - - for (i = 0; i < words; i++) - writel(src[i], dest + i); -} - -static inline void _sst_memcpy_fromio_32(u32 *dest, - const volatile __iomem u32 *src, size_t bytes) -{ - int i, words = bytes >> 2; - - for (i = 0; i < words; i++) - dest[i] = readl(src + i); -} - -void sst_memcpy_toio_32(struct sst_dsp *sst, - void __iomem *dest, void *src, size_t bytes) -{ - _sst_memcpy_toio_32(dest, src, bytes); -} -EXPORT_SYMBOL_GPL(sst_memcpy_toio_32); - -void sst_memcpy_fromio_32(struct sst_dsp *sst, void *dest, - void __iomem *src, size_t bytes) -{ - _sst_memcpy_fromio_32(dest, src, bytes); -} -EXPORT_SYMBOL_GPL(sst_memcpy_fromio_32); - /* Public API */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value) { @@ -100,29 +68,6 @@ u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset) } EXPORT_SYMBOL_GPL(sst_dsp_shim_read); -void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value) -{ - unsigned long flags; - - spin_lock_irqsave(&sst->spinlock, flags); - sst->ops->write64(sst->addr.shim, offset, value); - spin_unlock_irqrestore(&sst->spinlock, flags); -} -EXPORT_SYMBOL_GPL(sst_dsp_shim_write64); - -u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset) -{ - unsigned long flags; - u64 val; - - spin_lock_irqsave(&sst->spinlock, flags); - val = sst->ops->read64(sst->addr.shim, offset); - spin_unlock_irqrestore(&sst->spinlock, flags); - - return val; -} -EXPORT_SYMBOL_GPL(sst_dsp_shim_read64); - void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value) { sst->ops->write(sst->addr.shim, offset, value); @@ -135,18 +80,6 @@ u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset) } EXPORT_SYMBOL_GPL(sst_dsp_shim_read_unlocked); -void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value) -{ - sst->ops->write64(sst->addr.shim, offset, value); -} -EXPORT_SYMBOL_GPL(sst_dsp_shim_write64_unlocked); - -u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset) -{ - return sst->ops->read64(sst->addr.shim, offset); -} -EXPORT_SYMBOL_GPL(sst_dsp_shim_read64_unlocked); - int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, u32 mask, u32 value) { @@ -167,24 +100,6 @@ int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, } EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits_unlocked); -int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, - u64 mask, u64 value) -{ - bool change; - u64 old, new; - - old = sst_dsp_shim_read64_unlocked(sst, offset); - - new = (old & (~mask)) | (value & mask); - - change = (old != new); - if (change) - sst_dsp_shim_write64_unlocked(sst, offset, new); - - return change; -} -EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64_unlocked); - /* This is for registers bits with attribute RWC */ void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset, u32 mask, u32 value) @@ -214,19 +129,6 @@ int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, } EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits); -int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, - u64 mask, u64 value) -{ - unsigned long flags; - bool change; - - spin_lock_irqsave(&sst->spinlock, flags); - change = sst_dsp_shim_update_bits64_unlocked(sst, offset, mask, value); - spin_unlock_irqrestore(&sst->spinlock, flags); - return change; -} -EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); - /* This is for registers bits with attribute RWC */ void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset, u32 mask, u32 value) @@ -279,70 +181,6 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, } EXPORT_SYMBOL_GPL(sst_dsp_register_poll); -void sst_dsp_dump(struct sst_dsp *sst) -{ - if (sst->ops->dump) - sst->ops->dump(sst); -} -EXPORT_SYMBOL_GPL(sst_dsp_dump); - -void sst_dsp_reset(struct sst_dsp *sst) -{ - if (sst->ops->reset) - sst->ops->reset(sst); -} -EXPORT_SYMBOL_GPL(sst_dsp_reset); - -int sst_dsp_boot(struct sst_dsp *sst) -{ - if (sst->ops->boot) - sst->ops->boot(sst); - - return 0; -} -EXPORT_SYMBOL_GPL(sst_dsp_boot); - -int sst_dsp_wake(struct sst_dsp *sst) -{ - if (sst->ops->wake) - return sst->ops->wake(sst); - - return 0; -} -EXPORT_SYMBOL_GPL(sst_dsp_wake); - -void sst_dsp_sleep(struct sst_dsp *sst) -{ - if (sst->ops->sleep) - sst->ops->sleep(sst); -} -EXPORT_SYMBOL_GPL(sst_dsp_sleep); - -void sst_dsp_stall(struct sst_dsp *sst) -{ - if (sst->ops->stall) - sst->ops->stall(sst); -} -EXPORT_SYMBOL_GPL(sst_dsp_stall); - -void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg) -{ - sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY); - trace_sst_ipc_msg_tx(msg); -} -EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_tx); - -u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp) -{ - u32 msg; - - msg = sst_dsp_shim_read_unlocked(dsp, SST_IPCX); - trace_sst_ipc_msg_rx(msg); - - return msg; -} -EXPORT_SYMBOL_GPL(sst_dsp_ipc_msg_rx); - int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, size_t inbox_size, u32 outbox_offset, size_t outbox_size) { diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index ebd0dc5706a0..427970add9c7 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -212,10 +212,6 @@ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); int sst_dsp_shim_update_bits(struct sst_dsp *sst, u32 offset, u32 mask, u32 value); -void sst_dsp_shim_write64(struct sst_dsp *sst, u32 offset, u64 value); -u64 sst_dsp_shim_read64(struct sst_dsp *sst, u32 offset); -int sst_dsp_shim_update_bits64(struct sst_dsp *sst, u32 offset, - u64 mask, u64 value); void sst_dsp_shim_update_bits_forced(struct sst_dsp *sst, u32 offset, u32 mask, u32 value); @@ -224,10 +220,6 @@ void sst_dsp_shim_write_unlocked(struct sst_dsp *sst, u32 offset, u32 value); u32 sst_dsp_shim_read_unlocked(struct sst_dsp *sst, u32 offset); int sst_dsp_shim_update_bits_unlocked(struct sst_dsp *sst, u32 offset, u32 mask, u32 value); -void sst_dsp_shim_write64_unlocked(struct sst_dsp *sst, u32 offset, u64 value); -u64 sst_dsp_shim_read64_unlocked(struct sst_dsp *sst, u32 offset); -int sst_dsp_shim_update_bits64_unlocked(struct sst_dsp *sst, u32 offset, - u64 mask, u64 value); void sst_dsp_shim_update_bits_forced_unlocked(struct sst_dsp *sst, u32 offset, u32 mask, u32 value); @@ -236,21 +228,6 @@ void sst_shim32_write(void __iomem *addr, u32 offset, u32 value); u32 sst_shim32_read(void __iomem *addr, u32 offset); void sst_shim32_write64(void __iomem *addr, u32 offset, u64 value); u64 sst_shim32_read64(void __iomem *addr, u32 offset); -void sst_memcpy_toio_32(struct sst_dsp *sst, - void __iomem *dest, void *src, size_t bytes); -void sst_memcpy_fromio_32(struct sst_dsp *sst, - void *dest, void __iomem *src, size_t bytes); - -/* DSP reset & boot */ -void sst_dsp_reset(struct sst_dsp *sst); -int sst_dsp_boot(struct sst_dsp *sst); -int sst_dsp_wake(struct sst_dsp *sst); -void sst_dsp_sleep(struct sst_dsp *sst); -void sst_dsp_stall(struct sst_dsp *sst); - -/* Msg IO */ -void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg); -u32 sst_dsp_ipc_msg_rx(struct sst_dsp *dsp); /* Mailbox management */ int sst_dsp_mailbox_init(struct sst_dsp *sst, u32 inbox_offset, @@ -262,7 +239,4 @@ void sst_dsp_outbox_read(struct sst_dsp *sst, void *message, size_t bytes); int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask, u32 target, u32 time, char *operation); -/* Debug */ -void sst_dsp_dump(struct sst_dsp *sst); - #endif diff --git a/sound/soc/intel/common/sst-ipc.c b/sound/soc/intel/common/sst-ipc.c index 6068bb697e22..89c10724d2a3 100644 --- a/sound/soc/intel/common/sst-ipc.c +++ b/sound/soc/intel/common/sst-ipc.c @@ -254,33 +254,6 @@ void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, } EXPORT_SYMBOL_GPL(sst_ipc_tx_msg_reply_complete); -void sst_ipc_drop_all(struct sst_generic_ipc *ipc) -{ - struct ipc_message *msg, *tmp; - unsigned long flags; - int tx_drop_cnt = 0, rx_drop_cnt = 0; - - /* drop all TX and Rx messages before we stall + reset DSP */ - spin_lock_irqsave(&ipc->dsp->spinlock, flags); - - list_for_each_entry_safe(msg, tmp, &ipc->tx_list, list) { - list_move(&msg->list, &ipc->empty_list); - tx_drop_cnt++; - } - - list_for_each_entry_safe(msg, tmp, &ipc->rx_list, list) { - list_move(&msg->list, &ipc->empty_list); - rx_drop_cnt++; - } - - spin_unlock_irqrestore(&ipc->dsp->spinlock, flags); - - if (tx_drop_cnt || rx_drop_cnt) - dev_err(ipc->dev, "dropped IPC msg RX=%d, TX=%d\n", - tx_drop_cnt, rx_drop_cnt); -} -EXPORT_SYMBOL_GPL(sst_ipc_drop_all); - int sst_ipc_init(struct sst_generic_ipc *ipc) { int ret; diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 08c4831b2664..9c1f59d88636 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -82,7 +82,6 @@ struct ipc_message *sst_ipc_reply_find_msg(struct sst_generic_ipc *ipc, void sst_ipc_tx_msg_reply_complete(struct sst_generic_ipc *ipc, struct ipc_message *msg); -void sst_ipc_drop_all(struct sst_generic_ipc *ipc); int sst_ipc_init(struct sst_generic_ipc *ipc); void sst_ipc_fini(struct sst_generic_ipc *ipc); -- cgit From b4e60807182a243d9dfe985e9e13d295f5868f81 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:03 +0200 Subject: ASoC: Intel: Remove unused DSP interface fields With redundant DSP operations removed, several fields for structures: sst_ops, sst_addr and sst_dsp become obsolete. Remove them too. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-10-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 45 ----------------------------------- 1 file changed, 45 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 3787ef07b840..994698ff581e 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -22,26 +22,9 @@ * DSP Operations exported by platform Audio DSP driver. */ struct sst_ops { - /* DSP core boot / reset */ - void (*boot)(struct sst_dsp *); - void (*reset)(struct sst_dsp *); - int (*wake)(struct sst_dsp *); - void (*sleep)(struct sst_dsp *); - void (*stall)(struct sst_dsp *); - /* Shim IO */ void (*write)(void __iomem *addr, u32 offset, u32 value); u32 (*read)(void __iomem *addr, u32 offset); - void (*write64)(void __iomem *addr, u32 offset, u64 value); - u64 (*read64)(void __iomem *addr, u32 offset); - - /* DSP I/DRAM IO */ - void (*ram_read)(struct sst_dsp *sst, void *dest, void __iomem *src, - size_t bytes); - void (*ram_write)(struct sst_dsp *sst, void __iomem *dest, void *src, - size_t bytes); - - void (*dump)(struct sst_dsp *); /* IRQ handlers */ irqreturn_t (*irq_handler)(int irq, void *context); @@ -55,20 +38,12 @@ struct sst_ops { * Audio DSP memory offsets and addresses. */ struct sst_addr { - u32 lpe_base; - u32 shim_offset; - u32 iram_offset; - u32 dram_offset; - u32 dsp_iram_offset; - u32 dsp_dram_offset; u32 sram0_base; u32 sram1_base; u32 w0_stat_sz; u32 w0_up_sz; void __iomem *lpe; void __iomem *shim; - void __iomem *pci_cfg; - void __iomem *fw_ext; }; /* @@ -93,7 +68,6 @@ struct sst_dsp { spinlock_t spinlock; /* IPC locking */ struct mutex mutex; /* DSP FW lock */ struct device *dev; - struct device *dma_dev; void *thread_context; int irq; u32 id; @@ -110,27 +84,8 @@ struct sst_dsp { /* mailbox */ struct sst_mailbox mailbox; - /* HSW/Byt data */ - - /* list of free and used ADSP memory blocks */ - struct list_head used_block_list; - struct list_head free_block_list; - /* SST FW files loaded and their modules */ struct list_head module_list; - struct list_head fw_list; - - /* scratch buffer */ - struct list_head scratch_block_list; - u32 scratch_offset; - u32 scratch_size; - - /* platform data */ - struct sst_pdata *pdata; - - /* DMA FW loading */ - struct sst_dma *dma; - bool fw_use_dma; /* SKL data */ -- cgit From 7d07f9c1ba0e670d1a967f16eda53e5c87411753 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:04 +0200 Subject: ASoC: Intel: Remove SST-legacy specific constants As sound/soc/intel/haswell and /baytrail are no more, all SST-legacy specific constants and registers are redundant so remove them. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-11-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 3 - sound/soc/intel/common/sst-dsp.h | 143 ---------------------------------- sound/soc/intel/common/sst-ipc.h | 2 - 3 files changed, 148 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 994698ff581e..7d9834509f4a 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -15,9 +15,6 @@ #include "../skylake/skl-sst-dsp.h" -/* do we need to remove or keep */ -#define DSP_DRAM_ADDR_OFFSET 0x400000 - /* * DSP Operations exported by platform Audio DSP driver. */ diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 427970add9c7..f580e718b183 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -12,158 +12,15 @@ #include #include -/* SST Device IDs */ -#define SST_DEV_ID_LYNX_POINT 0x33C8 -#define SST_DEV_ID_WILDCAT_POINT 0x3438 -#define SST_DEV_ID_BYT 0x0F28 - -/* Supported SST DMA Devices */ -#define SST_DMA_TYPE_DW 1 - -/* autosuspend delay 5s*/ -#define SST_RUNTIME_SUSPEND_DELAY (5 * 1000) - /* SST Shim register map * The register naming can differ between products. Some products also * contain extra functionality. */ #define SST_CSR 0x00 -#define SST_PISR 0x08 -#define SST_PIMR 0x10 #define SST_ISRX 0x18 -#define SST_ISRD 0x20 #define SST_IMRX 0x28 -#define SST_IMRD 0x30 #define SST_IPCX 0x38 /* IPC IA -> SST */ #define SST_IPCD 0x40 /* IPC SST -> IA */ -#define SST_ISRSC 0x48 -#define SST_ISRLPESC 0x50 -#define SST_IMRSC 0x58 -#define SST_IMRLPESC 0x60 -#define SST_IPCSC 0x68 -#define SST_IPCLPESC 0x70 -#define SST_CLKCTL 0x78 -#define SST_CSR2 0x80 -#define SST_LTRC 0xE0 -#define SST_HMDC 0xE8 - -#define SST_SHIM_BEGIN SST_CSR -#define SST_SHIM_END SST_HDMC - -#define SST_DBGO 0xF0 - -#define SST_SHIM_SIZE 0x100 -#define SST_PWMCTRL 0x1000 - -/* SST Shim Register bits - * The register bit naming can differ between products. Some products also - * contain extra functionality. - */ - -/* CSR / CS */ -#define SST_CSR_RST (0x1 << 1) -#define SST_CSR_SBCS0 (0x1 << 2) -#define SST_CSR_SBCS1 (0x1 << 3) -#define SST_CSR_DCS(x) (x << 4) -#define SST_CSR_DCS_MASK (0x7 << 4) -#define SST_CSR_STALL (0x1 << 10) -#define SST_CSR_S0IOCS (0x1 << 21) -#define SST_CSR_S1IOCS (0x1 << 23) -#define SST_CSR_LPCS (0x1 << 31) -#define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS) -#define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1) -#define SST_BYT_CSR_RST (0x1 << 0) -#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1) -#define SST_BYT_CSR_STALL (0x1 << 2) -#define SST_BYT_CSR_PWAITMODE (0x1 << 3) - -/* ISRX / ISC */ -#define SST_ISRX_BUSY (0x1 << 1) -#define SST_ISRX_DONE (0x1 << 0) -#define SST_BYT_ISRX_REQUEST (0x1 << 1) - -/* ISRD / ISD */ -#define SST_ISRD_BUSY (0x1 << 1) -#define SST_ISRD_DONE (0x1 << 0) - -/* IMRX / IMC */ -#define SST_IMRX_BUSY (0x1 << 1) -#define SST_IMRX_DONE (0x1 << 0) -#define SST_BYT_IMRX_REQUEST (0x1 << 1) - -/* IMRD / IMD */ -#define SST_IMRD_DONE (0x1 << 0) -#define SST_IMRD_BUSY (0x1 << 1) -#define SST_IMRD_SSP0 (0x1 << 16) -#define SST_IMRD_DMAC0 (0x1 << 21) -#define SST_IMRD_DMAC1 (0x1 << 22) -#define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1) - -/* IPCX / IPCC */ -#define SST_IPCX_DONE (0x1 << 30) -#define SST_IPCX_BUSY (0x1 << 31) -#define SST_BYT_IPCX_DONE ((u64)0x1 << 62) -#define SST_BYT_IPCX_BUSY ((u64)0x1 << 63) - -/* IPCD */ -#define SST_IPCD_DONE (0x1 << 30) -#define SST_IPCD_BUSY (0x1 << 31) -#define SST_BYT_IPCD_DONE ((u64)0x1 << 62) -#define SST_BYT_IPCD_BUSY ((u64)0x1 << 63) - -/* CLKCTL */ -#define SST_CLKCTL_SMOS(x) (x << 24) -#define SST_CLKCTL_MASK (3 << 24) -#define SST_CLKCTL_DCPLCG (1 << 18) -#define SST_CLKCTL_SCOE1 (1 << 17) -#define SST_CLKCTL_SCOE0 (1 << 16) - -/* CSR2 / CS2 */ -#define SST_CSR2_SDFD_SSP0 (1 << 1) -#define SST_CSR2_SDFD_SSP1 (1 << 2) - -/* LTRC */ -#define SST_LTRC_VAL(x) (x << 0) - -/* HMDC */ -#define SST_HMDC_HDDA0(x) (x << 0) -#define SST_HMDC_HDDA1(x) (x << 7) -#define SST_HMDC_HDDA_E0_CH0 1 -#define SST_HMDC_HDDA_E0_CH1 2 -#define SST_HMDC_HDDA_E0_CH2 4 -#define SST_HMDC_HDDA_E0_CH3 8 -#define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0) -#define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1) -#define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2) -#define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3) -#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \ - SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3) -#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \ - SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3) - - -/* SST Vendor Defined Registers and bits */ -#define SST_VDRTCTL0 0xa0 -#define SST_VDRTCTL1 0xa4 -#define SST_VDRTCTL2 0xa8 -#define SST_VDRTCTL3 0xaC - -/* VDRTCTL0 */ -#define SST_VDRTCL0_D3PGD (1 << 0) -#define SST_VDRTCL0_D3SRAMPGD (1 << 1) -#define SST_VDRTCL0_DSRAMPGE_SHIFT 12 -#define SST_VDRTCL0_DSRAMPGE_MASK (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT) -#define SST_VDRTCL0_ISRAMPGE_SHIFT 2 -#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT) - -/* VDRTCTL2 */ -#define SST_VDRTCL2_DCLCGE (1 << 1) -#define SST_VDRTCL2_DTCGE (1 << 10) -#define SST_VDRTCL2_APLLSE_MASK (1 << 31) - -/* PMCS */ -#define SST_PMCS 0x84 -#define SST_PMCS_PS_MASK 0x3 struct sst_dsp; diff --git a/sound/soc/intel/common/sst-ipc.h b/sound/soc/intel/common/sst-ipc.h index 9c1f59d88636..77d651e888f9 100644 --- a/sound/soc/intel/common/sst-ipc.h +++ b/sound/soc/intel/common/sst-ipc.h @@ -15,8 +15,6 @@ #include #include -#define IPC_MAX_MAILBOX_BYTES 256 - struct sst_ipc_message { u64 header; void *data; -- cgit From b972153d6c53a89dc92d991c466a6b4800a9c91f Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:05 +0200 Subject: ASoC: Intel: Make atom components independent of sst-dsp With sound/soc/intel/haswell and /baytrail gone, registers left within sst-dsp header are atom-specific. Relocate these to atom internal header to make atom truely independent of sound/soc/common processing code. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-12-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/atom/sst/sst.c | 1 - sound/soc/intel/atom/sst/sst.h | 7 +++++++ sound/soc/intel/atom/sst/sst_acpi.c | 1 - sound/soc/intel/atom/sst/sst_drv_interface.c | 3 --- sound/soc/intel/atom/sst/sst_ipc.c | 1 - sound/soc/intel/atom/sst/sst_loader.c | 1 - sound/soc/intel/atom/sst/sst_pvt.c | 1 - sound/soc/intel/atom/sst/sst_stream.c | 1 - sound/soc/intel/boards/bytcht_es8316.c | 1 - sound/soc/intel/boards/bytcr_rt5640.c | 1 - sound/soc/intel/common/sst-dsp.h | 10 ---------- 11 files changed, 7 insertions(+), 21 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c index d450b9848028..e90590559185 100644 --- a/sound/soc/intel/atom/sst/sst.c +++ b/sound/soc/intel/atom/sst/sst.c @@ -26,7 +26,6 @@ #include #include "../sst-mfld-platform.h" #include "sst.h" -#include "../../common/sst-dsp.h" MODULE_AUTHOR("Vinod Koul "); MODULE_AUTHOR("Harsha Priya "); diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h index 2bee646e81b8..4d37d39fd8f4 100644 --- a/sound/soc/intel/atom/sst/sst.h +++ b/sound/soc/intel/atom/sst/sst.h @@ -34,6 +34,13 @@ #define MRFLD_FW_FEATURE_BASE_OFFSET 0x4 #define MRFLD_FW_BSS_RESET_BIT 0 +/* SST Shim register map */ +#define SST_CSR 0x00 +#define SST_ISRX 0x18 +#define SST_IMRX 0x28 +#define SST_IPCX 0x38 /* IPC IA -> SST */ +#define SST_IPCD 0x40 /* IPC SST -> IA */ + extern const struct dev_pm_ops intel_sst_pm; enum sst_states { SST_FW_LOADING = 1, diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c index f3cfe83b9ac6..f943a0884976 100644 --- a/sound/soc/intel/atom/sst/sst_acpi.c +++ b/sound/soc/intel/atom/sst/sst_acpi.c @@ -31,7 +31,6 @@ #include #include #include "../sst-mfld-platform.h" -#include "../../common/sst-dsp.h" #include "../../common/soc-intel-quirks.h" #include "sst.h" diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c index 762495385d5c..0af618dd8073 100644 --- a/sound/soc/intel/atom/sst/sst_drv_interface.c +++ b/sound/soc/intel/atom/sst/sst_drv_interface.c @@ -24,9 +24,6 @@ #include #include "../sst-mfld-platform.h" #include "sst.h" -#include "../../common/sst-dsp.h" - - #define NUM_CODEC 2 #define MIN_FRAGMENT 2 diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c index c2851a829a64..a8a9aa0057d3 100644 --- a/sound/soc/intel/atom/sst/sst_ipc.c +++ b/sound/soc/intel/atom/sst/sst_ipc.c @@ -24,7 +24,6 @@ #include #include "../sst-mfld-platform.h" #include "sst.h" -#include "../../common/sst-dsp.h" struct sst_block *sst_create_block(struct intel_sst_drv *ctx, u32 msg_id, u32 drv_id) diff --git a/sound/soc/intel/atom/sst/sst_loader.c b/sound/soc/intel/atom/sst/sst_loader.c index 5c42cce90ce2..1c9b0c9ec483 100644 --- a/sound/soc/intel/atom/sst/sst_loader.c +++ b/sound/soc/intel/atom/sst/sst_loader.c @@ -29,7 +29,6 @@ #include #include "../sst-mfld-platform.h" #include "sst.h" -#include "../../common/sst-dsp.h" void memcpy32_toio(void __iomem *dst, const void *src, int count) { diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c index c099f7df6168..e6a5c18a7018 100644 --- a/sound/soc/intel/atom/sst/sst_pvt.c +++ b/sound/soc/intel/atom/sst/sst_pvt.c @@ -26,7 +26,6 @@ #include #include "../sst-mfld-platform.h" #include "sst.h" -#include "../../common/sst-dsp.h" int sst_shim_write(void __iomem *addr, int offset, int value) { diff --git a/sound/soc/intel/atom/sst/sst_stream.c b/sound/soc/intel/atom/sst/sst_stream.c index c0221e103e79..ea1ef8a61fa6 100644 --- a/sound/soc/intel/atom/sst/sst_stream.c +++ b/sound/soc/intel/atom/sst/sst_stream.c @@ -23,7 +23,6 @@ #include #include "../sst-mfld-platform.h" #include "sst.h" -#include "../../common/sst-dsp.h" int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params) { diff --git a/sound/soc/intel/boards/bytcht_es8316.c b/sound/soc/intel/boards/bytcht_es8316.c index 414ae4bb5224..9c09b44b4d33 100644 --- a/sound/soc/intel/boards/bytcht_es8316.c +++ b/sound/soc/intel/boards/bytcht_es8316.c @@ -28,7 +28,6 @@ #include #include #include "../atom/sst-atom-controls.h" -#include "../common/sst-dsp.h" #include "../common/soc-intel-quirks.h" /* jd-inv + terminating entry */ diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index fc202747ba83..9dadf6561444 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -28,7 +28,6 @@ #include #include "../../codecs/rt5640.h" #include "../atom/sst-atom-controls.h" -#include "../common/sst-dsp.h" #include "../common/soc-intel-quirks.h" enum { diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index f580e718b183..021a36b2398a 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -12,16 +12,6 @@ #include #include -/* SST Shim register map - * The register naming can differ between products. Some products also - * contain extra functionality. - */ -#define SST_CSR 0x00 -#define SST_ISRX 0x18 -#define SST_IMRX 0x28 -#define SST_IPCX 0x38 /* IPC IA -> SST */ -#define SST_IPCD 0x40 /* IPC SST -> IA */ - struct sst_dsp; /* -- cgit From 720811f0e4ac5a31d38aaee20905692dd7150997 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:06 +0200 Subject: ASoC: Intel: Remove sst_pdata structure struct sst_pdata is unused among remaining /sound/soc/intel solution so remove it. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-13-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 2 +- sound/soc/intel/common/sst-dsp.h | 28 ---------------------------- sound/soc/intel/skylake/skl-sst-dsp.c | 2 +- 3 files changed, 2 insertions(+), 30 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 7d9834509f4a..5a5ce5069f9f 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -27,7 +27,7 @@ struct sst_ops { irqreturn_t (*irq_handler)(int irq, void *context); /* SST init and free */ - int (*init)(struct sst_dsp *sst, struct sst_pdata *pdata); + int (*init)(struct sst_dsp *sst); void (*free)(struct sst_dsp *sst); }; diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h index 021a36b2398a..f111fd3f334b 100644 --- a/sound/soc/intel/common/sst-dsp.h +++ b/sound/soc/intel/common/sst-dsp.h @@ -26,34 +26,6 @@ struct sst_dsp_device { void *thread_context; }; -/* - * SST Platform Data. - */ -struct sst_pdata { - /* ACPI data */ - u32 lpe_base; - u32 lpe_size; - u32 pcicfg_base; - u32 pcicfg_size; - u32 fw_base; - u32 fw_size; - int irq; - - /* Firmware */ - const struct firmware *fw; - - /* DMA */ - int resindex_dma_base; /* other fields invalid if equals to -1 */ - u32 dma_base; - u32 dma_size; - int dma_engine; - struct device *dma_dev; - - /* DSP */ - u32 id; - void *dsp; -}; - /* SHIM Read / Write */ void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value); u32 sst_dsp_shim_read(struct sst_dsp *sst, u32 offset); diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c index 225706d148d8..4ae3eae0d1fd 100644 --- a/sound/soc/intel/skylake/skl-sst-dsp.c +++ b/sound/soc/intel/skylake/skl-sst-dsp.c @@ -422,7 +422,7 @@ struct sst_dsp *skl_dsp_ctx_init(struct device *dev, /* Initialise SST Audio DSP */ if (sst->ops->init) { - ret = sst->ops->init(sst, NULL); + ret = sst->ops->init(sst); if (ret < 0) return NULL; } -- cgit From eb062e47f7c8cc28f19ba8f897481c22d13db1ec Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Tue, 6 Oct 2020 08:49:07 +0200 Subject: ASoC: Intel: Remove sst_dsp_get_thread_context While sst_dsp_get_thread_context() is declared as solution-agnostic, it is only used by /skylake/ solution. Majority of thread_context field usages are direct accesses. Improve code cohesiveness and convert to single usage model. Signed-off-by: Cezary Rojewski Reviewed-by: Andy Shevchenko Acked-by: Liam Girdwood Link: https://lore.kernel.org/r/20201006064907.16277-14-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/common/sst-dsp-priv.h | 5 ----- sound/soc/intel/skylake/cnl-sst.c | 2 +- sound/soc/intel/skylake/skl-sst-ipc.c | 2 +- 3 files changed, 2 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h index 5a5ce5069f9f..de2b44568feb 100644 --- a/sound/soc/intel/common/sst-dsp-priv.h +++ b/sound/soc/intel/common/sst-dsp-priv.h @@ -98,9 +98,4 @@ struct sst_dsp { struct snd_dma_buffer dmab; }; -static inline void *sst_dsp_get_thread_context(struct sst_dsp *sst) -{ - return sst->thread_context; -} - #endif diff --git a/sound/soc/intel/skylake/cnl-sst.c b/sound/soc/intel/skylake/cnl-sst.c index 355a7b116886..fcd8dff27ae8 100644 --- a/sound/soc/intel/skylake/cnl-sst.c +++ b/sound/soc/intel/skylake/cnl-sst.c @@ -311,7 +311,7 @@ static struct sst_ops cnl_ops = { static irqreturn_t cnl_dsp_irq_thread_handler(int irq, void *context) { struct sst_dsp *dsp = context; - struct skl_dev *cnl = sst_dsp_get_thread_context(dsp); + struct skl_dev *cnl = dsp->thread_context; struct sst_generic_ipc *ipc = &cnl->ipc; struct skl_ipc_header header = {0}; u32 hipcida, hipctdr, hipctdd; diff --git a/sound/soc/intel/skylake/skl-sst-ipc.c b/sound/soc/intel/skylake/skl-sst-ipc.c index 667cdddc289f..7a425271b08b 100644 --- a/sound/soc/intel/skylake/skl-sst-ipc.c +++ b/sound/soc/intel/skylake/skl-sst-ipc.c @@ -489,7 +489,7 @@ void skl_ipc_process_reply(struct sst_generic_ipc *ipc, irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context) { struct sst_dsp *dsp = context; - struct skl_dev *skl = sst_dsp_get_thread_context(dsp); + struct skl_dev *skl = dsp->thread_context; struct sst_generic_ipc *ipc = &skl->ipc; struct skl_ipc_header header = {0}; u32 hipcie, hipct, hipcte; -- cgit From cd7dea5e17a561e621e78e5863f5634cb6f9f097 Mon Sep 17 00:00:00 2001 From: Alex Dewar Date: Sun, 4 Oct 2020 11:25:36 +0100 Subject: ASoC: omap-mcbsp: Fix use of uninitialised pointer Commit 9c34d023dc35 ("ASoC: omap-mcbsp: Re-arrange files for core McBSP and Sidetone function split"), in rearranging various files, also replaced calls to platform_get_resource_by_name() + devm_ioremap_resource() with a single call to devm_platform_ioremap_resource_byname(). However, the struct resource is needed as we access its members so at present a null pointer is dereferenced. Fix by doing things the old way. Fixes: 9c34d023dc35 ("ASoC: omap-mcbsp: Re-arrange files for core McBSP and Sidetone function split") Signed-off-by: Alex Dewar Link: https://lore.kernel.org/r/20201004102535.325547-1-alex.dewar90@gmail.com Signed-off-by: Mark Brown --- sound/soc/ti/omap-mcbsp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c index 186cea91076f..6025b30bbe77 100644 --- a/sound/soc/ti/omap-mcbsp.c +++ b/sound/soc/ti/omap-mcbsp.c @@ -620,7 +620,11 @@ static int omap_mcbsp_init(struct platform_device *pdev) spin_lock_init(&mcbsp->lock); mcbsp->free = true; - mcbsp->io_base = devm_platform_ioremap_resource_byname(pdev, "mpu"); + 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); -- cgit From 86b9c4cdd76fcfc98c5967f63b4ca09620e67968 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 5 Oct 2020 12:12:23 -0700 Subject: ALSA: portman2x4: fix repeated word 'if' Correct duplicated word "if" to "if it". Signed-off-by: Randy Dunlap Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: alsa-devel@alsa-project.org Link: https://lore.kernel.org/r/20201005191223.21514-1-rdunlap@infradead.org Signed-off-by: Takashi Iwai --- sound/drivers/portman2x4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c index 38603cb2bd5b..c876cf9b5005 100644 --- a/sound/drivers/portman2x4.c +++ b/sound/drivers/portman2x4.c @@ -467,7 +467,7 @@ static int portman_probe(struct parport *p) parport_write_control(p, 0); /* Reset Strobe=0. */ /* Check if Tx circuitry is functioning properly. If initialized - * unit TxEmpty is false, send out char and see if if goes true. + * unit TxEmpty is false, send out char and see if it goes true. */ /* 8 */ parport_write_control(p, TXDATA0); /* Tx channel 0, strobe off. */ -- cgit From 0569b3d8ae17c29f8e09db2ba683be3915fe8b47 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 5 Oct 2020 12:12:44 -0700 Subject: ALSA: usb-audio: endpoint.c: fix repeated word 'there' Drop the duplicated word "there". Signed-off-by: Randy Dunlap Cc: Jaroslav Kysela Cc: Takashi Iwai Cc: alsa-devel@alsa-project.org Link: https://lore.kernel.org/r/20201005191244.23902-1-rdunlap@infradead.org Signed-off-by: Takashi Iwai --- sound/usb/endpoint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 5fbc8dd2f409..e2f9ce2f5b8b 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -318,7 +318,7 @@ static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep, /* * Send output urbs that have been prepared previously. URBs are dequeued - * from ep->ready_playback_urbs and in case there there aren't any available + * from ep->ready_playback_urbs and in case there aren't any available * or there are no packets that have been prepared, this function does * nothing. * -- cgit From 6db282c8a9edcbf84e699e45ec087baf07be2236 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 6 Oct 2020 16:20:24 +0100 Subject: ASoC: mchp-spdifrx: fix spelling mistake "overrrun" -> "overrun" There is a spelling mistake in a dev_warn message. Fix it. Signed-off-by: Colin Ian King Reviewed-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20201006152024.542418-1-colin.king@canonical.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdifrx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c index 726e4951d9a5..e6ded6f8453f 100644 --- a/sound/soc/atmel/mchp-spdifrx.c +++ b/sound/soc/atmel/mchp-spdifrx.c @@ -338,7 +338,7 @@ static irqreturn_t mchp_spdif_interrupt(int irq, void *dev_id) } if (pending & SPDIFRX_IR_OVERRUN) { - dev_warn(dev->dev, "Overrrun detected\n"); + dev_warn(dev->dev, "Overrun detected\n"); ret = IRQ_HANDLED; } -- cgit From ca184355db8e60290fa34bf61c13308e6f4f50d3 Mon Sep 17 00:00:00 2001 From: Jian-Hong Pan Date: Wed, 7 Oct 2020 13:22:25 +0800 Subject: ALSA: hda/realtek: Enable audio jacks of ASUS D700SA with ALC887 The ASUS D700SA desktop's audio (1043:2390) with ALC887 cannot detect the headset microphone and another headphone jack until ALC887_FIXUP_ASUS_HMIC and ALC887_FIXUP_ASUS_AUDIO quirks are applied. The NID 0x15 maps as the headset microphone and NID 0x19 maps as another headphone jack. Also need the function like alc887_fixup_asus_jack to enable the audio jacks. Signed-off-by: Jian-Hong Pan Signed-off-by: Kailang Yang Cc: Link: https://lore.kernel.org/r/20201007052224.22611-1-jhp@endlessos.org Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index cf896f398ae7..c18077b39449 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1929,6 +1929,8 @@ enum { ALC1220_FIXUP_CLEVO_P950, ALC1220_FIXUP_CLEVO_PB51ED, ALC1220_FIXUP_CLEVO_PB51ED_PINS, + ALC887_FIXUP_ASUS_AUDIO, + ALC887_FIXUP_ASUS_HMIC, }; static void alc889_fixup_coef(struct hda_codec *codec, @@ -2141,6 +2143,31 @@ static void alc1220_fixup_clevo_pb51ed(struct hda_codec *codec, alc_fixup_headset_mode_no_hp_mic(codec, fix, action); } +static void alc887_asus_hp_automute_hook(struct hda_codec *codec, + struct hda_jack_callback *jack) +{ + struct alc_spec *spec = codec->spec; + unsigned int vref; + + snd_hda_gen_hp_automute(codec, jack); + + if (spec->gen.hp_jack_present) + vref = AC_PINCTL_VREF_80; + else + vref = AC_PINCTL_VREF_HIZ; + snd_hda_set_pin_ctl(codec, 0x19, PIN_HP | vref); +} + +static void alc887_fixup_asus_jack(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + if (action != HDA_FIXUP_ACT_PROBE) + return; + snd_hda_set_pin_ctl_cache(codec, 0x1b, PIN_HP); + spec->gen.hp_automute_hook = alc887_asus_hp_automute_hook; +} + static const struct hda_fixup alc882_fixups[] = { [ALC882_FIXUP_ABIT_AW9D_MAX] = { .type = HDA_FIXUP_PINS, @@ -2398,6 +2425,20 @@ static const struct hda_fixup alc882_fixups[] = { .chained = true, .chain_id = ALC1220_FIXUP_CLEVO_PB51ED, }, + [ALC887_FIXUP_ASUS_AUDIO] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x15, 0x02a14150 }, /* use as headset mic, without its own jack detect */ + { 0x19, 0x22219420 }, + {} + }, + }, + [ALC887_FIXUP_ASUS_HMIC] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc887_fixup_asus_jack, + .chained = true, + .chain_id = ALC887_FIXUP_ASUS_AUDIO, + }, }; static const struct snd_pci_quirk alc882_fixup_tbl[] = { @@ -2431,6 +2472,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x13c2, "Asus A7M", ALC882_FIXUP_EAPD), SND_PCI_QUIRK(0x1043, 0x1873, "ASUS W90V", ALC882_FIXUP_ASUS_W90V), SND_PCI_QUIRK(0x1043, 0x1971, "Asus W2JC", ALC882_FIXUP_ASUS_W2JC), + SND_PCI_QUIRK(0x1043, 0x2390, "Asus D700SA", ALC887_FIXUP_ASUS_HMIC), SND_PCI_QUIRK(0x1043, 0x835f, "Asus Eee 1601", ALC888_FIXUP_EEE1601), SND_PCI_QUIRK(0x1043, 0x84bc, "ASUS ET2700", ALC887_FIXUP_ASUS_BASS), SND_PCI_QUIRK(0x1043, 0x8691, "ASUS ROG Ranger VIII", ALC882_FIXUP_GPIO3), -- cgit From b41c15f4e1c1f1657da15c482fa837c1b7384452 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 7 Oct 2020 10:49:28 +0300 Subject: ALSA: bebob: potential info leak in hwdep_read() The "count" variable needs to be capped on every path so that we don't copy too much information to the user. Fixes: 618eabeae711 ("ALSA: bebob: Add hwdep interface") Signed-off-by: Dan Carpenter Acked-by: Takashi Sakamoto Cc: Link: https://lore.kernel.org/r/20201007074928.GA2529578@mwanda Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob_hwdep.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c index 45b740f44c45..c362eb38ab90 100644 --- a/sound/firewire/bebob/bebob_hwdep.c +++ b/sound/firewire/bebob/bebob_hwdep.c @@ -36,12 +36,11 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count, } memset(&event, 0, sizeof(event)); + count = min_t(long, count, sizeof(event.lock_status)); if (bebob->dev_lock_changed) { event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS; event.lock_status.status = (bebob->dev_lock_count > 0); bebob->dev_lock_changed = false; - - count = min_t(long, count, sizeof(event.lock_status)); } spin_unlock_irq(&bebob->lock); -- cgit From 7dcd56123e312e8b17f85a597b33552a704ce45f Mon Sep 17 00:00:00 2001 From: Naoki Hayama Date: Thu, 8 Oct 2020 17:47:44 +0900 Subject: ALSA: hdspm: Fix typo arbitary Fix comment typo. s/arbitary/arbitrary/ Signed-off-by: Naoki Hayama Link: https://lore.kernel.org/r/e04a8c5b-8c59-3f02-34d3-c1a871d08cc2@lineo.co.jp Signed-off-by: Takashi Iwai --- sound/pci/rme9652/hdspm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c index e532312a5e1c..4a1f576dd9cf 100644 --- a/sound/pci/rme9652/hdspm.c +++ b/sound/pci/rme9652/hdspm.c @@ -1217,7 +1217,7 @@ static int snd_hdspm_use_is_exclusive(struct hdspm *hdspm) return ret; } -/* round arbitary sample rates to commonly known rates */ +/* round arbitrary sample rates to commonly known rates */ static int hdspm_round_frequency(int rate) { if (rate < 38050) -- cgit From 148ebf548a1af366fc797fcc7d03f0bb92b12a79 Mon Sep 17 00:00:00 2001 From: Jeremy Szu Date: Thu, 8 Oct 2020 18:56:44 +0800 Subject: ALSA: hda/realtek - The front Mic on a HP machine doesn't work On a HP ZCentral, the front Mic could not be detected. The codec of the HP ZCentrol is alc671 and it needs to override the pin configuration to enable the headset mic. Signed-off-by: Jeremy Szu Cc: Link: https://lore.kernel.org/r/20201008105645.65505-1-jeremy.szu@canonical.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_realtek.c | 1 + 1 file changed, 1 insertion(+) (limited to 'sound') diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index c18077b39449..f89506dffd5b 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -9676,6 +9676,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0698, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x069f, "Dell", ALC668_FIXUP_DELL_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1632, "HP RP5800", ALC662_FIXUP_HP_RP5800), + SND_PCI_QUIRK(0x103c, 0x873e, "HP", ALC671_FIXUP_HP_HEADSET_MIC2), SND_PCI_QUIRK(0x1043, 0x1080, "Asus UX501VW", ALC668_FIXUP_HEADSET_MODE), SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), -- cgit From 4049a3b87847fec754632d233bffbd608364917f Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Thu, 8 Oct 2020 10:46:59 +0530 Subject: Asoc:qcom:lpass-cpu:Update dts property read API Update dts property read API call with platform get property by name, as it make code more readable and avoid conflicts when array of properties to be used. Signed-off-by: V Sujith Kumar Reddy Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivasa Rao Tested-by: Srinivas Kandagatla Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1602134223-2562-4-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-cpu.c | 2 +- sound/soc/qcom/lpass-platform.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 0718a0fff9cb..12950d2e2755 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -575,7 +575,7 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) of_lpass_cpu_parse_dai_data(dev, drvdata); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); drvdata->lpaif = devm_ioremap_resource(dev, res); if (IS_ERR((void const __force *)drvdata->lpaif)) { diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index 7ac26290082f..e7cf4e595ebd 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -621,7 +621,7 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) struct lpass_variant *v = drvdata->variant; int ret; - drvdata->lpaif_irq = platform_get_irq(pdev, 0); + drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); if (drvdata->lpaif_irq < 0) return -ENODEV; -- cgit From d9e8e61243958409645c18c9267b6dbaaaf22364 Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Thu, 8 Oct 2020 10:47:00 +0530 Subject: Asoc: qcom: lpass:Update lpaif_dmactl members order Update the lpaif_dmactl struct members order to match HDMI reg map members sequence. Separate Interface reg map as it is used for I2S control but not for HDMI control, to make use of bulk API, which makes code more readable. Signed-off-by: V Sujith Kumar Reddy Reviewed-by: Srinivas Kandagatla Signed-off-by: Srinivasa Rao Tested-by: Srinivas Kandagatla Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1602134223-2562-5-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-platform.c | 8 ++++---- sound/soc/qcom/lpass.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index e7cf4e595ebd..db0d959b1619 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -71,13 +71,13 @@ static int lpass_platform_alloc_dmactl_fields(struct device *dev, rd_dmactl = drvdata->rd_dmactl; wr_dmactl = drvdata->wr_dmactl; - rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->bursten, - &v->rdma_bursten, 6); + rval = devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->intf, + &v->rdma_intf, 6); if (rval) return rval; - return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->bursten, - &v->wrdma_bursten, 6); + return devm_regmap_field_bulk_alloc(dev, map, &wr_dmactl->intf, + &v->wrdma_intf, 6); } static int lpass_platform_pcmops_open(struct snd_soc_component *component, diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 51c9991a0edf..7089d4cdf5e9 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -31,9 +31,9 @@ struct lpaif_i2sctl { struct lpaif_dmactl { + struct regmap_field *intf; struct regmap_field *bursten; struct regmap_field *wpscnt; - struct regmap_field *intf; struct regmap_field *fifowm; struct regmap_field *enable; struct regmap_field *dyncclk; @@ -110,17 +110,17 @@ struct lpass_variant { struct reg_field bitwidth; /* RD_DMA Register fields */ + struct reg_field rdma_intf; struct reg_field rdma_bursten; struct reg_field rdma_wpscnt; - struct reg_field rdma_intf; struct reg_field rdma_fifowm; struct reg_field rdma_enable; struct reg_field rdma_dyncclk; /* WR_DMA Register fields */ + struct reg_field wrdma_intf; struct reg_field wrdma_bursten; struct reg_field wrdma_wpscnt; - struct reg_field wrdma_intf; struct reg_field wrdma_fifowm; struct reg_field wrdma_enable; struct reg_field wrdma_dyncclk; -- cgit From 7cb37b7bd0d3c93e381ae7abf30971819966bd9d Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Thu, 8 Oct 2020 10:47:01 +0530 Subject: ASoC: qcom: Add support for lpass hdmi driver Upadate lpass cpu and platform driver to support audio over dp. Also add lpass-hdmi.c and lpass-hdmi.h. Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Reviewed-by: Srinivas Kandagatla Tested-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1602134223-2562-6-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/Kconfig | 5 + sound/soc/qcom/Makefile | 2 + sound/soc/qcom/lpass-apq8016.c | 4 +- sound/soc/qcom/lpass-cpu.c | 247 ++++++++++++++++++++++++- sound/soc/qcom/lpass-hdmi.c | 258 ++++++++++++++++++++++++++ sound/soc/qcom/lpass-hdmi.h | 102 +++++++++++ sound/soc/qcom/lpass-ipq806x.c | 4 +- sound/soc/qcom/lpass-lpaif-reg.h | 49 +++-- sound/soc/qcom/lpass-platform.c | 383 ++++++++++++++++++++++++++++++++------- sound/soc/qcom/lpass.h | 118 +++++++++++- 10 files changed, 1075 insertions(+), 97 deletions(-) create mode 100644 sound/soc/qcom/lpass-hdmi.c create mode 100644 sound/soc/qcom/lpass-hdmi.h (limited to 'sound') diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 04258e6aa164..2696ffcba880 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig @@ -12,6 +12,10 @@ config SND_SOC_LPASS_CPU tristate select REGMAP_MMIO +config SND_SOC_LPASS_HDMI + tristate + select REGMAP_MMIO + config SND_SOC_LPASS_PLATFORM tristate select REGMAP_MMIO @@ -30,6 +34,7 @@ config SND_SOC_LPASS_SC7180 tristate select SND_SOC_LPASS_CPU select SND_SOC_LPASS_PLATFORM + select SND_SOC_LPASS_HDMI config SND_SOC_STORM tristate "ASoC I2S support for Storm boards" diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index 7972c9479ab0..0bd90d74e3db 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile @@ -1,12 +1,14 @@ # SPDX-License-Identifier: GPL-2.0 # Platform snd-soc-lpass-cpu-objs := lpass-cpu.o +snd-soc-lpass-hdmi-objs := lpass-hdmi.o snd-soc-lpass-platform-objs := lpass-platform.o snd-soc-lpass-ipq806x-objs := lpass-ipq806x.o snd-soc-lpass-apq8016-objs := lpass-apq8016.o snd-soc-lpass-sc7180-objs := lpass-sc7180.o obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o +obj-$(CONFIG_SND_SOC_LPASS_HDMI) += snd-soc-lpass-hdmi.o obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o obj-$(CONFIG_SND_SOC_LPASS_IPQ806X) += snd-soc-lpass-ipq806x.o obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c index 5c8ae225cd5d..0aedb3a0a798 100644 --- a/sound/soc/qcom/lpass-apq8016.c +++ b/sound/soc/qcom/lpass-apq8016.c @@ -125,7 +125,7 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = { }; static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, - int direction) + int direction, unsigned int dai_id) { struct lpass_variant *v = drvdata->variant; int chan = 0; @@ -151,7 +151,7 @@ static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata, return chan; } -static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) { clear_bit(chan, &drvdata->dma_ch_bit_map); diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c index 12950d2e2755..ba2aca301a9b 100644 --- a/sound/soc/qcom/lpass-cpu.c +++ b/sound/soc/qcom/lpass-cpu.c @@ -478,6 +478,206 @@ static struct regmap_config lpass_cpu_regmap_config = { .cache_type = REGCACHE_FLAT, }; +static int lpass_hdmi_init_bitfields(struct device *dev, struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + unsigned int i; + struct lpass_hdmi_tx_ctl *tx_ctl; + struct regmap_field *legacy_en; + struct lpass_vbit_ctrl *vbit_ctl; + struct regmap_field *tx_parity; + struct lpass_dp_metadata_ctl *meta_ctl; + struct lpass_sstream_ctl *sstream_ctl; + struct regmap_field *ch_msb; + struct regmap_field *ch_lsb; + struct lpass_hdmitx_dmactl *tx_dmactl; + int rval; + + tx_ctl = devm_kzalloc(dev, sizeof(*tx_ctl), GFP_KERNEL); + if (!tx_ctl) + return -ENOMEM; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->soft_reset, tx_ctl->soft_reset); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->force_reset, tx_ctl->force_reset); + drvdata->tx_ctl = tx_ctl; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->legacy_en, legacy_en); + drvdata->hdmitx_legacy_en = legacy_en; + + vbit_ctl = devm_kzalloc(dev, sizeof(*vbit_ctl), GFP_KERNEL); + if (!vbit_ctl) + return -ENOMEM; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->replace_vbit, vbit_ctl->replace_vbit); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->vbit_stream, vbit_ctl->vbit_stream); + drvdata->vbit_ctl = vbit_ctl; + + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->calc_en, tx_parity); + drvdata->hdmitx_parity_calc_en = tx_parity; + + meta_ctl = devm_kzalloc(dev, sizeof(*meta_ctl), GFP_KERNEL); + if (!meta_ctl) + return -ENOMEM; + + rval = devm_regmap_field_bulk_alloc(dev, map, &meta_ctl->mute, &v->mute, 7); + if (rval) + return rval; + drvdata->meta_ctl = meta_ctl; + + sstream_ctl = devm_kzalloc(dev, sizeof(*sstream_ctl), GFP_KERNEL); + if (!sstream_ctl) + return -ENOMEM; + + rval = devm_regmap_field_bulk_alloc(dev, map, &sstream_ctl->sstream_en, &v->sstream_en, 9); + if (rval) + return rval; + + drvdata->sstream_ctl = sstream_ctl; + + for (i = 0; i < LPASS_MAX_HDMI_DMA_CHANNELS; i++) { + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->msb_bits, ch_msb); + drvdata->hdmitx_ch_msb[i] = ch_msb; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->lsb_bits, ch_lsb); + drvdata->hdmitx_ch_lsb[i] = ch_lsb; + + tx_dmactl = devm_kzalloc(dev, sizeof(*tx_dmactl), GFP_KERNEL); + if (!tx_dmactl) + return -ENOMEM; + + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_chs, tx_dmactl->use_hw_chs); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->use_hw_usr, tx_dmactl->use_hw_usr); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_chs_sel, tx_dmactl->hw_chs_sel); + QCOM_REGMAP_FIELD_ALLOC(dev, map, v->hw_usr_sel, tx_dmactl->hw_usr_sel); + drvdata->hdmi_tx_dmactl[i] = tx_dmactl; + } + return 0; +} + +static bool lpass_hdmi_regmap_writeable(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_DP_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQCLEAR_REG(v)) + return true; + + for (i = 0; i < v->hdmi_rdma_channels; i++) { + if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) + return true; + } + + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) + return true; + } + return false; +} + +static bool lpass_hdmi_regmap_readable(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + if (reg == LPASS_HDMI_TX_CTL_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) + return true; + + for (i = 0; i < v->hdmi_rdma_channels; i++) { + if (reg == LPASS_HDMI_TX_CH_LSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_CH_MSB_ADDR(v, i)) + return true; + if (reg == LPASS_HDMI_TX_DMA_ADDR(v, i)) + return true; + } + + if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_DP_ADDR(v)) + return true; + if (reg == LPASS_HDMI_TX_SSTREAM_ADDR(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQEN_REG(v)) + return true; + if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) + return true; + + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_HDMI_RDMACTL_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABASE_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMABUFF_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMAPER_REG(v, i)) + return true; + if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) + return true; + } + + return false; +} + +static bool lpass_hdmi_regmap_volatile(struct device *dev, unsigned int reg) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + int i; + + if (reg == LPASS_HDMITX_APP_IRQSTAT_REG(v)) + return true; + if (reg == LPASS_HDMI_TX_LEGACY_ADDR(v)) + return true; + + for (i = 0; i < v->rdma_channels; ++i) { + if (reg == LPAIF_HDMI_RDMACURR_REG(v, i)) + return true; + } + return false; +} + +struct regmap_config lpass_hdmi_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .writeable_reg = lpass_hdmi_regmap_writeable, + .readable_reg = lpass_hdmi_regmap_readable, + .volatile_reg = lpass_hdmi_regmap_volatile, + .cache_type = REGCACHE_FLAT, +}; + static unsigned int of_lpass_cpu_parse_sd_lines(struct device *dev, struct device_node *node, const char *name) @@ -535,13 +735,17 @@ static void of_lpass_cpu_parse_dai_data(struct device *dev, dev_err(dev, "valid dai id not found: %d\n", ret); continue; } - - data->mi2s_playback_sd_mode[id] = - of_lpass_cpu_parse_sd_lines(dev, node, - "qcom,playback-sd-lines"); - data->mi2s_capture_sd_mode[id] = - of_lpass_cpu_parse_sd_lines(dev, node, + if (id == LPASS_DP_RX) { + data->hdmi_port_enable = 1; + dev_err(dev, "HDMI Port is enabled: %d\n", id); + } else { + data->mi2s_playback_sd_mode[id] = + of_lpass_cpu_parse_sd_lines(dev, node, + "qcom,playback-sd-lines"); + data->mi2s_capture_sd_mode[id] = + of_lpass_cpu_parse_sd_lines(dev, node, "qcom,capture-sd-lines"); + } } } @@ -596,6 +800,27 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return PTR_ERR(drvdata->lpaif_map); } + if (drvdata->hdmi_port_enable) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-hdmiif"); + + drvdata->hdmiif = devm_ioremap_resource(dev, res); + if (IS_ERR((void const __force *)drvdata->hdmiif)) { + dev_err(dev, "error mapping reg resource: %ld\n", + PTR_ERR((void const __force *)drvdata->hdmiif)); + return PTR_ERR((void const __force *)drvdata->hdmiif); + } + + lpass_hdmi_regmap_config.max_register = LPAIF_HDMI_RDMAPER_REG(variant, + variant->hdmi_rdma_channels); + drvdata->hdmiif_map = devm_regmap_init_mmio(dev, drvdata->hdmiif, + &lpass_hdmi_regmap_config); + if (IS_ERR(drvdata->hdmiif_map)) { + dev_err(dev, "error initializing regmap: %ld\n", + PTR_ERR(drvdata->hdmiif_map)); + return PTR_ERR(drvdata->hdmiif_map); + } + } + if (variant->init) { ret = variant->init(pdev); if (ret) { @@ -606,6 +831,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) for (i = 0; i < variant->num_dai; i++) { dai_id = variant->dai_driver[i].id; + if (dai_id == LPASS_DP_RX) + continue; + drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(dev, variant->dai_osr_clk_names[i]); if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) { @@ -641,6 +869,13 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev) return ret; } + if (drvdata->hdmi_port_enable) { + ret = lpass_hdmi_init_bitfields(dev, drvdata->hdmiif_map); + if (ret) { + dev_err(dev, "%s error hdmi init failed\n", __func__); + return ret; + } + } ret = devm_snd_soc_register_component(dev, &lpass_cpu_comp_driver, variant->dai_driver, diff --git a/sound/soc/qcom/lpass-hdmi.c b/sound/soc/qcom/lpass-hdmi.c new file mode 100644 index 000000000000..172952d3a5d6 --- /dev/null +++ b/sound/soc/qcom/lpass-hdmi.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * lpass-hdmi.c -- ALSA SoC HDMI-CPU DAI driver for QTi LPASS HDMI + */ + + +#include +#include +#include +#include +#include +#include +#include +#include "lpass-lpaif-reg.h" +#include "lpass.h" + +static int lpass_hdmi_daiops_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + snd_pcm_format_t format = params_format(params); + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + unsigned int ret; + unsigned int bitwidth; + unsigned int word_length; + unsigned int ch_sts_buf0; + unsigned int ch_sts_buf1; + unsigned int data_format; + unsigned int sampling_freq; + unsigned int ch = 0; + struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl; + struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl; + + bitwidth = snd_pcm_format_width(format); + if (bitwidth < 0) { + dev_err(dai->dev, "%s invalid bit width given : %d\n", + __func__, bitwidth); + return bitwidth; + } + + switch (bitwidth) { + case 16: + word_length = LPASS_DP_AUDIO_BITWIDTH16; + break; + case 24: + word_length = LPASS_DP_AUDIO_BITWIDTH24; + break; + default: + dev_err(dai->dev, "%s invalid bit width given : %d\n", + __func__, bitwidth); + return -EINVAL; + } + + switch (rate) { + case 32000: + sampling_freq = LPASS_SAMPLING_FREQ32; + break; + case 44100: + sampling_freq = LPASS_SAMPLING_FREQ44; + break; + case 48000: + sampling_freq = LPASS_SAMPLING_FREQ48; + break; + default: + dev_err(dai->dev, "%s invalid bit width given : %d\n", + __func__, bitwidth); + return -EINVAL; + } + data_format = LPASS_DATA_FORMAT_LINEAR; + ch_sts_buf0 = (((data_format << LPASS_DATA_FORMAT_SHIFT) & LPASS_DATA_FORMAT_MASK) + | ((sampling_freq << LPASS_FREQ_BIT_SHIFT) & LPASS_FREQ_BIT_MASK)); + ch_sts_buf1 = (word_length) & LPASS_WORDLENGTH_MASK; + + ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_RESET); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->tx_ctl->soft_reset, LPASS_TX_CTL_CLEAR); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_legacy_en, LPASS_HDMITX_LEGACY_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_parity_calc_en, HDMITX_PARITY_CALC_EN); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->vbit_ctl->replace_vbit, REPLACE_VBIT); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->vbit_ctl->vbit_stream, LINEAR_PCM_DATA); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_ch_msb[0], ch_sts_buf1); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmitx_ch_lsb[0], ch_sts_buf0); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_chs, HW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_chs_sel, SW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->use_hw_usr, HW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->hdmi_tx_dmactl[0]->hw_usr_sel, SW_MODE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->as_sdp_cc, channels - 1); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->as_sdp_ct, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->aif_db4, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->frequency, sampling_freq); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mst_index, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->dptx_index, LPASS_META_DEFAULT_VAL); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dma_sel, ch); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->auto_bbit_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->layout, LPASS_SSTREAM_DEFAULT_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->layout_sp, LPASS_LAYOUT_SP_DEFAULT); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_audio, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->set_sp_on_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_sp_b_hw_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_staffing_en, LPASS_SSTREAM_DEFAULT_ENABLE); + if (ret) + return ret; + + return ret; +} + +static int lpass_hdmi_daiops_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + int ret; + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + + ret = regmap_field_write(drvdata->sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(drvdata->meta_ctl->mute, LPASS_MUTE_DISABLE); + if (ret) + return ret; + + return ret; +} + +static int lpass_hdmi_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) +{ + struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); + struct lpass_dp_metadata_ctl *meta_ctl = drvdata->meta_ctl; + struct lpass_sstream_ctl *sstream_ctl = drvdata->sstream_ctl; + int ret = -EINVAL; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_DISABLE); + if (ret) + return ret; + + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + ret = regmap_field_write(sstream_ctl->sstream_en, LPASS_SSTREAM_DISABLE); + if (ret) + return ret; + + ret = regmap_field_write(meta_ctl->mute, LPASS_MUTE_ENABLE); + if (ret) + return ret; + + ret = regmap_field_write(sstream_ctl->dp_audio, 0); + if (ret) + return ret; + + break; + } + return ret; +} + +const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops = { + .hw_params = lpass_hdmi_daiops_hw_params, + .prepare = lpass_hdmi_daiops_prepare, + .trigger = lpass_hdmi_daiops_trigger, +}; +EXPORT_SYMBOL_GPL(asoc_qcom_lpass_hdmi_dai_ops); + +MODULE_DESCRIPTION("QTi LPASS HDMI Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/qcom/lpass-hdmi.h b/sound/soc/qcom/lpass-hdmi.h new file mode 100644 index 000000000000..ee74d783027a --- /dev/null +++ b/sound/soc/qcom/lpass-hdmi.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * + * lpass_hdmi.h - Definitions for the QTi LPASS HDMI + */ + +#ifndef __LPASS_HDMI_H__ +#define __LPASS_HDMI_H__ + +#include + +#define LPASS_HDMITX_LEGACY_DISABLE 0x0 +#define LPASS_HDMITX_LEGACY_ENABLE 0x1 +#define LPASS_DP_AUDIO_BITWIDTH16 0x0 +#define LPASS_DP_AUDIO_BITWIDTH24 0xb +#define LPASS_DATA_FORMAT_SHIFT 0x1 +#define LPASS_FREQ_BIT_SHIFT 24 +#define LPASS_DATA_FORMAT_LINEAR 0x0 +#define LPASS_DATA_FORMAT_NON_LINEAR 0x1 +#define LPASS_SAMPLING_FREQ32 0x3 +#define LPASS_SAMPLING_FREQ44 0x0 +#define LPASS_SAMPLING_FREQ48 0x2 +#define LPASS_TX_CTL_RESET 0x1 +#define LPASS_TX_CTL_CLEAR 0x0 +#define LPASS_SSTREAM_ENABLE 1 +#define LPASS_SSTREAM_DISABLE 0 +#define LPASS_LAYOUT_SP_DEFAULT 0xf +#define LPASS_SSTREAM_DEFAULT_ENABLE 1 +#define LPASS_SSTREAM_DEFAULT_DISABLE 0 +#define LPASS_MUTE_ENABLE 1 +#define LPASS_MUTE_DISABLE 0 +#define LPASS_META_DEFAULT_VAL 0 +#define HW_MODE 1 +#define SW_MODE 0 +#define LEGACY_LPASS_LPAIF 1 +#define LEGACY_LPASS_HDMI 0 +#define REPLACE_VBIT 0x1 +#define LINEAR_PCM_DATA 0x0 +#define NON_LINEAR_PCM_DATA 0x1 +#define HDMITX_PARITY_CALC_EN 0x1 +#define HDMITX_PARITY_CALC_DIS 0x0 +#define LPASS_DATA_FORMAT_MASK GENMASK(1, 1) +#define LPASS_WORDLENGTH_MASK GENMASK(3, 0) +#define LPASS_FREQ_BIT_MASK GENMASK(27, 24) + +#define LPASS_HDMI_TX_CTL_ADDR(v) (v->hdmi_tx_ctl_addr) +#define LPASS_HDMI_TX_LEGACY_ADDR(v) (v->hdmi_legacy_addr) +#define LPASS_HDMI_TX_VBIT_CTL_ADDR(v) (v->hdmi_vbit_addr) +#define LPASS_HDMI_TX_PARITY_ADDR(v) (v->hdmi_parity_addr) +#define LPASS_HDMI_TX_DP_ADDR(v) (v->hdmi_DP_addr) +#define LPASS_HDMI_TX_SSTREAM_ADDR(v) (v->hdmi_sstream_addr) + +#define LPASS_HDMI_TX_CH_LSB_ADDR(v, port) \ + (v->hdmi_ch_lsb_addr + v->ch_stride * (port)) +#define LPASS_HDMI_TX_CH_MSB_ADDR(v, port) \ + (v->hdmi_ch_msb_addr + v->ch_stride * (port)) +#define LPASS_HDMI_TX_DMA_ADDR(v, port) \ + (v->hdmi_dmactl_addr + v->hdmi_dma_stride * (port)) + +struct lpass_sstream_ctl { + struct regmap_field *sstream_en; + struct regmap_field *dma_sel; + struct regmap_field *auto_bbit_en; + struct regmap_field *layout; + struct regmap_field *layout_sp; + struct regmap_field *set_sp_on_en; + struct regmap_field *dp_audio; + struct regmap_field *dp_staffing_en; + struct regmap_field *dp_sp_b_hw_en; +}; + +struct lpass_dp_metadata_ctl { + struct regmap_field *mute; + struct regmap_field *as_sdp_cc; + struct regmap_field *as_sdp_ct; + struct regmap_field *aif_db4; + struct regmap_field *frequency; + struct regmap_field *mst_index; + struct regmap_field *dptx_index; +}; + +struct lpass_hdmi_tx_ctl { + struct regmap_field *soft_reset; + struct regmap_field *force_reset; +}; + +struct lpass_hdmitx_dmactl { + struct regmap_field *use_hw_chs; + struct regmap_field *use_hw_usr; + struct regmap_field *hw_chs_sel; + struct regmap_field *hw_usr_sel; +}; + +struct lpass_vbit_ctrl { + struct regmap_field *replace_vbit; + struct regmap_field *vbit_stream; +}; + +extern const struct snd_soc_dai_ops asoc_qcom_lpass_hdmi_dai_ops; + +#endif /* __LPASS_HDMI_H__ */ diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c index 72f09b3a4f6b..832a9161484e 100644 --- a/sound/soc/qcom/lpass-ipq806x.c +++ b/sound/soc/qcom/lpass-ipq806x.c @@ -96,7 +96,7 @@ static int ipq806x_lpass_exit(struct platform_device *pdev) return 0; } -static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) +static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir, unsigned int dai_id) { if (dir == SNDRV_PCM_STREAM_PLAYBACK) return IPQ806X_LPAIF_RDMA_CHAN_MI2S; @@ -104,7 +104,7 @@ static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir) return -EINVAL; } -static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) { return 0; } diff --git a/sound/soc/qcom/lpass-lpaif-reg.h b/sound/soc/qcom/lpass-lpaif-reg.h index 5258e60d3646..08f3fe508b85 100644 --- a/sound/soc/qcom/lpass-lpaif-reg.h +++ b/sound/soc/qcom/lpass-lpaif-reg.h @@ -70,6 +70,14 @@ #define LPAIF_IRQSTAT_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0x4, (port)) #define LPAIF_IRQCLEAR_REG(v, port) LPAIF_IRQ_REG_ADDR(v, 0xC, (port)) + +#define LPASS_HDMITX_APP_IRQ_REG_ADDR(v, addr) \ + ((v->hdmi_irq_reg_base) + (addr)) + +#define LPASS_HDMITX_APP_IRQEN_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x4) +#define LPASS_HDMITX_APP_IRQSTAT_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0x8) +#define LPASS_HDMITX_APP_IRQCLEAR_REG(v) LPASS_HDMITX_APP_IRQ_REG_ADDR(v, 0xC) + #define LPAIF_IRQ_BITSTRIDE 3 #define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) @@ -77,8 +85,22 @@ #define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) #define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) +#define LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) (1 << (14 + chan)) +#define LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan) (1 << (24 + chan)) +#define LPAIF_IRQ_HDMI_METADONE BIT(23) /* LPAIF DMA */ +#define LPAIF_HDMI_RDMA_REG_ADDR(v, addr, chan) \ + (v->hdmi_rdma_reg_base + (addr) + v->hdmi_rdma_reg_stride * (chan)) + +#define LPAIF_HDMI_RDMACTL_AUDINTF(id) (id << LPAIF_RDMACTL_AUDINTF_SHIFT) + +#define LPAIF_HDMI_RDMACTL_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x00, (chan)) +#define LPAIF_HDMI_RDMABASE_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x04, (chan)) +#define LPAIF_HDMI_RDMABUFF_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x08, (chan)) +#define LPAIF_HDMI_RDMACURR_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x0C, (chan)) +#define LPAIF_HDMI_RDMAPER_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x10, (chan)) +#define LPAIF_HDMI_RDMAPERCNT_REG(v, chan) LPAIF_HDMI_RDMA_REG_ADDR(v, 0x14, (chan)) #define LPAIF_RDMA_REG_ADDR(v, addr, chan) \ (v->rdma_reg_base + (addr) + v->rdma_reg_stride * (chan)) @@ -103,17 +125,22 @@ #define LPAIF_WRDMAPER_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan)) #define LPAIF_WRDMAPERCNT_REG(v, chan) LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan)) -#define __LPAIF_DMA_REG(v, chan, dir, reg) \ - (dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ - LPAIF_RDMA##reg##_REG(v, chan) : \ - LPAIF_WRDMA##reg##_REG(v, chan) - -#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL) -#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE) -#define LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF) -#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR) -#define LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER) -#define LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT) +#define LPAIF_INTFDMA_REG(v, chan, reg, dai_id) \ + ((v->dai_driver[dai_id].id == LPASS_DP_RX) ? \ + LPAIF_HDMI_RDMA##reg##_REG(v, chan) : \ + LPAIF_RDMA##reg##_REG(v, chan)) + +#define __LPAIF_DMA_REG(v, chan, dir, reg, dai_id) \ + ((dir == SNDRV_PCM_STREAM_PLAYBACK) ? \ + (LPAIF_INTFDMA_REG(v, chan, reg, dai_id)) : \ + LPAIF_WRDMA##reg##_REG(v, chan)) + +#define LPAIF_DMACTL_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CTL, dai_id) +#define LPAIF_DMABASE_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BASE, dai_id) +#define LPAIF_DMABUFF_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, BUFF, dai_id) +#define LPAIF_DMACURR_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, CURR, dai_id) +#define LPAIF_DMAPER_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PER, dai_id) +#define LPAIF_DMAPERCNT_REG(v, chan, dir, dai_id) __LPAIF_DMA_REG(v, chan, dir, PERCNT, dai_id) #define LPAIF_DMACTL_BURSTEN_SINGLE 0 #define LPAIF_DMACTL_BURSTEN_INCR4 1 diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c index db0d959b1619..49abbb2bd99a 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -80,6 +80,23 @@ static int lpass_platform_alloc_dmactl_fields(struct device *dev, &v->wrdma_intf, 6); } +static int lpass_platform_alloc_hdmidmactl_fields(struct device *dev, + struct regmap *map) +{ + struct lpass_data *drvdata = dev_get_drvdata(dev); + struct lpass_variant *v = drvdata->variant; + struct lpaif_dmactl *rd_dmactl; + + rd_dmactl = devm_kzalloc(dev, sizeof(struct lpaif_dmactl), GFP_KERNEL); + if (rd_dmactl == NULL) + return -ENOMEM; + + drvdata->hdmi_rd_dmactl = rd_dmactl; + + return devm_regmap_field_bulk_alloc(dev, map, &rd_dmactl->bursten, + &v->hdmi_rdma_bursten, 8); +} + static int lpass_platform_pcmops_open(struct snd_soc_component *component, struct snd_pcm_substream *substream) { @@ -90,6 +107,8 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, struct lpass_variant *v = drvdata->variant; int ret, dma_ch, dir = substream->stream; struct lpass_pcm_data *data; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) @@ -99,25 +118,28 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component, runtime->private_data = data; if (v->alloc_dma_channel) - dma_ch = v->alloc_dma_channel(drvdata, dir); + dma_ch = v->alloc_dma_channel(drvdata, dir, dai_id); else dma_ch = 0; if (dma_ch < 0) return dma_ch; - drvdata->substream[dma_ch] = substream; - - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMACTL_REG(v, dma_ch, dir), 0); + if (cpu_dai->driver->id == LPASS_DP_RX) { + map = drvdata->hdmiif_map; + drvdata->hdmi_substream[dma_ch] = substream; + } else { + map = drvdata->lpaif_map; + drvdata->substream[dma_ch] = substream; + } + data->dma_ch = dma_ch; + ret = regmap_write(map, + LPAIF_DMACTL_REG(v, dma_ch, dir, data->i2s_port), 0); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); return ret; } - - data->dma_ch = dma_ch; - snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; @@ -139,14 +161,20 @@ static int lpass_platform_pcmops_close(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct lpass_variant *v = drvdata->variant; struct lpass_pcm_data *data; + unsigned int dai_id = cpu_dai->driver->id; data = runtime->private_data; - drvdata->substream[data->dma_ch] = NULL; + if (dai_id == LPASS_DP_RX) + drvdata->hdmi_substream[data->dma_ch] = NULL; + else + drvdata->substream[data->dma_ch] = NULL; if (v->free_dma_channel) - v->free_dma_channel(drvdata, data->dma_ch); + v->free_dma_channel(drvdata, data->dma_ch, dai_id); kfree(data); return 0; @@ -157,6 +185,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; @@ -168,10 +197,15 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, int id, dir = substream->stream; int bitwidth; int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start; + unsigned int dai_id = cpu_dai->driver->id; if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - dmactl = drvdata->rd_dmactl; id = pcm_data->dma_ch; + if (dai_id == LPASS_DP_RX) + dmactl = drvdata->hdmi_rd_dmactl; + else + dmactl = drvdata->rd_dmactl; + } else { dmactl = drvdata->wr_dmactl; id = pcm_data->dma_ch - v->wrdma_channel_start; @@ -190,18 +224,48 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, return ret; } - regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8); + ret = regmap_fields_write(dmactl->fifowm, id, LPAIF_DMACTL_FIFOWM_8); if (ret) { dev_err(soc_runtime->dev, "error updating fifowm field: %d\n", ret); return ret; } - regmap_fields_write(dmactl->intf, id, LPAIF_DMACTL_AUDINTF(dma_port)); - if (ret) { - dev_err(soc_runtime->dev, "error updating audintf field: %d\n", ret); - return ret; - } + switch (dai_id) { + case LPASS_DP_RX: + ret = regmap_fields_write(dmactl->burst8, id, + LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating burst8en field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->burst16, id, + LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating burst16en field: %d\n", ret); + return ret; + } + ret = regmap_fields_write(dmactl->dynburst, id, + LPAIF_DMACTL_BURSTEN_INCR4); + if (ret) { + dev_err(soc_runtime->dev, "error updating dynbursten field: %d\n", ret); + return ret; + } + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + ret = regmap_fields_write(dmactl->intf, id, + LPAIF_DMACTL_AUDINTF(dma_port)); + if (ret) { + dev_err(soc_runtime->dev, "error updating audio interface field: %d\n", + ret); + return ret; + } + break; + default: + dev_err(soc_runtime->dev, "%s: invalid interface: %d\n", __func__, dai_id); + break; + } switch (bitwidth) { case 16: switch (channels) { @@ -219,8 +283,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, regval = LPAIF_DMACTL_WPSCNT_FOUR; break; default: - dev_err(soc_runtime->dev, - "invalid PCM config given: bw=%d, ch=%u\n", + dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n", bitwidth, channels); return -EINVAL; } @@ -232,20 +295,27 @@ static int lpass_platform_pcmops_hw_params(struct snd_soc_component *component, regval = LPAIF_DMACTL_WPSCNT_ONE; break; case 2: - regval = LPAIF_DMACTL_WPSCNT_TWO; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_ONE : + LPAIF_DMACTL_WPSCNT_TWO); break; case 4: - regval = LPAIF_DMACTL_WPSCNT_FOUR; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_TWO : + LPAIF_DMACTL_WPSCNT_FOUR); break; case 6: - regval = LPAIF_DMACTL_WPSCNT_SIX; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_THREE : + LPAIF_DMACTL_WPSCNT_SIX); break; case 8: - regval = LPAIF_DMACTL_WPSCNT_EIGHT; + regval = (dai_id == LPASS_DP_RX ? + LPAIF_DMACTL_WPSCNT_FOUR : + LPAIF_DMACTL_WPSCNT_EIGHT); break; default: - dev_err(soc_runtime->dev, - "invalid PCM config given: bw=%d, ch=%u\n", + dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n", bitwidth, channels); return -EINVAL; } @@ -270,15 +340,23 @@ static int lpass_platform_pcmops_hw_free(struct snd_soc_component *component, struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int reg; int ret; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; - reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream); - ret = regmap_write(drvdata->lpaif_map, reg, 0); + if (dai_id == LPASS_DP_RX) + map = drvdata->hdmiif_map; + else + map = drvdata->lpaif_map; + + reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream, dai_id); + ret = regmap_write(map, reg, 0); if (ret) dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n", ret); @@ -291,33 +369,43 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; struct lpaif_dmactl *dmactl; + struct regmap *map; int ret, id, ch, dir = substream->stream; + unsigned int dai_id = cpu_dai->driver->id; + ch = pcm_data->dma_ch; if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - dmactl = drvdata->rd_dmactl; + if (dai_id == LPASS_DP_RX) { + dmactl = drvdata->hdmi_rd_dmactl; + map = drvdata->hdmiif_map; + } else { + dmactl = drvdata->rd_dmactl; + map = drvdata->lpaif_map; + } + id = pcm_data->dma_ch; } else { dmactl = drvdata->wr_dmactl; id = pcm_data->dma_ch - v->wrdma_channel_start; + map = drvdata->lpaif_map; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMABASE_REG(v, ch, dir), - runtime->dma_addr); + ret = regmap_write(map, LPAIF_DMABASE_REG(v, ch, dir, dai_id), + runtime->dma_addr); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n", ret); return ret; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMABUFF_REG(v, ch, dir), + ret = regmap_write(map, LPAIF_DMABUFF_REG(v, ch, dir, dai_id), (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n", @@ -325,8 +413,7 @@ static int lpass_platform_pcmops_prepare(struct snd_soc_component *component, return ret; } - ret = regmap_write(drvdata->lpaif_map, - LPAIF_DMAPER_REG(v, ch, dir), + ret = regmap_write(map, LPAIF_DMAPER_REG(v, ch, dir, dai_id), (snd_pcm_lib_period_bytes(substream) >> 2) - 1); if (ret) { dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n", @@ -349,18 +436,26 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, int cmd) { struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; struct lpaif_dmactl *dmactl; + struct regmap *map; int ret, ch, id; int dir = substream->stream; + unsigned int reg_irqclr = 0, val_irqclr = 0; + unsigned int reg_irqen = 0, val_irqen = 0, val_mask = 0; + unsigned int dai_id = cpu_dai->driver->id; ch = pcm_data->dma_ch; if (dir == SNDRV_PCM_STREAM_PLAYBACK) { - dmactl = drvdata->rd_dmactl; id = pcm_data->dma_ch; + if (dai_id == LPASS_DP_RX) + dmactl = drvdata->hdmi_rd_dmactl; + else + dmactl = drvdata->rd_dmactl; } else { dmactl = drvdata->wr_dmactl; id = pcm_data->dma_ch - v->wrdma_channel_start; @@ -370,31 +465,63 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - /* clear status before enabling interrupts */ - ret = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch)); + ret = regmap_fields_write(dmactl->enable, id, + LPAIF_DMACTL_ENABLE_ON); if (ret) { dev_err(soc_runtime->dev, - "error writing to irqclear reg: %d\n", ret); + "error writing to rdmactl reg: %d\n", ret); return ret; } + switch (dai_id) { + case LPASS_DP_RX: + ret = regmap_fields_write(dmactl->dyncclk, id, + LPAIF_DMACTL_DYNCLK_ON); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg: %d\n", ret); + return ret; + } + map = drvdata->hdmiif_map; + reg_irqclr = LPASS_HDMITX_APP_IRQCLEAR_REG(v); + val_irqclr = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + + reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v); + val_mask = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + val_irqen = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + map = drvdata->lpaif_map; + reg_irqclr = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val_irqclr = LPAIF_IRQ_ALL(ch); + - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch), - LPAIF_IRQ_ALL(ch)); + reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = LPAIF_IRQ_ALL(ch); + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } + + ret = regmap_write(map, reg_irqclr, val_irqclr); if (ret) { - dev_err(soc_runtime->dev, - "error writing to irqen reg: %d\n", ret); + dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", ret); return ret; } - - ret = regmap_fields_write(dmactl->enable, id, - LPAIF_DMACTL_ENABLE_ON); + ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen); if (ret) { - dev_err(soc_runtime->dev, - "error writing to rdmactl reg: %d\n", ret); + dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret); return ret; } break; @@ -408,10 +535,36 @@ static int lpass_platform_pcmops_trigger(struct snd_soc_component *component, "error writing to rdmactl reg: %d\n", ret); return ret; } + switch (dai_id) { + case LPASS_DP_RX: + ret = regmap_fields_write(dmactl->dyncclk, id, + LPAIF_DMACTL_DYNCLK_OFF); + if (ret) { + dev_err(soc_runtime->dev, + "error writing to rdmactl reg: %d\n", ret); + return ret; + } + map = drvdata->hdmiif_map; + reg_irqen = LPASS_HDMITX_APP_IRQEN_REG(v); + val_mask = (LPAIF_IRQ_ALL(ch) | + LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(ch) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(ch)); + val_irqen = 0; + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + map = drvdata->lpaif_map; + reg_irqen = LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST); + val_mask = LPAIF_IRQ_ALL(ch); + val_irqen = 0; + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } - ret = regmap_update_bits(drvdata->lpaif_map, - LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ALL(ch), 0); + ret = regmap_update_bits(map, reg_irqen, val_mask, val_irqen); if (ret) { dev_err(soc_runtime->dev, "error writing to irqen reg: %d\n", ret); @@ -428,25 +581,33 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer( struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_data *drvdata = snd_soc_component_get_drvdata(component); struct snd_pcm_runtime *rt = substream->runtime; struct lpass_pcm_data *pcm_data = rt->private_data; struct lpass_variant *v = drvdata->variant; unsigned int base_addr, curr_addr; int ret, ch, dir = substream->stream; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; + + if (dai_id == LPASS_DP_RX) + map = drvdata->hdmiif_map; + else + map = drvdata->lpaif_map; ch = pcm_data->dma_ch; - ret = regmap_read(drvdata->lpaif_map, - LPAIF_DMABASE_REG(v, ch, dir), &base_addr); + ret = regmap_read(map, + LPAIF_DMABASE_REG(v, ch, dir, dai_id), &base_addr); if (ret) { dev_err(soc_runtime->dev, "error reading from rdmabase reg: %d\n", ret); return ret; } - ret = regmap_read(drvdata->lpaif_map, - LPAIF_DMACURR_REG(v, ch, dir), &curr_addr); + ret = regmap_read(map, + LPAIF_DMACURR_REG(v, ch, dir, dai_id), &curr_addr); if (ret) { dev_err(soc_runtime->dev, "error reading from rdmacurr reg: %d\n", ret); @@ -472,14 +633,35 @@ static irqreturn_t lpass_dma_interrupt_handler( int chan, u32 interrupts) { struct snd_soc_pcm_runtime *soc_runtime = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(soc_runtime, 0); struct lpass_variant *v = drvdata->variant; irqreturn_t ret = IRQ_NONE; int rv; - + unsigned int reg = 0, val = 0; + struct regmap *map; + unsigned int dai_id = cpu_dai->driver->id; + + switch (dai_id) { + case LPASS_DP_RX: + map = drvdata->hdmiif_map; + reg = LPASS_HDMITX_APP_IRQCLEAR_REG(v); + val = (LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan)); + break; + case MI2S_PRIMARY: + case MI2S_SECONDARY: + map = drvdata->lpaif_map; + reg = LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST); + val = 0; + break; + default: + dev_err(soc_runtime->dev, "%s: invalid %d interface\n", __func__, dai_id); + return -EINVAL; + } if (interrupts & LPAIF_IRQ_PER(chan)) { - rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_PER(chan)); + + rv = regmap_write(map, reg, LPAIF_IRQ_PER(chan) | val); if (rv) { dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", rv); @@ -490,9 +672,7 @@ static irqreturn_t lpass_dma_interrupt_handler( } if (interrupts & LPAIF_IRQ_XRUN(chan)) { - rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_XRUN(chan)); + rv = regmap_write(map, reg, LPAIF_IRQ_XRUN(chan) | val); if (rv) { dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", rv); @@ -504,9 +684,7 @@ static irqreturn_t lpass_dma_interrupt_handler( } if (interrupts & LPAIF_IRQ_ERR(chan)) { - rv = regmap_write(drvdata->lpaif_map, - LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST), - LPAIF_IRQ_ERR(chan)); + rv = regmap_write(map, reg, LPAIF_IRQ_ERR(chan) | val); if (rv) { dev_err(soc_runtime->dev, "error writing to irqclear reg: %d\n", rv); @@ -517,6 +695,16 @@ static irqreturn_t lpass_dma_interrupt_handler( ret = IRQ_HANDLED; } + if (interrupts & val) { + rv = regmap_write(map, reg, val); + if (rv) { + dev_err(soc_runtime->dev, + "error writing to irqclear reg: %d\n", rv); + return IRQ_NONE; + } + ret = IRQ_HANDLED; + } + return ret; } @@ -548,6 +736,37 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) return IRQ_HANDLED; } +static irqreturn_t lpass_platform_hdmiif_irq(int irq, void *data) +{ + struct lpass_data *drvdata = data; + struct lpass_variant *v = drvdata->variant; + unsigned int irqs; + int rv, chan; + + rv = regmap_read(drvdata->hdmiif_map, + LPASS_HDMITX_APP_IRQSTAT_REG(v), &irqs); + if (rv) { + pr_err("error reading from irqstat reg: %d\n", rv); + return IRQ_NONE; + } + + /* Handle per channel interrupts */ + for (chan = 0; chan < LPASS_MAX_HDMI_DMA_CHANNELS; chan++) { + if (irqs & (LPAIF_IRQ_ALL(chan) | LPAIF_IRQ_HDMI_REQ_ON_PRELOAD(chan) | + LPAIF_IRQ_HDMI_METADONE | + LPAIF_IRQ_HDMI_SDEEP_AUD_DIS(chan)) + && drvdata->hdmi_substream[chan]) { + rv = lpass_dma_interrupt_handler( + drvdata->hdmi_substream[chan], + drvdata, chan, irqs); + if (rv != IRQ_HANDLED) + return rv; + } + } + + return IRQ_HANDLED; +} + static int lpass_platform_pcm_new(struct snd_soc_component *component, struct snd_soc_pcm_runtime *soc_runtime) { @@ -649,6 +868,32 @@ int asoc_qcom_lpass_platform_register(struct platform_device *pdev) return ret; } + if (drvdata->hdmi_port_enable) { + drvdata->hdmiif_irq = platform_get_irq_byname(pdev, "lpass-irq-hdmi"); + if (drvdata->hdmiif_irq < 0) + return -ENODEV; + + ret = devm_request_irq(&pdev->dev, drvdata->hdmiif_irq, + lpass_platform_hdmiif_irq, 0, "lpass-irq-hdmi", drvdata); + if (ret) { + dev_err(&pdev->dev, "irq hdmi request failed: %d\n", ret); + return ret; + } + ret = regmap_write(drvdata->hdmiif_map, + LPASS_HDMITX_APP_IRQEN_REG(v), 0); + if (ret) { + dev_err(&pdev->dev, "error writing to hdmi irqen reg: %d\n", ret); + return ret; + } + + ret = lpass_platform_alloc_hdmidmactl_fields(&pdev->dev, + drvdata->hdmiif_map); + if (ret) { + dev_err(&pdev->dev, + "error initializing hdmidmactl fields: %d\n", ret); + return ret; + } + } return devm_snd_soc_register_component(&pdev->dev, &lpass_component_driver, NULL, 0); } diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h index 7089d4cdf5e9..b4830f353796 100644 --- a/sound/soc/qcom/lpass.h +++ b/sound/soc/qcom/lpass.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2011,2013-2015,2020 The Linux Foundation. All rights reserved. * * lpass.h - Definitions for the QTi LPASS */ @@ -12,10 +12,20 @@ #include #include #include +#include +#include "lpass-hdmi.h" #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 #define LPASS_MAX_MI2S_PORTS (8) #define LPASS_MAX_DMA_CHANNELS (8) +#define LPASS_MAX_HDMI_DMA_CHANNELS (4) + +#define QCOM_REGMAP_FIELD_ALLOC(d, m, f, mf) \ + do { \ + mf = devm_regmap_field_alloc(d, m, f); \ + if (IS_ERR(mf)) \ + return -EINVAL; \ + } while (0) struct lpaif_i2sctl { struct regmap_field *loopback; @@ -37,6 +47,9 @@ struct lpaif_dmactl { struct regmap_field *fifowm; struct regmap_field *enable; struct regmap_field *dyncclk; + struct regmap_field *burst8; + struct regmap_field *burst16; + struct regmap_field *dynburst; }; /* Both the CPU DAI and platform drivers will access this data */ @@ -54,24 +67,29 @@ struct lpass_data { /* MI2S SD lines to use for playback/capture */ unsigned int mi2s_playback_sd_mode[LPASS_MAX_MI2S_PORTS]; unsigned int mi2s_capture_sd_mode[LPASS_MAX_MI2S_PORTS]; + int hdmi_port_enable; /* low-power audio interface (LPAIF) registers */ void __iomem *lpaif; + void __iomem *hdmiif; /* regmap backed by the low-power audio interface (LPAIF) registers */ struct regmap *lpaif_map; + struct regmap *hdmiif_map; /* interrupts from the low-power audio interface (LPAIF) */ int lpaif_irq; - + int hdmiif_irq; /* SOC specific variations in the LPASS IP integration */ struct lpass_variant *variant; /* bit map to keep track of static channel allocations */ unsigned long dma_ch_bit_map; + unsigned long hdmi_dma_ch_bit_map; /* used it for handling interrupt per dma channel */ struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS]; + struct snd_pcm_substream *hdmi_substream[LPASS_MAX_HDMI_DMA_CHANNELS]; /* SOC specific clock list */ struct clk_bulk_data *clks; @@ -81,22 +99,36 @@ struct lpass_data { struct lpaif_i2sctl *i2sctl; struct lpaif_dmactl *rd_dmactl; struct lpaif_dmactl *wr_dmactl; + struct lpaif_dmactl *hdmi_rd_dmactl; + /* Regmap fields of HDMI_CTRL registers*/ + struct regmap_field *hdmitx_legacy_en; + struct regmap_field *hdmitx_parity_calc_en; + struct regmap_field *hdmitx_ch_msb[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct regmap_field *hdmitx_ch_lsb[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct lpass_hdmi_tx_ctl *tx_ctl; + struct lpass_vbit_ctrl *vbit_ctl; + struct lpass_hdmitx_dmactl *hdmi_tx_dmactl[LPASS_MAX_HDMI_DMA_CHANNELS]; + struct lpass_dp_metadata_ctl *meta_ctl; + struct lpass_sstream_ctl *sstream_ctl; }; /* Vairant data per each SOC */ struct lpass_variant { - u32 i2sctrl_reg_base; - u32 i2sctrl_reg_stride; - u32 i2s_ports; u32 irq_reg_base; u32 irq_reg_stride; u32 irq_ports; u32 rdma_reg_base; u32 rdma_reg_stride; u32 rdma_channels; + u32 hdmi_rdma_reg_base; + u32 hdmi_rdma_reg_stride; + u32 hdmi_rdma_channels; u32 wrdma_reg_base; u32 wrdma_reg_stride; u32 wrdma_channels; + u32 i2sctrl_reg_base; + u32 i2sctrl_reg_stride; + u32 i2s_ports; /* I2SCTL Register fields */ struct reg_field loopback; @@ -109,6 +141,78 @@ struct lpass_variant { struct reg_field wssrc; struct reg_field bitwidth; + u32 hdmi_irq_reg_base; + u32 hdmi_irq_reg_stride; + u32 hdmi_irq_ports; + + /* HDMI specific controls */ + u32 hdmi_tx_ctl_addr; + u32 hdmi_legacy_addr; + u32 hdmi_vbit_addr; + u32 hdmi_ch_lsb_addr; + u32 hdmi_ch_msb_addr; + u32 ch_stride; + u32 hdmi_parity_addr; + u32 hdmi_dmactl_addr; + u32 hdmi_dma_stride; + u32 hdmi_DP_addr; + u32 hdmi_sstream_addr; + + /* HDMI SSTREAM CTRL fields */ + struct reg_field sstream_en; + struct reg_field dma_sel; + struct reg_field auto_bbit_en; + struct reg_field layout; + struct reg_field layout_sp; + struct reg_field set_sp_on_en; + struct reg_field dp_audio; + struct reg_field dp_staffing_en; + struct reg_field dp_sp_b_hw_en; + + /* HDMI DP METADATA CTL fields */ + struct reg_field mute; + struct reg_field as_sdp_cc; + struct reg_field as_sdp_ct; + struct reg_field aif_db4; + struct reg_field frequency; + struct reg_field mst_index; + struct reg_field dptx_index; + + /* HDMI TX CTRL fields */ + struct reg_field soft_reset; + struct reg_field force_reset; + + /* HDMI TX DMA CTRL */ + struct reg_field use_hw_chs; + struct reg_field use_hw_usr; + struct reg_field hw_chs_sel; + struct reg_field hw_usr_sel; + + /* HDMI VBIT CTRL */ + struct reg_field replace_vbit; + struct reg_field vbit_stream; + + /* HDMI TX LEGACY */ + struct reg_field legacy_en; + + /* HDMI TX PARITY */ + struct reg_field calc_en; + + /* HDMI CH LSB */ + struct reg_field lsb_bits; + + /* HDMI CH MSB */ + struct reg_field msb_bits; + + struct reg_field hdmi_rdma_bursten; + struct reg_field hdmi_rdma_wpscnt; + struct reg_field hdmi_rdma_fifowm; + struct reg_field hdmi_rdma_enable; + struct reg_field hdmi_rdma_dyncclk; + struct reg_field hdmi_rdma_burst8; + struct reg_field hdmi_rdma_burst16; + struct reg_field hdmi_rdma_dynburst; + /* RD_DMA Register fields */ struct reg_field rdma_intf; struct reg_field rdma_bursten; @@ -134,8 +238,8 @@ struct lpass_variant { /* SOC specific initialization like clocks */ int (*init)(struct platform_device *pdev); int (*exit)(struct platform_device *pdev); - int (*alloc_dma_channel)(struct lpass_data *data, int direction); - int (*free_dma_channel)(struct lpass_data *data, int ch); + int (*alloc_dma_channel)(struct lpass_data *data, int direction, unsigned int dai_id); + int (*free_dma_channel)(struct lpass_data *data, int ch, unsigned int dai_id); /* SOC specific dais */ struct snd_soc_dai_driver *dai_driver; -- cgit From 03f20e209d07968c410fc404b3d636dc446d3ef2 Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Thu, 8 Oct 2020 10:47:02 +0530 Subject: Asoc: qcom: lpass-platform : Increase buffer size Increase buffer size to support audio over DP. Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Mandadapu Tested-by: Srinivas Kandagatla Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1602134223-2562-7-git-send-email-srivasam@codeaurora.org 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 49abbb2bd99a..36d1512ffd1f 100644 --- a/sound/soc/qcom/lpass-platform.c +++ b/sound/soc/qcom/lpass-platform.c @@ -23,7 +23,7 @@ struct lpass_pcm_data { int i2s_port; }; -#define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) +#define LPASS_PLATFORM_BUFFER_SIZE (24 * 2 * 1024) #define LPASS_PLATFORM_PERIODS 2 static const struct snd_pcm_hardware lpass_platform_pcm_hardware = { -- cgit From 2ad63dc8df6b6108aee82dc77c820e3918bc0a65 Mon Sep 17 00:00:00 2001 From: V Sujith Kumar Reddy Date: Thu, 8 Oct 2020 10:47:03 +0530 Subject: ASoC: qcom: sc7180: Add support for audio over DP Add support for audio playback over DP in lpass sc7180 platform driver. Update lpass_variant structure for hdmi data configuaration. Signed-off-by: V Sujith Kumar Reddy Signed-off-by: Srinivasa Rao Tested-by: Srinivas Kandagatla Reviewed-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/1602134223-2562-8-git-send-email-srivasam@codeaurora.org Signed-off-by: Mark Brown --- sound/soc/qcom/lpass-sc7180.c | 116 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 102 insertions(+), 14 deletions(-) (limited to 'sound') diff --git a/sound/soc/qcom/lpass-sc7180.c b/sound/soc/qcom/lpass-sc7180.c index a8a3d8f8f567..c6292f9e613f 100644 --- a/sound/soc/qcom/lpass-sc7180.c +++ b/sound/soc/qcom/lpass-sc7180.c @@ -60,38 +60,65 @@ static struct snd_soc_dai_driver sc7180_lpass_cpu_dai_driver[] = { .probe = &asoc_qcom_lpass_cpu_dai_probe, .ops = &asoc_qcom_lpass_cpu_dai_ops, }, + [LPASS_DP_RX] = { + .id = LPASS_DP_RX, + .name = "Hdmi", + .playback = { + .stream_name = "Hdmi Playback", + .formats = SNDRV_PCM_FMTBIT_S24, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + }, + .ops = &asoc_qcom_lpass_hdmi_dai_ops, + }, }; static int sc7180_lpass_alloc_dma_channel(struct lpass_data *drvdata, - int direction) + int direction, unsigned int dai_id) { struct lpass_variant *v = drvdata->variant; int chan = 0; - if (direction == SNDRV_PCM_STREAM_PLAYBACK) { - chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, - v->rdma_channels); + if (dai_id == LPASS_DP_RX) { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->hdmi_dma_ch_bit_map, + v->hdmi_rdma_channels); + + if (chan >= v->hdmi_rdma_channels) + return -EBUSY; + } + set_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + } else { + if (direction == SNDRV_PCM_STREAM_PLAYBACK) { + chan = find_first_zero_bit(&drvdata->dma_ch_bit_map, + v->rdma_channels); if (chan >= v->rdma_channels) return -EBUSY; - } else { - chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, + } else { + chan = find_next_zero_bit(&drvdata->dma_ch_bit_map, v->wrdma_channel_start + v->wrdma_channels, v->wrdma_channel_start); - if (chan >= v->wrdma_channel_start + v->wrdma_channels) - return -EBUSY; - } - - set_bit(chan, &drvdata->dma_ch_bit_map); + if (chan >= v->wrdma_channel_start + v->wrdma_channels) + return -EBUSY; + } + set_bit(chan, &drvdata->dma_ch_bit_map); + } return chan; } -static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan) +static int sc7180_lpass_free_dma_channel(struct lpass_data *drvdata, int chan, unsigned int dai_id) { - clear_bit(chan, &drvdata->dma_ch_bit_map); + if (dai_id == LPASS_DP_RX) + clear_bit(chan, &drvdata->hdmi_dma_ch_bit_map); + else + clear_bit(chan, &drvdata->dma_ch_bit_map); return 0; } @@ -144,6 +171,9 @@ static struct lpass_variant sc7180_data = { .rdma_reg_base = 0xC000, .rdma_reg_stride = 0x1000, .rdma_channels = 5, + .hdmi_rdma_reg_base = 0x64000, + .hdmi_rdma_reg_stride = 0x1000, + .hdmi_rdma_channels = 4, .dmactl_audif_start = 1, .wrdma_reg_base = 0x18000, .wrdma_reg_stride = 0x1000, @@ -163,7 +193,7 @@ static struct lpass_variant sc7180_data = { .rdma_dyncclk = REG_FIELD_ID(0xC000, 21, 21, 5, 0x1000), .rdma_bursten = REG_FIELD_ID(0xC000, 20, 20, 5, 0x1000), .rdma_wpscnt = REG_FIELD_ID(0xC000, 16, 19, 5, 0x1000), - .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000), + .rdma_intf = REG_FIELD_ID(0xC000, 12, 15, 5, 0x1000), .rdma_fifowm = REG_FIELD_ID(0xC000, 1, 5, 5, 0x1000), .rdma_enable = REG_FIELD_ID(0xC000, 0, 0, 5, 0x1000), @@ -174,6 +204,64 @@ static struct lpass_variant sc7180_data = { .wrdma_fifowm = REG_FIELD_ID(0x18000, 1, 5, 4, 0x1000), .wrdma_enable = REG_FIELD_ID(0x18000, 0, 0, 4, 0x1000), + .hdmi_tx_ctl_addr = 0x1000, + .hdmi_legacy_addr = 0x1008, + .hdmi_vbit_addr = 0x610c0, + .hdmi_ch_lsb_addr = 0x61048, + .hdmi_ch_msb_addr = 0x6104c, + .ch_stride = 0x8, + .hdmi_parity_addr = 0x61034, + .hdmi_dmactl_addr = 0x61038, + .hdmi_dma_stride = 0x4, + .hdmi_DP_addr = 0x610c8, + .hdmi_sstream_addr = 0x6101c, + .hdmi_irq_reg_base = 0x63000, + .hdmi_irq_ports = 1, + + .hdmi_rdma_dyncclk = REG_FIELD_ID(0x64000, 14, 14, 4, 0x1000), + .hdmi_rdma_bursten = REG_FIELD_ID(0x64000, 13, 13, 4, 0x1000), + .hdmi_rdma_burst8 = REG_FIELD_ID(0x64000, 15, 15, 4, 0x1000), + .hdmi_rdma_burst16 = REG_FIELD_ID(0x64000, 16, 16, 4, 0x1000), + .hdmi_rdma_dynburst = REG_FIELD_ID(0x64000, 18, 18, 4, 0x1000), + .hdmi_rdma_wpscnt = REG_FIELD_ID(0x64000, 10, 12, 4, 0x1000), + .hdmi_rdma_fifowm = REG_FIELD_ID(0x64000, 1, 5, 4, 0x1000), + .hdmi_rdma_enable = REG_FIELD_ID(0x64000, 0, 0, 4, 0x1000), + + .sstream_en = REG_FIELD(0x6101c, 0, 0), + .dma_sel = REG_FIELD(0x6101c, 1, 2), + .auto_bbit_en = REG_FIELD(0x6101c, 3, 3), + .layout = REG_FIELD(0x6101c, 4, 4), + .layout_sp = REG_FIELD(0x6101c, 5, 8), + .set_sp_on_en = REG_FIELD(0x6101c, 10, 10), + .dp_audio = REG_FIELD(0x6101c, 11, 11), + .dp_staffing_en = REG_FIELD(0x6101c, 12, 12), + .dp_sp_b_hw_en = REG_FIELD(0x6101c, 13, 13), + + .mute = REG_FIELD(0x610c8, 0, 0), + .as_sdp_cc = REG_FIELD(0x610c8, 1, 3), + .as_sdp_ct = REG_FIELD(0x610c8, 4, 7), + .aif_db4 = REG_FIELD(0x610c8, 8, 15), + .frequency = REG_FIELD(0x610c8, 16, 21), + .mst_index = REG_FIELD(0x610c8, 28, 29), + .dptx_index = REG_FIELD(0x610c8, 30, 31), + + .soft_reset = REG_FIELD(0x1000, 31, 31), + .force_reset = REG_FIELD(0x1000, 30, 30), + + .use_hw_chs = REG_FIELD(0x61038, 0, 0), + .use_hw_usr = REG_FIELD(0x61038, 1, 1), + .hw_chs_sel = REG_FIELD(0x61038, 2, 4), + .hw_usr_sel = REG_FIELD(0x61038, 5, 6), + + .replace_vbit = REG_FIELD(0x610c0, 0, 0), + .vbit_stream = REG_FIELD(0x610c0, 1, 1), + + .legacy_en = REG_FIELD(0x1008, 0, 0), + .calc_en = REG_FIELD(0x61034, 0, 0), + .lsb_bits = REG_FIELD(0x61048, 0, 31), + .msb_bits = REG_FIELD(0x6104c, 0, 31), + + .clk_name = (const char*[]) { "pcnoc-sway-clk", "audio-core", -- cgit From 6101bf71192f543799a796274e160f7dfc10f2d2 Mon Sep 17 00:00:00 2001 From: Olivier Moysan Date: Wed, 7 Oct 2020 17:34:58 +0200 Subject: ASoC: stm32: dfsdm: change rate limits The DFSDM can support a larger rate range than currently supported in driver. Increase rate upper limit to 48kHz and allow all rates in the range 8kHz to 48kHz. Signed-off-by: Olivier Moysan Link: https://lore.kernel.org/r/20201007153459.22155-2-olivier.moysan@st.com Signed-off-by: Mark Brown --- sound/soc/stm/stm32_adfsdm.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'sound') diff --git a/sound/soc/stm/stm32_adfsdm.c b/sound/soc/stm/stm32_adfsdm.c index ec27c13af04f..c4031988f981 100644 --- a/sound/soc/stm/stm32_adfsdm.c +++ b/sound/soc/stm/stm32_adfsdm.c @@ -47,9 +47,6 @@ static const struct snd_pcm_hardware stm32_adfsdm_pcm_hw = { SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_PAUSE, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, - .rate_min = 8000, - .rate_max = 32000, - .channels_min = 1, .channels_max = 1, @@ -143,8 +140,9 @@ static const struct snd_soc_dai_driver stm32_adfsdm_dai = { .channels_max = 1, .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE, - .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | - SNDRV_PCM_RATE_32000), + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, + .rate_max = 48000, }, .ops = &stm32_adfsdm_dai_ops, }; -- cgit From f38d43dafb0ccd85034fe22647d353ee8be03ab6 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 7 Oct 2020 15:57:00 +0200 Subject: ASoC: Intel: catpt: Fix compilation when CONFIG_MODULES is disabled module_is_live() is available only when CONFIG_MODULES is enabled. Replace its usage with try_module_get() which is present regardless of said config's status. Fixes: 7a10b66a5df9 ("ASoC: Intel: catpt: Device driver lifecycle") Reported-by: Randy Dunlap Signed-off-by: Cezary Rojewski Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20201007135701.20372-1-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/catpt/device.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/soc/intel/catpt/device.c b/sound/soc/intel/catpt/device.c index 390ffb203de0..a70179959795 100644 --- a/sound/soc/intel/catpt/device.c +++ b/sound/soc/intel/catpt/device.c @@ -81,10 +81,11 @@ static int __maybe_unused catpt_resume(struct device *dev) if (ret) return ret; - if (!module_is_live(dev->driver->owner)) { + if (!try_module_get(dev->driver->owner)) { dev_info(dev, "module unloading, skipping fw boot\n"); return 0; } + module_put(dev->driver->owner); ret = catpt_boot_firmware(cdev, true); if (ret) { @@ -107,10 +108,12 @@ static int __maybe_unused catpt_resume(struct device *dev) static int __maybe_unused catpt_runtime_suspend(struct device *dev) { - if (!module_is_live(dev->driver->owner)) { + if (!try_module_get(dev->driver->owner)) { dev_info(dev, "module unloading, skipping suspend\n"); return 0; } + module_put(dev->driver->owner); + return catpt_suspend(dev); } -- cgit From 56a53ece74e4a5d47f6915f6b81623cec5151f09 Mon Sep 17 00:00:00 2001 From: Cezary Rojewski Date: Wed, 7 Oct 2020 15:57:01 +0200 Subject: ASoC: Intel: catpt: Add explicit DMADEVICES kconfig dependency catpt selects DW_DMAC_CORE which requires DMADEVICES. Fix unmet direct dependencies warning by updating driver's depends-on list. Fixes: 6cbfa11d2694 ("ASoC: Intel: Select catpt and deprecate haswell") Reported-by: Randy Dunlap Signed-off-by: Cezary Rojewski Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20201007135701.20372-2-cezary.rojewski@intel.com Signed-off-by: Mark Brown --- sound/soc/intel/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index e53e9ffc5e2f..d5bae5d1ab6f 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -37,7 +37,7 @@ config SND_SOC_INTEL_SST config SND_SOC_INTEL_CATPT tristate "Haswell and Broadwell" depends on ACPI || COMPILE_TEST - depends on SND_DMA_SGBUF + depends on DMADEVICES && SND_DMA_SGBUF select DW_DMAC_CORE select SND_SOC_ACPI_INTEL_MATCH help -- cgit From 827ed8a0fa50bdd365c9f4c9f6ef561ca7032e49 Mon Sep 17 00:00:00 2001 From: Dan Murphy Date: Wed, 7 Oct 2020 10:53:41 -0500 Subject: ASoC: tas2764: Add the driver for the TAS2764 Introduce the Texas Instruments TAS2764 amplifier driver with I/V sense for loud speaker applications. Signed-off-by: Dan Murphy Link: https://lore.kernel.org/r/20201007155341.10139-2-dmurphy@ti.com Signed-off-by: Mark Brown --- sound/soc/codecs/Kconfig | 5 + sound/soc/codecs/Makefile | 2 + sound/soc/codecs/tas2764.c | 688 +++++++++++++++++++++++++++++++++++++++++++++ sound/soc/codecs/tas2764.h | 92 ++++++ 4 files changed, 787 insertions(+) create mode 100644 sound/soc/codecs/tas2764.c create mode 100644 sound/soc/codecs/tas2764.h (limited to 'sound') diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index a62e0fb467d9..34c6dd04b85a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -195,6 +195,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_STI_SAS imply SND_SOC_TAS2552 imply SND_SOC_TAS2562 + imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS5086 imply SND_SOC_TAS571X @@ -1303,6 +1304,10 @@ config SND_SOC_TAS2562 tristate "Texas Instruments TAS2562 Mono Audio amplifier" depends on I2C +config SND_SOC_TAS2764 + tristate "Texas Instruments TAS2764 Mono Audio amplifier" + depends on I2C + config SND_SOC_TAS2770 tristate "Texas Instruments TAS2770 speaker amplifier" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 0404bc1ddcfb..11ce98c25d6c 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -304,6 +304,7 @@ snd-soc-simple-amplifier-objs := simple-amplifier.o snd-soc-tpa6130a2-objs := tpa6130a2.o snd-soc-tas2552-objs := tas2552.o snd-soc-tas2562-objs := tas2562.o +snd-soc-tas2764-objs := tas2764.o obj-$(CONFIG_SND_SOC_88PM860X) += snd-soc-88pm860x.o obj-$(CONFIG_SND_SOC_AB8500_CODEC) += snd-soc-ab8500-codec.o @@ -517,6 +518,7 @@ obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o +obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c new file mode 100644 index 000000000000..14a193e48dc7 --- /dev/null +++ b/sound/soc/codecs/tas2764.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Driver for the Texas Instruments TAS2764 CODEC +// Copyright (C) 2020 Texas Instruments Inc. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tas2764.h" + +struct tas2764_priv { + struct snd_soc_component *component; + struct gpio_desc *reset_gpio; + struct gpio_desc *sdz_gpio; + struct regmap *regmap; + struct device *dev; + + int v_sense_slot; + int i_sense_slot; +}; + +static void tas2764_reset(struct tas2764_priv *tas2764) +{ + if (tas2764->reset_gpio) { + gpiod_set_value_cansleep(tas2764->reset_gpio, 0); + msleep(20); + gpiod_set_value_cansleep(tas2764->reset_gpio, 1); + } + + snd_soc_component_write(tas2764->component, TAS2764_SW_RST, + TAS2764_RST); +} + +static int tas2764_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + + switch (level) { + case SND_SOC_BIAS_ON: + snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_ACTIVE); + break; + case SND_SOC_BIAS_STANDBY: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_MUTE); + break; + case SND_SOC_BIAS_OFF: + snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); + break; + + default: + dev_err(tas2764->dev, + "wrong power level setting %d\n", level); + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_PM +static int tas2764_codec_suspend(struct snd_soc_component *component) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); + + if (ret < 0) + return ret; + + if (tas2764->sdz_gpio) + gpiod_set_value_cansleep(tas2764->sdz_gpio, 0); + + regcache_cache_only(tas2764->regmap, true); + regcache_mark_dirty(tas2764->regmap); + + return 0; +} + +static int tas2764_codec_resume(struct snd_soc_component *component) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + if (tas2764->sdz_gpio) + gpiod_set_value_cansleep(tas2764->sdz_gpio, 1); + + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_ACTIVE); + + if (ret < 0) + return ret; + + regcache_cache_only(tas2764->regmap, false); + + return regcache_sync(tas2764->regmap); +} +#else +#define tas2764_codec_suspend NULL +#define tas2764_codec_resume NULL +#endif + +static const char * const tas2764_ASI1_src[] = { + "I2C offset", "Left", "Right", "LeftRightDiv2", +}; + +static SOC_ENUM_SINGLE_DECL( + tas2764_ASI1_src_enum, TAS2764_TDM_CFG2, 4, tas2764_ASI1_src); + +static const struct snd_kcontrol_new tas2764_asi1_mux = + SOC_DAPM_ENUM("ASI1 Source", tas2764_ASI1_src_enum); + +static int tas2764_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_MUTE); + break; + case SND_SOC_DAPM_PRE_PMD: + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_SHUTDOWN); + break; + default: + dev_err(tas2764->dev, "Unsupported event\n"); + return -EINVAL; + } + + if (ret < 0) + return ret; + + return 0; +} + +static const struct snd_kcontrol_new isense_switch = + SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, 1, 1); +static const struct snd_kcontrol_new vsense_switch = + SOC_DAPM_SINGLE("Switch", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, 1, 1); + +static const struct snd_soc_dapm_widget tas2764_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2764_asi1_mux), + SND_SOC_DAPM_SWITCH("ISENSE", TAS2764_PWR_CTRL, TAS2764_ISENSE_POWER_EN, + 1, &isense_switch), + SND_SOC_DAPM_SWITCH("VSENSE", TAS2764_PWR_CTRL, TAS2764_VSENSE_POWER_EN, + 1, &vsense_switch), + SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2764_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON") +}; + +static const struct snd_soc_dapm_route tas2764_audio_map[] = { + {"ASI1 Sel", "I2C offset", "ASI1"}, + {"ASI1 Sel", "Left", "ASI1"}, + {"ASI1 Sel", "Right", "ASI1"}, + {"ASI1 Sel", "LeftRightDiv2", "ASI1"}, + {"DAC", NULL, "ASI1 Sel"}, + {"OUT", NULL, "DAC"}, + {"ISENSE", "Switch", "IMON"}, + {"VSENSE", "Switch", "VMON"}, +}; + +static int tas2764_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + mute ? TAS2764_PWR_CTRL_MUTE : 0); + + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_set_bitwidth(struct tas2764_priv *tas2764, int bitwidth) +{ + struct snd_soc_component *component = tas2764->component; + int sense_en; + int val; + int ret; + + switch (bitwidth) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = snd_soc_component_update_bits(component, + TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXW_MASK, + TAS2764_TDM_CFG2_RXW_16BITS); + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = snd_soc_component_update_bits(component, + TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXW_MASK, + TAS2764_TDM_CFG2_RXW_24BITS); + break; + case SNDRV_PCM_FORMAT_S32_LE: + ret = snd_soc_component_update_bits(component, + TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXW_MASK, + TAS2764_TDM_CFG2_RXW_32BITS); + break; + + default: + return -EINVAL; + } + + if (ret < 0) + return ret; + + val = snd_soc_component_read(tas2764->component, TAS2764_PWR_CTRL); + if (val < 0) + return val; + + if (val & (1 << TAS2764_VSENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2764_TDM_CFG5_VSNS_ENABLE; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_VSNS_ENABLE, + sense_en); + if (ret < 0) + return ret; + + if (val & (1 << TAS2764_ISENSE_POWER_EN)) + sense_en = 0; + else + sense_en = TAS2764_TDM_CFG6_ISNS_ENABLE; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_ISNS_ENABLE, + sense_en); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_set_samplerate(struct tas2764_priv *tas2764, int samplerate) +{ + struct snd_soc_component *component = tas2764->component; + int ramp_rate_val; + int ret; + + switch (samplerate) { + case 48000: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ | + TAS2764_TDM_CFG0_44_1_48KHZ; + break; + case 44100: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ | + TAS2764_TDM_CFG0_44_1_48KHZ; + break; + case 96000: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_48KHZ | + TAS2764_TDM_CFG0_88_2_96KHZ; + break; + case 88200: + ramp_rate_val = TAS2764_TDM_CFG0_SMP_44_1KHZ | + TAS2764_TDM_CFG0_88_2_96KHZ; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG0, + TAS2764_TDM_CFG0_SMP_MASK | + TAS2764_TDM_CFG0_MASK, + ramp_rate_val); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + ret = tas2764_set_bitwidth(tas2764, params_format(params)); + if (ret < 0) + return ret; + + return tas2764_set_samplerate(tas2764, params_rate(params)); +} + +static int tas2764_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + u8 tdm_rx_start_slot = 0, asi_cfg_1 = 0; + int iface; + int ret; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + asi_cfg_1 = TAS2764_TDM_CFG1_RX_RISING; + break; + case SND_SOC_DAIFMT_IB_NF: + asi_cfg_1 = TAS2764_TDM_CFG1_RX_FALLING; + break; + default: + dev_err(tas2764->dev, "ASI format Inverse is not found\n"); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1, + TAS2764_TDM_CFG1_RX_MASK, + asi_cfg_1); + if (ret < 0) + return ret; + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + iface = TAS2764_TDM_CFG2_SCFG_I2S; + tdm_rx_start_slot = 1; + break; + case SND_SOC_DAIFMT_DSP_B: + case SND_SOC_DAIFMT_LEFT_J: + iface = TAS2764_TDM_CFG2_SCFG_LEFT_J; + tdm_rx_start_slot = 0; + break; + default: + dev_err(tas2764->dev, + "DAI Format is not found, fmt=0x%x\n", fmt); + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG1, + TAS2764_TDM_CFG1_MASK, + (tdm_rx_start_slot << TAS2764_TDM_CFG1_51_SHIFT)); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_SCFG_MASK, iface); + if (ret < 0) + return ret; + + return 0; +} + +static int tas2764_set_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int left_slot, right_slot; + int slots_cfg; + int slot_size; + int ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + if (slots == 1) { + if (tx_mask != 1) + return -EINVAL; + left_slot = 0; + right_slot = 0; + } else { + left_slot = __ffs(tx_mask); + tx_mask &= ~(1 << left_slot); + if (tx_mask == 0) { + right_slot = left_slot; + } else { + right_slot = __ffs(tx_mask); + tx_mask &= ~(1 << right_slot); + } + } + + if (tx_mask != 0 || left_slot >= slots || right_slot >= slots) + return -EINVAL; + + slots_cfg = (right_slot << TAS2764_TDM_CFG3_RXS_SHIFT) | left_slot; + + ret = snd_soc_component_write(component, TAS2764_TDM_CFG3, slots_cfg); + if (ret) + return ret; + + switch (slot_width) { + case 16: + slot_size = TAS2764_TDM_CFG2_RXS_16BITS; + break; + case 24: + slot_size = TAS2764_TDM_CFG2_RXS_24BITS; + break; + case 32: + slot_size = TAS2764_TDM_CFG2_RXS_32BITS; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG2, + TAS2764_TDM_CFG2_RXS_MASK, + slot_size); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_50_MASK, + tas2764->v_sense_slot); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_50_MASK, + tas2764->i_sense_slot); + if (ret < 0) + return ret; + + return 0; +} + +static struct snd_soc_dai_ops tas2764_dai_ops = { + .mute_stream = tas2764_mute, + .hw_params = tas2764_hw_params, + .set_fmt = tas2764_set_fmt, + .set_tdm_slot = tas2764_set_dai_tdm_slot, + .no_capture_mute = 1, +}; + +#define TAS2764_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +#define TAS2764_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_88200) + +static struct snd_soc_dai_driver tas2764_dai_driver[] = { + { + .name = "tas2764 ASI1", + .id = 0, + .playback = { + .stream_name = "ASI1 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = TAS2764_RATES, + .formats = TAS2764_FORMATS, + }, + .capture = { + .stream_name = "ASI1 Capture", + .channels_min = 0, + .channels_max = 2, + .rates = TAS2764_RATES, + .formats = TAS2764_FORMATS, + }, + .ops = &tas2764_dai_ops, + .symmetric_rates = 1, + }, +}; + +static int tas2764_codec_probe(struct snd_soc_component *component) +{ + struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component); + int ret; + + tas2764->component = component; + + if (tas2764->sdz_gpio) + gpiod_set_value_cansleep(tas2764->sdz_gpio, 1); + + tas2764_reset(tas2764); + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG5, + TAS2764_TDM_CFG5_VSNS_ENABLE, 0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(tas2764->component, TAS2764_TDM_CFG6, + TAS2764_TDM_CFG6_ISNS_ENABLE, 0); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL, + TAS2764_PWR_CTRL_MASK, + TAS2764_PWR_CTRL_MUTE); + if (ret < 0) + return ret; + + return 0; +} + +static DECLARE_TLV_DB_SCALE(tas2764_digital_tlv, 1100, 50, 0); +static DECLARE_TLV_DB_SCALE(tas2764_playback_volume, -10000, 50, 0); + +static const struct snd_kcontrol_new tas2764_snd_controls[] = { + SOC_SINGLE_TLV("Speaker Volume", TAS2764_DVC, 0, + TAS2764_DVC_MAX, 1, tas2764_playback_volume), + SOC_SINGLE_TLV("Amp Gain Volume", TAS2764_CHNL_0, 0, 0x14, 0, + tas2764_digital_tlv), +}; + +static const struct snd_soc_component_driver soc_component_driver_tas2764 = { + .probe = tas2764_codec_probe, + .suspend = tas2764_codec_suspend, + .resume = tas2764_codec_resume, + .set_bias_level = tas2764_set_bias_level, + .controls = tas2764_snd_controls, + .num_controls = ARRAY_SIZE(tas2764_snd_controls), + .dapm_widgets = tas2764_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas2764_dapm_widgets), + .dapm_routes = tas2764_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas2764_audio_map), + .idle_bias_on = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct reg_default tas2764_reg_defaults[] = { + { TAS2764_PAGE, 0x00 }, + { TAS2764_SW_RST, 0x00 }, + { TAS2764_PWR_CTRL, 0x1a }, + { TAS2764_DVC, 0x00 }, + { TAS2764_CHNL_0, 0x00 }, + { TAS2764_TDM_CFG0, 0x09 }, + { TAS2764_TDM_CFG1, 0x02 }, + { TAS2764_TDM_CFG2, 0x0a }, + { TAS2764_TDM_CFG3, 0x10 }, + { TAS2764_TDM_CFG5, 0x42 }, +}; + +static const struct regmap_range_cfg tas2764_regmap_ranges[] = { + { + .range_min = 0, + .range_max = 1 * 128, + .selector_reg = TAS2764_PAGE, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tas2764_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .reg_defaults = tas2764_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas2764_reg_defaults), + .cache_type = REGCACHE_RBTREE, + .ranges = tas2764_regmap_ranges, + .num_ranges = ARRAY_SIZE(tas2764_regmap_ranges), + .max_register = 1 * 128, +}; + +static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764) +{ + int ret = 0; + + tas2764->reset_gpio = devm_gpiod_get_optional(tas2764->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(tas2764->reset_gpio)) { + if (PTR_ERR(tas2764->reset_gpio) == -EPROBE_DEFER) { + tas2764->reset_gpio = NULL; + return -EPROBE_DEFER; + } + } + + tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH); + if (IS_ERR(tas2764->sdz_gpio)) { + if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + tas2764->sdz_gpio = NULL; + } + + ret = fwnode_property_read_u32(dev->fwnode, "ti,imon-slot-no", + &tas2764->i_sense_slot); + if (ret) + tas2764->i_sense_slot = 0; + + ret = fwnode_property_read_u32(dev->fwnode, "ti,vmon-slot-no", + &tas2764->v_sense_slot); + if (ret) + tas2764->v_sense_slot = 2; + + return 0; +} + +static int tas2764_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tas2764_priv *tas2764; + int result; + + tas2764 = devm_kzalloc(&client->dev, sizeof(struct tas2764_priv), + GFP_KERNEL); + if (!tas2764) + return -ENOMEM; + + tas2764->dev = &client->dev; + i2c_set_clientdata(client, tas2764); + dev_set_drvdata(&client->dev, tas2764); + + tas2764->regmap = devm_regmap_init_i2c(client, &tas2764_i2c_regmap); + if (IS_ERR(tas2764->regmap)) { + result = PTR_ERR(tas2764->regmap); + dev_err(&client->dev, "Failed to allocate register map: %d\n", + result); + return result; + } + + if (client->dev.of_node) { + result = tas2764_parse_dt(&client->dev, tas2764); + if (result) { + dev_err(tas2764->dev, "%s: Failed to parse devicetree\n", + __func__); + return result; + } + } + + return devm_snd_soc_register_component(tas2764->dev, + &soc_component_driver_tas2764, + tas2764_dai_driver, + ARRAY_SIZE(tas2764_dai_driver)); +} + +static const struct i2c_device_id tas2764_i2c_id[] = { + { "tas2764", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id); + +#if defined(CONFIG_OF) +static const struct of_device_id tas2764_of_match[] = { + { .compatible = "ti,tas2764" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tas2764_of_match); +#endif + +static struct i2c_driver tas2764_i2c_driver = { + .driver = { + .name = "tas2764", + .of_match_table = of_match_ptr(tas2764_of_match), + }, + .probe = tas2764_i2c_probe, + .id_table = tas2764_i2c_id, +}; +module_i2c_driver(tas2764_i2c_driver); + +MODULE_AUTHOR("Dan Murphy "); +MODULE_DESCRIPTION("TAS2764 I2C Smart Amplifier driver"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h new file mode 100644 index 000000000000..67d6fd903c42 --- /dev/null +++ b/sound/soc/codecs/tas2764.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tas2764.h - ALSA SoC Texas Instruments TAS2764 Mono Audio Amplifier + * + * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com + * + * Author: Dan Murphy + */ + +#ifndef __TAS2764__ +#define __TAS2764__ + +/* Book Control Register */ +#define TAS2764_BOOKCTL_PAGE 0 +#define TAS2764_BOOKCTL_REG 127 +#define TAS2764_REG(page, reg) ((page * 128) + reg) + +/* Page */ +#define TAS2764_PAGE TAS2764_REG(0X0, 0x00) +#define TAS2764_PAGE_PAGE_MASK 255 + +/* Software Reset */ +#define TAS2764_SW_RST TAS2764_REG(0X0, 0x01) +#define TAS2764_RST BIT(0) + +/* Power Control */ +#define TAS2764_PWR_CTRL TAS2764_REG(0X0, 0x02) +#define TAS2764_PWR_CTRL_MASK GENMASK(1, 0) +#define TAS2764_PWR_CTRL_ACTIVE 0x0 +#define TAS2764_PWR_CTRL_MUTE BIT(0) +#define TAS2764_PWR_CTRL_SHUTDOWN BIT(1) + +#define TAS2764_VSENSE_POWER_EN 3 +#define TAS2764_ISENSE_POWER_EN 4 + +/* Digital Volume Control */ +#define TAS2764_DVC TAS2764_REG(0X0, 0x1a) +#define TAS2764_DVC_MAX 0xc9 + +#define TAS2764_CHNL_0 TAS2764_REG(0X0, 0x03) + +/* TDM Configuration Reg0 */ +#define TAS2764_TDM_CFG0 TAS2764_REG(0X0, 0x08) +#define TAS2764_TDM_CFG0_SMP_MASK BIT(5) +#define TAS2764_TDM_CFG0_SMP_48KHZ 0x0 +#define TAS2764_TDM_CFG0_SMP_44_1KHZ BIT(5) +#define TAS2764_TDM_CFG0_MASK GENMASK(3, 1) +#define TAS2764_TDM_CFG0_44_1_48KHZ BIT(3) +#define TAS2764_TDM_CFG0_88_2_96KHZ (BIT(3) | BIT(1)) + +/* TDM Configuration Reg1 */ +#define TAS2764_TDM_CFG1 TAS2764_REG(0X0, 0x09) +#define TAS2764_TDM_CFG1_MASK GENMASK(5, 1) +#define TAS2764_TDM_CFG1_51_SHIFT 1 +#define TAS2764_TDM_CFG1_RX_MASK BIT(0) +#define TAS2764_TDM_CFG1_RX_RISING 0x0 +#define TAS2764_TDM_CFG1_RX_FALLING BIT(0) + +/* TDM Configuration Reg2 */ +#define TAS2764_TDM_CFG2 TAS2764_REG(0X0, 0x0a) +#define TAS2764_TDM_CFG2_RXW_MASK GENMASK(3, 2) +#define TAS2764_TDM_CFG2_RXW_16BITS 0x0 +#define TAS2764_TDM_CFG2_RXW_24BITS BIT(3) +#define TAS2764_TDM_CFG2_RXW_32BITS (BIT(3) | BIT(2)) +#define TAS2764_TDM_CFG2_RXS_MASK GENMASK(1, 0) +#define TAS2764_TDM_CFG2_RXS_16BITS 0x0 +#define TAS2764_TDM_CFG2_RXS_24BITS BIT(0) +#define TAS2764_TDM_CFG2_RXS_32BITS BIT(1) +#define TAS2764_TDM_CFG2_SCFG_MASK GENMASK(5, 4) +#define TAS2764_TDM_CFG2_SCFG_I2S 0x0 +#define TAS2764_TDM_CFG2_SCFG_LEFT_J BIT(4) +#define TAS2764_TDM_CFG2_SCFG_RIGHT_J BIT(5) + +/* TDM Configuration Reg3 */ +#define TAS2764_TDM_CFG3 TAS2764_REG(0X0, 0x0c) +#define TAS2764_TDM_CFG3_RXS_MASK GENMASK(7, 4) +#define TAS2764_TDM_CFG3_RXS_SHIFT 0x4 +#define TAS2764_TDM_CFG3_MASK GENMASK(3, 0) + +/* TDM Configuration Reg5 */ +#define TAS2764_TDM_CFG5 TAS2764_REG(0X0, 0x0e) +#define TAS2764_TDM_CFG5_VSNS_MASK BIT(6) +#define TAS2764_TDM_CFG5_VSNS_ENABLE BIT(6) +#define TAS2764_TDM_CFG5_50_MASK GENMASK(5, 0) + +/* TDM Configuration Reg6 */ +#define TAS2764_TDM_CFG6 TAS2764_REG(0X0, 0x0f) +#define TAS2764_TDM_CFG6_ISNS_MASK BIT(6) +#define TAS2764_TDM_CFG6_ISNS_ENABLE BIT(6) +#define TAS2764_TDM_CFG6_50_MASK GENMASK(5, 0) + +#endif /* __TAS2764__ */ -- cgit From 18096cb0bcff1ebf4459ea5f6dc940d8eaf4b942 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 8 Oct 2020 11:54:00 +0300 Subject: ASoC: ti: davinci-mcasp: Use &pdev->dev for early dev_warn At this point mcasp->dev is not initialized and we would have NULL pointer dereference if we would have failed to get the mem memory resource by name. Signed-off-by: Peter Ujfalusi Link: https://lore.kernel.org/r/20201008085400.19944-1-peter.ujfalusi@ti.com Signed-off-by: Mark Brown --- sound/soc/ti/davinci-mcasp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c index 3ffdd0f6292a..a6b72ad53b43 100644 --- a/sound/soc/ti/davinci-mcasp.c +++ b/sound/soc/ti/davinci-mcasp.c @@ -2155,7 +2155,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu"); if (!mem) { - dev_warn(mcasp->dev, + dev_warn(&pdev->dev, "\"mpu\" mem resource not found, using index 0\n"); mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!mem) { -- cgit From b899e4fd7a331065d01ca14809c9e55f113f7d05 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 9 Oct 2020 15:35:27 +0300 Subject: ASoC: mchp-spdiftx: remove 'TX' from playback stream name Do not include the 'TX' in the stream name since it's obvious for playback. Signed-off-by: Codrin Ciubotariu Link: https://lore.kernel.org/r/20201009123527.2770629-1-codrin.ciubotariu@microchip.com Signed-off-by: Mark Brown --- sound/soc/atmel/mchp-spdiftx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/soc/atmel/mchp-spdiftx.c b/sound/soc/atmel/mchp-spdiftx.c index 36c23eb3a5ad..82c1eecd2528 100644 --- a/sound/soc/atmel/mchp-spdiftx.c +++ b/sound/soc/atmel/mchp-spdiftx.c @@ -744,7 +744,7 @@ static struct snd_soc_dai_driver mchp_spdiftx_dai = { .probe = mchp_spdiftx_dai_probe, .remove = mchp_spdiftx_dai_remove, .playback = { - .stream_name = "S/PDIF TX Playback", + .stream_name = "S/PDIF Playback", .channels_min = 1, .channels_max = 2, .rates = MCHP_SPDIFTX_RATES, -- cgit From 86f29c7442ac4ba5fe19fc2ada457f76c0080dd6 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 8 Oct 2020 17:11:05 +0100 Subject: ASoC: dmaengine: Document support for TX only or RX only streams We intentionally do not return an error if we get a permanent failure from dma_request_chan() in order to support systems which have TX only or RX only channels. Add a comment documenting this. Reported-by: Andy Shevchenko Signed-off-by: Mark Brown Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201008161105.21804-1-broonie@kernel.org Signed-off-by: Mark Brown --- sound/soc/soc-generic-dmaengine-pcm.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'sound') diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c index fb95c1464e66..9ef80a48707e 100644 --- a/sound/soc/soc-generic-dmaengine-pcm.c +++ b/sound/soc/soc-generic-dmaengine-pcm.c @@ -386,6 +386,11 @@ static int dmaengine_pcm_request_chan_of(struct dmaengine_pcm *pcm, name = config->chan_names[i]; chan = dma_request_chan(dev, name); if (IS_ERR(chan)) { + /* + * Only report probe deferral errors, channels + * might not be present for devices that + * support only TX or only RX. + */ if (PTR_ERR(chan) == -EPROBE_DEFER) return -EPROBE_DEFER; pcm->chan[i] = NULL; -- cgit From 96e503f9000f2ad17d550cd884a5e386eb7f532f Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 6 Oct 2020 19:17:22 +0300 Subject: ALSA: hda/i915 - fix list corruption with concurrent probes Current hdac_i915 uses a static completion instance to wait for i915 driver to complete the component bind. This design is not safe if multiple HDA controllers are active and communicating with different i915 instances, and can lead to list corruption and failed audio driver probe. Fix the design by moving completion mechanism to common acomp code and remove the related code from hdac_i915. Fixes: 7b882fe3e3e8 ("ALSA: hda - handle multiple i915 device instances") Co-developed-by: Kai Vehmanen Signed-off-by: Kai Vehmanen Link: https://lore.kernel.org/r/20201006161722.500256-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/hda/hdac_component.c | 3 +++ sound/hda/hdac_i915.c | 23 +++-------------------- 2 files changed, 6 insertions(+), 20 deletions(-) (limited to 'sound') diff --git a/sound/hda/hdac_component.c b/sound/hda/hdac_component.c index 89126c6fd216..bb37e7e0bd79 100644 --- a/sound/hda/hdac_component.c +++ b/sound/hda/hdac_component.c @@ -210,12 +210,14 @@ static int hdac_component_master_bind(struct device *dev) goto module_put; } + complete_all(&acomp->master_bind_complete); return 0; module_put: module_put(acomp->ops->owner); out_unbind: component_unbind_all(dev, acomp); + complete_all(&acomp->master_bind_complete); return ret; } @@ -296,6 +298,7 @@ int snd_hdac_acomp_init(struct hdac_bus *bus, if (!acomp) return -ENOMEM; acomp->audio_ops = aops; + init_completion(&acomp->master_bind_complete); bus->audio_component = acomp; devres_add(dev, acomp); diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c index 5f0a1aa6ad84..454474ac5716 100644 --- a/sound/hda/hdac_i915.c +++ b/sound/hda/hdac_i915.c @@ -11,8 +11,6 @@ #include #include -static struct completion bind_complete; - #define IS_HSW_CONTROLLER(pci) (((pci)->device == 0x0a0c) || \ ((pci)->device == 0x0c0c) || \ ((pci)->device == 0x0d0c) || \ @@ -130,19 +128,6 @@ static bool i915_gfx_present(void) return pci_dev_present(ids); } -static int i915_master_bind(struct device *dev, - struct drm_audio_component *acomp) -{ - complete_all(&bind_complete); - /* clear audio_ops here as it was needed only for completion call */ - acomp->audio_ops = NULL; - return 0; -} - -static const struct drm_audio_component_audio_ops i915_init_ops = { - .master_bind = i915_master_bind -}; - /** * snd_hdac_i915_init - Initialize i915 audio component * @bus: HDA core bus @@ -163,9 +148,7 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!i915_gfx_present()) return -ENODEV; - init_completion(&bind_complete); - - err = snd_hdac_acomp_init(bus, &i915_init_ops, + err = snd_hdac_acomp_init(bus, NULL, i915_component_master_match, sizeof(struct i915_audio_component) - sizeof(*acomp)); if (err < 0) @@ -177,8 +160,8 @@ int snd_hdac_i915_init(struct hdac_bus *bus) if (!IS_ENABLED(CONFIG_MODULES) || !request_module("i915")) { /* 60s timeout */ - wait_for_completion_timeout(&bind_complete, - msecs_to_jiffies(60 * 1000)); + wait_for_completion_timeout(&acomp->master_bind_complete, + msecs_to_jiffies(60 * 1000)); } } if (!acomp->ops) { -- cgit From 46394db4410bb640cccb0985dfb03ed4312cad2b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Oct 2020 11:19:33 +0200 Subject: ALSA: hda: use semicolons rather than commas to separate statements Replace commas with semicolons. What is done is essentially described by the following Coccinelle semantic patch (http://coccinelle.lip6.fr/): // @@ expression e1,e2; @@ e1 -, +; e2 ... when any // Signed-off-by: Julia Lawall Link: https://lore.kernel.org/r/1602407979-29038-3-git-send-email-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_ca0132.c | 2 +- sound/pci/hda/patch_hdmi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c index 9779978e4bc7..2b38b2a716a1 100644 --- a/sound/pci/hda/patch_ca0132.c +++ b/sound/pci/hda/patch_ca0132.c @@ -3114,7 +3114,7 @@ static int dspxfr_one_seg(struct hda_codec *codec, } data = fls->data; - chip_addx = fls->chip_addr, + chip_addx = fls->chip_addr; words_to_write = fls->count; if (!words_to_write) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 055440740184..0ffbfcb91256 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2451,7 +2451,7 @@ static int alloc_generic_hdmi(struct hda_codec *codec) spec->chmap.ops.get_chmap = hdmi_get_chmap; spec->chmap.ops.set_chmap = hdmi_set_chmap; spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached; - spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc, + spec->chmap.ops.get_spk_alloc = hdmi_get_spk_alloc; codec->spec = spec; hdmi_array_init(spec, 4); -- cgit From fe160a22aa2dc558d819660d41e08b86f041cc77 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 11 Oct 2020 11:19:35 +0200 Subject: ALSA: fireworks: use semicolons rather than commas to separate statements Replace commas with semicolons. What is done is essentially described by the following Coccinelle semantic patch (http://coccinelle.lip6.fr/): // @@ expression e1,e2; @@ e1 -, +; e2 ... when any // Signed-off-by: Julia Lawall Acked-by: Takashi Sakamoto Link: https://lore.kernel.org/r/1602407979-29038-5-git-send-email-Julia.Lawall@inria.fr Signed-off-by: Takashi Iwai --- sound/firewire/fireworks/fireworks_pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'sound') diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c index 980580dfbb39..a0d5db1d8eb2 100644 --- a/sound/firewire/fireworks/fireworks_pcm.c +++ b/sound/firewire/fireworks/fireworks_pcm.c @@ -148,7 +148,7 @@ pcm_init_hw_params(struct snd_efw *efw, } /* limit rates */ - runtime->hw.rates = efw->supported_sampling_rate, + runtime->hw.rates = efw->supported_sampling_rate; snd_pcm_limit_hw_rates(runtime); limit_channels(&runtime->hw, pcm_channels); -- cgit From a6e7d0a4bdb02a7a3ffe0b44aaa8842b7efdd056 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Mon, 12 Oct 2020 13:27:04 +0300 Subject: ALSA: hda: fix jack detection with Realtek codecs when in D3 In case HDA controller becomes active, but codec is runtime suspended, jack detection is not successful and no interrupt is raised. This has been observed with multiple Realtek codecs and HDA controllers from different vendors. Bug does not occur if both codec and controller are active, or both are in suspend. Bug can be easily hit on desktop systems with no built-in speaker. The problem can be fixed by powering up the codec once after every controller runtime resume. Even if codec goes back to suspend later, the jack detection will continue to work. Add a flag to 'hda_codec' to describe codecs that require this flow from the controller driver. Modify __azx_runtime_resume() to use pm_request_resume() to make the intent clearer. Mark all Realtek codecs with the new forced_resume flag. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=209379 Cc: Kailang Yang Co-developed-by: Kai-Heng Feng Signed-off-by: Kai-Heng Feng Signed-off-by: Kai Vehmanen Cc: Link: https://lore.kernel.org/r/20201012102704.794423-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_intel.c | 14 ++++++++------ sound/pci/hda/patch_realtek.c | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c index 61e495187b1a..749b88090970 100644 --- a/sound/pci/hda/hda_intel.c +++ b/sound/pci/hda/hda_intel.c @@ -1002,12 +1002,14 @@ static void __azx_runtime_resume(struct azx *chip, bool from_rt) azx_init_pci(chip); hda_intel_init_chip(chip, true); - if (status && from_rt) { - list_for_each_codec(codec, &chip->bus) - if (!codec->relaxed_resume && - (status & (1 << codec->addr))) - schedule_delayed_work(&codec->jackpoll_work, - codec->jackpoll_interval); + if (from_rt) { + list_for_each_codec(codec, &chip->bus) { + if (codec->relaxed_resume) + continue; + + if (codec->forced_resume || (status & (1 << codec->addr))) + pm_request_resume(hda_codec_dev(codec)); + } } /* power down again for link-controlled chips */ diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index f89506dffd5b..f2398721ac1e 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -1150,6 +1150,7 @@ static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid) codec->single_adc_amp = 1; /* FIXME: do we need this for all Realtek codec models? */ codec->spdif_status_reset = 1; + codec->forced_resume = 1; codec->patch_ops = alc_patch_ops; err = alc_codec_rename_from_preset(codec); -- cgit From ce1558c285f9ad04c03b46833a028230771cc0a7 Mon Sep 17 00:00:00 2001 From: Kai Vehmanen Date: Tue, 13 Oct 2020 18:26:28 +0300 Subject: ALSA: hda/hdmi: fix incorrect locking in hdmi_pcm_close A race exists between closing a PCM and update of ELD data. In hdmi_pcm_close(), hinfo->nid value is modified without taking spec->pcm_lock. If this happens concurrently while processing an ELD update in hdmi_pcm_setup_pin(), converter assignment may be done incorrectly. This bug was found by hitting a WARN_ON in snd_hda_spdif_ctls_assign() in a HDMI receiver connection stress test: [2739.684569] WARNING: CPU: 5 PID: 2090 at sound/pci/hda/patch_hdmi.c:1898 check_non_pcm_per_cvt+0x41/0x50 [snd_hda_codec_hdmi] ... [2739.684707] Call Trace: [2739.684720] update_eld+0x121/0x5a0 [snd_hda_codec_hdmi] [2739.684736] hdmi_present_sense+0x21e/0x3b0 [snd_hda_codec_hdmi] [2739.684750] check_presence_and_report+0x81/0xd0 [snd_hda_codec_hdmi] [2739.684842] intel_audio_codec_enable+0x122/0x190 [i915] Fixes: 42b2987079ec ("ALSA: hda - hdmi playback without monitor in dynamic pcm bind mode") Signed-off-by: Kai Vehmanen Cc: Link: https://lore.kernel.org/r/20201013152628.920764-1-kai.vehmanen@linux.intel.com Signed-off-by: Takashi Iwai --- sound/pci/hda/patch_hdmi.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'sound') diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 0ffbfcb91256..ccd1df059654 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -2046,22 +2046,25 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, int pinctl; int err = 0; + mutex_lock(&spec->pcm_lock); if (hinfo->nid) { pcm_idx = hinfo_to_pcm_index(codec, hinfo); - if (snd_BUG_ON(pcm_idx < 0)) - return -EINVAL; + if (snd_BUG_ON(pcm_idx < 0)) { + err = -EINVAL; + goto unlock; + } cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid); - if (snd_BUG_ON(cvt_idx < 0)) - return -EINVAL; + if (snd_BUG_ON(cvt_idx < 0)) { + err = -EINVAL; + goto unlock; + } per_cvt = get_cvt(spec, cvt_idx); - snd_BUG_ON(!per_cvt->assigned); per_cvt->assigned = 0; hinfo->nid = 0; azx_stream(get_azx_dev(substream))->stripe = 0; - mutex_lock(&spec->pcm_lock); snd_hda_spdif_ctls_unassign(codec, pcm_idx); clear_bit(pcm_idx, &spec->pcm_in_use); pin_idx = hinfo_to_pin_index(codec, hinfo); @@ -2091,10 +2094,11 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo, per_pin->setup = false; per_pin->channels = 0; mutex_unlock(&per_pin->lock); - unlock: - mutex_unlock(&spec->pcm_lock); } +unlock: + mutex_unlock(&spec->pcm_lock); + return err; } -- cgit