* [PATCH v1 1/6] ASoC: qcom: qdsp6: add topology-driven Audio IF support
2026-06-10 15:45 [PATCH v1 0/6] ASoC: qcom: add AudioReach TDM backend support Prasad Kumpatla
@ 2026-06-10 15:45 ` Prasad Kumpatla
2026-06-15 9:28 ` Srinivas Kandagatla
2026-06-10 15:45 ` [PATCH v1 2/6] ASoC: qcom: q6apm-lpass-dais: add TDM DAI operations Prasad Kumpatla
` (4 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Prasad Kumpatla @ 2026-06-10 15:45 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel,
Prasad Kumpatla
Add topology parsing and media-format programming for Audio IF
source and sink modules.
Add the Audio IF module IDs, the required topology tokens, and a
dedicated topology loader that stores the parsed interface
configuration in the AudioReach module state. Also add the Audio IF
media-format path that sends the interface configuration, hardware
endpoint media format, and frame-duration parameters for Audio IF
modules.
This keeps the serial-interface configuration topology-driven while
still allowing the machine driver to provide runtime slot and media
format settings. The same Audio IF path can then be reused for TDM,
PCM, and I2S style backends.
The new UAPI tokens (AR_TKN_U32_MODULE_SYNC_SRC=262 through
AR_TKN_U32_MODULE_INV_EXT_BIT_CLK=276) are added.
MODULE_ID_AUDIO_IF_SINK (0x0700117C) and MODULE_ID_AUDIO_IF_SOURCE
(0x0700117D) are introduced in this patch.
Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
---
include/uapi/sound/snd_ar_tokens.h | 58 ++++++++++++++++
sound/soc/qcom/qdsp6/audioreach.c | 97 ++++++++++++++++++++++++++
sound/soc/qcom/qdsp6/audioreach.h | 62 +++++++++++++++++
sound/soc/qcom/qdsp6/topology.c | 108 +++++++++++++++++++++++++++++
4 files changed, 325 insertions(+)
diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h
index 6b8102eaa..355a1e629 100644
--- a/include/uapi/sound/snd_ar_tokens.h
+++ b/include/uapi/sound/snd_ar_tokens.h
@@ -168,6 +168,48 @@ enum ar_event_types {
* LOG_WAIT = 0,
* LOG_IMMEDIATELY = 1
*
+ * %AR_TKN_U32_MODULE_SYNC_SRC: Frame sync source
+ * 0 = external, 1 = internal
+ *
+ * %AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control
+ * 0 = disable, 1 = enable
+ *
+ * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask
+ *
+ * %AR_TKN_U32_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame
+ *
+ * %AR_TKN_U32_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32)
+ *
+ * %AR_TKN_U32_MODULE_SYNC_MODE: Frame sync mode
+ * 0 = short pulse, 1 = long pulse
+ *
+ * %AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity
+ * 0 = normal, 1 = inverted
+ *
+ * %AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync
+ * 0 = no delay, 1 = one cycle delay
+ *
+ * %AR_TKN_U32_MODULE_INTF_MODE: Audio IF interface mode
+ * AUDIO_IF_INTF_MODE_TDM = 0,
+ * AUDIO_IF_INTF_MODE_PCM = 1,
+ * AUDIO_IF_INTF_MODE_I2S = 2
+ *
+ * %AR_TKN_U32_MODULE_QAIF_TYPE: QAIF hardware port type index
+ *
+ * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane
+ *
+ * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz
+ *
+ * %AR_TKN_U32_MODULE_BIT_CLK_TYPE: Bit clock type
+ * 0 = internal, 1 = external,
+ * 2 = skip (bypass bit clock enable)
+ *
+ * %AR_TKN_U32_MODULE_INV_INT_BIT_CLK: Invert internal bit clock
+ * 0 = normal, 1 = inverted
+ *
+ * %AR_TKN_U32_MODULE_INV_EXT_BIT_CLK: Invert external bit clock
+ * 0 = normal, 1 = inverted
+ *
* %AR_TKN_DAI_INDEX: dai index
*
*/
@@ -240,6 +282,22 @@ enum ar_event_types {
#define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260
#define AR_TKN_U32_MODULE_LOG_MODE 261
+#define AR_TKN_U32_MODULE_SYNC_SRC 262
+#define AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE 263
+#define AR_TKN_U32_MODULE_SLOT_MASK 264
+#define AR_TKN_U32_MODULE_NSLOTS_PER_FRAME 265
+#define AR_TKN_U32_MODULE_SLOT_WIDTH 266
+#define AR_TKN_U32_MODULE_SYNC_MODE 267
+#define AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE 268
+#define AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY 269
+#define AR_TKN_U32_MODULE_INTF_MODE 270
+#define AR_TKN_U32_MODULE_QAIF_TYPE 271
+#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272
+#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273
+#define AR_TKN_U32_MODULE_BIT_CLK_TYPE 274
+#define AR_TKN_U32_MODULE_INV_INT_BIT_CLK 275
+#define AR_TKN_U32_MODULE_INV_EXT_BIT_CLK 276
+
#define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006
struct audioreach_module_priv_data {
__le32 size; /* size in bytes of the array, including all elements */
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index e6e9eb2e8..62140ce8e 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -152,6 +152,13 @@ struct apm_i2s_module_intf_cfg {
#define APM_I2S_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_i2s_module_intf_cfg), 8)
+struct apm_audio_if_module_intf_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_audio_if_intf_cfg cfg;
+} __packed;
+
+#define APM_AUDIO_IF_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_audio_if_module_intf_cfg), 8)
+
struct apm_module_hw_ep_mf_cfg {
struct apm_module_param_data param_data;
struct param_id_hw_ep_mf mf;
@@ -168,6 +175,13 @@ struct apm_module_frame_size_factor_cfg {
#define APM_FS_CFG_PSIZE ALIGN(sizeof(struct apm_module_frame_size_factor_cfg), 8)
+struct apm_module_hw_ep_frame_duration_cfg {
+ struct apm_module_param_data param_data;
+ struct param_id_hw_ep_frame_duration frame_duration;
+} __packed;
+
+#define APM_HW_EP_FRAME_DURATION_PSIZE ALIGN(sizeof(struct apm_module_hw_ep_frame_duration_cfg), 8)
+
struct apm_module_hw_ep_power_mode_cfg {
struct apm_module_param_data param_data;
struct param_id_hw_ep_power_mode_cfg power_mode;
@@ -1042,6 +1056,85 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph,
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
+static int audioreach_audio_if_set_media_format(struct q6apm_graph *graph,
+ const struct audioreach_module *module,
+ const struct audioreach_module_config *cfg)
+{
+ struct apm_module_hw_ep_frame_duration_cfg *fd_cfg;
+ struct apm_module_param_data *param_data;
+ struct apm_audio_if_module_intf_cfg *intf_cfg;
+ struct apm_module_hw_ep_mf_cfg *hw_cfg;
+ int ic_sz = APM_AUDIO_IF_INTF_CFG_PSIZE;
+ int ep_sz = APM_HW_EP_CFG_PSIZE;
+ int fd_sz = APM_HW_EP_FRAME_DURATION_PSIZE;
+ int size = ic_sz + ep_sz + fd_sz;
+ /*
+ * A zero machine override means use the topology default; zero is not
+ * a valid override for these TDM slot fields.
+ */
+ u32 slot_mask = cfg->slot_mask ? cfg->slot_mask : module->slot_mask;
+ u16 nslots_per_frame = cfg->nslots_per_frame ?
+ (u16)cfg->nslots_per_frame : module->nslots_per_frame;
+ u16 slot_width = cfg->slot_width ? (u16)cfg->slot_width : module->slot_width;
+ void *p;
+
+ struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
+ if (IS_ERR(pkt))
+ return PTR_ERR(pkt);
+
+ p = (void *)pkt + GPR_HDR_SIZE + APM_CMD_HDR_SIZE;
+ intf_cfg = p;
+
+ param_data = &intf_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_AUDIO_IF_INTF_CFG;
+ param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE;
+ intf_cfg->cfg.qaif_type = module->qaif_type;
+ intf_cfg->cfg.intf_idx = (u16)module->hw_interface_idx;
+ intf_cfg->cfg.intf_mode = module->intf_mode;
+ intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable;
+ intf_cfg->cfg.active_slot_mask = slot_mask;
+ intf_cfg->cfg.nslots_per_frame = nslots_per_frame;
+ intf_cfg->cfg.slot_width = slot_width;
+ intf_cfg->cfg.active_lane_mask = module->active_lane_mask;
+ intf_cfg->cfg.frame_sync_rate = module->frame_sync_rate;
+ intf_cfg->cfg.frame_sync_src = module->sync_src;
+ intf_cfg->cfg.frame_sync_mode = module->sync_mode;
+ intf_cfg->cfg.invert_frame_sync_pulse = module->ctrl_invert_sync_pulse;
+ intf_cfg->cfg.frame_sync_data_delay = module->ctrl_sync_data_delay;
+ intf_cfg->cfg.bit_clk_type = module->bit_clk_type;
+ intf_cfg->cfg.inv_int_bit_clk = module->inv_int_bit_clk;
+ intf_cfg->cfg.inv_ext_bit_clk = module->inv_ext_bit_clk;
+
+ p += ic_sz;
+ hw_cfg = p;
+ param_data = &hw_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_MF_CFG;
+ param_data->param_size = ep_sz - APM_MODULE_PARAM_DATA_SIZE;
+
+ hw_cfg->mf.sample_rate = cfg->sample_rate;
+ hw_cfg->mf.bit_width = cfg->bit_width;
+ hw_cfg->mf.num_channels = cfg->num_channels;
+ hw_cfg->mf.data_format = module->data_format;
+
+ p += ep_sz;
+ fd_cfg = p;
+ param_data = &fd_cfg->param_data;
+ param_data->module_instance_id = module->instance_id;
+ param_data->error_code = 0;
+ param_data->param_id = PARAM_ID_HW_EP_FRAME_DURATION;
+ param_data->param_size = fd_sz - APM_MODULE_PARAM_DATA_SIZE;
+ fd_cfg->frame_duration.frame_duration_in_us = AUDIO_IF_FRAME_DURATION_US;
+ fd_cfg->frame_duration.allow_frame_duration_normalization = 1;
+ fd_cfg->frame_duration.min_normalized_frame_dur_us = 1;
+ fd_cfg->frame_duration.max_normalized_frame_dur_us = 100000;
+
+ return q6apm_send_cmd_sync(graph->apm, pkt, 0);
+}
+
static int audioreach_logging_set_media_format(struct q6apm_graph *graph,
const struct audioreach_module *module)
{
@@ -1411,6 +1504,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph,
if (!rc)
rc = audioreach_module_enable(graph, module, true);
break;
+ case MODULE_ID_AUDIO_IF_SOURCE:
+ case MODULE_ID_AUDIO_IF_SINK:
+ rc = audioreach_audio_if_set_media_format(graph, module, cfg);
+ break;
default:
rc = 0;
diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
index 62a2fd79b..1dc29ddfd 100644
--- a/sound/soc/qcom/qdsp6/audioreach.h
+++ b/sound/soc/qcom/qdsp6/audioreach.h
@@ -22,6 +22,8 @@ struct q6apm_graph;
#define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
#define MODULE_ID_I2S_SINK 0x0700100A
#define MODULE_ID_I2S_SOURCE 0x0700100B
+#define MODULE_ID_AUDIO_IF_SINK 0x0700117C
+#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D
#define MODULE_ID_SAL 0x07001010
#define MODULE_ID_MFC 0x07001015
#define MODULE_ID_DATA_LOGGING 0x0700101A
@@ -544,6 +546,41 @@ struct param_id_i2s_intf_cfg {
#define PORT_ID_I2S_OUPUT 1
#define I2S_STACK_SIZE 2048
+#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11
+
+#define AUDIO_IF_INTF_MODE_TDM 0x0
+#define AUDIO_IF_INTF_MODE_PCM 0x1
+#define AUDIO_IF_INTF_MODE_I2S 0x2
+
+struct param_id_audio_if_intf_cfg {
+ uint16_t qaif_type;
+ uint16_t intf_idx;
+ uint16_t intf_mode;
+ uint16_t ctrl_data_out_enable;
+ uint32_t active_slot_mask;
+ uint16_t nslots_per_frame;
+ uint16_t slot_width;
+ uint32_t active_lane_mask;
+ uint32_t frame_sync_rate;
+ uint16_t frame_sync_src;
+ uint16_t frame_sync_mode;
+ uint16_t invert_frame_sync_pulse;
+ uint16_t frame_sync_data_delay;
+ uint16_t bit_clk_type;
+ uint8_t inv_int_bit_clk;
+ uint8_t inv_ext_bit_clk;
+} __packed;
+
+#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F
+#define AUDIO_IF_FRAME_DURATION_US 1000
+
+struct param_id_hw_ep_frame_duration {
+ uint32_t frame_duration_in_us;
+ uint32_t allow_frame_duration_normalization;
+ uint32_t min_normalized_frame_dur_us;
+ uint32_t max_normalized_frame_dur_us;
+} __packed;
+
#define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154
struct param_id_display_port_intf_cfg {
@@ -877,6 +914,28 @@ struct audioreach_module {
uint32_t data_format;
uint32_t hw_interface_type;
+ /* Audio IF module (TDM/PCM/I2S) */
+ /*
+ * uint32_t fields first to minimise intra-block padding;
+ * 2 bytes of trailing padding remain after inv_ext_bit_clk
+ * before the next uint32_t field (interleave_type).
+ */
+ uint32_t slot_mask;
+ uint32_t active_lane_mask;
+ uint32_t frame_sync_rate;
+ uint16_t qaif_type;
+ uint16_t sync_src;
+ uint16_t ctrl_data_out_enable;
+ uint16_t nslots_per_frame;
+ uint16_t slot_width;
+ uint16_t intf_mode;
+ uint16_t sync_mode;
+ uint16_t ctrl_invert_sync_pulse;
+ uint16_t ctrl_sync_data_delay;
+ uint16_t bit_clk_type;
+ uint8_t inv_int_bit_clk;
+ uint8_t inv_ext_bit_clk;
+
/* PCM module specific */
uint32_t interleave_type;
@@ -907,6 +966,9 @@ struct audioreach_module_config {
u32 channel_allocation;
u32 sd_line_mask;
int fmt;
+ u32 slot_mask;
+ u16 nslots_per_frame;
+ u16 slot_width;
struct snd_codec codec;
u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
};
diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
index 1f69fba6d..2ae7ac3d2 100644
--- a/sound/soc/qcom/qdsp6/topology.c
+++ b/sound/soc/qcom/qdsp6/topology.c
@@ -753,6 +753,108 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
return 0;
}
+static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod,
+ const struct snd_soc_tplg_vendor_array *mod_array)
+{
+ const struct snd_soc_tplg_vendor_value_elem *mod_elem;
+ int tkn_count = 0;
+ u32 val;
+
+ mod_elem = mod_array->value;
+
+ while (tkn_count < le32_to_cpu(mod_array->num_elems)) {
+ val = le32_to_cpu(mod_elem->value);
+ switch (le32_to_cpu(mod_elem->token)) {
+ case AR_TKN_U32_MODULE_HW_IF_IDX:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->hw_interface_idx = val;
+ break;
+ case AR_TKN_U32_MODULE_FMT_DATA:
+ mod->data_format = val;
+ break;
+ case AR_TKN_U32_MODULE_HW_IF_TYPE:
+ mod->hw_interface_type = val;
+ break;
+ case AR_TKN_U32_MODULE_SYNC_SRC:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->sync_src = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->ctrl_data_out_enable = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_SLOT_MASK:
+ mod->slot_mask = val;
+ break;
+ case AR_TKN_U32_MODULE_NSLOTS_PER_FRAME:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->nslots_per_frame = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_SLOT_WIDTH:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->slot_width = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_INTF_MODE:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->intf_mode = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_SYNC_MODE:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->sync_mode = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->ctrl_invert_sync_pulse = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->ctrl_sync_data_delay = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_QAIF_TYPE:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->qaif_type = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK:
+ mod->active_lane_mask = val;
+ break;
+ case AR_TKN_U32_MODULE_FRAME_SYNC_RATE:
+ mod->frame_sync_rate = val;
+ break;
+ case AR_TKN_U32_MODULE_BIT_CLK_TYPE:
+ if (val > U16_MAX)
+ return -EINVAL;
+ mod->bit_clk_type = (u16)val;
+ break;
+ case AR_TKN_U32_MODULE_INV_INT_BIT_CLK:
+ if (val > U8_MAX)
+ return -EINVAL;
+ mod->inv_int_bit_clk = (u8)val;
+ break;
+ case AR_TKN_U32_MODULE_INV_EXT_BIT_CLK:
+ if (val > U8_MAX)
+ return -EINVAL;
+ mod->inv_ext_bit_clk = (u8)val;
+ break;
+ default:
+ break;
+ }
+ tkn_count++;
+ mod_elem++;
+ }
+
+ return 0;
+}
+
static int audioreach_widget_dp_module_load(struct audioreach_module *mod,
const struct snd_soc_tplg_vendor_array *mod_array)
{
@@ -806,6 +908,12 @@ static int audioreach_widget_load_buffer(struct snd_soc_component *component,
case MODULE_ID_I2S_SOURCE:
audioreach_widget_i2s_module_load(mod, mod_array);
break;
+ case MODULE_ID_AUDIO_IF_SINK:
+ case MODULE_ID_AUDIO_IF_SOURCE:
+ ret = audioreach_widget_audio_if_module_load(mod, mod_array);
+ if (ret)
+ return ret;
+ break;
case MODULE_ID_DISPLAY_PORT_SINK:
audioreach_widget_dp_module_load(mod, mod_array);
break;
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v1 1/6] ASoC: qcom: qdsp6: add topology-driven Audio IF support
2026-06-10 15:45 ` [PATCH v1 1/6] ASoC: qcom: qdsp6: add topology-driven Audio IF support Prasad Kumpatla
@ 2026-06-15 9:28 ` Srinivas Kandagatla
0 siblings, 0 replies; 9+ messages in thread
From: Srinivas Kandagatla @ 2026-06-15 9:28 UTC (permalink / raw)
To: Prasad Kumpatla, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel
On 6/10/26 4:45 PM, Prasad Kumpatla wrote:
> Add topology parsing and media-format programming for Audio IF
> source and sink modules.
>
> Add the Audio IF module IDs, the required topology tokens, and a
> dedicated topology loader that stores the parsed interface
> configuration in the AudioReach module state. Also add the Audio IF
> media-format path that sends the interface configuration, hardware
> endpoint media format, and frame-duration parameters for Audio IF
> modules.
>
> This keeps the serial-interface configuration topology-driven while
> still allowing the machine driver to provide runtime slot and media
> format settings. The same Audio IF path can then be reused for TDM,
> PCM, and I2S style backends.
>
> The new UAPI tokens (AR_TKN_U32_MODULE_SYNC_SRC=262 through
> AR_TKN_U32_MODULE_INV_EXT_BIT_CLK=276) are added.
>
> MODULE_ID_AUDIO_IF_SINK (0x0700117C) and MODULE_ID_AUDIO_IF_SOURCE
> (0x0700117D) are introduced in this patch.
>
Which platform is this tested on, also please send a PR to
github.com/linux-msm/audioreach-topology to add thse new tokens.
> Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
> ---
> include/uapi/sound/snd_ar_tokens.h | 58 ++++++++++++++++
> sound/soc/qcom/qdsp6/audioreach.c | 97 ++++++++++++++++++++++++++
> sound/soc/qcom/qdsp6/audioreach.h | 62 +++++++++++++++++
> sound/soc/qcom/qdsp6/topology.c | 108 +++++++++++++++++++++++++++++
> 4 files changed, 325 insertions(+)
>
> diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h
> index 6b8102eaa..355a1e629 100644
> --- a/include/uapi/sound/snd_ar_tokens.h
> +++ b/include/uapi/sound/snd_ar_tokens.h
> @@ -168,6 +168,48 @@ enum ar_event_types {
> * LOG_WAIT = 0,
> * LOG_IMMEDIATELY = 1
> *
> + * %AR_TKN_U32_MODULE_SYNC_SRC: Frame sync source
> + * 0 = external, 1 = internal
> + *
> + * %AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control
> + * 0 = disable, 1 = enable
> + *
> + * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask
> + *
> + * %AR_TKN_U32_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame
> + *
> + * %AR_TKN_U32_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32)
> + *
> + * %AR_TKN_U32_MODULE_SYNC_MODE: Frame sync mode
> + * 0 = short pulse, 1 = long pulse
We have 3 possible values, please correct this, also you could add
defines for these values.
> + *
> + * %AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity
> + * 0 = normal, 1 = inverted
> + *
> + * %AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync
> + * 0 = no delay, 1 = one cycle delay
Exactly same here, we have 2 cyle delay too.
> + *
> + * %AR_TKN_U32_MODULE_INTF_MODE: Audio IF interface mode
> + * AUDIO_IF_INTF_MODE_TDM = 0,
> + * AUDIO_IF_INTF_MODE_PCM = 1,
> + * AUDIO_IF_INTF_MODE_I2S = 2
Same here, defines for these.
> + *
> + * %AR_TKN_U32_MODULE_QAIF_TYPE: QAIF hardware port type index
> + *
> + * %AR_TKN_U32_MODULE_ACTIVE_LANE_MASK: Active lane bitmask for multi-lane
> + *
> + * %AR_TKN_U32_MODULE_FRAME_SYNC_RATE: Frame sync rate in Hz
> + *
> + * %AR_TKN_U32_MODULE_BIT_CLK_TYPE: Bit clock type
> + * 0 = internal, 1 = external,
> + * 2 = skip (bypass bit clock enable)
> + *
> + * %AR_TKN_U32_MODULE_INV_INT_BIT_CLK: Invert internal bit clock
> + * 0 = normal, 1 = inverted
> + *
> + * %AR_TKN_U32_MODULE_INV_EXT_BIT_CLK: Invert external bit clock
> + * 0 = normal, 1 = inverted
> + *
> * %AR_TKN_DAI_INDEX: dai index
> *
> */
> @@ -240,6 +282,22 @@ enum ar_event_types {
> #define AR_TKN_U32_MODULE_LOG_TAP_POINT_ID 260
> #define AR_TKN_U32_MODULE_LOG_MODE 261
>
> +#define AR_TKN_U32_MODULE_SYNC_SRC 262
> +#define AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE 263
> +#define AR_TKN_U32_MODULE_SLOT_MASK 264
> +#define AR_TKN_U32_MODULE_NSLOTS_PER_FRAME 265
> +#define AR_TKN_U32_MODULE_SLOT_WIDTH 266
> +#define AR_TKN_U32_MODULE_SYNC_MODE 267
> +#define AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE 268
> +#define AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY 269
> +#define AR_TKN_U32_MODULE_INTF_MODE 270
> +#define AR_TKN_U32_MODULE_QAIF_TYPE 271
> +#define AR_TKN_U32_MODULE_ACTIVE_LANE_MASK 272
> +#define AR_TKN_U32_MODULE_FRAME_SYNC_RATE 273
> +#define AR_TKN_U32_MODULE_BIT_CLK_TYPE 274
> +#define AR_TKN_U32_MODULE_INV_INT_BIT_CLK 275
> +#define AR_TKN_U32_MODULE_INV_EXT_BIT_CLK 276
> +
Here you prefix the tokens with U32, however in dirver this values are
validated against U8 and U16, So please fix the prefixes to reflect the
range.
...
>
> default:
> rc = 0;
> diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h
> index 62a2fd79b..1dc29ddfd 100644
> --- a/sound/soc/qcom/qdsp6/audioreach.h
> +++ b/sound/soc/qcom/qdsp6/audioreach.h
> @@ -22,6 +22,8 @@ struct q6apm_graph;
> #define MODULE_ID_PLACEHOLDER_DECODER 0x07001009
> #define MODULE_ID_I2S_SINK 0x0700100A
> #define MODULE_ID_I2S_SOURCE 0x0700100B
> +#define MODULE_ID_AUDIO_IF_SINK 0x0700117C
> +#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D
Please place it in the assending order.
> #define MODULE_ID_SAL 0x07001010
> #define MODULE_ID_MFC 0x07001015
> #define MODULE_ID_DATA_LOGGING 0x0700101A
> @@ -544,6 +546,41 @@ struct param_id_i2s_intf_cfg {
> #define PORT_ID_I2S_OUPUT 1
> #define I2S_STACK_SIZE 2048
>
> +#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11
> +
> +#define AUDIO_IF_INTF_MODE_TDM 0x0
> +#define AUDIO_IF_INTF_MODE_PCM 0x1
> +#define AUDIO_IF_INTF_MODE_I2S 0x2
> +
> +struct param_id_audio_if_intf_cfg {
I know that we have not added documentation for all the structures, but
Am in process of adding them. Can you add kernel doc to these structs.
> + uint16_t qaif_type;
> + uint16_t intf_idx;
> + uint16_t intf_mode;
> + uint16_t ctrl_data_out_enable;
> + uint32_t active_slot_mask;
> + uint16_t nslots_per_frame;
> + uint16_t slot_width;
> + uint32_t active_lane_mask;
> + uint32_t frame_sync_rate;
> + uint16_t frame_sync_src;
> + uint16_t frame_sync_mode;
> + uint16_t invert_frame_sync_pulse;
> + uint16_t frame_sync_data_delay;
> + uint16_t bit_clk_type;
> + uint8_t inv_int_bit_clk;
> + uint8_t inv_ext_bit_clk;
> +} __packed;
> +
> +#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F
> +#define AUDIO_IF_FRAME_DURATION_US 1000
Why is this hardcoded?
> +
> +struct param_id_hw_ep_frame_duration {
> + uint32_t frame_duration_in_us;
> + uint32_t allow_frame_duration_normalization;
> + uint32_t min_normalized_frame_dur_us;
> + uint32_t max_normalized_frame_dur_us;
> +} __packed;
> +
> #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154
>
> struct param_id_display_port_intf_cfg {
> @@ -877,6 +914,28 @@ struct audioreach_module {
> uint32_t data_format;
> uint32_t hw_interface_type;
>
> + /* Audio IF module (TDM/PCM/I2S) */
> + /*
> + * uint32_t fields first to minimise intra-block padding;
Why do we need this comments does not add a real value here?
> + * 2 bytes of trailing padding remain after inv_ext_bit_clk
> + * before the next uint32_t field (interleave_type).
> + */
> + uint32_t slot_mask;
> + uint32_t active_lane_mask;
> + uint32_t frame_sync_rate;
> + uint16_t qaif_type;
> + uint16_t sync_src;
> + uint16_t ctrl_data_out_enable;
> + uint16_t nslots_per_frame;
> + uint16_t slot_width;
> + uint16_t intf_mode;
> + uint16_t sync_mode;
> + uint16_t ctrl_invert_sync_pulse;
> + uint16_t ctrl_sync_data_delay;
> + uint16_t bit_clk_type;
> + uint8_t inv_int_bit_clk;
> + uint8_t inv_ext_bit_clk;
> +
> /* PCM module specific */
> uint32_t interleave_type;
>
> @@ -907,6 +966,9 @@ struct audioreach_module_config {
> u32 channel_allocation;
> u32 sd_line_mask;
> int fmt;
> + u32 slot_mask;
> + u16 nslots_per_frame;
> + u16 slot_width;
> struct snd_codec codec;
> u8 channel_map[AR_PCM_MAX_NUM_CHANNEL];
> };
> diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c
> index 1f69fba6d..2ae7ac3d2 100644
> --- a/sound/soc/qcom/qdsp6/topology.c
> +++ b/sound/soc/qcom/qdsp6/topology.c
> @@ -753,6 +753,108 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod,
> return 0;
> }
>
> +static int audioreach_widget_audio_if_module_load(struct audioreach_module *mod,
> + const struct snd_soc_tplg_vendor_array *mod_array)
> +{
> + const struct snd_soc_tplg_vendor_value_elem *mod_elem;
> + int tkn_count = 0;
> + u32 val;
> +
> + mod_elem = mod_array->value;
> +
> + while (tkn_count < le32_to_cpu(mod_array->num_elems)) {
> + val = le32_to_cpu(mod_elem->value);
> + switch (le32_to_cpu(mod_elem->token)) {
> + case AR_TKN_U32_MODULE_HW_IF_IDX:
> + if (val > U16_MAX)
> + return -EINVAL;
Plese fix such instances as suggested at the top.
> + mod->hw_interface_idx = val;
> + break;
> + case AR_TKN_U32_MODULE_FMT_DATA:
> + mod->data_format = val;
> + break;
> + case AR_TKN_U32_MODULE_HW_IF_TYPE:
> + mod->hw_interface_type = val;
where are we using this?
> + break;
> + case AR_TKN_U32_MODULE_SYNC_SRC:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->sync_src = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->ctrl_data_out_enable = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_SLOT_MASK:
> + mod->slot_mask = val;
> + break;
> + case AR_TKN_U32_MODULE_NSLOTS_PER_FRAME:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->nslots_per_frame = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_SLOT_WIDTH:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->slot_width = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_INTF_MODE:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->intf_mode = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_SYNC_MODE:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->sync_mode = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->ctrl_invert_sync_pulse = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->ctrl_sync_data_delay = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_QAIF_TYPE:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->qaif_type = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_ACTIVE_LANE_MASK:
> + mod->active_lane_mask = val;
> + break;
> + case AR_TKN_U32_MODULE_FRAME_SYNC_RATE:
> + mod->frame_sync_rate = val;
> + break;
> + case AR_TKN_U32_MODULE_BIT_CLK_TYPE:
> + if (val > U16_MAX)
> + return -EINVAL;
> + mod->bit_clk_type = (u16)val;
> + break;
> + case AR_TKN_U32_MODULE_INV_INT_BIT_CLK:
> + if (val > U8_MAX)
> + return -EINVAL;
> + mod->inv_int_bit_clk = (u8)val;
> + break;
> + case AR_TKN_U32_MODULE_INV_EXT_BIT_CLK:
> + if (val > U8_MAX)
> + return -EINVAL;
> + mod->inv_ext_bit_clk = (u8)val;
> + break;
> + default:
> + break;
> + }
> + tkn_count++;
> + mod_elem++;
> + }
> +
> + return 0;
> +}
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 2/6] ASoC: qcom: q6apm-lpass-dais: add TDM DAI operations
2026-06-10 15:45 [PATCH v1 0/6] ASoC: qcom: add AudioReach TDM backend support Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 1/6] ASoC: qcom: qdsp6: add topology-driven Audio IF support Prasad Kumpatla
@ 2026-06-10 15:45 ` Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 3/6] dt-bindings: sound: qcom,q6dsp-lpass-ports: add Audio IF clocks Prasad Kumpatla
` (3 subsequent siblings)
5 siblings, 0 replies; 9+ messages in thread
From: Prasad Kumpatla @ 2026-06-10 15:45 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel,
Prasad Kumpatla
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.
Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
---
sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 64 ++++++++++++++++++++++++-
1 file changed, 63 insertions(+), 1 deletion(-)
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index 143750afb..d07b2d751 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -336,6 +336,55 @@ 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;
+
+ 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:
+ 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;
+ }
+
+ 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;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dai_ops q6dma_ops = {
.prepare = q6apm_lpass_dai_prepare,
.startup = q6apm_lpass_dai_startup,
@@ -365,6 +414,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,
@@ -390,9 +450,10 @@ static int of_q6apm_parse_dai_data(struct device *dev,
}
switch (id) {
- /* MI2S specific properties */
+ /* MI2S/TDM child clocks */
case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
case QUINARY_MI2S_RX ... QUINARY_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)) {
@@ -448,6 +509,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);
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v1 3/6] dt-bindings: sound: qcom,q6dsp-lpass-ports: add Audio IF clocks
2026-06-10 15:45 [PATCH v1 0/6] ASoC: qcom: add AudioReach TDM backend support Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 1/6] ASoC: qcom: qdsp6: add topology-driven Audio IF support Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 2/6] ASoC: qcom: q6apm-lpass-dais: add TDM DAI operations Prasad Kumpatla
@ 2026-06-10 15:45 ` Prasad Kumpatla
2026-06-11 8:59 ` Krzysztof Kozlowski
2026-06-10 15:45 ` [PATCH v1 4/6] ASoC: qcom: q6prm: add Audio IF clock IDs Prasad Kumpatla
` (2 subsequent siblings)
5 siblings, 1 reply; 9+ messages in thread
From: Prasad Kumpatla @ 2026-06-10 15:45 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel,
Prasad Kumpatla
Add the LPASS Audio IF clock IDs used by newer backend interfaces.
Platforms using Audio IF module backends request the interface bit
clocks through q6prm. Add the Audio IF IBIT and EBIT IDs to the binding
header so these clocks can be referenced from device trees.
Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
---
.../sound/qcom,q6dsp-lpass-ports.h | 57 +++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
index 45850f2d4..bc860fcbf 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
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH v1 3/6] dt-bindings: sound: qcom,q6dsp-lpass-ports: add Audio IF clocks
2026-06-10 15:45 ` [PATCH v1 3/6] dt-bindings: sound: qcom,q6dsp-lpass-ports: add Audio IF clocks Prasad Kumpatla
@ 2026-06-11 8:59 ` Krzysztof Kozlowski
0 siblings, 0 replies; 9+ messages in thread
From: Krzysztof Kozlowski @ 2026-06-11 8:59 UTC (permalink / raw)
To: Prasad Kumpatla, Liam Girdwood, Mark Brown, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Jaroslav Kysela, Takashi Iwai,
Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel
On 10/06/2026 17:45, Prasad Kumpatla wrote:
> Add the LPASS Audio IF clock IDs used by newer backend interfaces.
>
> Platforms using Audio IF module backends request the interface bit
> clocks through q6prm. Add the Audio IF IBIT and EBIT IDs to the binding
> header so these clocks can be referenced from device trees.
>
> Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
> ---
> .../sound/qcom,q6dsp-lpass-ports.h | 57 +++++++++++++++++++
> 1 file changed, 57 insertions(+)
>
> diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
> index 45850f2d4..bc860fcbf 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
Missing indent
> +/** Clock ID of the Audio Intf 0 external bit clock (EBIT). */
This is not kerneldoc. Please do not introduce your own style.
> +#define LPASS_CLK_ID_AUD_INTF0_EBIT 72
Why everything has "AUD" middle prefix? What is Audio IF and how does it
differ from Audio on this device? IOW, Why Audio has to be specified? Is
there non-Audio block?
> +/** Clock ID of the Audio Intf 1 internal bit clock (IBIT). */
All these comments are pointless - you repeat the define name. Explain
once what is ibit, ebit etc, not every time.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v1 4/6] ASoC: qcom: q6prm: add Audio IF clock IDs
2026-06-10 15:45 [PATCH v1 0/6] ASoC: qcom: add AudioReach TDM backend support Prasad Kumpatla
` (2 preceding siblings ...)
2026-06-10 15:45 ` [PATCH v1 3/6] dt-bindings: sound: qcom,q6dsp-lpass-ports: add Audio IF clocks Prasad Kumpatla
@ 2026-06-10 15:45 ` Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 5/6] ASoC: qcom: common: add DAI-node TDM slot helpers Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 6/6] ASoC: qcom: sc8280xp: add TDM hw_params support Prasad Kumpatla
5 siblings, 0 replies; 9+ messages in thread
From: Prasad Kumpatla @ 2026-06-10 15:45 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel,
Prasad Kumpatla
Add the q6prm clock table entries and internal DSP clock IDs for LPASS
Audio IF backend clocks.
The public binding IDs map to q6prm DSP clock IDs starting at 0x500 for
Audio IF0 IBIT/EBIT. Add the internal definitions and register all Audio
IF IBIT and EBIT clocks so machine drivers can request them through the
APM clock controller.
Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
---
sound/soc/qcom/qdsp6/q6prm-clocks.c | 28 ++++++++++++++++++++++++++++
sound/soc/qcom/qdsp6/q6prm.h | 29 +++++++++++++++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c
index 51b131fa9..b6755da6a 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 7b751486c..c1838d80c 100644
--- a/sound/soc/qcom/qdsp6/q6prm.h
+++ b/sound/soc/qcom/qdsp6/q6prm.h
@@ -98,6 +98,35 @@
/* Clock ID for RX CORE MCLK2 2X MCLK */
#define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF0_IBIT 0x500
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF0_EBIT 0x501
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF1_IBIT 0x502
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF1_EBIT 0x503
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF2_IBIT 0x504
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF2_EBIT 0x505
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF3_IBIT 0x506
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF3_EBIT 0x507
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF4_IBIT 0x508
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF4_EBIT 0x509
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF5_IBIT 0x50A
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF5_EBIT 0x50B
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF6_IBIT 0x50C
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF6_EBIT 0x50D
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF7_IBIT 0x50E
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF7_EBIT 0x50F
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF8_IBIT 0x510
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF8_EBIT 0x511
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF9_IBIT 0x512
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF9_EBIT 0x513
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF10_IBIT 0x514
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF10_EBIT 0x515
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF11_IBIT 0x516
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF11_EBIT 0x517
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF12_IBIT 0x518
+#define Q6PRM_LPASS_CLK_ID_AUD_INTF12_EBIT 0x519
+#define Q6PRM_LPASS_CLK_ID_AUD_VA_INTF0_IBIT 0x550
+#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
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v1 5/6] ASoC: qcom: common: add DAI-node TDM slot helpers
2026-06-10 15:45 [PATCH v1 0/6] ASoC: qcom: add AudioReach TDM backend support Prasad Kumpatla
` (3 preceding siblings ...)
2026-06-10 15:45 ` [PATCH v1 4/6] ASoC: qcom: q6prm: add Audio IF clock IDs Prasad Kumpatla
@ 2026-06-10 15:45 ` Prasad Kumpatla
2026-06-10 15:45 ` [PATCH v1 6/6] ASoC: qcom: sc8280xp: add TDM hw_params support Prasad Kumpatla
5 siblings, 0 replies; 9+ messages in thread
From: Prasad Kumpatla @ 2026-06-10 15:45 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel,
Prasad Kumpatla
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 helpers parse endpoint masks, validate the shared slot count and
slot width, and program CPU and codec DAIs with the resulting slot
configuration. A cfg-based apply helper is provided for callers that
already parsed the DT data and want to avoid a second DT traversal.
Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
---
sound/soc/qcom/common.c | 164 ++++++++++++++++++++++++++++++++++++++++
sound/soc/qcom/common.h | 14 ++++
2 files changed, 178 insertions(+)
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index cf1f3a767..5ca720ecf 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -23,6 +23,170 @@ 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_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;
+ int i;
+ int 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)
+ 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)
+ 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)
{
struct device_node *np;
diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
index ee6662885..02b24caec 100644
--- a/sound/soc/qcom/common.h
+++ b/sound/soc/qcom/common.h
@@ -9,7 +9,21 @@
#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_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);
int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH v1 6/6] ASoC: qcom: sc8280xp: add TDM hw_params support
2026-06-10 15:45 [PATCH v1 0/6] ASoC: qcom: add AudioReach TDM backend support Prasad Kumpatla
` (4 preceding siblings ...)
2026-06-10 15:45 ` [PATCH v1 5/6] ASoC: qcom: common: add DAI-node TDM slot helpers Prasad Kumpatla
@ 2026-06-10 15:45 ` Prasad Kumpatla
5 siblings, 0 replies; 9+ messages in thread
From: Prasad Kumpatla @ 2026-06-10 15:45 UTC (permalink / raw)
To: Liam Girdwood, Mark Brown, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Jaroslav Kysela, Takashi Iwai, Srinivas Kandagatla
Cc: linux-arm-msm, linux-sound, devicetree, linux-kernel,
Prasad Kumpatla
Add 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 backend CPU and codec endpoints. Reuse the
parsed configuration when programming DAIs so hw_params does not need a
second DT traversal.
Derive the LPASS backend bit clock from the runtime TDM parameters and
request it through the backend child-clock path using LPAIF_MI2S_BCLK.
Program codec sysclk in hw_params so codec PLL setup happens before the
stream is triggered.
Signed-off-by: Prasad Kumpatla <prasad.kumpatla@oss.qualcomm.com>
---
sound/soc/qcom/sc8280xp.c | 59 +++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 1f3afc6d0..e5d23e244 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -83,6 +83,63 @@ static inline int sc8280xp_get_bclk_freq(struct snd_pcm_hw_params *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;
+ 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 = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
+ if (ret)
+ return ret;
+
+ ret = qcom_snd_apply_dai_tdm_slots_cfg(rtd, &cpu_cfg, &codec_cfg);
+ 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;
+
+ if (data->snd_soc_common_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;
+ }
+ }
+
+ if (data->snd_soc_common_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;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
{
struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
@@ -186,6 +243,8 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
0, mclk_freq,
SND_SOC_CLOCK_IN);
break;
+ case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ return sc8280xp_tdm_hw_params(substream, params);
default:
break;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 9+ messages in thread