diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 6b8102eaa121f1..8cc57810bb73f0 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -240,6 +240,16 @@ 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_RESERVED 270 + #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 2365424a9b42bf..12cc98f18bbf69 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_tdm_module_intf_cfg { + struct apm_module_param_data param_data; + struct param_id_tdm_intf_cfg cfg; +} __packed; + +#define APM_TDM_INTF_CFG_PSIZE ALIGN(sizeof(struct apm_tdm_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; @@ -1067,6 +1074,78 @@ static int audioreach_i2s_set_media_format(struct q6apm_graph *graph, return rc; } +static int audioreach_tdm_set_media_format(struct q6apm_graph *graph, + struct audioreach_module *module, + struct audioreach_module_config *cfg) +{ + struct apm_module_frame_size_factor_cfg *fs_cfg; + struct apm_module_param_data *param_data; + struct apm_tdm_module_intf_cfg *intf_cfg; + struct apm_module_hw_ep_mf_cfg *hw_cfg; + int ic_sz, ep_sz, fs_sz; + int rc, payload_size; + struct gpr_pkt *pkt; + void *p; + + ic_sz = APM_TDM_INTF_CFG_PSIZE; + ep_sz = APM_HW_EP_CFG_PSIZE; + fs_sz = APM_FS_CFG_PSIZE; + + payload_size = ic_sz + ep_sz + fs_sz; + + pkt = audioreach_alloc_apm_cmd_pkt(payload_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_TDM_INTF_CFG; + param_data->param_size = ic_sz - APM_MODULE_PARAM_DATA_SIZE; + + intf_cfg->cfg.intf_idx = module->hw_interface_idx; + intf_cfg->cfg.sync_src = module->sync_src; + intf_cfg->cfg.ctrl_data_out_enable = module->ctrl_data_out_enable; + intf_cfg->cfg.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.sync_mode = module->sync_mode; + intf_cfg->cfg.ctrl_invert_sync_pulse = module->ctrl_invert_sync_pulse; + intf_cfg->cfg.ctrl_sync_data_delay = module->ctrl_sync_data_delay; + intf_cfg->cfg.reserved = module->reserved; + + 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; + fs_cfg = p; + param_data = &fs_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_SIZE_FACTOR; + param_data->param_size = fs_sz - APM_MODULE_PARAM_DATA_SIZE; + fs_cfg->frame_size_factor = 1; + + rc = q6apm_send_cmd_sync(graph->apm, pkt, 0); + + kfree(pkt); + + return rc; +} + static int audioreach_logging_set_media_format(struct q6apm_graph *graph, struct audioreach_module *module) { @@ -1306,6 +1385,10 @@ int audioreach_set_media_format(struct q6apm_graph *graph, struct audioreach_mod case MODULE_ID_I2S_SINK: rc = audioreach_i2s_set_media_format(graph, module, cfg); break; + case MODULE_ID_TDM_SOURCE: + case MODULE_ID_TDM_SINK: + rc = audioreach_tdm_set_media_format(graph, module, cfg); + break; case MODULE_ID_WR_SHARED_MEM_EP: rc = audioreach_shmem_set_media_format(graph, module, cfg); break; diff --git a/sound/soc/qcom/qdsp6/audioreach.h b/sound/soc/qcom/qdsp6/audioreach.h index d1b60b36468a86..d029bfedf12087 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -20,6 +20,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_TDM_SINK 0x0700100E +#define MODULE_ID_TDM_SOURCE 0x0700100F #define MODULE_ID_SAL 0x07001010 #define MODULE_ID_MFC 0x07001015 #define MODULE_ID_DATA_LOGGING 0x0700101A @@ -493,6 +495,36 @@ struct param_id_i2s_intf_cfg { #define PORT_ID_I2S_OUPUT 1 #define I2S_STACK_SIZE 2048 +#define CONFIG_TDM_SYNC_SRC_EXTERNAL 0x0 +#define CONFIG_TDM_SYNC_SRC_INTERNAL 0x1 + +#define PARAM_ID_TDM_INTF_CFG 0x0800101B + +struct param_id_tdm_intf_cfg { + uint32_t lpaif_type; + uint32_t intf_idx; + uint16_t sync_src; + uint16_t ctrl_data_out_enable; + uint32_t slot_mask; + uint16_t nslots_per_frame; + uint16_t slot_width; + uint16_t sync_mode; + uint16_t ctrl_invert_sync_pulse; + uint16_t ctrl_sync_data_delay; + uint16_t reserved; +} __packed; + + +#define TDM_INTF_TYPE_PRIMARY 0 +#define TDM_INTF_TYPE_SECOINDARY 1 +#define TDM_INTF_TYPE_TERTINARY 2 +#define TDM_INTF_TYPE_QUATERNARY 3 +#define TDM_INTF_TYPE_QUINARY 4 + +#define PORT_ID_TDM_INPUT 2 +#define PORT_ID_TDM_OUPUT 1 +#define TDM_STACK_SIZE 2048 + #define PARAM_ID_DISPLAY_PORT_INTF_CFG 0x08001154 struct param_id_display_port_intf_cfg { @@ -746,6 +778,17 @@ struct audioreach_module { uint32_t data_format; uint32_t hw_interface_type; + /* TDM module */ + uint32_t sync_src; + uint32_t ctrl_data_out_enable; + uint32_t slot_mask; + uint32_t nslots_per_frame; + uint32_t slot_width; + uint32_t sync_mode; + uint32_t ctrl_invert_sync_pulse; + uint32_t ctrl_sync_data_delay; + uint32_t reserved; + /* PCM module specific */ uint32_t interleave_type; diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index 528756f1332bcf..4198dc289a1f1a 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -273,6 +273,14 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .set_fmt = q6i2s_set_fmt, }; +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 = q6i2s_set_fmt, +}; + 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, @@ -298,6 +306,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/topology.c b/sound/soc/qcom/qdsp6/topology.c index f61285e7dcf209..7bc83757a0af2c 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, + struct snd_soc_tplg_vendor_array *mod_array) +{ + 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, struct snd_soc_tplg_vendor_array *mod_array) { @@ -806,6 +862,10 @@ 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_TDM_SINK: + case MODULE_ID_TDM_SOURCE: + audioreach_widget_tdm_module_load(mod, mod_array); + break; case MODULE_ID_DISPLAY_PORT_SINK: audioreach_widget_dp_module_load(mod, mod_array); break;