From ad4f1cf9735fd515632c5d17600ba39318b17772 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 9 Mar 2026 16:42:58 +0530 Subject: [PATCH 1/8] ASoC: qcom: qdsp6: q6prm: add the missing LPASS MCLK clock IDs Add the missing LPASS MCLK ids for the q6prm ADSP. Co-developed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/qdsp6/q6prm-clocks.c | 5 +++++ sound/soc/qcom/qdsp6/q6prm.h | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 4c574b48ab004..51b131fa95316 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -42,6 +42,11 @@ static const struct q6dsp_clk_init q6prm_clks[] = { Q6PRM_CLK(LPASS_CLK_ID_INT5_MI2S_IBIT), Q6PRM_CLK(LPASS_CLK_ID_INT6_MI2S_IBIT), Q6PRM_CLK(LPASS_CLK_ID_QUI_MI2S_OSR), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_1), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_2), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_3), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_4), + Q6PRM_CLK(LPASS_CLK_ID_MCLK_5), Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_MCLK), Q6PRM_CLK(LPASS_CLK_ID_WSA_CORE_NPL_MCLK), Q6PRM_CLK(LPASS_CLK_ID_VA_CORE_MCLK), diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index a988a32086fe1..8296370e3cbf7 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -52,6 +52,17 @@ /* Clock ID for QUINARY MI2S OSR CLK */ #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 +/* Clock ID for MCLK1 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 +/* Clock ID for MCLK2 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 +/* Clock ID for MCLK3 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 +/* Clock ID for MCLK4 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 +/* Clock ID for MCLK5 */ +#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 + #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 From 34070a5576adc1d4f9310c3701bce82b5ad14cdb Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 9 Mar 2026 16:42:59 +0530 Subject: [PATCH 2/8] ASoC: qcom: q6apm-lpass-dais: Add MI2S clock control Add support for MI2S clock control within q6apm-lpass DAIs, including handling of MCLK, BCLK, and ECLK via the DAI .set_sysclk callback. Each MI2S port now retrieves its clock handles from the device tree, allowing per-port clock configuration and proper enable/disable during startup and shutdown. Co-developed-by: Srinivas Kandagatla Signed-off-by: Srinivas Kandagatla Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 186 +++++++++++++++++++----- sound/soc/qcom/qdsp6/q6prm.h | 4 + 2 files changed, 155 insertions(+), 35 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 006b283484d9e..1e739a474936c 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -2,10 +2,12 @@ // Copyright (c) 2021, Linaro Limited #include +#include #include #include #include #include +#include #include #include #include @@ -15,13 +17,22 @@ #include "q6dsp-common.h" #include "audioreach.h" #include "q6apm.h" +#include "q6prm.h" #define AUDIOREACH_BE_PCM_BASE 16 +struct q6apm_dai_priv_data { + struct clk *mclk; + struct clk *bclk; + struct clk *eclk; + bool mclk_enabled, bclk_enabled, eclk_enabled; +}; + struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; + struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; static int q6dma_set_channel_map(struct snd_soc_dai *dai, @@ -150,7 +161,7 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct rc = q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; if (rc < 0) - dev_err(dai->dev, "failed to stop APM port (%d)\n", rc); + dev_err(dai->dev, "fail to close APM port (%d)\n", rc); } if (dai_data->graph[dai->id]) { @@ -159,31 +170,6 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } -static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - int ret = 0; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (!dai_data->is_port_started[dai->id]) { - ret = q6apm_graph_start(dai_data->graph[dai->id]); - if (ret < 0) - dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - else - dai_data->is_port_started[dai->id] = true; - } - break; - default: - break; - } - - return ret; -} - static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -196,14 +182,18 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + q6apm_graph_close(dai_data->graph[dai->id]); + dai_data->graph[dai->id] = NULL; + } } /** * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); rc = PTR_ERR(graph); @@ -224,6 +214,14 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); goto err; } + + rc = q6apm_graph_start(dai_data->graph[dai->id]); + if (rc < 0) { + dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); + goto err; + } + dai_data->is_port_started[dai->id] = true; + return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -240,7 +238,7 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s int graph_id = dai->id; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); return PTR_ERR(graph); @@ -251,6 +249,70 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } +static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + return q6apm_lpass_dai_startup(substream, dai); +} + +static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + if (dai_data->priv[dai->id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].mclk); + dai_data->priv[dai->id].mclk_enabled = false; + } + + if (dai_data->priv[dai->id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].bclk); + dai_data->priv[dai->id].bclk_enabled = false; + } + + if (dai_data->priv[dai->id].eclk_enabled) { + clk_disable_unprepare(dai_data->priv[dai->id].eclk); + dai_data->priv[dai->id].eclk_enabled = false; + } + q6apm_lpass_dai_shutdown(substream, dai); +} + +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct clk *sysclk; + bool *enabled; + int ret = 0; + + switch (clk_id) { + case LPAIF_MI2S_MCLK: + sysclk = dai_data->priv[dai->id].mclk; + enabled = &dai_data->priv[dai->id].mclk_enabled; + break; + case LPAIF_MI2S_BCLK: + sysclk = dai_data->priv[dai->id].bclk; + enabled = &dai_data->priv[dai->id].bclk_enabled; + break; + case LPAIF_MI2S_ECLK: + sysclk = dai_data->priv[dai->id].eclk; + enabled = &dai_data->priv[dai->id].eclk_enabled; + break; + default: + break; + } + + if (sysclk) { + clk_set_rate(sysclk, freq); + ret = clk_prepare_enable(sysclk); + if (ret) { + dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); + return ret; + } + + *enabled = true; + } + + return ret; +} + static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -267,17 +329,16 @@ static const struct snd_soc_dai_ops q6dma_ops = { .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, - .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6apm_lpass_dai_shutdown, + .startup = q6i2s_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, - .trigger = q6apm_lpass_dai_trigger, + .set_sysclk = q6i2s_set_sysclk, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -286,7 +347,6 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .shutdown = q6apm_lpass_dai_shutdown, .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, - .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { @@ -294,9 +354,61 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, .be_pcm_base = AUDIOREACH_BE_PCM_BASE, .use_dai_pcm_id = true, - .remove_order = SND_SOC_COMP_ORDER_FIRST, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + struct device_node *node; + int ret; + + for_each_child_of_node(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) { + dev_err(dev, "valid dai id not found:%d\n", ret); + continue; + } + + switch (id) { + /* MI2S specific properties */ + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + if (PTR_ERR(priv->mclk) == -EPROBE_DEFER) + return dev_err_probe(dev, PTR_ERR(priv->mclk), + "unable to get mi2s mclk\n"); + priv->mclk = NULL; + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + if (PTR_ERR(priv->bclk) == -EPROBE_DEFER) + return dev_err_probe(dev, PTR_ERR(priv->bclk), + "unable to get mi2s bclk\n"); + priv->bclk = NULL; + } + + priv->eclk = of_clk_get_by_name(node, "eclk"); + if (IS_ERR(priv->eclk)) { + if (PTR_ERR(priv->eclk) == -EPROBE_DEFER) + return dev_err_probe(dev, PTR_ERR(priv->eclk), + "unable to get mi2s eclk\n"); + priv->eclk = NULL; + } + break; + default: + break; + } + } + + return 0; +} + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -304,12 +416,16 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) struct snd_soc_dai_driver *dais; struct device *dev = &pdev->dev; int num_dais; + int ret; dai_data = devm_kzalloc(dev, sizeof(*dai_data), GFP_KERNEL); if (!dai_data) return -ENOMEM; dev_set_drvdata(dev, dai_data); + ret = of_q6apm_parse_dai_data(dev, dai_data); + if (ret) + return ret; memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index 8296370e3cbf7..a00d1eda1e708 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -3,6 +3,10 @@ #ifndef __Q6PRM_H__ #define __Q6PRM_H__ +#define LPAIF_MI2S_MCLK 1 +#define LPAIF_MI2S_BCLK 2 +#define LPAIF_MI2S_ECLK 3 + /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 /* Clock ID for Primary I2S EBIT */ From 962f5d77076914d7cb9040db7b203308ab80a021 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 29 May 2026 14:25:00 +0530 Subject: [PATCH 3/8] ASoC: qcom: q6prm: add Audio IF clock IDs Add the LPASS Audio IF clock IDs used by newer backend interfaces. Hawi uses an Audio IF TDM backend and requests the interface bit clock through q6prm. Add the missing Audio IF IBIT and EBIT IDs to the clock tables and the public header so those clocks can be requested from the APM clock controller. Also add generic selector values for backend .set_sysclk() users so the same API can request mclk, bclk or eclk. Signed-off-by: Prasad Kumpatla --- .../sound/qcom,q6dsp-lpass-ports.h | 57 ++++++++++++++ sound/soc/qcom/qdsp6/q6prm-clocks.c | 28 +++++++ sound/soc/qcom/qdsp6/q6prm.h | 77 +++++++++++++++++-- 3 files changed, 154 insertions(+), 8 deletions(-) diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index 45850f2d43425..bc860fcbf0f51 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -233,6 +233,63 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 +/** Clock ID of the Audio Intf 0 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF0_IBIT 71 +/** Clock ID of the Audio Intf 0 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF0_EBIT 72 +/** Clock ID of the Audio Intf 1 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF1_IBIT 73 +/** Clock ID of the Audio Intf 1 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF1_EBIT 74 +/** Clock ID of the Audio Intf 2 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF2_IBIT 75 +/** Clock ID of the Audio Intf 2 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF2_EBIT 76 +/** Clock ID of the Audio Intf 3 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF3_IBIT 77 +/** Clock ID of the Audio Intf 3 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF3_EBIT 78 +/** Clock ID of the Audio Intf 4 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF4_IBIT 79 +/** Clock ID of the Audio Intf 4 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF4_EBIT 80 +/** Clock ID of the Audio Intf 5 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF5_IBIT 81 +/** Clock ID of the Audio Intf 5 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF5_EBIT 82 +/** Clock ID of the Audio Intf 6 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF6_IBIT 83 +/** Clock ID of the Audio Intf 6 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF6_EBIT 84 +/** Clock ID of the Audio Intf 7 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF7_IBIT 85 +/** Clock ID of the Audio Intf 7 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF7_EBIT 86 +/** Clock ID of the Audio Intf 8 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF8_IBIT 87 +/** Clock ID of the Audio Intf 8 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF8_EBIT 88 +/** Clock ID of the Audio Intf 9 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF9_IBIT 89 +/** Clock ID of the Audio Intf 9 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF9_EBIT 90 +/** Clock ID of the Audio Intf 10 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF10_IBIT 91 +/** Clock ID of the Audio Intf 10 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF10_EBIT 92 +/** Clock ID of the Audio Intf 11 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF11_IBIT 93 +/** Clock ID of the Audio Intf 11 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF11_EBIT 94 +/** Clock ID of the Audio Intf 12 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_INTF12_IBIT 95 +/** Clock ID of the Audio Intf 12 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_INTF12_EBIT 96 +/** Clock ID of the Audio VA Intf 0 internal bit clock (IBIT). */ +#define LPASS_CLK_ID_AUD_VA_INTF0_IBIT 97 +/** Clock ID of the Audio VA Intf 0 external bit clock (EBIT). */ +#define LPASS_CLK_ID_AUD_VA_INTF0_EBIT 98 + #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 51b131fa95316..b6755da6a6da8 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -64,6 +64,34 @@ static const struct q6dsp_clk_init q6prm_clks[] = { Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_MCLK), Q6PRM_CLK(LPASS_CLK_ID_WSA2_CORE_TX_2X_MCLK), Q6PRM_CLK(LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF0_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF0_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF1_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF1_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF2_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF2_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF3_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF3_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF4_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF4_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF5_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF5_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF6_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF6_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF7_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF7_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF8_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF8_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF9_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF9_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF10_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF10_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF11_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF11_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF12_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_INTF12_EBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_VA_INTF0_IBIT), + Q6PRM_CLK(LPASS_CLK_ID_AUD_VA_INTF0_EBIT), Q6DSP_VOTE_CLK(LPASS_HW_MACRO_VOTE, Q6PRM_HW_CORE_ID_LPASS, "LPASS_HW_MACRO"), Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index a00d1eda1e708..a4903990f859a 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -3,9 +3,13 @@ #ifndef __Q6PRM_H__ #define __Q6PRM_H__ -#define LPAIF_MI2S_MCLK 1 -#define LPAIF_MI2S_BCLK 2 -#define LPAIF_MI2S_ECLK 3 +#define LPAIF_MI2S_TDM_MCLK 1 +#define LPAIF_MI2S_TDM_BCLK 2 +#define LPAIF_MI2S_TDM_ECLK 3 + +#define LPAIF_MI2S_MCLK LPAIF_MI2S_TDM_MCLK +#define LPAIF_MI2S_BCLK LPAIF_MI2S_TDM_BCLK +#define LPAIF_MI2S_ECLK LPAIF_MI2S_TDM_ECLK /* Clock ID for Primary I2S IBIT */ #define Q6PRM_LPASS_CLK_ID_PRI_MI2S_IBIT 0x100 @@ -57,15 +61,15 @@ #define Q6PRM_LPASS_CLK_ID_QUI_MI2S_OSR 0x116 /* Clock ID for MCLK1 */ -#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 +#define Q6PRM_LPASS_CLK_ID_MCLK_1 0x300 /* Clock ID for MCLK2 */ -#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 +#define Q6PRM_LPASS_CLK_ID_MCLK_2 0x301 /* Clock ID for MCLK3 */ -#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 +#define Q6PRM_LPASS_CLK_ID_MCLK_3 0x302 /* Clock ID for MCLK4 */ -#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 +#define Q6PRM_LPASS_CLK_ID_MCLK_4 0x303 /* Clock ID for MCLK5 */ -#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 +#define Q6PRM_LPASS_CLK_ID_MCLK_5 0x304 #define Q6PRM_LPASS_CLK_ID_WSA_CORE_MCLK 0x305 #define Q6PRM_LPASS_CLK_ID_WSA_CORE_NPL_MCLK 0x306 @@ -98,6 +102,63 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 +/** Clock ID of the Audio Intf 0 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF0_IBIT 0x500 +/** Clock ID of the Audio Intf 0 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF0_EBIT 0x501 +/** Clock ID of the Audio Intf 1 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF1_IBIT 0x502 +/** Clock ID of the Audio Intf 1 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF1_EBIT 0x503 +/** Clock ID of the Audio Intf 2 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF2_IBIT 0x504 +/** Clock ID of the Audio Intf 2 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF2_EBIT 0x505 +/** Clock ID of the Audio Intf 3 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF3_IBIT 0x506 +/** Clock ID of the Audio Intf 3 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF3_EBIT 0x507 +/** Clock ID of the Audio Intf 4 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF4_IBIT 0x508 +/** Clock ID of the Audio Intf 4 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF4_EBIT 0x509 +/** Clock ID of the Audio Intf 5 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF5_IBIT 0x50A +/** Clock ID of the Audio Intf 5 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF5_EBIT 0x50B +/** Clock ID of the Audio Intf 6 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF6_IBIT 0x50C +/** Clock ID of the Audio Intf 6 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF6_EBIT 0x50D +/** Clock ID of the Audio Intf 7 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF7_IBIT 0x50E +/** Clock ID of the Audio Intf 7 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF7_EBIT 0x50F +/** Clock ID of the Audio Intf 8 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF8_IBIT 0x510 +/** Clock ID of the Audio Intf 8 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF8_EBIT 0x511 +/** Clock ID of the Audio Intf 9 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF9_IBIT 0x512 +/** Clock ID of the Audio Intf 9 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF9_EBIT 0x513 +/** Clock ID of the Audio Intf 10 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF10_IBIT 0x514 +/** Clock ID of the Audio Intf 10 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF10_EBIT 0x515 +/** Clock ID of the Audio Intf 11 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF11_IBIT 0x516 +/** Clock ID of the Audio Intf 11 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF11_EBIT 0x517 +/** Clock ID of the Audio Intf 12 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF12_IBIT 0x518 +/** Clock ID of the Audio Intf 12 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_INTF12_EBIT 0x519 +/** Clock ID of the Audio VA Intf 0 internal bit clock (IBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_VA_INTF0_IBIT 0x550 +/** Clock ID of the Audio VA Intf 0 external bit clock (EBIT). */ +#define Q6PRM_LPASS_CLK_ID_AUD_VA_INTF0_EBIT 0x551 + #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 #define Q6PRM_HW_CORE_ID_LPASS 1 From 6bfe1b50a0d5b7ca0f9ff08878ffc2be967fd0a3 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 29 May 2026 14:25:01 +0530 Subject: [PATCH 4/8] ASoC: qcom: q6apm-lpass-dais: extend child clocks to TDM Reuse the LPASS backend child-clock support for both MI2S and TDM. Switch the child clock lookup to a common helper so each backend can pick up optional "mclk", "bclk" or "eclk" entries from its DT child node. Route .set_sysclk() through the shared helper and use the same shutdown path so enabled backend clocks are disabled consistently. Keep TDM slot programming in q6apm-lpass-dais so machine drivers can configure the backend port and still reuse the common child-clock support. Signed-off-by: Prasad Kumpatla --- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 326 +++++++++++++++--------- 1 file changed, 209 insertions(+), 117 deletions(-) diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 1e739a474936c..b35379d9fd04c 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -25,7 +25,9 @@ struct q6apm_dai_priv_data { struct clk *mclk; struct clk *bclk; struct clk *eclk; - bool mclk_enabled, bclk_enabled, eclk_enabled; + bool mclk_enabled; + bool bclk_enabled; + bool eclk_enabled; }; struct q6apm_lpass_dai_data { @@ -35,6 +37,55 @@ struct q6apm_lpass_dai_data { struct q6apm_dai_priv_data priv[APM_PORT_MAX]; }; +static struct clk *q6apm_lpass_get_child_clk(struct device *dev, + struct device_node *node, + const char *name) +{ + struct clk *clk; + + clk = devm_get_clk_from_child(dev, node, name); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) == -EPROBE_DEFER) + return ERR_PTR(dev_err_probe(dev, PTR_ERR(clk), + "unable to get %s\n", name)); + + return NULL; + } + + return clk; +} + +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + struct device_node *node; + + for_each_child_of_node(dev->of_node, node) { + struct q6apm_dai_priv_data *priv; + int ret; + int id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret || id < 0 || id >= APM_PORT_MAX) + continue; + + priv = &data->priv[id]; + priv->mclk = q6apm_lpass_get_child_clk(dev, node, "mclk"); + if (IS_ERR(priv->mclk)) + return PTR_ERR(priv->mclk); + + priv->bclk = q6apm_lpass_get_child_clk(dev, node, "bclk"); + if (IS_ERR(priv->bclk)) + return PTR_ERR(priv->bclk); + + priv->eclk = q6apm_lpass_get_child_clk(dev, node, "eclk"); + if (IS_ERR(priv->eclk)) + return PTR_ERR(priv->eclk); + } + + return 0; +} + static int q6dma_set_channel_map(struct snd_soc_dai *dai, unsigned int tx_num, const unsigned int *tx_ch_mask, @@ -152,7 +203,8 @@ static int q6dma_hw_params(struct snd_pcm_substream *substream, return 0; } -static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); int rc; @@ -161,7 +213,7 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct rc = q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; if (rc < 0) - dev_err(dai->dev, "fail to close APM port (%d)\n", rc); + dev_err(dai->dev, "failed to stop APM port (%d)\n", rc); } if (dai_data->graph[dai->id]) { @@ -170,6 +222,58 @@ static void q6apm_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct } } +static void q6lpass_disable_clocks(struct q6apm_dai_priv_data *priv) +{ + if (priv->mclk_enabled) { + clk_disable_unprepare(priv->mclk); + priv->mclk_enabled = false; + } + + if (priv->bclk_enabled) { + clk_disable_unprepare(priv->bclk); + priv->bclk_enabled = false; + } + + if (priv->eclk_enabled) { + clk_disable_unprepare(priv->eclk); + priv->eclk_enabled = false; + } +} + +static void q6lpass_dai_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + + q6lpass_disable_clocks(&dai_data->priv[dai->id]); + q6apm_lpass_dai_shutdown(substream, dai); +} + +static int q6apm_lpass_dai_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + int ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (!dai_data->is_port_started[dai->id]) { + ret = q6apm_graph_start(dai_data->graph[dai->id]); + if (ret < 0) + dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); + else + dai_data->is_port_started[dai->id] = true; + } + break; + default: + break; + } + + return ret; +} + static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); @@ -182,18 +286,14 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s q6apm_graph_stop(dai_data->graph[dai->id]); dai_data->is_port_started[dai->id] = false; - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - q6apm_graph_close(dai_data->graph[dai->id]); - dai_data->graph[dai->id] = NULL; - } } /** * It is recommend to load DSP with source graph first and then sink * graph, so sequence for playback and capture will be different */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK && dai_data->graph[dai->id] == NULL) { + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); rc = PTR_ERR(graph); @@ -214,14 +314,6 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dev_err(dai->dev, "Failed to prepare Graph %d\n", rc); goto err; } - - rc = q6apm_graph_start(dai_data->graph[dai->id]); - if (rc < 0) { - dev_err(dai->dev, "Failed to start APM port %d\n", dai->id); - goto err; - } - dai_data->is_port_started[dai->id] = true; - return 0; err: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -238,7 +330,7 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s int graph_id = dai->id; if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id); + graph = q6apm_graph_open(dai->dev, NULL, dai->dev, graph_id, substream->stream); if (IS_ERR(graph)) { dev_err(dai->dev, "Failed to open graph (%d)\n", graph_id); return PTR_ERR(graph); @@ -249,96 +341,134 @@ static int q6apm_lpass_dai_startup(struct snd_pcm_substream *substream, struct s return 0; } -static int q6i2s_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - return q6apm_lpass_dai_startup(substream, dai); + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + + cfg->fmt = fmt; + + return 0; } -static void q6i2s_lpass_dai_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) +static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + unsigned int cap_mask; - if (dai_data->priv[dai->id].mclk_enabled) { - clk_disable_unprepare(dai_data->priv[dai->id].mclk); - dai_data->priv[dai->id].mclk_enabled = false; + if ((slot_width != 16) && (slot_width != 32)) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", + __func__, slot_width); + return -EINVAL; } - if (dai_data->priv[dai->id].bclk_enabled) { - clk_disable_unprepare(dai_data->priv[dai->id].bclk); - dai_data->priv[dai->id].bclk_enabled = false; + switch (slots) { + case 2: + cap_mask = 0x03; + break; + case 4: + cap_mask = 0x0f; + break; + case 8: + cap_mask = 0xff; + break; + case 16: + cap_mask = 0xffff; + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", + __func__, slots); + return -EINVAL; } - if (dai_data->priv[dai->id].eclk_enabled) { - clk_disable_unprepare(dai_data->priv[dai->id].eclk); - dai_data->priv[dai->id].eclk_enabled = false; + switch (dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + cfg->nslots_per_frame = slots; + cfg->slot_width = slot_width; + cfg->slot_mask = ((dai->id & 0x1) ? tx_mask : rx_mask) & cap_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", + __func__, dai->id); + return -EINVAL; } - q6apm_lpass_dai_shutdown(substream, dai); + + return 0; } -static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) +static int q6i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) { struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - struct clk *sysclk; - bool *enabled; - int ret = 0; + struct q6apm_dai_priv_data *priv = &dai_data->priv[dai->id]; + struct clk *sysclk = NULL; + bool *enabled = NULL; + int ret; switch (clk_id) { - case LPAIF_MI2S_MCLK: - sysclk = dai_data->priv[dai->id].mclk; - enabled = &dai_data->priv[dai->id].mclk_enabled; + case LPAIF_MI2S_TDM_MCLK: + sysclk = priv->mclk; + enabled = &priv->mclk_enabled; break; - case LPAIF_MI2S_BCLK: - sysclk = dai_data->priv[dai->id].bclk; - enabled = &dai_data->priv[dai->id].bclk_enabled; + case LPAIF_MI2S_TDM_BCLK: + sysclk = priv->bclk; + enabled = &priv->bclk_enabled; break; - case LPAIF_MI2S_ECLK: - sysclk = dai_data->priv[dai->id].eclk; - enabled = &dai_data->priv[dai->id].eclk_enabled; + case LPAIF_MI2S_TDM_ECLK: + sysclk = priv->eclk; + enabled = &priv->eclk_enabled; break; default: - break; + return 0; } - if (sysclk) { - clk_set_rate(sysclk, freq); - ret = clk_prepare_enable(sysclk); - if (ret) { - dev_err(dai->dev, "Error, Unable to prepare (%d) sysclk\n", clk_id); - return ret; - } + if (!sysclk || !freq) + return 0; - *enabled = true; - } + ret = clk_set_rate(sysclk, freq); + if (ret) + return ret; - return ret; -} + if (*enabled) + return 0; -static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -{ - struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); - struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + ret = clk_prepare_enable(sysclk); + if (ret) + return ret; - cfg->fmt = fmt; + *enabled = true; return 0; } +static int q6lpass_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + return q6i2s_set_sysclk(dai, clk_id, freq, dir); +} + static const struct snd_soc_dai_ops q6dma_ops = { .prepare = q6apm_lpass_dai_prepare, .startup = q6apm_lpass_dai_startup, .shutdown = q6apm_lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6i2s_dai_startup, - .shutdown = q6i2s_lpass_dai_shutdown, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6lpass_dai_shutdown, .set_channel_map = q6dma_set_channel_map, .hw_params = q6dma_hw_params, .set_fmt = q6i2s_set_fmt, - .set_sysclk = q6i2s_set_sysclk, + .set_sysclk = q6lpass_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_dai_ops q6hdmi_ops = { @@ -347,6 +477,19 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .shutdown = q6apm_lpass_dai_shutdown, .hw_params = q6hdmi_hw_params, .set_fmt = q6i2s_set_fmt, + .trigger = q6apm_lpass_dai_trigger, +}; + +static const struct snd_soc_dai_ops q6tdm_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6lpass_dai_shutdown, + .set_channel_map = q6dma_set_channel_map, + .set_tdm_slot = q6tdm_set_tdm_slot, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6lpass_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { @@ -354,61 +497,9 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, .be_pcm_base = AUDIOREACH_BE_PCM_BASE, .use_dai_pcm_id = true, + .remove_order = SND_SOC_COMP_ORDER_FIRST, }; -static int of_q6apm_parse_dai_data(struct device *dev, - struct q6apm_lpass_dai_data *data) -{ - struct device_node *node; - int ret; - - for_each_child_of_node(dev->of_node, node) { - struct q6apm_dai_priv_data *priv; - int id; - - ret = of_property_read_u32(node, "reg", &id); - if (ret || id < 0 || id >= APM_PORT_MAX) { - dev_err(dev, "valid dai id not found:%d\n", ret); - continue; - } - - switch (id) { - /* MI2S specific properties */ - case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: - priv = &data->priv[id]; - priv->mclk = of_clk_get_by_name(node, "mclk"); - if (IS_ERR(priv->mclk)) { - if (PTR_ERR(priv->mclk) == -EPROBE_DEFER) - return dev_err_probe(dev, PTR_ERR(priv->mclk), - "unable to get mi2s mclk\n"); - priv->mclk = NULL; - } - - priv->bclk = of_clk_get_by_name(node, "bclk"); - if (IS_ERR(priv->bclk)) { - if (PTR_ERR(priv->bclk) == -EPROBE_DEFER) - return dev_err_probe(dev, PTR_ERR(priv->bclk), - "unable to get mi2s bclk\n"); - priv->bclk = NULL; - } - - priv->eclk = of_clk_get_by_name(node, "eclk"); - if (IS_ERR(priv->eclk)) { - if (PTR_ERR(priv->eclk) == -EPROBE_DEFER) - return dev_err_probe(dev, PTR_ERR(priv->eclk), - "unable to get mi2s eclk\n"); - priv->eclk = NULL; - } - break; - default: - break; - } - } - - return 0; -} - static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -431,6 +522,7 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; cfg.q6hdmi_ops = &q6hdmi_ops; + cfg.q6tdm_ops = &q6tdm_ops; dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); From 5a93734561a9fba53d0828db9ab275bfefa13ef1 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Fri, 29 May 2026 21:24:37 +0530 Subject: [PATCH 5/8] ASoC: qcom: common: add DAI-node TDM slot helpers Add common helpers to parse standard dai-tdm-slot-* properties from the CPU and codec child nodes of a backend DAI link and apply the result to the active DAIs. QCOM machine drivers already use qcom_snd_parse_of() to build links from DT, but they lacked a shared helper to translate endpoint TDM properties into snd_soc_dai_set_tdm_slot() calls. Boards therefore had to carry ad hoc parsing or rely on non-standard DT properties. The new helpers parse the endpoint masks, validate the shared slot count and slot width, and program the CPU and codec DAIs with the resulting slot configuration. This keeps the DT description declarative and lets machine drivers reuse standard ASoC APIs for TDM backend setup. Signed-off-by: Prasad Kumpatla --- sound/soc/qcom/common.c | 155 ++++++++++++++++++++++++++++++++++++++++ sound/soc/qcom/common.h | 11 +++ 2 files changed, 166 insertions(+) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index cf1f3a767ceef..c4f3ab5911957 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -23,6 +23,161 @@ static const struct snd_soc_dapm_widget qcom_jack_snd_widgets[] = { SND_SOC_DAPM_SPK("DP7 Jack", NULL), }; +static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_card *card = rtd->card; + struct device_node *np; + struct device_node *cpu_np; + struct of_phandle_args args; + int ret; + + if (!card->dev || !card->dev->of_node) + return NULL; + + for_each_available_child_of_node(card->dev->of_node, np) { + cpu_np = of_get_child_by_name(np, "cpu"); + if (!cpu_np) + continue; + + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", + "#sound-dai-cells", 0, &args); + of_node_put(cpu_np); + if (ret) + continue; + + if (args.np == rtd->dai_link->cpus[0].of_node && + args.args_count == 1 && args.args[0] == cpu_dai->id) { + of_node_put(args.np); + return np; + } + + of_node_put(args.np); + } + + return NULL; +} + +static int qcom_snd_parse_tdm_slot(struct device_node *np, + struct qcom_snd_tdm_slot_cfg *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + + return snd_soc_of_parse_tdm_slot(np, &cfg->tx_mask, &cfg->rx_mask, + &cfg->slots, &cfg->slot_width); +} + +static int qcom_snd_normalize_tdm_slots(struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + unsigned int slots; + unsigned int slot_width; + + if (cpu_cfg->slots && codec_cfg->slots && cpu_cfg->slots != codec_cfg->slots) + return -EINVAL; + + if (cpu_cfg->slot_width && codec_cfg->slot_width && + cpu_cfg->slot_width != codec_cfg->slot_width) + return -EINVAL; + + slots = cpu_cfg->slots ?: codec_cfg->slots; + if (!slots) + return 0; + + slot_width = cpu_cfg->slot_width ?: codec_cfg->slot_width; + if (!slot_width) + return -EINVAL; + + cpu_cfg->slots = slots; + codec_cfg->slots = slots; + cpu_cfg->slot_width = slot_width; + codec_cfg->slot_width = slot_width; + + return 0; +} + +static int qcom_snd_parse_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + struct device_node *link_np; + struct device_node *cpu_np = NULL; + struct device_node *codec_np = NULL; + int ret; + + link_np = qcom_snd_get_link_node(rtd); + if (!link_np) + return -EINVAL; + + cpu_np = of_get_child_by_name(link_np, "cpu"); + codec_np = of_get_child_by_name(link_np, "codec"); + if (!cpu_np || !codec_np) { + ret = -EINVAL; + goto out; + } + + ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); + if (ret) + goto out; + + ret = qcom_snd_parse_tdm_slot(codec_np, codec_cfg); +out: + of_node_put(codec_np); + of_node_put(cpu_np); + of_node_put(link_np); + + return ret; +} + +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg) +{ + int ret; + + ret = qcom_snd_parse_dai_tdm_slots(rtd, cpu_cfg, codec_cfg); + if (ret) + return ret; + + return qcom_snd_normalize_tdm_slots(cpu_cfg, codec_cfg); +} +EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); + +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + int i; + int ret; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + if (!cpu_cfg.slots) + return ret; + + ret = snd_soc_dai_set_tdm_slot(cpu_dai, cpu_cfg.tx_mask, cpu_cfg.rx_mask, + cpu_cfg.slots, cpu_cfg.slot_width); + if (ret < 0 && ret != -ENOTSUPP) + return ret; + + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, + codec_cfg.tx_mask, + codec_cfg.rx_mask, + codec_cfg.slots, + codec_cfg.slot_width); + if (ret < 0 && ret != -ENOTSUPP) + return ret; + } + + return 0; +} +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); + int qcom_snd_parse_of(struct snd_soc_card *card) { struct device_node *np; diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index ee6662885593c..b73c25045e672 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -9,7 +9,18 @@ #define LPASS_MAX_PORT (SENARY_MI2S_TX + 1) +struct qcom_snd_tdm_slot_cfg { + unsigned int tx_mask; + unsigned int rx_mask; + unsigned int slots; + unsigned int slot_width; +}; + int qcom_snd_parse_of(struct snd_soc_card *card); +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + struct qcom_snd_tdm_slot_cfg *cpu_cfg, + struct qcom_snd_tdm_slot_cfg *codec_cfg); +int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd); int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd, struct snd_soc_jack *jack, bool *jack_setup); int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd, From 0369398edde8b6fe114eff29d6f630e82aa4bfe1 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 9 Mar 2026 16:43:00 +0530 Subject: [PATCH 6/8] ASoC: qcom: sc8280xp: ASoC: qcom: sc8280xp: enhance machine driver for board-specific config The sc8280xp machine driver is currently written with a largely SoC-centric view and assumes a uniform audio topology across all boards. In practice, multiple products based on the same SoC use different board designs and external audio components, which require board-specific configuration to function correctly. Several Qualcomm platforms like talos integrate third-party audio codecs or use different external audio paths. These designs often require additional configuration such as explicit MI2S MCLK settings for audio to work. This change enhances the sc8280xp machine driver to support board-specific configuration such as allowing each board variant to provide its own DAPM widgets and routes, reflecting the actual audio components and connectors present and enabling MI2S MCLK programming for boards that use external codecs requiring a stable master clock. Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/sc8280xp.c | 180 ++++++++++++++++++++++++++++++++++---- 1 file changed, 162 insertions(+), 18 deletions(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 7925aa3f63ba0..c2e9323dff90c 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -12,17 +12,62 @@ #include #include #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" +#include "qdsp6/q6prm.h" #include "common.h" #include "sdw.h" +#define MCLK_FREQ 12288000 +#define MCLK_NATIVE_FREQ 11289600 + +static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Mic Jack", NULL), + SND_SOC_DAPM_SPK("DP0 Jack", NULL), + SND_SOC_DAPM_SPK("DP1 Jack", NULL), + SND_SOC_DAPM_SPK("DP2 Jack", NULL), + SND_SOC_DAPM_SPK("DP3 Jack", NULL), + SND_SOC_DAPM_SPK("DP4 Jack", NULL), + SND_SOC_DAPM_SPK("DP5 Jack", NULL), + SND_SOC_DAPM_SPK("DP6 Jack", NULL), + SND_SOC_DAPM_SPK("DP7 Jack", NULL), +}; + +struct snd_soc_common { + char *driver_name; + const struct snd_soc_dapm_widget *dapm_widgets; + int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + int num_dapm_routes; + bool mi2s_mclk_enable; +}; + struct sc8280xp_snd_data { bool stream_prepared[AFE_PORT_MAX]; struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; + struct snd_soc_common *snd_soc_common_priv; bool jack_setup; }; +static inline int sc8280xp_get_mclk_feq(unsigned int rate) +{ + int freq = MCLK_FREQ; + + switch (rate) { + case SNDRV_PCM_RATE_11025: + case SNDRV_PCM_RATE_44100: + case SNDRV_PCM_RATE_88200: + freq = MCLK_NATIVE_FREQ; + break; + default: + break; + } + + return freq; +} + static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) { struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); @@ -32,10 +77,6 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) int dp_pcm_id = 0; switch (cpu_dai->id) { - case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX...QUINARY_MI2S_TX: - snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - break; case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* @@ -96,6 +137,31 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } +static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + int mclk_freq = sc8280xp_get_mclk_feq(params_rate(params)); + + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX...QUINARY_MI2S_TX: + snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + + if (data->snd_soc_common_priv->mi2s_mclk_enable) + snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_MCLK, mclk_freq, + SND_SOC_CLOCK_IN); + break; + default: + break; + }; + + return 0; +} + static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -117,6 +183,7 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) static const struct snd_soc_ops sc8280xp_be_ops = { .startup = qcom_snd_sdw_startup, .shutdown = qcom_snd_sdw_shutdown, + .hw_params = sc8280xp_snd_hw_params, .hw_free = sc8280xp_snd_hw_free, .prepare = sc8280xp_snd_prepare, }; @@ -145,37 +212,114 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); if (!card) return -ENOMEM; - card->owner = THIS_MODULE; + /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev); + if (!data->snd_soc_common_priv) + return -ENOMEM; + + card->owner = THIS_MODULE; card->dev = dev; dev_set_drvdata(dev, card); snd_soc_card_set_drvdata(card, data); + card->dapm_widgets = data->snd_soc_common_priv->dapm_widgets; + card->num_dapm_widgets = data->snd_soc_common_priv->num_dapm_widgets; + card->dapm_routes = data->snd_soc_common_priv->dapm_routes; + card->num_dapm_routes = data->snd_soc_common_priv->num_dapm_routes; + ret = qcom_snd_parse_of(card); if (ret) return ret; - card->driver_name = of_device_get_match_data(dev); + card->driver_name = data->snd_soc_common_priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } +static struct snd_soc_common kaanapali_priv_data = { + .driver_name = "kaanapali", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common qcs9100_priv_data = { + .driver_name = "sa8775p", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common qcs615_priv_data = { + .driver_name = "qcs615", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .mi2s_mclk_enable = true, +}; + +static struct snd_soc_common qcm6490_priv_data = { + .driver_name = "qcm6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common qcs6490_priv_data = { + .driver_name = "qcs6490", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common qcs8275_priv_data = { + .driver_name = "qcs8300", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common sc8280xp_priv_data = { + .driver_name = "sc8280xp", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common sm8450_priv_data = { + .driver_name = "sm8450", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common sm8550_priv_data = { + .driver_name = "sm8550", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common sm8650_priv_data = { + .driver_name = "sm8650", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + +static struct snd_soc_common sm8750_priv_data = { + .driver_name = "sm8750", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,kaanapali-sndcard", "kaanapali"}, - {.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"}, - {.compatible = "qcom,qcs615-sndcard", "qcs615"}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"}, - {.compatible = "qcom,qcs8275-sndcard", "qcs8300"}, - {.compatible = "qcom,qcs9075-sndcard", "sa8775p"}, - {.compatible = "qcom,qcs9100-sndcard", "sa8775p"}, - {.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"}, - {.compatible = "qcom,sm8450-sndcard", "sm8450"}, - {.compatible = "qcom,sm8550-sndcard", "sm8550"}, - {.compatible = "qcom,sm8650-sndcard", "sm8650"}, - {.compatible = "qcom,sm8750-sndcard", "sm8750"}, + {.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data}, + {.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data}, + {.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data}, + {.compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data}, + {.compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data}, + {.compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data}, + {.compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data}, + {.compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data}, + {.compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data}, + {.compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data}, + {.compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data}, + {.compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data}, {} }; From 44e5aebb2e9e2aac929e8971bb0dff61472c0595 Mon Sep 17 00:00:00 2001 From: Prasad Kumpatla Date: Wed, 3 Jun 2026 13:48:29 +0530 Subject: [PATCH 7/8] ASoC: qcom: sc8280xp: add Audio IF backend support Add Audio IF TDM backend handling to the sc8280xp machine driver. Use the common QCOM DAI-node TDM helper to parse the standard dai-tdm-slot-* properties from the backend CPU and codec endpoints and apply them during TDM hw_params(). Derive the LPASS backend bit clock from the runtime TDM parameters and request it through the backend child-clock path. Program the codec sysclk in the active stream path so the WSA885X playback sequence matches the working hardware flow. --- sound/soc/qcom/sc8280xp.c | 201 ++++++++++++++++++++++++++------------ 1 file changed, 136 insertions(+), 65 deletions(-) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index c2e9323dff90c..526b40615e2e6 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -2,6 +2,7 @@ // Copyright (c) 2022, Linaro Limited #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include #include "qdsp6/q6afe.h" -#include "qdsp6/q6apm.h" #include "qdsp6/q6prm.h" #include "common.h" #include "sdw.h" @@ -20,7 +20,7 @@ #define MCLK_FREQ 12288000 #define MCLK_NATIVE_FREQ 11289600 -static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { +static const struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { SND_SOC_DAPM_HP("Headphone Jack", NULL), SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_SPK("DP0 Jack", NULL), @@ -34,7 +34,7 @@ static struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { }; struct snd_soc_common { - char *driver_name; + const char *driver_name; const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; @@ -47,25 +47,72 @@ struct sc8280xp_snd_data { struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; - struct snd_soc_common *snd_soc_common_priv; + const struct snd_soc_common *snd_soc_common_priv; bool jack_setup; }; -static inline int sc8280xp_get_mclk_feq(unsigned int rate) +static int sc8280xp_tdm_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, + struct snd_soc_dai *cpu_dai) { - int freq = MCLK_FREQ; + int ret; - switch (rate) { - case SNDRV_PCM_RATE_11025: - case SNDRV_PCM_RATE_44100: - case SNDRV_PCM_RATE_88200: - freq = MCLK_NATIVE_FREQ; - break; - default: - break; + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret && ret != -ENOTSUPP) + dev_err(rtd->dev, "%s: failed to set cpu fmt: %d\n", __func__, ret); + + return ret < 0 && ret != -ENOTSUPP ? ret : 0; +} + +static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); + struct snd_soc_dai *codec_dai; + struct qcom_snd_tdm_slot_cfg cpu_cfg; + struct qcom_snd_tdm_slot_cfg codec_cfg; + unsigned int bclk_freq; + int ret; + int i; + + ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + if (!cpu_cfg.slots) + return 0; + + ret = sc8280xp_tdm_set_dai_fmt(rtd, cpu_dai); + if (ret) + return ret; + + ret = qcom_snd_apply_dai_tdm_slots(rtd); + if (ret) + return ret; + + bclk_freq = snd_soc_tdm_params_to_bclk(params, cpu_cfg.slot_width, + cpu_cfg.slots, 1); + if (!bclk_freq) + return -EINVAL; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_TDM_BCLK, + bclk_freq, SND_SOC_CLOCK_IN); + if (ret < 0 && ret != -ENOTSUPP) { + dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", __func__, ret); + return ret; } - return freq; + for_each_rtd_codec_dais(rtd, i, codec_dai) { + ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret < 0 && ret != -ENOTSUPP) { + dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", + __func__, codec_dai->name, ret); + return ret; + } + } + + return 0; } static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) @@ -77,6 +124,8 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) int dp_pcm_id = 0; switch (cpu_dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + return sc8280xp_tdm_set_dai_fmt(rtd, cpu_dai); case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* @@ -108,6 +157,23 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); } +static inline int sc8280xp_get_mclk_feq(unsigned int rate) +{ + int freq = MCLK_FREQ; + + switch (rate) { + case SNDRV_PCM_RATE_11025: + case SNDRV_PCM_RATE_44100: + case SNDRV_PCM_RATE_88200: + freq = MCLK_NATIVE_FREQ; + break; + default: + break; + } + + return freq; +} + static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -137,38 +203,44 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) +static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); - int mclk_freq = sc8280xp_get_mclk_feq(params_rate(params)); - - switch (cpu_dai->id) { - case PRIMARY_MI2S_RX...QUATERNARY_MI2S_TX: - case QUINARY_MI2S_RX...QUINARY_MI2S_TX: - snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - - if (data->snd_soc_common_priv->mi2s_mclk_enable) - snd_soc_dai_set_sysclk(cpu_dai, - LPAIF_MI2S_MCLK, mclk_freq, - SND_SOC_CLOCK_IN); - break; - default: - break; - }; - return 0; + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); } -static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) +static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); + unsigned int mclk_freq = sc8280xp_get_mclk_feq(params_rate(params)); + int ret; - return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); + switch (cpu_dai->id) { + case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: + case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); + if (ret < 0 && ret != -ENOTSUPP) + return ret; + + if (!data->snd_soc_common_priv->mi2s_mclk_enable) + return 0; + + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_MCLK, + mclk_freq, SND_SOC_CLOCK_IN); + return ret < 0 && ret != -ENOTSUPP ? ret : 0; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + return sc8280xp_tdm_hw_params(substream, params); + default: + break; + } + + return 0; } static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) @@ -213,14 +285,13 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) if (!card) return -ENOMEM; - /* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->snd_soc_common_priv = (struct snd_soc_common *)of_device_get_match_data(dev); + data->snd_soc_common_priv = of_device_get_match_data(dev); if (!data->snd_soc_common_priv) - return -ENOMEM; + return -EINVAL; card->owner = THIS_MODULE; card->dev = dev; @@ -240,86 +311,86 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) return devm_snd_soc_register_card(dev, card); } -static struct snd_soc_common kaanapali_priv_data = { +static const struct snd_soc_common kaanapali_priv_data = { .driver_name = "kaanapali", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common qcs9100_priv_data = { - .driver_name = "sa8775p", +static const struct snd_soc_common qcm6490_priv_data = { + .driver_name = "qcm6490", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common qcs615_priv_data = { +static const struct snd_soc_common qcs615_priv_data = { .driver_name = "qcs615", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), .mi2s_mclk_enable = true, }; -static struct snd_soc_common qcm6490_priv_data = { - .driver_name = "qcm6490", +static const struct snd_soc_common qcs6490_priv_data = { + .driver_name = "qcs6490", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common qcs6490_priv_data = { - .driver_name = "qcs6490", +static const struct snd_soc_common qcs8275_priv_data = { + .driver_name = "qcs8300", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common qcs8275_priv_data = { - .driver_name = "qcs8300", +static const struct snd_soc_common qcs9100_priv_data = { + .driver_name = "sa8775p", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common sc8280xp_priv_data = { +static const struct snd_soc_common sc8280xp_priv_data = { .driver_name = "sc8280xp", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common sm8450_priv_data = { +static const struct snd_soc_common sm8450_priv_data = { .driver_name = "sm8450", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common sm8550_priv_data = { +static const struct snd_soc_common sm8550_priv_data = { .driver_name = "sm8550", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common sm8650_priv_data = { +static const struct snd_soc_common sm8650_priv_data = { .driver_name = "sm8650", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static struct snd_soc_common sm8750_priv_data = { +static const struct snd_soc_common sm8750_priv_data = { .driver_name = "sm8750", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; static const struct of_device_id snd_sc8280xp_dt_match[] = { - {.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data}, - {.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data}, - {.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data}, - {.compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data}, - {.compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data}, - {.compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data}, - {.compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data}, - {.compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data}, - {.compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data}, - {.compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data}, - {.compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data}, - {.compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data}, + { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, + { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, + { .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data }, + { .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data }, + { .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data }, + { .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data }, + { .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data }, + { .compatible = "qcom,sc8280xp-sndcard", .data = &sc8280xp_priv_data }, + { .compatible = "qcom,sm8450-sndcard", .data = &sm8450_priv_data }, + { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, + { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, + { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, {} }; From 975371c5b70cf03d9991761a482219c653e47e1f Mon Sep 17 00:00:00 2001 From: Pratyush Meduri Date: Wed, 3 Jun 2026 13:48:29 +0530 Subject: [PATCH 8/8] ASoC: qcom: sc8280xp: add Shikra machine support Register the Shikra machine compatible in the sc8280xp machine driver and include the VA codec DMA TX backend in the capture channel fixup. --- sound/soc/qcom/sc8280xp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 526b40615e2e6..474fc4ef6823d 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -189,6 +189,7 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, channels->min = 2; channels->max = 2; switch (cpu_dai->id) { + case VA_CODEC_DMA_TX_0: case TX_CODEC_DMA_TX_0: case TX_CODEC_DMA_TX_1: case TX_CODEC_DMA_TX_2: @@ -378,6 +379,12 @@ static const struct snd_soc_common sm8750_priv_data = { .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; +static const struct snd_soc_common shikra_priv_data = { + .driver_name = "shikra", + .dapm_widgets = sc8280xp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), +}; + static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, { .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data }, @@ -391,6 +398,7 @@ static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,sm8550-sndcard", .data = &sm8550_priv_data }, { .compatible = "qcom,sm8650-sndcard", .data = &sm8650_priv_data }, { .compatible = "qcom,sm8750-sndcard", .data = &sm8750_priv_data }, + { .compatible = "qcom,shikra-sndcard", .data = &shikra_priv_data }, {} };