From 1fd69cbb48cbb6e3ab187e08a1fd5dfbb408a45f Mon Sep 17 00:00:00 2001 From: Pratyush Meduri Date: Tue, 26 May 2026 12:36:18 +0530 Subject: [PATCH 1/2] uapi: sound: add audioreach token IDs Add the missing AudioReach token IDs used by the Shikra QAIF/TDM control path in topology parsing and module configuration. These additions extend the existing token namespace without changing existing token values. Signed-off-by: Pratyush Meduri --- include/uapi/sound/snd_ar_tokens.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 6b8102eaa121f..6cd84768a9f99 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -239,6 +239,15 @@ enum ar_event_types { #define AR_TKN_U32_MODULE_LOG_CODE 259 #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_RESERVED 270 #define SND_SOC_AR_TPLG_MODULE_CFG_TYPE 0x01001006 struct audioreach_module_priv_data { From 3ea3b1b95936d4bd41ce8b38f4f0d04d317f8aca Mon Sep 17 00:00:00 2001 From: Pratyush Meduri Date: Tue, 26 May 2026 12:36:19 +0530 Subject: [PATCH 2/2] ASoC: qcom: qdsp6: add Shikra AudioReach and LPASS DAI support Add Shikra AudioReach plumbing in qdsp6, including new LPASS port IDs, AUDIO_IF/TDM token handling, and LPASS DAI integration for q6apm graphs. The update wires the new q6tdm dai ops, extends topology parsing for AUDIO_IF/TDM parameters, and keeps runtime graph/media-format handling in line with existing q6apm flows. Signed-off-by: Pratyush Meduri --- sound/soc/qcom/qdsp6/audioreach.c | 185 +++++++++++++++++++++- sound/soc/qcom/qdsp6/audioreach.h | 89 +++++++++++ sound/soc/qcom/qdsp6/q6apm-dai.c | 170 +++++++++++++++++++- sound/soc/qcom/qdsp6/q6apm-lpass-dais.c | 69 ++++++++ sound/soc/qcom/qdsp6/q6apm.c | 2 +- sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c | 2 +- sound/soc/qcom/qdsp6/q6prm-clocks.c | 28 ++++ sound/soc/qcom/qdsp6/q6prm.h | 58 +++++++ sound/soc/qcom/qdsp6/topology.c | 66 ++++++++ 9 files changed, 663 insertions(+), 6 deletions(-) diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index a13f753eff98c..6ec2e073ca4b5 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; @@ -248,7 +255,7 @@ static void *__audioreach_alloc_pkt(int payload_size, uint32_t opcode, uint32_t pkt->hdr.dest_port = dest_port; pkt->hdr.src_port = src_port; - pkt->hdr.dest_domain = GPR_DOMAIN_ID_ADSP; + pkt->hdr.dest_domain = GPR_DOMAIN_ID_MODEM; pkt->hdr.src_domain = GPR_DOMAIN_ID_APPS; pkt->hdr.token = token; pkt->hdr.opcode = opcode; @@ -1042,6 +1049,117 @@ 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 param_id_hw_ep_frame_duration *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 = ALIGN(sizeof(struct apm_module_param_data) + + sizeof(struct param_id_hw_ep_frame_duration), 8); + int size = ic_sz + ep_sz + fd_sz; + void *p; + + /* + * AUDIO_IF (QAIF) sends three params in one APM_CMD_SET_CFG: + * 1. PARAM_ID_AUDIO_IF_INTF_CFG (0x08001B11) - interface config + * 2. PARAM_ID_HW_EP_MF_CFG (0x08001017) - media format + * 3. PARAM_ID_HW_EP_FRAME_DURATION (0x08001B2F) - frame duration + * + * NOTE: AUDIO_IF does NOT support PARAM_ID_HW_EP_FRAME_SIZE_FACTOR + * (0x08001018) - using it causes ADSP_EUNSUPPORTED (3) on Hawi. + */ + 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; + + /* Interface config from topology binary tokens */ + intf_cfg->cfg.qaif_type = 0; /* QAIF */ + intf_cfg->cfg.intf_idx = 2; + intf_cfg->cfg.intf_mode = module->sync_mode; /* TDM=0/PCM=1/I2S=2 */ + intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; + intf_cfg->cfg.active_slot_mask = module->slot_mask; + intf_cfg->cfg.nslots_per_frame = module->nslots_per_frame; + intf_cfg->cfg.slot_width = module->slot_width; + intf_cfg->cfg.active_lane_mask = 2; + intf_cfg->cfg.frame_sync_rate = 0; + intf_cfg->cfg.frame_sync_src = module->sync_src; + intf_cfg->cfg.frame_sync_mode = 1; + 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 = 0; + intf_cfg->cfg.inv_int_bit_clk = 0; + intf_cfg->cfg.inv_ext_bit_clk = 0; + + 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; + /* 3rd param: PARAM_ID_HW_EP_FRAME_DURATION */ + param_data = p; + 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 = sizeof(*fd_cfg); + fd_cfg = (struct param_id_hw_ep_frame_duration *)(param_data + 1); + fd_cfg->frame_duration_in_us = 1000; /* 48 samples @ 48 kHz */ + fd_cfg->allow_frame_duration_normalization = 1; + fd_cfg->min_normalized_frame_dur_us = 1; + fd_cfg->max_normalized_frame_dur_us = 100000; + + dev_err(graph->apm->dev, + "%s: AUDIO_IF SET_CFG:\n" + " module_id=0x%x instance_id=0x%x\n" + " INTF_CFG: qaif_type=%u intf_idx=%u intf_mode=%u(%s)\n" + " INTF_CFG: ctrl_data_out=%u active_slot_mask=0x%x nslots=%u slot_width=%u\n" + " INTF_CFG: active_lane_mask=0x%x frame_sync_rate=%u frame_sync_src=%u\n" + " INTF_CFG: frame_sync_mode=%u invert_fs=%u fs_data_delay=%u bit_clk_type=%u\n" + " HW_EP_MF: sample_rate=%u bit_width=%u num_channels=%u data_format=%u\n" + " FRAME_DUR: dur_us=%u normalize=%u\n", + __func__, + module->module_id, module->instance_id, + intf_cfg->cfg.qaif_type, intf_cfg->cfg.intf_idx, intf_cfg->cfg.intf_mode, + intf_cfg->cfg.intf_mode == 0 ? "TDM" : + intf_cfg->cfg.intf_mode == 1 ? "PCM" : "I2S", + intf_cfg->cfg.ctrl_data_out_enable, + intf_cfg->cfg.active_slot_mask, intf_cfg->cfg.nslots_per_frame, + intf_cfg->cfg.slot_width, + intf_cfg->cfg.active_lane_mask, intf_cfg->cfg.frame_sync_rate, + intf_cfg->cfg.frame_sync_src, + intf_cfg->cfg.frame_sync_mode, intf_cfg->cfg.invert_frame_sync_pulse, + intf_cfg->cfg.frame_sync_data_delay, intf_cfg->cfg.bit_clk_type, + hw_cfg->mf.sample_rate, hw_cfg->mf.bit_width, + hw_cfg->mf.num_channels, hw_cfg->mf.data_format, + fd_cfg->frame_duration_in_us, + fd_cfg->allow_frame_duration_normalization); + + 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) { @@ -1341,6 +1459,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, case MODULE_ID_I2S_SINK: rc = audioreach_i2s_set_media_format(graph, module, cfg); break; + case MODULE_ID_AUDIO_IF_SOURCE: + case MODULE_ID_AUDIO_IF_SINK: + rc = audioreach_audio_if_set_media_format(graph, module, cfg); + break; case MODULE_ID_WR_SHARED_MEM_EP: rc = audioreach_shmem_set_media_format(graph, module, cfg); break; @@ -1401,6 +1523,67 @@ void audioreach_graph_free_buf(struct q6apm_graph *graph) } EXPORT_SYMBOL_GPL(audioreach_graph_free_buf); +int audioreach_map_memory_regions(struct q6apm_graph *graph, unsigned int dir, size_t period_sz, + unsigned int periods, bool is_contiguous) +{ + struct apm_shared_map_region_payload *mregions; + struct apm_cmd_shared_mem_map_regions *cmd; + u32 num_regions, buf_sz, payload_size; + struct audioreach_graph_data *data; + struct gpr_pkt *pkt __free(kfree) = NULL; + void *p; + int i; + + if (dir == SNDRV_PCM_STREAM_PLAYBACK) + data = &graph->rx_data; + else + data = &graph->tx_data; + + if (is_contiguous) { + num_regions = 1; + buf_sz = period_sz * periods; + } else { + buf_sz = period_sz; + num_regions = periods; + } + + /* DSP expects size should be aligned to 4K */ + buf_sz = ALIGN(buf_sz, 4096); + + payload_size = sizeof(*cmd) + (sizeof(*mregions) * num_regions); + + pkt = audioreach_alloc_apm_pkt(payload_size, + APM_CMD_SHARED_MEM_MAP_REGIONS, dir, + graph->port->id); + if (IS_ERR(pkt)) + return PTR_ERR(pkt); + + p = (void *)pkt + GPR_HDR_SIZE; + cmd = p; + cmd->mem_pool_id = APM_MEMORY_MAP_SHMEM8_4K_POOL; + cmd->num_regions = num_regions; + + cmd->property_flag = 0x0; + + mregions = p + sizeof(*cmd); + + mutex_lock(&graph->lock); + + for (i = 0; i < num_regions; i++) { + struct audio_buffer *ab; + + ab = &data->buf[i]; + mregions->shm_addr_lsw = lower_32_bits(ab->phys); + mregions->shm_addr_msw = upper_32_bits(ab->phys); + mregions->mem_size_bytes = buf_sz; + ++mregions; + } + mutex_unlock(&graph->lock); + + return audioreach_graph_send_cmd_sync(graph, pkt, APM_CMD_RSP_SHARED_MEM_MAP_REGIONS); +} +EXPORT_SYMBOL_GPL(audioreach_map_memory_regions); + int audioreach_shared_memory_send_eos(struct q6apm_graph *graph) { struct data_cmd_wr_sh_mem_ep_eos *eos; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index 6859770b38a6c..85eac8d8af83b 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -20,6 +20,21 @@ struct q6apm_graph; #define MODULE_ID_PLACEHOLDER_DECODER 0x07001009 #define MODULE_ID_I2S_SINK 0x0700100A #define MODULE_ID_I2S_SOURCE 0x0700100B +/* + * TDM module IDs - from upstream patch 0001. + * Used on legacy platforms (sm8x50, sc8280xp etc.). + * Hawi uses MODULE_ID_AUDIO_IF_SINK/SOURCE instead. + */ +#define MODULE_ID_TDM_SINK 0x0700100E +#define MODULE_ID_TDM_SOURCE 0x0700100F +/* + * QAIF - Qualcomm Audio Interface (Unified Audio Interface) + * Used on Hawi and newer SoCs as a unified replacement for TDM/I2S. + * Confirmed from art_mtp ACDB: TMDE shows PARAM_ID_AUDIO_IF_CFG (0x08001B11) + * for these module instances. + */ +#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 @@ -495,6 +510,50 @@ struct param_id_i2s_intf_cfg { #define PORT_ID_I2S_OUPUT 1 #define I2S_STACK_SIZE 2048 +/* + * QAIF - Qualcomm Audio Interface (Unified Audio Interface) - Hawi + * MODULE_ID_AUDIO_IF_SINK = 0x0700117C + * MODULE_ID_AUDIO_IF_SOURCE = 0x0700117D + * + * Two params sent at runtime (same pattern as I2S/DMA): + * 1. PARAM_ID_HW_EP_MF_CFG (0x08001017) - media format + * 2. PARAM_ID_AUDIO_IF_INTF_CFG (0x08001B11) - interface config + * + * intf_mode selects the physical interface type: + * TDM=0, PCM=1, I2S=2 + * + * Struct layout from audio_if_api.xml (maxSize=0x24, 36 bytes). + */ +#define PARAM_ID_AUDIO_IF_INTF_CFG 0x08001B11 + +/* intf_mode values */ +#define AUDIO_IF_INTF_MODE_TDM 0x0 +#define AUDIO_IF_INTF_MODE_PCM 0x1 +#define AUDIO_IF_INTF_MODE_I2S 0x2 + +/* qaif_type values */ +#define AUDIO_IF_TYPE_QAIF 0x0 +#define AUDIO_IF_TYPE_QAIF_VA 0x1 + +struct param_id_audio_if_intf_cfg { + u16 qaif_type; /* byteOffset=0x00 */ + u16 intf_idx; /* byteOffset=0x02 */ + u16 intf_mode; /* byteOffset=0x04: TDM=0, PCM=1, I2S=2 */ + u16 ctrl_data_out_enable; /* byteOffset=0x06 */ + u32 active_slot_mask; /* byteOffset=0x08 */ + u16 nslots_per_frame; /* byteOffset=0x0C */ + u16 slot_width; /* byteOffset=0x0E */ + u32 active_lane_mask; /* byteOffset=0x10 */ + u32 frame_sync_rate; /* byteOffset=0x14 */ + u16 frame_sync_src; /* byteOffset=0x18 */ + u16 frame_sync_mode; /* byteOffset=0x1A */ + u16 invert_frame_sync_pulse; /* byteOffset=0x1C */ + u16 frame_sync_data_delay; /* byteOffset=0x1E */ + u16 bit_clk_type; /* byteOffset=0x20 */ + u8 inv_int_bit_clk; /* byteOffset=0x22 */ + u8 inv_ext_bit_clk; /* byteOffset=0x23 */ +} __packed; + #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 struct param_id_display_port_intf_cfg { @@ -514,6 +573,21 @@ struct param_id_hw_ep_mf { #define PARAM_ID_HW_EP_FRAME_SIZE_FACTOR 0x08001018 +/* + * PARAM_ID_HW_EP_FRAME_DURATION (0x08001B2F) + * Used by AUDIO_IF (QAIF) module instead of PARAM_ID_HW_EP_FRAME_SIZE_FACTOR. + * AUDIO_IF does NOT support FRAME_SIZE_FACTOR - using it causes + * ADSP_EUNSUPPORTED (3) on Hawi. Use FRAME_DURATION with 1000 us default. + */ +#define PARAM_ID_HW_EP_FRAME_DURATION 0x08001B2F + +struct param_id_hw_ep_frame_duration { + u32 frame_duration_in_us; + u32 allow_frame_duration_normalization; + u32 min_normalized_frame_dur_us; + u32 max_normalized_frame_dur_us; +} __packed; + struct param_id_fram_size_factor { uint32_t frame_size_factor; } __packed; @@ -786,6 +860,17 @@ struct audioreach_module { uint32_t data_format; uint32_t hw_interface_type; + /* TDM / AUDIO_IF (QAIF) shared interface fields */ + u32 sync_src; + u32 ctrl_data_out_enable; + u32 slot_mask; + u32 nslots_per_frame; + u32 slot_width; + u32 sync_mode; + u32 ctrl_invert_sync_pulse; + u32 ctrl_sync_data_delay; + u32 reserved; + /* PCM module specific */ uint32_t interleave_type; @@ -839,6 +924,10 @@ int audioreach_tplg_init(struct snd_soc_component *component); /* Module specific */ void audioreach_graph_free_buf(struct q6apm_graph *graph); +int audioreach_map_memory_regions(struct q6apm_graph *graph, + unsigned int dir, size_t period_sz, + unsigned int periods, + bool is_contiguous); int audioreach_send_cmd_sync(struct device *dev, gpr_device_t *gdev, struct gpr_ibasic_rsp_result_t *result, struct mutex *cmd_lock, gpr_port_t *port, wait_queue_head_t *cmd_wait, const struct gpr_pkt *pkt, uint32_t rsp_opcode); diff --git a/sound/soc/qcom/qdsp6/q6apm-dai.c b/sound/soc/qcom/qdsp6/q6apm-dai.c index 3a1be41df096c..2a42bc2e651cb 100644 --- a/sound/soc/qcom/qdsp6/q6apm-dai.c +++ b/sound/soc/qcom/qdsp6/q6apm-dai.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include "q6apm.h" @@ -33,6 +34,7 @@ #define COMPR_PLAYBACK_MAX_NUM_FRAGMENTS (16 * 4) #define COMPR_PLAYBACK_MIN_FRAGMENT_SIZE (8 * 1024) #define COMPR_PLAYBACK_MIN_NUM_FRAGMENTS (4) +#define Q6APM_MAX_VMIDS 8 #define SID_MASK_DEFAULT 0xF static const struct snd_compr_codec_caps q6apm_compr_caps = { @@ -66,12 +68,17 @@ struct q6apm_dai_rtd { unsigned int pcm_size; unsigned int pcm_count; unsigned int periods; + unsigned int scm_size; uint64_t bytes_sent; uint64_t bytes_received; uint64_t copied_total; uint16_t bits_per_sample; snd_pcm_uframes_t queue_ptr; bool next_track; + phys_addr_t dma_addr; + u64 scm_src_perms; + bool scm_assigned; + bool scm_assign_attempted; enum stream_state state; struct q6apm_graph *graph; spinlock_t lock; @@ -80,8 +87,117 @@ struct q6apm_dai_rtd { struct q6apm_dai_data { long long sid; + int num_vmids; + u32 vmids[Q6APM_MAX_VMIDS]; + bool use_scm_assign; }; +static int q6apm_dai_assign_memory(struct snd_soc_component *component, + struct q6apm_dai_rtd *prtd, + const struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm *dst_vmids; + int dst_count = 0; + int ret; + int index; + + if (!pdata->use_scm_assign) + return 0; + + if (prtd->scm_assigned) + return 0; + + if (!prtd->dma_addr || !prtd->pcm_size) { + dev_err(component->dev, + "%s: invalid dma addr/size addr=%pa size=%u\n", + __func__, &prtd->dma_addr, prtd->pcm_size); + return -EINVAL; + } + + if (!qcom_scm_is_available()) { + dev_err(component->dev, "%s: qcom_scm not ready\n", __func__); + return -EPROBE_DEFER; + } + + dst_vmids = kcalloc(pdata->num_vmids + 1, sizeof(*dst_vmids), + GFP_KERNEL); + if (!dst_vmids) + return -ENOMEM; + + /* Always keep HLOS RW so CPU can continue buffer access */ + dst_vmids[dst_count].vmid = QCOM_SCM_VMID_HLOS; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + + for (index = 0; index < pdata->num_vmids; index++) { + if (pdata->vmids[index] == QCOM_SCM_VMID_HLOS) + continue; + + dst_vmids[dst_count].vmid = pdata->vmids[index]; + dst_vmids[dst_count].perm = QCOM_SCM_PERM_RW; + dst_count++; + } + + prtd->scm_size = ALIGN(prtd->pcm_size, PAGE_SIZE); + prtd->scm_src_perms = BIT(QCOM_SCM_VMID_HLOS); + prtd->scm_assign_attempted = true; + + dev_dbg(component->dev, + "%s: assigning dma=%pa size=%u vmids=%d sid=0x%llx\n", + __func__, &prtd->dma_addr, prtd->scm_size, dst_count, pdata->sid); + + ret = qcom_scm_assign_mem(prtd->dma_addr, prtd->scm_size, + &prtd->scm_src_perms, dst_vmids, dst_count); + kfree(dst_vmids); + if (ret) { + dev_err(component->dev, + "%s: qcom_scm_assign_mem failed ret=%d dma=%pa size=%u\n", + __func__, ret, &prtd->dma_addr, prtd->scm_size); + return ret; + } + + prtd->scm_assigned = true; + dev_dbg(component->dev, "%s: assign success src_perms=0x%llx\n", + __func__, prtd->scm_src_perms); + + return 0; +} + +static void q6apm_dai_unassign_memory(struct snd_soc_component *component, + struct q6apm_dai_rtd *prtd, + const struct q6apm_dai_data *pdata) +{ + struct qcom_scm_vmperm hlos = { + .vmid = QCOM_SCM_VMID_HLOS, + .perm = QCOM_SCM_PERM_RW, + }; + int ret; + + if (!pdata->use_scm_assign || !prtd->scm_assigned) + return; + + if (!qcom_scm_is_available()) { + dev_err(component->dev, + "%s: qcom_scm not available during unassign\n", __func__); + return; + } + + ret = qcom_scm_assign_mem(prtd->dma_addr, prtd->scm_size, + &prtd->scm_src_perms, &hlos, 1); + if (ret) { + dev_err(component->dev, + "%s: qcom_scm_assign_mem(unassign) failed ret=%d dma=%pa size=%u src_perms=0x%llx\n", + __func__, ret, &prtd->dma_addr, prtd->scm_size, + prtd->scm_src_perms); + return; + } + + prtd->scm_src_perms = BIT(QCOM_SCM_VMID_HLOS); + prtd->scm_assigned = false; + dev_dbg(component->dev, "%s: unassign success dma=%pa size=%u\n", + __func__, &prtd->dma_addr, prtd->scm_size); +} + static const struct snd_pcm_hardware q6apm_dai_hardware_capture = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | @@ -939,6 +1055,8 @@ static int q6apm_dai_probe(struct platform_device *pdev) struct device_node *node = dev->of_node; struct q6apm_dai_data *pdata; struct of_phandle_args args; + u32 sid = 0; + int vmids; int rc; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); @@ -946,10 +1064,56 @@ static int q6apm_dai_probe(struct platform_device *pdev) return -ENOMEM; rc = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args); - if (rc < 0) - pdata->sid = -1; - else + if (rc < 0) { + /* Optional explicit SID override if iommus absent */ + if (!of_property_read_u32(node, "qcom,dsp-sid", &sid)) { + pdata->sid = sid & SID_MASK_DEFAULT; + dev_info(dev, "%s: using qcom,dsp-sid sid=0x%llx\n", + __func__, pdata->sid); + } else { + pdata->sid = -1; + dev_info(dev, + "%s: no iommus/qcom,dsp-sid, SID disabled\n", + __func__); + } + } else { pdata->sid = args.args[0] & SID_MASK_DEFAULT; + dev_info(dev, "%s: using iommus sid=0x%llx\n", + __func__, pdata->sid); + } + + /* Optional VMID mode; if absent, behave exactly as legacy path */ + vmids = of_property_count_u32_elems(node, "qcom,vmid"); + if (vmids == -EINVAL) { + pdata->num_vmids = 0; + pdata->use_scm_assign = false; + dev_info(dev, + "%s: qcom,vmid absent, scm-assign disabled\n", __func__); + } else if (vmids < 0) { + dev_err(dev, + "%s: failed to count qcom,vmid elements: %d\n", + __func__, vmids); + return vmids; + } else if (vmids > Q6APM_MAX_VMIDS) { + dev_err(dev, + "%s: too many qcom,vmid entries (%d), max=%d\n", + __func__, vmids, Q6APM_MAX_VMIDS); + return -EINVAL; + } else { + rc = of_property_read_u32_array(node, "qcom,vmid", + pdata->vmids, vmids); + if (rc) { + dev_err(dev, + "%s: failed to read qcom,vmid ret=%d\n", + __func__, rc); + return rc; + } + + pdata->num_vmids = vmids; + pdata->use_scm_assign = true; + dev_info(dev, "%s: scm-assign enabled vmid_count=%d\n", + __func__, vmids); + } dev_set_drvdata(dev, pdata); diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 006b283484d9e..40e2ae62f88cc 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -22,6 +22,10 @@ struct q6apm_lpass_dai_data { struct q6apm_graph *graph[APM_PORT_MAX]; bool is_port_started[APM_PORT_MAX]; struct audioreach_module_config module_config[APM_PORT_MAX]; + /* TDM slot config stored by set_tdm_slot(), applied in prepare() */ + unsigned int tdm_nslots[APM_PORT_MAX]; + unsigned int tdm_slot_width[APM_PORT_MAX]; + unsigned int tdm_slot_mask[APM_PORT_MAX]; }; static int q6dma_set_channel_map(struct snd_soc_dai *dai, @@ -212,6 +216,38 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s dai_data->graph[graph_id] = graph; } + /* + * Apply TDM slot config stored by set_tdm_slot(). + * set_tdm_slot() is called before graph_open() so the graph was NULL + * at that time. Now that the graph is open, apply the stored values + * to override the topology defaults (nslots=8) with the runtime + * values from the machine driver (nslots=4 from qcom,tdm-slots DT). + */ + if (dai_data->tdm_nslots[dai->id] && dai_data->graph[dai->id]) { + struct audioreach_module *tdm_mod; + + tdm_mod = q6apm_find_module_by_mid(dai_data->graph[dai->id], + MODULE_ID_AUDIO_IF_SINK); + if (!tdm_mod) + tdm_mod = q6apm_find_module_by_mid(dai_data->graph[dai->id], + MODULE_ID_AUDIO_IF_SOURCE); + if (!tdm_mod) + tdm_mod = q6apm_find_module_by_mid(dai_data->graph[dai->id], + MODULE_ID_TDM_SINK); + if (!tdm_mod) + tdm_mod = q6apm_find_module_by_mid(dai_data->graph[dai->id], + MODULE_ID_TDM_SOURCE); + if (tdm_mod) { + tdm_mod->nslots_per_frame = dai_data->tdm_nslots[dai->id]; + tdm_mod->slot_width = dai_data->tdm_slot_width[dai->id]; + tdm_mod->slot_mask = dai_data->tdm_slot_mask[dai->id]; + } else { + dev_warn(dai->dev, + "no AUDIO_IF/TDM module found in graph %d\n", + dai->id); + } + } + cfg->direction = substream->stream; rc = q6apm_graph_media_format_pcm(dai_data->graph[dai->id], cfg); if (rc) { @@ -289,6 +325,38 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .trigger = q6apm_lpass_dai_trigger, }; +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); + + /* RX DAI ids are even, TX are odd */ + dai_data->tdm_nslots[dai->id] = slots; + dai_data->tdm_slot_width[dai->id] = slot_width; + dai_data->tdm_slot_mask[dai->id] = (dai->id & 0x1) ? tx_mask : rx_mask; + + return 0; +} + +static int q6tdm_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + return 0; +} + +static const struct snd_soc_dai_ops q6tdm_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6apm_lpass_dai_shutdown, + .set_channel_map = q6dma_set_channel_map, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_tdm_slot = q6tdm_set_tdm_slot, + .set_sysclk = q6tdm_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, @@ -315,6 +383,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); diff --git a/sound/soc/qcom/qdsp6/q6apm.c b/sound/soc/qcom/qdsp6/q6apm.c index 2ab378fb50326..584601cdf7b10 100644 --- a/sound/soc/qcom/qdsp6/q6apm.c +++ b/sound/soc/qcom/qdsp6/q6apm.c @@ -791,8 +791,8 @@ struct audioreach_module *q6apm_find_module_by_mid(struct q6apm_graph *graph, ui struct q6apm *apm = graph->apm; return __q6apm_find_module_by_mid(apm, info, mid); - } +EXPORT_SYMBOL_GPL(q6apm_find_module_by_mid); static int apm_callback(const struct gpr_resp_pkt *data, void *priv, int op) { diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c index 03838582aeade..7bb4383fcb35a 100644 --- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c +++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c @@ -12,7 +12,7 @@ #include #include "q6dsp-lpass-clocks.h" -#define Q6DSP_MAX_CLK_ID 104 +#define Q6DSP_MAX_CLK_ID 132 #define Q6DSP_LPASS_CLK_ROOT_DEFAULT 0 diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 4c574b48ab004..907302ad00d8f 100644 --- a/sound/soc/qcom/qdsp6/q6prm-clocks.c +++ b/sound/soc/qcom/qdsp6/q6prm-clocks.c @@ -63,6 +63,34 @@ static const struct q6dsp_clk_init q6prm_clks[] = { "LPASS_HW_MACRO"), Q6DSP_VOTE_CLK(LPASS_HW_DCODEC_VOTE, Q6PRM_HW_CORE_ID_DCODEC, "LPASS_HW_DCODEC"), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF0_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF0_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF1_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF1_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF2_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF2_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF3_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF3_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF4_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF4_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF5_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF5_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF6_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF6_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF7_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF7_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF8_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF8_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF9_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF9_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF10_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF10_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF11_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF11_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF12_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_INTF12_EBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_VA_INTF0_IBIT), + Q6PRM_CLK(QAIF_CLK_ID_AUD_VA_INTF0_EBIT), }; static const struct q6dsp_clk_desc q6dsp_clk_q6prm __maybe_unused = { diff --git a/sound/soc/qcom/qdsp6/q6prm.h b/sound/soc/qcom/qdsp6/q6prm.h index a988a32086fe1..2b8129ea372c6 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -83,6 +83,64 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF0_IBIT 0x500 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF0_EBIT 0x501 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF1_IBIT 0x502 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF1_EBIT 0x503 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF2_IBIT 0x504 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF2_EBIT 0x505 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF3_IBIT 0x506 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF3_EBIT 0x507 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF4_IBIT 0x508 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF4_EBIT 0x509 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF5_IBIT 0x50A +#define Q6PRM_QAIF_CLK_ID_AUD_INTF5_EBIT 0x50B +#define Q6PRM_QAIF_CLK_ID_AUD_INTF6_IBIT 0x50C +#define Q6PRM_QAIF_CLK_ID_AUD_INTF6_EBIT 0x50D +#define Q6PRM_QAIF_CLK_ID_AUD_INTF7_IBIT 0x50E +#define Q6PRM_QAIF_CLK_ID_AUD_INTF7_EBIT 0x50F +#define Q6PRM_QAIF_CLK_ID_AUD_INTF8_IBIT 0x510 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF8_EBIT 0x511 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF9_IBIT 0x512 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF9_EBIT 0x513 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF10_IBIT 0x514 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF10_EBIT 0x515 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF11_IBIT 0x516 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF11_EBIT 0x517 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF12_IBIT 0x518 +#define Q6PRM_QAIF_CLK_ID_AUD_INTF12_EBIT 0x519 +#define Q6PRM_QAIF_CLK_ID_AUD_VA_INTF0_IBIT 0x550 +#define Q6PRM_QAIF_CLK_ID_AUD_VA_INTF0_EBIT 0x551 + +#define Q6PRM_LPASS_CLK_ID_AUD_INTF0_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF0_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF0_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF0_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF1_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF1_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF1_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF1_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF2_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF2_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF2_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF2_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF3_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF3_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF3_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF3_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF4_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF4_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF4_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF4_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF5_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF5_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF5_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF5_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF6_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF6_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF6_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF6_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF7_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF7_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF7_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF7_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF8_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF8_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF8_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF8_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF9_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF9_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF9_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF9_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF10_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF10_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF10_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF10_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF11_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF11_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF11_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF11_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF12_IBIT Q6PRM_QAIF_CLK_ID_AUD_INTF12_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_INTF12_EBIT Q6PRM_QAIF_CLK_ID_AUD_INTF12_EBIT +#define Q6PRM_LPASS_CLK_ID_AUD_VA_INTF0_IBIT Q6PRM_QAIF_CLK_ID_AUD_VA_INTF0_IBIT +#define Q6PRM_LPASS_CLK_ID_AUD_VA_INTF0_EBIT Q6PRM_QAIF_CLK_ID_AUD_VA_INTF0_EBIT + #define Q6PRM_LPASS_CLK_SRC_INTERNAL 1 #define Q6PRM_LPASS_CLK_ROOT_DEFAULT 0 #define Q6PRM_HW_CORE_ID_LPASS 1 diff --git a/sound/soc/qcom/qdsp6/topology.c b/sound/soc/qcom/qdsp6/topology.c index 1f69fba6de26d..a0f3edc92b47c 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -753,6 +753,62 @@ static int audioreach_widget_i2s_module_load(struct audioreach_module *mod, return 0; } +static int audioreach_widget_tdm_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; + + mod_elem = mod_array->value; + + while (tkn_count <= (le32_to_cpu(mod_array->num_elems) - 1)) { + switch (le32_to_cpu(mod_elem->token)) { + case AR_TKN_U32_MODULE_HW_IF_IDX: + mod->hw_interface_idx = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_HW_IF_TYPE: + mod->hw_interface_type = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SYNC_SRC: + mod->sync_src = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_CTRL_DATA_OUT_ENABLE: + mod->ctrl_data_out_enable = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SLOT_MASK: + mod->slot_mask = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_NSLOTS_PER_FRAME: + mod->nslots_per_frame = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SLOT_WIDTH: + mod->slot_width = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_SYNC_MODE: + mod->sync_mode = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_CTRL_INVERT_SYNC_PULSE: + mod->ctrl_invert_sync_pulse = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_CTRL_SYNC_DATA_DELAY: + mod->ctrl_sync_data_delay = le32_to_cpu(mod_elem->value); + break; + case AR_TKN_U32_MODULE_RESERVED: + mod->reserved = le32_to_cpu(mod_elem->value); + 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 +862,16 @@ 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: + /* + * AUDIO_IF (QAIF) uses the same topology tokens as TDM: + * HW_IF_IDX, HW_IF_TYPE, FMT_DATA, SYNC_SRC, SLOT_MASK, + * NSLOTS_PER_FRAME, SLOT_WIDTH, SYNC_MODE, etc. + * Reuse the TDM module loader. + */ + audioreach_widget_tdm_module_load(mod, mod_array); + break; case MODULE_ID_DISPLAY_PORT_SINK: audioreach_widget_dp_module_load(mod, mod_array); break;