diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml index c1bd19299763..3e7216d61a5e 100644 --- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml +++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml @@ -95,6 +95,19 @@ patternProperties: sound-dai: maxItems: 1 + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + + patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots + platform: description: Holds subnode which indicates platform dai. type: object @@ -114,6 +127,19 @@ patternProperties: minItems: 1 maxItems: 8 + dai-tdm-slot-num: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Number of slots in use + + dai-tdm-slot-width: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Width, in bits, of each slot + + patternProperties: + '^dai-tdm-slot-[rt]x-mask$': + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Slot mask for active TDM slots + required: - link-name - cpu diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h index ca84952c3884..2e879a3c09ae 100644 --- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h +++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h @@ -237,6 +237,35 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 70 +#define LAPSS_CLK_ID_QAIF_IF0_IBIT 71 +#define LAPSS_CLK_ID_QAIF_IF0_EBIT 72 +#define LAPSS_CLK_ID_QAIF_IF1_IBIT 73 +#define LAPSS_CLK_ID_QAIF_IF1_EBIT 74 +#define LAPSS_CLK_ID_QAIF_IF2_IBIT 75 +#define LAPSS_CLK_ID_QAIF_IF2_EBIT 76 +#define LAPSS_CLK_ID_QAIF_IF3_IBIT 77 +#define LAPSS_CLK_ID_QAIF_IF3_EBIT 78 +#define LAPSS_CLK_ID_QAIF_IF4_IBIT 79 +#define LAPSS_CLK_ID_QAIF_IF4_EBIT 80 +#define LAPSS_CLK_ID_QAIF_IF5_IBIT 81 +#define LAPSS_CLK_ID_QAIF_IF5_EBIT 82 +#define LAPSS_CLK_ID_QAIF_IF6_IBIT 83 +#define LAPSS_CLK_ID_QAIF_IF6_EBIT 84 +#define LAPSS_CLK_ID_QAIF_IF7_IBIT 85 +#define LAPSS_CLK_ID_QAIF_IF7_EBIT 86 +#define LAPSS_CLK_ID_QAIF_IF8_IBIT 87 +#define LAPSS_CLK_ID_QAIF_IF8_EBIT 88 +#define LAPSS_CLK_ID_QAIF_IF9_IBIT 89 +#define LAPSS_CLK_ID_QAIF_IF9_EBIT 90 +#define LAPSS_CLK_ID_QAIF_IF10_IBIT 91 +#define LAPSS_CLK_ID_QAIF_IF10_EBIT 92 +#define LAPSS_CLK_ID_QAIF_IF11_IBIT 93 +#define LAPSS_CLK_ID_QAIF_IF11_EBIT 94 +#define LAPSS_CLK_ID_QAIF_IF12_IBIT 95 +#define LAPSS_CLK_ID_QAIF_IF12_EBIT 96 +#define LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 97 +#define LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 98 + #define LPASS_HW_AVTIMER_VOTE 101 #define LPASS_HW_MACRO_VOTE 102 #define LPASS_HW_DCODEC_VOTE 103 diff --git a/include/uapi/sound/snd_ar_tokens.h b/include/uapi/sound/snd_ar_tokens.h index 6b8102eaa121..1700e3f5cb64 100644 --- a/include/uapi/sound/snd_ar_tokens.h +++ b/include/uapi/sound/snd_ar_tokens.h @@ -168,6 +168,60 @@ enum ar_event_types { * LOG_WAIT = 0, * LOG_IMMEDIATELY = 1 * + * %AR_TKN_U16_MODULE_SYNC_SRC: Frame sync source + * AR_AUDIO_IF_SYNC_SRC_EXTERNAL = 0, + * AR_AUDIO_IF_SYNC_SRC_INTERNAL = 1 + * + * %AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE: Enable data-out tri-state control + * AR_AUDIO_IF_CTRL_DATA_OE_DISABLE = 0, + * AR_AUDIO_IF_CTRL_DATA_OE_ENABLE = 1 + * + * %AR_TKN_U32_MODULE_SLOT_MASK: Active TDM slot bitmask + * + * %AR_TKN_U16_MODULE_NSLOTS_PER_FRAME: Number of slots per TDM frame + * + * %AR_TKN_U16_MODULE_SLOT_WIDTH: Slot width in bits (16 or 32) + * + * %AR_TKN_U16_MODULE_SYNC_MODE: Frame sync mode + * AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT = 0, + * AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT = 1, + * AR_AUDIO_IF_FRAME_SYNC_MODE_LONG = 2 + * + * %AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: Invert frame sync pulse polarity + * AR_AUDIO_IF_SYNC_NORMAL = 0, + * AR_AUDIO_IF_SYNC_INVERTED = 1 + * + * %AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: Data delay relative to frame sync + * AR_AUDIO_IF_DATA_DELAY_NONE = 0, + * AR_AUDIO_IF_DATA_DELAY_1_CYCLE = 1, + * AR_AUDIO_IF_DATA_DELAY_2_CYCLE = 2 + * + * %AR_TKN_U16_MODULE_INTF_MODE: Audio IF interface mode + * AR_AUDIO_IF_INTF_MODE_TDM = 0, + * AR_AUDIO_IF_INTF_MODE_PCM = 1, + * AR_AUDIO_IF_INTF_MODE_I2S = 2 + * + * %AR_TKN_U16_MODULE_QAIF_TYPE: QAIF hardware port type index + * AR_AUDIO_IF_QAIF = 0, + * AR_AUDIO_IF_QAIF_VA = 1 + * + * %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_U16_MODULE_BIT_CLK_TYPE: Bit clock type + * AR_AUDIO_IF_BIT_CLK_INTERNAL = 0, + * AR_AUDIO_IF_BIT_CLK_EXTERNAL = 1, + * AR_AUDIO_IF_BIT_CLK_SKIP = 2 + * + * %AR_TKN_U8_MODULE_INV_INT_BIT_CLK: Invert internal bit clock + * AR_AUDIO_IF_CLK_NORMAL = 0, + * AR_AUDIO_IF_CLK_INVERTED = 1 + * + * %AR_TKN_U8_MODULE_INV_EXT_BIT_CLK: Invert external bit clock + * AR_AUDIO_IF_CLK_NORMAL = 0, + * AR_AUDIO_IF_CLK_INVERTED = 1 + * * %AR_TKN_DAI_INDEX: dai index * */ @@ -240,6 +294,45 @@ 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_U16_MODULE_SYNC_SRC 262 +#define AR_TKN_U16_MODULE_CTRL_DATA_OUT_ENABLE 263 +#define AR_TKN_U32_MODULE_SLOT_MASK 264 +#define AR_TKN_U16_MODULE_NSLOTS_PER_FRAME 265 +#define AR_TKN_U16_MODULE_SLOT_WIDTH 266 +#define AR_TKN_U16_MODULE_SYNC_MODE 267 +#define AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE 268 +#define AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY 269 +#define AR_TKN_U16_MODULE_INTF_MODE 270 +#define AR_TKN_U16_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_U16_MODULE_BIT_CLK_TYPE 274 +#define AR_TKN_U8_MODULE_INV_INT_BIT_CLK 275 +#define AR_TKN_U8_MODULE_INV_EXT_BIT_CLK 276 + +#define AR_AUDIO_IF_SYNC_SRC_EXTERNAL 0 +#define AR_AUDIO_IF_SYNC_SRC_INTERNAL 1 +#define AR_AUDIO_IF_CTRL_DATA_OE_DISABLE 0 +#define AR_AUDIO_IF_CTRL_DATA_OE_ENABLE 1 +#define AR_AUDIO_IF_INTF_MODE_TDM 0 +#define AR_AUDIO_IF_INTF_MODE_PCM 1 +#define AR_AUDIO_IF_INTF_MODE_I2S 2 +#define AR_AUDIO_IF_QAIF 0 +#define AR_AUDIO_IF_QAIF_VA 1 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_SHORT 0 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_ONE_SLOT 1 +#define AR_AUDIO_IF_FRAME_SYNC_MODE_LONG 2 +#define AR_AUDIO_IF_SYNC_NORMAL 0 +#define AR_AUDIO_IF_SYNC_INVERTED 1 +#define AR_AUDIO_IF_DATA_DELAY_NONE 0 +#define AR_AUDIO_IF_DATA_DELAY_1_CYCLE 1 +#define AR_AUDIO_IF_DATA_DELAY_2_CYCLE 2 +#define AR_AUDIO_IF_BIT_CLK_INTERNAL 0 +#define AR_AUDIO_IF_BIT_CLK_EXTERNAL 1 +#define AR_AUDIO_IF_BIT_CLK_SKIP 2 +#define AR_AUDIO_IF_CLK_NORMAL 0 +#define AR_AUDIO_IF_CLK_INVERTED 1 + #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/common.c b/sound/soc/qcom/common.c index f8782e5cfaae..d231024206db 100644 --- a/sound/soc/qcom/common.c +++ b/sound/soc/qcom/common.c @@ -23,6 +23,161 @@ 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 of_phandle_args args; + int ret; + + if (!card->dev || !card->dev->of_node) + return NULL; + + for_each_available_child_of_node_scoped(card->dev->of_node, np) { + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(np, "cpu"); + + if (!cpu_np) + continue; + + ret = of_parse_phandle_with_args(cpu_np, "sound-dai", "#sound-dai-cells", 0, + &args); + 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 of_node_get(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 __free(device_node) = qcom_snd_get_link_node(rtd); + int ret; + + if (!link_np) + return -EINVAL; + + struct device_node *cpu_np __free(device_node) = + of_get_child_by_name(link_np, "cpu"); + struct device_node *codec_np __free(device_node) = + of_get_child_by_name(link_np, "codec"); + if (!cpu_np || !codec_np) + return -EINVAL; + + ret = qcom_snd_parse_tdm_slot(cpu_np, cpu_cfg); + if (ret) + return ret; + + return qcom_snd_parse_tdm_slot(codec_np, codec_cfg); +} + +int qcom_snd_get_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd, + 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 *dev = card->dev; diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h index 48b114eb46a5..c1deac109f24 100644 --- a/sound/soc/qcom/common.h +++ b/sound/soc/qcom/common.h @@ -9,7 +9,21 @@ #define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 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, diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c index 0cc840aca69d..cce0ad31ff0c 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; @@ -1052,6 +1066,81 @@ 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; + 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 = AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE; + fd_cfg->frame_duration.min_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MIN_US; + fd_cfg->frame_duration.max_normalized_frame_dur_us = AUDIO_IF_FRAME_DURATION_MAX_US; + + 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) { @@ -1438,6 +1527,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 62a2fd79bbcb..35541b2d8c99 100644 --- a/sound/soc/qcom/qdsp6/audioreach.h +++ b/sound/soc/qcom/qdsp6/audioreach.h @@ -36,6 +36,8 @@ struct q6apm_graph; #define MODULE_ID_SPEAKER_PROTECTION 0x070010E2 #define MODULE_ID_SPEAKER_PROTECTION_VI 0x070010E3 #define MODULE_ID_OPUS_DEC 0x07001174 +#define MODULE_ID_AUDIO_IF_SINK 0x0700117C +#define MODULE_ID_AUDIO_IF_SOURCE 0x0700117D #define APM_CMD_GET_SPF_STATE 0x01001021 #define APM_CMD_RSP_GET_SPF_STATE 0x02001007 @@ -544,6 +546,74 @@ 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 + +/* + * struct param_id_audio_if_intf_cfg - Audio interface configuration + * @qaif_type: Audio interface type (e.g. QAIF, QAIF_VA) + * @intf_idx: Interface instance index + * @intf_mode: Interface operating mode (TDM/PCM/I2S) + * @ctrl_data_out_enable: Enable sharing of data-out signal with other masters + * @active_slot_mask: Bitmask indicating active slots + * @nslots_per_frame: Number of slots per audio frame + * @slot_width: Width of each slot in bits + * @active_lane_mask: Bitmask of active data lanes + * @frame_sync_rate: Frame sync rate in Hz + * @frame_sync_src: Frame sync source selection + * @frame_sync_mode: Frame sync mode configuration + * @invert_frame_sync_pulse: Invert frame sync polarity when set + * @frame_sync_data_delay: Data delay from frame sync in bit clocks + * @bit_clk_type: Bit clock type (internal / external) + * @inv_int_bit_clk: Invert internal bit clock when set + * @inv_ext_bit_clk: Invert external bit clock when set + * + * This structure defines configuration parameters for the Qualcomm + * Audio Interface (QAIF) block. It is used to program interface + * characteristics such as slot configuration, clocking and frame + * synchronization behaviour. + */ +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 +#define AUDIO_IF_FRAME_DURATION_NORMALIZATION_ENABLE 1 +#define AUDIO_IF_FRAME_DURATION_MIN_US 1 +#define AUDIO_IF_FRAME_DURATION_MAX_US 100000 + +/** + * struct param_id_hw_ep_frame_duration - Hardware endpoint frame duration + * @frame_duration_in_us: Frame duration in microseconds. + * @allow_frame_duration_normalization: Permit SPF to normalize frame duration. + * @min_normalized_frame_dur_us: Minimum normalized frame duration in microseconds. + * @max_normalized_frame_dur_us: Maximum normalized frame duration in microseconds. + * + * This structure configures the frame duration for the Audio IF hardware + * endpoint and, when enabled, the allowed normalization range. + */ +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 +947,23 @@ struct audioreach_module { uint32_t data_format; uint32_t hw_interface_type; + /* Audio IF module (TDM/PCM/I2S) */ + u32 slot_mask; + u32 active_lane_mask; + u32 frame_sync_rate; + u16 qaif_type; + u16 sync_src; + u16 ctrl_data_out_enable; + u16 nslots_per_frame; + u16 slot_width; + u16 intf_mode; + u16 sync_mode; + u16 ctrl_invert_sync_pulse; + u16 ctrl_sync_data_delay; + u16 bit_clk_type; + u8 inv_int_bit_clk; + u8 inv_ext_bit_clk; + /* PCM module specific */ uint32_t interleave_type; @@ -907,6 +994,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/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c index e68e8b000e07..e204fd59e512 100644 --- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c +++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c @@ -358,6 +358,53 @@ static int q6i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) return 0; } +static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct q6apm_lpass_dai_data *dai_data = dev_get_drvdata(dai->dev); + struct audioreach_module_config *cfg = &dai_data->module_config[dai->id]; + unsigned int cap_mask, slot_mask; + + if (slot_width != 16 && slot_width != 32) { + dev_err(dai->dev, "%s: invalid slot_width %d\n", __func__, slot_width); + return -EINVAL; + } + + switch (slots) { + case 2: + case 4: + case 8: + case 16: + cap_mask = GENMASK(slots - 1, 0); + break; + default: + dev_err(dai->dev, "%s: invalid slots %d\n", __func__, slots); + return -EINVAL; + } + + switch (dai->id) { + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask; + if (slot_mask & ~cap_mask) { + dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n", + __func__, slot_mask, slots); + return -EINVAL; + } + + cfg->nslots_per_frame = slots; + cfg->slot_width = slot_width; + cfg->slot_mask = slot_mask; + break; + default: + dev_err(dai->dev, "%s: invalid dai id 0x%x\n", __func__, dai->id); + return -EINVAL; + } + + return 0; +} + static const struct snd_soc_dai_ops q6dma_ops = { .prepare = q6apm_lpass_dai_prepare, .startup = q6apm_lpass_dai_startup, @@ -387,6 +434,17 @@ static const struct snd_soc_dai_ops q6hdmi_ops = { .trigger = q6apm_lpass_dai_trigger, }; +static const struct snd_soc_dai_ops q6tdm_ops = { + .prepare = q6apm_lpass_dai_prepare, + .startup = q6apm_lpass_dai_startup, + .shutdown = q6i2s_lpass_dai_shutdown, + .set_tdm_slot = q6tdm_set_tdm_slot, + .hw_params = q6dma_hw_params, + .set_fmt = q6i2s_set_fmt, + .set_sysclk = q6i2s_set_sysclk, + .trigger = q6apm_lpass_dai_trigger, +}; + static const struct snd_soc_component_driver q6apm_lpass_dai_component = { .name = "q6apm-be-dai-component", .of_xlate_dai_name = q6dsp_audio_ports_of_xlate_dai_name, @@ -415,6 +473,7 @@ static int of_q6apm_parse_dai_data(struct device *dev, case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX: case QUINARY_MI2S_RX ... QUINARY_MI2S_TX: case SENARY_MI2S_RX ... SENARY_MI2S_TX: + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: priv = &data->priv[id]; priv->mclk = of_clk_get_by_name(node, "mclk"); if (IS_ERR(priv->mclk)) { @@ -479,6 +538,7 @@ static int q6apm_lpass_dai_dev_probe(struct platform_device *pdev) cfg.q6i2s_ops = &q6i2s_ops; cfg.q6dma_ops = &q6dma_ops; cfg.q6hdmi_ops = &q6hdmi_ops; + cfg.q6tdm_ops = &q6tdm_ops; dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais); return devm_snd_soc_register_component(dev, &q6apm_lpass_dai_component, dais, num_dais); diff --git a/sound/soc/qcom/qdsp6/q6prm-clocks.c b/sound/soc/qcom/qdsp6/q6prm-clocks.c index 02dad9ee9804..f613e2aee75e 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(LAPSS_CLK_ID_QAIF_IF0_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF0_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF1_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF2_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF3_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF4_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF5_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF6_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF7_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF8_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF9_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF10_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF11_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_QAIF_IF12_EBIT), + Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_IBIT), + Q6PRM_CLK(LAPSS_CLK_ID_VA_QAIF_IF0_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 938b1bfce287..cca77cd92bc1 100644 --- a/sound/soc/qcom/qdsp6/q6prm.h +++ b/sound/soc/qcom/qdsp6/q6prm.h @@ -97,6 +97,35 @@ /* Clock ID for RX CORE MCLK2 2X MCLK */ #define Q6PRM_LPASS_CLK_ID_RX_CORE_MCLK2_2X_MCLK 0x318 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_IBIT 0x500 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF0_EBIT 0x501 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_IBIT 0x502 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF1_EBIT 0x503 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_IBIT 0x504 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF2_EBIT 0x505 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_IBIT 0x506 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF3_EBIT 0x507 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_IBIT 0x508 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF4_EBIT 0x509 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_IBIT 0x50A +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF5_EBIT 0x50B +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_IBIT 0x50C +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF6_EBIT 0x50D +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_IBIT 0x50E +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF7_EBIT 0x50F +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_IBIT 0x510 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF8_EBIT 0x511 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_IBIT 0x512 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF9_EBIT 0x513 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_IBIT 0x514 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF10_EBIT 0x515 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_IBIT 0x516 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF11_EBIT 0x517 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_IBIT 0x518 +#define Q6PRM_LAPSS_CLK_ID_QAIF_IF12_EBIT 0x519 +#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_IBIT 0x550 +#define Q6PRM_LAPSS_CLK_ID_VA_QAIF_IF0_EBIT 0x551 + #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 1f69fba6de26..54661bcb006c 100644 --- a/sound/soc/qcom/qdsp6/topology.c +++ b/sound/soc/qcom/qdsp6/topology.c @@ -753,6 +753,103 @@ 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: + mod->hw_interface_idx = val; + break; + case AR_TKN_U32_MODULE_FMT_DATA: + mod->data_format = val; + break; + case AR_TKN_U16_MODULE_SYNC_SRC: + if (val > U16_MAX) + return -EINVAL; + mod->sync_src = (u16)val; + break; + case AR_TKN_U16_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_U16_MODULE_NSLOTS_PER_FRAME: + if (val > U16_MAX) + return -EINVAL; + mod->nslots_per_frame = (u16)val; + break; + case AR_TKN_U16_MODULE_SLOT_WIDTH: + if (val > U16_MAX) + return -EINVAL; + mod->slot_width = (u16)val; + break; + case AR_TKN_U16_MODULE_INTF_MODE: + if (val > U16_MAX) + return -EINVAL; + mod->intf_mode = (u16)val; + break; + case AR_TKN_U16_MODULE_SYNC_MODE: + if (val > U16_MAX) + return -EINVAL; + mod->sync_mode = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_INVERT_SYNC_PULSE: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_invert_sync_pulse = (u16)val; + break; + case AR_TKN_U16_MODULE_CTRL_SYNC_DATA_DELAY: + if (val > U16_MAX) + return -EINVAL; + mod->ctrl_sync_data_delay = (u16)val; + break; + case AR_TKN_U16_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_U16_MODULE_BIT_CLK_TYPE: + if (val > U16_MAX) + return -EINVAL; + mod->bit_clk_type = (u16)val; + break; + case AR_TKN_U8_MODULE_INV_INT_BIT_CLK: + if (val > U8_MAX) + return -EINVAL; + mod->inv_int_bit_clk = (u8)val; + break; + case AR_TKN_U8_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 +903,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; diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c index c3ce3e05b260..597c0d887d2f 100644 --- a/sound/soc/qcom/sc8280xp.c +++ b/sound/soc/qcom/sc8280xp.c @@ -106,6 +106,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->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->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); @@ -229,6 +286,8 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, return ret; } break; + case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7: + return sc8280xp_tdm_hw_params(substream, params); default: break; }