summaryrefslogtreecommitdiff
path: root/sound/soc/qcom
diff options
context:
space:
mode:
authorPrasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>2026-08-04 12:33:02 +0530
committerMark Brown <broonie@kernel.org>2026-08-04 17:32:18 +0100
commit4d084017589312a0da2bec3474ef2035c5f4a407 (patch)
tree42dd5c4a516cc7c9fabb6f7fe44e88f4247bd29c /sound/soc/qcom
parent67605cbf8dfe79f10fc189f6e794d2843e249918 (diff)
ASoC: qcom: q6apm-lpass-dais: add TDM DAI operations
Add TDM DAI operations to q6apm-lpass-dais so AudioReach TDM backends can be configured through the normal ASoC hw_params and DAI setup flow. The TDM set_tdm_slot() callback validates the supported slot width and slot count, stores the active slot mask in the AudioReach module configuration, and leaves existing DMA, I2S and HDMI paths unchanged. Reuse the existing LPASS child-clock handling for TDM nodes as well as MI2S nodes, since TDM backends also request optional backend clocks through the machine driver set_sysclk() path. Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com> Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com> Link: https://patch.msgid.link/20260804070307.117119-3-prasad.kumpatla@oss.qualcomm.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/qcom')
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index e68e8b000e07..e204fd59e512 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -358,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+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, slot_mask;
+
+ if (slot_width != 16 && slot_width != 32) {
+ dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width);
+ return -EINVAL;
+ }
+
+ switch (slots) {
+ case 2:
+ case 4:
+ case 8:
+ case 16:
+ cap_mask = GENMASK(slots - 1, 0);
+ break;
+ default:
+ 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 = slot_mask;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops q6dma_ops = {
.prepare = q6apm_lpass_dai_prepare,
.startup = q6apm_lpass_dai_startup,
@@ -387,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = {
.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 = 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 = {
.name = "q6apm-be-dai-component",
.of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name,
@@ -415,6 +473,7 @@ static int of_q6apm_parse_dai_data(struct device *dev,
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)) {
@@ -479,6 +538,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);