From 77913499890c1cb5e552538c7fd4c89dd3c30f36 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 13 Aug 2026 09:34:02 +0530 Subject: [PATCH 1/8] PENDING: ASoC: codec: adau1977: Add OF device match table The ADAU1977 I2C driver lacks an OF device match table, preventing it from binding to codec instances described via Device Tree. Systems using compatible strings such as "adi,adau1977", "adi,adau1978", or "adi,adau1979" fail to probe the driver as a result. Add an of_match_table covering all three variants and wire it into the I2C driver struct to enable DT-based driver matching. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mohammad Rafi Shaik --- sound/soc/codecs/Kconfig | 2 +- sound/soc/codecs/adau1977-i2c.c | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 9ceb474a18de9..cf07b75e8d92a 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -529,7 +529,7 @@ config SND_SOC_ADAU1977_SPI select REGMAP_SPI config SND_SOC_ADAU1977_I2C - tristate + tristate "Texas Instruments ADAU1977 CODEC (I2C)" depends on I2C select SND_SOC_ADAU1977 select REGMAP_I2C diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index d1c6c4ddf5063..9f54fa8375b15 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -34,9 +34,18 @@ static const struct i2c_device_id adau1977_i2c_ids[] = { }; MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); +static const struct of_device_id adau1977_i2c_of_match[] __maybe_unused = { + { .compatible = "adi,adau1977" }, + { .compatible = "adi,adau1978" }, + { .compatible = "adi,adau1979" }, + { }, +}; +MODULE_DEVICE_TABLE(of, adau1977_i2c_of_match); + static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", + .of_match_table = of_match_ptr(adau1977_i2c_of_match), }, .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, From a714282c0665b58eb563ba7eed15ccf79f9f334d Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Tue, 18 Aug 2026 15:58:16 +0530 Subject: [PATCH 2/8] PENDING: ASoC: dt-bindings: qcom,sm8250: add nord soundcard Register "qcom,nord-ride-sndcard" as a valid compatible string in the qcom,sm8250 sound card binding. This covers both the nord-rrd and nord-ride-sx board variants that share the same soundcard driver. Signed-off-by: Mohammad Rafi Shaik --- Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index f224e84919cdb..2cfbc9ddbc2c7 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -33,6 +33,7 @@ properties: - fairphone,fp5-sndcard - qcom,apq8096-sndcard - qcom,glymur-sndcard + - qcom,nord-ride-sndcard - qcom,qcm6490-idp-sndcard - qcom,qcs615-sndcard - qcom,qcs6490-rb3gen2-sndcard From 01adc98799d4f9c8db2ccfba923a5c512c1c1184 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Tue, 18 Aug 2026 15:31:36 +0530 Subject: [PATCH 3/8] PENDING: ASoC: qcom: sc8280xp: add nord soundcard Add machine driver support for the nord soundcard (qcom,nord-ride-sndcard). The nord platform uses PCM1681 for playback and ADAU1977 for capture over Tertiary MI2S, with the codec system clock sourced from LRCLK. Introduce a nord_ride_priv_data descriptor that sets the driver name, enables codec sysclk configuration, selects LRCLK as the sysclk source, and applies the appropriate codec DAI format. Refactor shared MI2S and TDM helpers in common.c and q6apm-lpass-dais.c to support the per-card private data cleanly. Signed-off-by: Mohammad Rafi Shaik --- sound/soc/qcom/common.c | 93 +++---- sound/soc/qcom/common.h | 3 + sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 312 +++++++++++++----------- sound/soc/qcom/sc8280xp.c | 257 ++++++++++++------- 4 files changed, 379 insertions(+), 286 deletions(-) diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c index 767606ea3ab7b..27ffc88a9fb41 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -28,28 +28,28 @@ static struct device_node *qcom_snd_get_link_node(struct snd_soc_pcm_runtime *rt { 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"); + for_each_available_child_of_node_scoped(card->dev->of_node, np) { + struct device_node *cpu_np __free(device_node) = + 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); + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, + &args); 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; + return of_node_get(np); } of_node_put(args.np); @@ -100,33 +100,24 @@ 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; + struct device_node *link_np __free(device_node) = qcom_snd_get_link_node(rtd); 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; - } + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(link_np, "cpu"); + struct device_node *codec_np __free(device_node) = + of_get_child_by_name(link_np, "codec"); + if (!cpu_np || !codec_np) + return -EINVAL; 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; - return ret; + return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); } int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, @@ -143,39 +134,49 @@ int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, } EXPORT_SYMBOL_GPL(qcom_snd_get_dai_tdm_slots); -int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd) +int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const struct qcom_snd_tdm_slot_cfg *codec_cfg) { 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; + if (!cpu_cfg->slots) + return 0; - 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 != -EOPNOTSUPP) + 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) 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 != -EOPNOTSUPP) + codec_cfg->tx_mask, + codec_cfg->rx_mask, + codec_cfg->slots, + codec_cfg->slot_width); + if (ret && ret != -ENOTSUPP && ret != -EOPNOTSUPP) return ret; } return 0; } +EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots_cfg); + +int qcom_snd_apply_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_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg); + if (ret) + return ret == -EINVAL ? 0 : ret; + + return qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); +} EXPORT_SYMBOL_GPL(qcom_snd_apply_dai_tdm_slots); int qcom_snd_parse_of(struct snd_soc_card *card) @@ -246,7 +247,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = of_property_read_string(np, "link-name", &link->name); if (ret) { - dev_err(card->dev, "error getting codec dai_link name\n"); + dev_err(dev, "error getting codec dai_link name\n"); return ret; } @@ -264,7 +265,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) ret = snd_soc_of_get_dlc(cpu, &args, link->cpus, 0); if (ret) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: error getting cpu dai name\n", link->name); return ret; } @@ -281,7 +282,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) "sound-dai", 0); if (!link->platforms->of_node) { - dev_err(card->dev, "%s: platform dai not found\n", link->name); + dev_err(dev, "%s: platform dai not found\n", link->name); return -EINVAL; } } else { @@ -291,7 +292,7 @@ int qcom_snd_parse_of(struct snd_soc_card *card) if (codec) { ret = snd_soc_of_get_dai_link_codecs(dev, codec, link); if (ret < 0) { - dev_err_probe(card->dev, ret, + dev_err_probe(dev, ret, "%s: codec dai not found\n", link->name); return ret; } diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index b73c25045e672..02b24caec485e 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -20,6 +20,9 @@ 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_cfg(struct snd_soc_pcm_runtime *rtd, + const struct qcom_snd_tdm_slot_cfg *cpu_cfg, + const 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); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index ff92ac7e522d3..e204fd59e5129 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -24,10 +24,7 @@ struct q6apm_dai_priv_data { struct clk *mclk; struct clk *bclk; - struct clk *eclk; - bool mclk_enabled; - bool bclk_enabled; - bool eclk_enabled; + bool mclk_enabled, bclk_enabled; }; struct q6apm_lpass_dai_data { @@ -37,52 +34,35 @@ 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) +static void q6apm_lpass_dai_disable_clocks(struct q6apm_lpass_dai_data *dai_data, int id) { - 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; + if (dai_data->priv[id].mclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].mclk); + dai_data->priv[id].mclk_enabled = false; } - return clk; + if (dai_data->priv[id].bclk_enabled) { + clk_disable_unprepare(dai_data->priv[id].bclk); + dai_data->priv[id].bclk_enabled = false; + } } -static int of_q6apm_parse_dai_data(struct device *dev, - struct q6apm_lpass_dai_data *data) +static void q6apm_lpass_dai_put_clocks(struct q6apm_lpass_dai_data *dai_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); + int i; - priv->bclk = q6apm_lpass_get_child_clk(dev, node, "bclk"); - if (IS_ERR(priv->bclk)) - return PTR_ERR(priv->bclk); + for (i = 0; i < APM_PORT_MAX; i++) { + q6apm_lpass_dai_disable_clocks(dai_data, i); - priv->eclk = q6apm_lpass_get_child_clk(dev, node, "eclk"); - if (IS_ERR(priv->eclk)) - return PTR_ERR(priv->eclk); + if (dai_data->priv[i].mclk) { + clk_put(dai_data->priv[i].mclk); + dai_data->priv[i].mclk = NULL; + } + if (dai_data->priv[i].bclk) { + clk_put(dai_data->priv[i].bclk); + dai_data->priv[i].bclk = NULL; + } } - - return 0; } static int q6dma_set_channel_map(struct snd_soc_dai *dai, @@ -220,33 +200,6 @@ 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) { @@ -290,7 +243,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s * 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]) { + 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); @@ -339,6 +292,62 @@ 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); + + q6apm_lpass_dai_shutdown(substream, dai); + q6apm_lpass_dai_disable_clocks(dai_data, dai->id); +} + +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 = NULL; + bool *enabled = NULL; + 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; + default: + return -EINVAL; + } + + if (sysclk) { + ret = clk_set_rate(sysclk, freq); + if (ret) { + dev_err(dai->dev, "Error, Unable to set rate (%d) for sysclk %d\n", + freq, clk_id); + return ret; + } + + if (*enabled) + return 0; + + 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); @@ -356,99 +365,46 @@ static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, { 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; + unsigned int cap_mask, slot_mask; if (slot_width != 16 && slot_width != 32) { - dev_err(dai->dev, "%s: invalid slot_width %d\n", - __func__, slot_width); + dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); return -EINVAL; } 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; + cap_mask = GENMASK(slots - 1, 0); break; default: - dev_err(dai->dev, "%s: invalid slots %d\n", - __func__, slots); + dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); return -EINVAL; } switch (dai->id) { case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; + if (slot_mask & ~cap_mask) { + dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", + __func__, slot_mask, slots); + return -EINVAL; + } + cfg->nslots_per_frame = slots; cfg->slot_width = slot_width; - cfg->slot_mask = ((dai->id & 0x1) ? tx_mask : rx_mask) & cap_mask; + cfg->slot_mask = slot_mask; break; default: - dev_err(dai->dev, "%s: invalid dai id 0x%x\n", - __func__, dai->id); + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); return -EINVAL; } return 0; } -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 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_TDM_MCLK: - sysclk = priv->mclk; - enabled = &priv->mclk_enabled; - break; - case LPAIF_MI2S_TDM_BCLK: - sysclk = priv->bclk; - enabled = &priv->bclk_enabled; - break; - case LPAIF_MI2S_TDM_ECLK: - sysclk = priv->eclk; - enabled = &priv->eclk_enabled; - break; - default: - return 0; - } - - if (!sysclk || !freq) - return 0; - - ret = clk_set_rate(sysclk, freq); - if (ret) - return ret; - - if (*enabled) - return 0; - - ret = clk_prepare_enable(sysclk); - if (ret) - return ret; - - *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, @@ -460,12 +416,12 @@ static const struct snd_soc_dai_ops q6dma_ops = { static const struct snd_soc_dai_ops q6i2s_ops = { .prepare = q6apm_lpass_dai_prepare, - .startup = q6apm_lpass_dai_startup, - .shutdown = q6lpass_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, - .set_sysclk = q6lpass_set_sysclk, + .set_sysclk = q6i2s_set_sysclk, .trigger = q6apm_lpass_dai_trigger, }; @@ -479,15 +435,14 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { }; 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, + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, + .set_tdm_slot = q6tdm_set_tdm_slot, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, }; static const struct snd_soc_component_driver q6apm_lpass_dai_component = { @@ -498,6 +453,65 @@ static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .remove_order = SND_SOC_COMP_ORDER_FIRST, }; +static int of_q6apm_parse_dai_data(struct device *dev, + struct q6apm_lpass_dai_data *data) +{ + int ret; + + for_each_child_of_node_scoped(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: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + priv = &data->priv[id]; + priv->mclk = of_clk_get_by_name(node, "mclk"); + if (IS_ERR(priv->mclk)) { + int err = PTR_ERR(priv->mclk); + + priv->mclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s mclk\n"); + } + } + + priv->bclk = of_clk_get_by_name(node, "bclk"); + if (IS_ERR(priv->bclk)) { + int err = PTR_ERR(priv->bclk); + + priv->bclk = NULL; + if (err == -EPROBE_DEFER) { + q6apm_lpass_dai_put_clocks(data); + return dev_err_probe(dev, err, + "unable to get mi2s bclk\n"); + } + } + break; + default: + break; + } + } + + return 0; +} + +static void q6apm_lpass_dai_clocks_action(void *data) +{ + q6apm_lpass_dai_put_clocks(data); +} + static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) { struct q6dsp_audio_port_dai_driver_config cfg; @@ -516,6 +530,10 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_add_action_or_reset(dev, q6apm_lpass_dai_clocks_action, dai_data); + if (ret) + return ret; + memset(&cfg, 0, sizeof(cfg)); cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index 5e98f8c428338..9ddbb65684c66 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -14,14 +14,22 @@ #include #include #include "qdsp6/q6afe.h" +#include "qdsp6/q6apm.h" #include "qdsp6/q6prm.h" +#include "qdsp6/q6dsp-common.h" #include "common.h" #include "sdw.h" -#define MCLK_FREQ 12288000 -#define MCLK_NATIVE_FREQ 11289600 +#define I2S_MCLKFS 256 -static const struct snd_soc_dapm_widget sc8280xp_dapm_widgets[] = { +#define I2S_MCLK_RATE(rate) \ + ((rate) * (I2S_MCLKFS)) +#define I2S_BIT_RATE(rate, channels, format) \ + ((rate) * (channels) * (format)) + +#define LRCLK_SYSCLK 1 + +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), @@ -48,7 +56,7 @@ static const struct snd_kcontrol_new shikra_controls[] = { SOC_DAPM_PIN_SWITCH("Speaker"), }; -struct snd_soc_common { +struct qcom_snd_soc_common { const char *driver_name; const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; @@ -56,10 +64,13 @@ struct snd_soc_common { int num_dapm_routes; const struct snd_kcontrol_new *controls; int num_controls; - bool mi2s_mclk_enable; - bool mi2s_bclk_enable; unsigned int codec_dai_fmt; + unsigned int codec_sysclk_src; bool codec_sysclk_set; + bool mi2s_mclk_enable; + bool mi2s_bclk_enable; + bool wcd_jack; + int (*snd_prepare)(struct snd_pcm_substream *substream); }; struct sc8280xp_snd_data { @@ -67,27 +78,39 @@ struct sc8280xp_snd_data { struct snd_soc_card *card; struct snd_soc_jack jack; struct snd_soc_jack dp_jack[8]; - const struct snd_soc_common *snd_soc_common_priv; + const struct qcom_snd_soc_common *priv; bool dsp_bypass_mode; bool jack_setup; }; -static int sc8280xp_tdm_set_dai_fmt(struct snd_soc_pcm_runtime *rtd, - struct snd_soc_dai *cpu_dai) +static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params) { - int ret; + int rate = params_rate(params); - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - if (ret && ret != -EOPNOTSUPP) - dev_err(rtd->dev, "%s: failed to set cpu fmt: %d\n", __func__, ret); + switch (rate) { + case 11025: + case 44100: + case 88200: + return I2S_MCLK_RATE(44100); + default: + break; + } + + return I2S_MCLK_RATE(rate); +} - return ret < 0 && ret != -EOPNOTSUPP ? ret : 0; +static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *params) +{ + return I2S_BIT_RATE(params_rate(params), + params_channels(params), + snd_pcm_format_width(params_format(params))); } 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 sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); 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; @@ -103,11 +126,11 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, if (!cpu_cfg.slots) return 0; - ret = sc8280xp_tdm_set_dai_fmt(rtd, cpu_dai); + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); if (ret) return ret; - ret = qcom_snd_apply_dai_tdm_slots(rtd); + ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg); if (ret) return ret; @@ -115,18 +138,25 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream, 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 != -EOPNOTSUPP) { - dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", __func__, ret); - return ret; + if (data->priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(rtd->dev, "%s: failed to set cpu sysclk: %d\n", + __func__, ret); + return ret; + } } - 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 != -EOPNOTSUPP) { - dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", - __func__, codec_dai->name, ret); - return ret; + if (data->priv->codec_sysclk_set) { + 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) { + dev_err(rtd->dev, "%s: failed to set codec sysclk on %s: %d\n", + __func__, codec_dai->name, ret); + return ret; + } } } @@ -145,10 +175,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) case WSA_CODEC_DMA_RX_0: case WSA_CODEC_DMA_RX_1: /* - * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume - * to reduce the risk of speaker damage until we have active - * speaker protection in place. - */ + * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume + * to reduce the risk of speaker damage until we have active + * speaker protection in place. + */ snd_soc_limit_volume(card, "WSA_RX0 Digital Volume", 81); snd_soc_limit_volume(card, "WSA_RX1 Digital Volume", 81); snd_soc_limit_volume(card, "SpkrLeft PA Volume", 17); @@ -170,24 +200,10 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) if (dp_jack) return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id); - return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); -} + if (data->priv->wcd_jack) + 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; + return 0; } static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -213,6 +229,7 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, case LPASS_CDC_DMA_VA_TX1: channels->min = 1; break; + break; default: break; } @@ -221,24 +238,17 @@ static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, return 0; } -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); - - return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); -} - 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 *codec_dai = snd_soc_rtd_to_codec(rtd, 0); 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 mclk_freq = sc8280xp_get_mclk_freq(params); + int bclk_freq = sc8280xp_get_bclk_freq(params); + int rate = params_rate(params); int ret; - struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0); if (data->dsp_bypass_mode) return 0; @@ -246,24 +256,50 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, switch (cpu_dai->id) { case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: + case SENARY_MI2S_RX ... SENARY_MI2S_TX: ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP); - if (ret < 0 && ret != -EOPNOTSUPP) + if (ret && ret != -ENOTSUPP) return ret; - if (data->snd_soc_common_priv->codec_dai_fmt) - snd_soc_dai_set_fmt(codec_dai, - data->snd_soc_common_priv->codec_dai_fmt); + if (data->priv->codec_dai_fmt) { + ret = snd_soc_dai_set_fmt(codec_dai, + data->priv->codec_dai_fmt); + if (ret && ret != -ENOTSUPP) + return ret; + } - if (data->snd_soc_common_priv->codec_sysclk_set) - snd_soc_dai_set_sysclk(codec_dai, 0, mclk_freq, - SND_SOC_CLOCK_IN); + if (data->priv->mi2s_mclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_MCLK, mclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } - if (!data->snd_soc_common_priv->mi2s_bclk_enable) - return 0; + if (data->priv->mi2s_bclk_enable) { + ret = snd_soc_dai_set_sysclk(cpu_dai, + LPAIF_MI2S_BCLK, bclk_freq, + SND_SOC_CLOCK_OUT); + if (ret) + return ret; + } - ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_MCLK, - mclk_freq, SND_SOC_CLOCK_IN); - return ret < 0 && ret != -EOPNOTSUPP ? ret : 0; + if (data->priv->codec_sysclk_set) { + if (data->priv->codec_sysclk_src == LRCLK_SYSCLK) { + ret = snd_soc_component_set_sysclk(codec_dai->component, 0, + LRCLK_SYSCLK, + rate, SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } else { + ret = snd_soc_dai_set_sysclk(codec_dai, + 0, mclk_freq, + SND_SOC_CLOCK_IN); + if (ret && ret != -ENOTSUPP) + return ret; + } + } + break; case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: return sc8280xp_tdm_hw_params(substream, params); default: @@ -273,6 +309,23 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return 0; } +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); + + if (data->priv->snd_prepare) { + int ret; + + ret = data->priv->snd_prepare(substream); + if (ret) + return ret; + } + + return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]); +} + static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); @@ -315,24 +368,25 @@ 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 = of_device_get_match_data(dev); - if (!data->snd_soc_common_priv) - return -EINVAL; + data->priv = of_device_get_match_data(dev); + if (!data->priv) + return -ENODEV; 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; - card->controls = data->snd_soc_common_priv->controls; - card->num_controls = data->snd_soc_common_priv->num_controls; + card->dapm_widgets = data->priv->dapm_widgets; + card->num_dapm_widgets = data->priv->num_dapm_widgets; + card->dapm_routes = data->priv->dapm_routes; + card->num_dapm_routes = data->priv->num_dapm_routes; + card->controls = data->priv->controls; + card->num_controls = data->priv->num_controls; ret = qcom_snd_parse_of(card); if (ret) @@ -341,55 +395,68 @@ static int sc8280xp_platform_probe(struct platform_device *pdev) data->dsp_bypass_mode = of_property_read_bool(dev->of_node, "qcom,adsp-bypass-mode"); - card->driver_name = data->snd_soc_common_priv->driver_name; + card->driver_name = data->priv->driver_name; sc8280xp_add_be_ops(card); return devm_snd_soc_register_card(dev, card); } -static const struct snd_soc_common kaanapali_priv_data = { +static const struct qcom_snd_soc_common kaanapali_priv_data = { .driver_name = "kaanapali", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, +}; + +static const struct qcom_snd_soc_common nord_ride_priv_data = { + .driver_name = "nord", + .mi2s_bclk_enable = true, + .codec_sysclk_set = true, + .codec_sysclk_src = LRCLK_SYSCLK, + .codec_dai_fmt = SND_SOC_DAIFMT_CBC_CFC | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_I2S, }; -static const struct snd_soc_common qcm6490_priv_data = { +static const struct qcom_snd_soc_common qcm6490_priv_data = { .driver_name = "qcm6490", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, }; -static const struct snd_soc_common qcs615_priv_data = { +static const struct qcom_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 const struct snd_soc_common qcs6490_priv_data = { +static const struct qcom_snd_soc_common qcs6490_priv_data = { .driver_name = "qcs6490", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, }; -static const struct snd_soc_common qcs8275_priv_data = { +static const struct qcom_snd_soc_common qcs8275_priv_data = { .driver_name = "qcs8300", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common qcs9100_priv_data = { +static const struct qcom_snd_soc_common qcs9100_priv_data = { .driver_name = "sa8775p", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), }; -static const struct snd_soc_common sc8280xp_priv_data = { +static const struct qcom_snd_soc_common sc8280xp_priv_data = { .driver_name = "sc8280xp", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, }; -static const struct snd_soc_common shikra_priv_data = { +static const struct qcom_snd_soc_common shikra_priv_data = { .driver_name = "shikra", .dapm_widgets = shikra_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(shikra_dapm_widgets), @@ -402,25 +469,28 @@ static const struct snd_soc_common shikra_priv_data = { .codec_sysclk_set = true, }; -static const struct snd_soc_common sm8450_priv_data = { +static const struct qcom_snd_soc_common sm8450_priv_data = { .driver_name = "sm8450", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, }; -static const struct snd_soc_common sm8550_priv_data = { +static const struct qcom_snd_soc_common sm8550_priv_data = { .driver_name = "sm8550", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, }; -static const struct snd_soc_common sm8650_priv_data = { +static const struct qcom_snd_soc_common sm8650_priv_data = { .driver_name = "sm8650", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), + .wcd_jack = true, }; -static const struct snd_soc_common sm8750_priv_data = { +static const struct qcom_snd_soc_common sm8750_priv_data = { .driver_name = "sm8750", .dapm_widgets = sc8280xp_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets), @@ -428,6 +498,7 @@ static const struct snd_soc_common sm8750_priv_data = { static const struct of_device_id snd_sc8280xp_dt_match[] = { { .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data }, + { .compatible = "qcom,nord-ride-sndcard", .data = &nord_ride_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 }, From fbe073dd5636645b5be8181dc2c1cd73e36ab0ed Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Tue, 18 Aug 2026 16:00:43 +0530 Subject: [PATCH 4/8] PENDING: ASoC: codec: pcm1681: Enable system clock before regmap access The PCM1681 DAC requires the SCK (system clock) input to be present for proper device operation. On platforms where SCK is provided by a controllable clock source, register accesses over I2C may fail when the clock is not enabled. Add optional clock support to the PCM1681 driver by acquiring the "sclk" clock, enabling it during probe, and registering a managed cleanup action to disable it during device removal or probe failure. This allows platforms to model the PCM1681 system clock through the common clock framework and ensures the device is operational before regmap initialization and register accesses occur. Signed-off-by: Mohammad Rafi Shaik --- sound/soc/codecs/pcm1681.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index cb923cecb47fc..0abcd0c730970 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,7 @@ struct pcm1681_private { unsigned int deemph; /* Current rate for deemphasis control */ unsigned int rate; + struct clk *sclk; }; static const int pcm1681_deemph[] = { 44100, 48000, 32000 }; @@ -304,6 +306,22 @@ static int pcm1681_i2c_probe(struct i2c_client *client) if (!priv) return -ENOMEM; + priv->sclk = devm_clk_get_optional(&client->dev, "sclk"); + if (IS_ERR(priv->sclk)) + return dev_err_probe(&client->dev, PTR_ERR(priv->sclk), + "Failed to get sclk\n"); + + ret = clk_prepare_enable(priv->sclk); + if (ret) + return dev_err_probe(&client->dev, ret, + "Failed to enable sclk\n"); + + ret = devm_add_action_or_reset(&client->dev, + (void (*)(void *))clk_disable_unprepare, + priv->sclk); + if (ret) + return ret; + priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap); if (IS_ERR(priv->regmap)) { ret = PTR_ERR(priv->regmap); From 866575073892df2ef614ecf0bb0ac15c36a0d548 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Thu, 13 Aug 2026 09:18:02 +0530 Subject: [PATCH 5/8] PENDING: arm64: dts: qcom: nord: Add gpr node Add the GPR (Generic Packet Router) node to the nord SoC DTSI along with the APM (Audio Process Manager) and PRM (Proxy Resource Manager) audio services. These nodes are required by the Q6APM audio stack to route audio packets between the application processor and the DSP. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mohammad Rafi Shaik --- arch/arm64/boot/dts/qcom/nord.dtsi | 36 ++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord.dtsi b/arch/arm64/boot/dts/qcom/nord.dtsi index 3d472d2434b64..6da4483f85b63 100644 --- a/arch/arm64/boot/dts/qcom/nord.dtsi +++ b/arch/arm64/boot/dts/qcom/nord.dtsi @@ -1862,6 +1862,42 @@ dma-coherent; }; }; + + gpr { + compatible = "qcom,gpr"; + qcom,glink-channels = "adsp_apps"; + qcom,domain = ; + qcom,intents = <512 20>; + label = "adsp"; + #address-cells = <1>; + #size-cells = <0>; + + q6apm: service@1 { + compatible = "qcom,q6apm"; + reg = ; + #sound-dai-cells = <0>; + + q6apmbedai: bedais { + compatible = "qcom,q6apm-lpass-dais"; + #sound-dai-cells = <1>; + }; + + q6apmdai: dais { + compatible = "qcom,q6apm-dais"; + iommus = <&apps_smmu_0 0x09E1 0x0400>; + }; + }; + + q6prm: service@2 { + compatible = "qcom,q6prm"; + reg = ; + + q6prmcc: clock-controller { + compatible = "qcom,q6prm-lpass-clocks"; + #clock-cells = <2>; + }; + }; + }; }; }; From e4aa4acb705ee9883cff121e90e567f0a4ace4fb Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Tue, 18 Aug 2026 15:16:04 +0530 Subject: [PATCH 6/8] PENDING: arm64: dts: qcom: nord-rrd: Add sound support Enable audio on the nord RRD board. Add a soundcard node using the qcom,nord-ride-sndcard compatible with two DAI links: - PCM1681 playback over Tertiary MI2S RX - ADAU1977 capture over Tertiary MI2S TX Add MI2S pin mux states (data, SCLK, WS on GPIO 73-76) and MCLK pin states (MCLK0/MCLK1 on GPIO 99/100) to nord-embedded.dtsi so they can be shared with other nord board variants. Pull in nord-pmics.dtsi for the PMIC nodes required by the audio regulators. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mohammad Rafi Shaik --- arch/arm64/boot/dts/qcom/nord-embedded.dtsi | 37 +++++++ arch/arm64/boot/dts/qcom/nord-rrd.dts | 113 ++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi index 0d1c561b1f9e0..2fa59bb14b264 100644 --- a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi @@ -1403,6 +1403,43 @@ }; &tlmm { + aud_intfc8_mi2s_active: aud-intfc8-mi2s-active-state { + data-pins { + pins = "gpio75", "gpio76"; + function = "aud_intfc8_data"; + drive-strength = <8>; + bias-disable; + }; + + sclk-pins { + pins = "gpio73"; + function = "aud_intfc8_clk"; + drive-strength = <8>; + bias-disable; + }; + + ws-pins { + pins = "gpio74"; + function = "aud_intfc8_ws"; + drive-strength = <8>; + bias-disable; + }; + }; + + aud_mclk0_mira_active: aud-mclk0-mira-active-state { + pins = "gpio99"; + function = "aud_mclk0_mira"; + drive-strength = <8>; + bias-disable; + }; + + aud_mclk1_mira_active: aud-mclk1-mira-active-state { + pins = "gpio100"; + function = "aud_mclk1_mira"; + drive-strength = <8>; + bias-disable; + }; + qup_i2c0_default: qup-i2c0-default-state { pins = "gpio111", "gpio112"; function = "qup0_se0"; diff --git a/arch/arm64/boot/dts/qcom/nord-rrd.dts b/arch/arm64/boot/dts/qcom/nord-rrd.dts index ddc534e2214b4..ca6b45dc248d5 100644 --- a/arch/arm64/boot/dts/qcom/nord-rrd.dts +++ b/arch/arm64/boot/dts/qcom/nord-rrd.dts @@ -6,9 +6,11 @@ /dts-v1/; #include +#include #include #include "nord-embedded.dtsi" +#include "nord-pmics.dtsi" / { model = "Qualcomm Technologies, Inc. IQ10 RRD"; @@ -52,6 +54,46 @@ }; }; + sound { + compatible = "qcom,nord-ride-sndcard"; + model = "NORD-RRD"; + + pinctrl-0 = <&aud_intfc8_mi2s_active>, <&aud_mclk0_mira_active>; + pinctrl-names = "default"; + + aud-intfc8-mi2s-playback-dai-link { + link-name = "PCM1681 Playback"; + + codec { + sound-dai = <&pcm1681>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + aud-intfc8-mi2s-capture-dai-link { + link-name = "ADAU1977 Capture"; + + codec { + sound-dai = <&adau1979>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_TX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + vreg_pre_reg_3p3: regulator-pre-reg-3p3 { compatible = "regulator-fixed"; regulator-name = "vreg_pre_reg_3p3"; @@ -521,6 +563,77 @@ }; }; +&i2c18 { + clock-frequency = <400000>; + + status = "okay"; + + expander0: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&pmic_l_gpios 6 GPIO_ACTIVE_LOW>; + reset-gpios = <&pmic_l_gpios 5 GPIO_ACTIVE_LOW>; + + click-pop-mute-hog { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "click-pop-mute-n"; + }; + + dac-enable-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "dac_enable"; + }; + }; + + /* PCM1681 DAC */ + pcm1681: pcm1681@4c { + compatible = "ti,pcm1681"; + reg = <0x4c>; + + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "sclk"; + assigned-clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + assigned-clock-rates = <12288000>; + + #sound-dai-cells = <0>; + }; + + /* ADI ADAU1979 - 4-channel 24-bit ADC */ + adau1979: adau1979@31 { + compatible = "adi,adau1979"; + reg = <0x31>; + + AVDD-supply = <&vreg_pre_reg_3p3>; + IOVDD-supply = <&vreg_pre_reg_3p3>; + #sound-dai-cells = <0>; + }; +}; + +&q6apmbedai { + #address-cells = <1>; + #size-cells = <0>; + + dai@20 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; + + dai@21 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; +}; + &qupv3_0 { firmware-name = "qcom/nord/qupv3fw.elf"; From fcbfc89b6b8dd6a6ecd46e9997c56a4e0eff3491 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Tue, 18 Aug 2026 16:32:13 +0530 Subject: [PATCH 7/8] PENDING: arm64: dts: qcom: nord-ride-sx: Add sound support Enable audio on the nord-ride-sx board. Add a soundcard node using the qcom,nord-ride-sndcard compatible with two DAI links: - PCM1681 playback over Tertiary MI2S RX - ADAU1977 capture over Tertiary MI2S TX Add a fixed 3.3 V pre-regulator node and PCM1681/ADAU1979 codec nodes on I2C18, with the PCM1681 system clock supplied via MCLK0. Assisted-by: Claude:claude-opus-4-7 Signed-off-by: Mohammad Rafi Shaik --- arch/arm64/boot/dts/qcom/nord-ride-sx.dts | 113 ++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord-ride-sx.dts b/arch/arm64/boot/dts/qcom/nord-ride-sx.dts index b8839e1b86421..461d644c31eb1 100644 --- a/arch/arm64/boot/dts/qcom/nord-ride-sx.dts +++ b/arch/arm64/boot/dts/qcom/nord-ride-sx.dts @@ -6,6 +6,7 @@ /dts-v1/; #include +#include #include #include "nord-embedded.dtsi" @@ -78,6 +79,54 @@ enable-active-high; regulator-always-on; }; + + sound { + compatible = "qcom,nord-ride-sndcard"; + model = "NORD-RIDE-SX"; + + pinctrl-0 = <&aud_intfc8_mi2s_active>, <&aud_mclk0_mira_active>; + pinctrl-names = "default"; + + aud-intfc8-mi2s-playback-dai-link { + link-name = "PCM1681 Playback"; + + codec { + sound-dai = <&pcm1681>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_RX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + + aud-intfc8-mi2s-capture-dai-link { + link-name = "ADAU1977 Capture"; + + codec { + sound-dai = <&adau1979>; + }; + + cpu { + sound-dai = <&q6apmbedai TERTIARY_MI2S_TX>; + }; + + platform { + sound-dai = <&q6apm>; + }; + }; + }; + + vreg_pre_reg_3p3: regulator-pre-reg-3p3 { + compatible = "regulator-fixed"; + regulator-name = "vreg_pre_reg_3p3"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; }; &apps_rsc { @@ -561,6 +610,53 @@ clock-frequency = <400000>; status = "okay"; + expander0: gpio@20 { + compatible = "ti,tca6408"; + reg = <0x20>; + #gpio-cells = <2>; + gpio-controller; + #interrupt-cells = <2>; + interrupt-controller; + interrupts-extended = <&pmic_l_gpios 6 GPIO_ACTIVE_LOW>; + reset-gpios = <&pmic_l_gpios 5 GPIO_ACTIVE_LOW>; + + click-pop-mute-hog { + gpio-hog; + gpios = <5 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "click-pop-mute-n"; + }; + + dac-enable-hog { + gpio-hog; + gpios = <6 GPIO_ACTIVE_HIGH>; + output-high; + line-name = "dac_enable"; + }; + }; + + /* PCM1681 DAC */ + pcm1681: pcm1681@4c { + compatible = "ti,pcm1681"; + reg = <0x4c>; + + clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "sclk"; + assigned-clocks = <&q6prmcc LPASS_CLK_ID_MCLK_1 LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + assigned-clock-rates = <12288000>; + + #sound-dai-cells = <0>; + }; + + /* ADI ADAU1979 - 4-channel 24-bit ADC */ + adau1979: adau1979@31 { + compatible = "adi,adau1979"; + reg = <0x31>; + + AVDD-supply = <&vreg_pre_reg_3p3>; + IOVDD-supply = <&vreg_pre_reg_3p3>; + #sound-dai-cells = <0>; + }; }; &i2c19 { @@ -622,6 +718,23 @@ status = "okay"; }; +&q6apmbedai { + #address-cells = <1>; + #size-cells = <0>; + + dai@20 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; + + dai@21 { + reg = ; + clocks = <&q6prmcc LPASS_CLK_ID_AUD_INTF8_IBIT LPASS_CLK_ATTRIBUTE_COUPLE_NO>; + clock-names = "bclk"; + }; +}; + &uart15 { status = "okay"; }; From d21e47a1c096b7a87c8978e79ec0d44c8b88bfd1 Mon Sep 17 00:00:00 2001 From: Mohammad Rafi Shaik Date: Mon, 17 Aug 2026 23:52:52 +0530 Subject: [PATCH 8/8] PENDING: arm64: defconfig: Enable PCM1681 and ADAU1977 Select SND_SOC_PCM1681 and SND_SOC_ADAU1977_I2C to build the TI PCM1681 DAC and Analog Devices ADAU1977 ADC drivers needed by the nord soundcard. Remove the corresponding entries from prune.config since PCA953X GPIO expander is now required for nord board support. Signed-off-by: Mohammad Rafi Shaik --- arch/arm64/configs/defconfig | 2 ++ arch/arm64/configs/prune.config | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 60178ffbcb2e3..8955cfb5de699 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -1101,6 +1101,8 @@ CONFIG_SND_SOC_MT8195=m CONFIG_SND_SOC_MT8195_MT6359=m CONFIG_SND_SOC_MT8365=m CONFIG_SND_SOC_MT8365_MT6357=m +CONFIG_SND_SOC_PCM1681=m +CONFIG_SND_SOC_ADAU1977_I2C=m CONFIG_SND_MESON_AXG_SOUND_CARD=m CONFIG_SND_MESON_GX_SOUND_CARD=m CONFIG_SND_SOC_QCOM=m diff --git a/arch/arm64/configs/prune.config b/arch/arm64/configs/prune.config index 40665245e406f..804c32654fdd7 100644 --- a/arch/arm64/configs/prune.config +++ b/arch/arm64/configs/prune.config @@ -153,8 +153,6 @@ # CONFIG_GPIO_MB86S7X is not set # CONFIG_GPIO_XGENE is not set # CONFIG_GPIO_MAX732X is not set -# CONFIG_GPIO_PCA953X is not set -# CONFIG_GPIO_PCA953X_IRQ is not set # CONFIG_GPIO_BD9571MWV is not set # CONFIG_GPIO_MAX77620 is not set # CONFIG_POWER_RESET_BRCMSTB is not set