Browse Source

Merge "dsp: add support for SVA to use afe topology deregister"

qctecmdr 4 years ago
parent
commit
5caa78db45
8 changed files with 584 additions and 85 deletions
  1. 126 0
      asoc/codecs/ep92/ep92.c
  2. 2 57
      asoc/codecs/ep92/ep92.h
  3. 46 0
      asoc/msm-pcm-routing-v2.c
  4. 312 28
      dsp/q6afe.c
  5. 38 0
      dsp/q6core.c
  6. 23 0
      include/dsp/apr_audio-v2.h
  7. 34 0
      include/dsp/q6afe-v2.h
  8. 3 0
      include/dsp/q6core.h

+ 126 - 0
asoc/codecs/ep92/ep92.c

@@ -44,6 +44,61 @@ static const unsigned int ep92_dsd_freq_table[4] = {
 	64, 128, 256, 0
 };
 
+/* EP92 register default values */
+static struct reg_default ep92_reg_defaults[] = {
+	{EP92_BI_VENDOR_ID_0,                   0x17},
+	{EP92_BI_VENDOR_ID_1,                   0x7A},
+	{EP92_BI_DEVICE_ID_0,                   0x94},
+	{EP92_BI_DEVICE_ID_1,                   0xA3},
+	{EP92_BI_VERSION_NUM,                   0x10},
+	{EP92_BI_VERSION_YEAR,                  0x09},
+	{EP92_BI_VERSION_MONTH,                 0x07},
+	{EP92_BI_VERSION_DATE,                  0x06},
+	{EP92_BI_GENERAL_INFO_0,                0x00},
+	{EP92_BI_GENERAL_INFO_1,                0x00},
+	{EP92_BI_GENERAL_INFO_2,                0x00},
+	{EP92_BI_GENERAL_INFO_3,                0x00},
+	{EP92_BI_GENERAL_INFO_4,                0x00},
+	{EP92_BI_GENERAL_INFO_5,                0x00},
+	{EP92_BI_GENERAL_INFO_6,                0x00},
+	{EP92_ISP_MODE_ENTER_ISP,               0x00},
+	{EP92_GENERAL_CONTROL_0,                0x20},
+	{EP92_GENERAL_CONTROL_1,                0x00},
+	{EP92_GENERAL_CONTROL_2,                0x00},
+	{EP92_GENERAL_CONTROL_3,                0x10},
+	{EP92_GENERAL_CONTROL_4,                0x00},
+	{EP92_CEC_EVENT_CODE,                   0x00},
+	{EP92_CEC_EVENT_PARAM_1,                0x00},
+	{EP92_CEC_EVENT_PARAM_2,                0x00},
+	{EP92_CEC_EVENT_PARAM_3,                0x00},
+	{EP92_CEC_EVENT_PARAM_4,                0x00},
+	{EP92_AUDIO_INFO_SYSTEM_STATUS_0,       0x00},
+	{EP92_AUDIO_INFO_SYSTEM_STATUS_1,       0x00},
+	{EP92_AUDIO_INFO_AUDIO_STATUS,          0x00},
+	{EP92_AUDIO_INFO_CHANNEL_STATUS_0,      0x00},
+	{EP92_AUDIO_INFO_CHANNEL_STATUS_1,      0x00},
+	{EP92_AUDIO_INFO_CHANNEL_STATUS_2,      0x00},
+	{EP92_AUDIO_INFO_CHANNEL_STATUS_3,      0x00},
+	{EP92_AUDIO_INFO_CHANNEL_STATUS_4,      0x00},
+	{EP92_AUDIO_INFO_ADO_INFO_FRAME_0,      0x00},
+	{EP92_AUDIO_INFO_ADO_INFO_FRAME_1,      0x00},
+	{EP92_AUDIO_INFO_ADO_INFO_FRAME_2,      0x00},
+	{EP92_AUDIO_INFO_ADO_INFO_FRAME_3,      0x00},
+	{EP92_AUDIO_INFO_ADO_INFO_FRAME_4,      0x00},
+	{EP92_AUDIO_INFO_ADO_INFO_FRAME_5,      0x00},
+	{EP92_OTHER_PACKETS_HDMI_VS_0,          0x00},
+	{EP92_OTHER_PACKETS_HDMI_VS_1,          0x00},
+	{EP92_OTHER_PACKETS_ACP_PACKET,         0x00},
+	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_0,   0x00},
+	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_1,   0x00},
+	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_2,   0x00},
+	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_3,   0x00},
+	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_4,   0x00},
+	{EP92_OTHER_PACKETS_GC_PACKET_0,        0x00},
+	{EP92_OTHER_PACKETS_GC_PACKET_1,        0x00},
+	{EP92_OTHER_PACKETS_GC_PACKET_2,        0x00},
+};
+
 static bool ep92_volatile_register(struct device *dev, unsigned int reg)
 {
 	/* do not cache register state in regmap */
@@ -115,6 +170,77 @@ struct ep92_pdata {
 #endif /* CONFIG_DEBUG_FS */
 };
 
+struct ep92_mclk_cfg_info {
+	uint32_t in_sample_rate;
+	uint32_t out_mclk_freq;
+	uint8_t mul_val;
+};
+
+#define EP92_MCLK_MUL_512		0x3
+#define EP92_MCLK_MUL_384		0x2
+#define EP92_MCLK_MUL_256		0x1
+#define EP92_MCLK_MUL_128		0x0
+#define EP92_MCLK_MUL_MASK		0x3
+
+/**
+ * ep92_set_ext_mclk - Configure the mclk based on sample freq
+ *
+ * @codec: handle pointer to ep92 codec
+ * @mclk_freq: mclk frequency to be set
+ *
+ * Returns 0 for sucess or appropriate negative error code
+ */
+int ep92_set_ext_mclk(struct snd_soc_codec *codec, uint32_t mclk_freq)
+{
+	unsigned int samp_freq = 0;
+	struct ep92_pdata *ep92 = NULL;
+	uint8_t value = 0;
+	int ret = 0;
+
+	if (!codec)
+		return -EINVAL;
+
+	ep92 = snd_soc_codec_get_drvdata(codec);
+
+	samp_freq = ep92_samp_freq_table[(ep92->ai.audio_status) &
+						EP92_AI_RATE_MASK];
+
+	if (!mclk_freq || (mclk_freq % samp_freq)) {
+		pr_err("%s incompatbile mclk:%u and sample freq:%u\n",
+				__func__, mclk_freq, samp_freq);
+		return -EINVAL;
+	}
+
+	switch (mclk_freq / samp_freq) {
+	case 512:
+		value = EP92_MCLK_MUL_512;
+		break;
+	case 384:
+		value = EP92_MCLK_MUL_384;
+		break;
+	case 256:
+		value = EP92_MCLK_MUL_256;
+		break;
+	case 128:
+		value = EP92_MCLK_MUL_128;
+		break;
+	default:
+		dev_err(codec->dev, "unsupported mclk:%u for sample freq:%u\n",
+					mclk_freq, samp_freq);
+		return -EINVAL;
+	}
+
+	pr_debug("%s mclk:%u, in sample freq:%u, write reg:0x%02x val:0x%02x\n",
+		__func__, mclk_freq, samp_freq,
+		EP92_GENERAL_CONTROL_2, EP92_MCLK_MUL_MASK & value);
+
+	ret = snd_soc_update_bits(codec, EP92_GENERAL_CONTROL_2,
+					EP92_MCLK_MUL_MASK, value);
+
+	return (((ret == 0) || (ret == 1)) ? 0 : ret);
+}
+EXPORT_SYMBOL(ep92_set_ext_mclk);
+
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 static int debugfs_codec_open_op(struct inode *inode, struct file *file)
 {

+ 2 - 57
asoc/codecs/ep92/ep92.h

@@ -69,63 +69,6 @@
 
 #define   EP92_MAX_REGISTER_ADDR                EP92_OTHER_PACKETS_GC_PACKET_2
 
-
-/* EP92 register default values */
-static struct reg_default ep92_reg_defaults[] = {
-	{EP92_BI_VENDOR_ID_0,                   0x17},
-	{EP92_BI_VENDOR_ID_1,                   0x7A},
-	{EP92_BI_DEVICE_ID_0,                   0x94},
-	{EP92_BI_DEVICE_ID_1,                   0xA3},
-	{EP92_BI_VERSION_NUM,                   0x10},
-	{EP92_BI_VERSION_YEAR,                  0x09},
-	{EP92_BI_VERSION_MONTH,                 0x07},
-	{EP92_BI_VERSION_DATE,                  0x06},
-	{EP92_BI_GENERAL_INFO_0,                0x00},
-	{EP92_BI_GENERAL_INFO_1,                0x00},
-	{EP92_BI_GENERAL_INFO_2,                0x00},
-	{EP92_BI_GENERAL_INFO_3,                0x00},
-	{EP92_BI_GENERAL_INFO_4,                0x00},
-	{EP92_BI_GENERAL_INFO_5,                0x00},
-	{EP92_BI_GENERAL_INFO_6,                0x00},
-	{EP92_ISP_MODE_ENTER_ISP,               0x00},
-	{EP92_GENERAL_CONTROL_0,                0x20},
-	{EP92_GENERAL_CONTROL_1,                0x00},
-	{EP92_GENERAL_CONTROL_2,                0x00},
-	{EP92_GENERAL_CONTROL_3,                0x10},
-	{EP92_GENERAL_CONTROL_4,                0x00},
-	{EP92_CEC_EVENT_CODE,                   0x00},
-	{EP92_CEC_EVENT_PARAM_1,                0x00},
-	{EP92_CEC_EVENT_PARAM_2,                0x00},
-	{EP92_CEC_EVENT_PARAM_3,                0x00},
-	{EP92_CEC_EVENT_PARAM_4,                0x00},
-	{EP92_AUDIO_INFO_SYSTEM_STATUS_0,       0x00},
-	{EP92_AUDIO_INFO_SYSTEM_STATUS_1,       0x00},
-	{EP92_AUDIO_INFO_AUDIO_STATUS,          0x00},
-	{EP92_AUDIO_INFO_CHANNEL_STATUS_0,      0x00},
-	{EP92_AUDIO_INFO_CHANNEL_STATUS_1,      0x00},
-	{EP92_AUDIO_INFO_CHANNEL_STATUS_2,      0x00},
-	{EP92_AUDIO_INFO_CHANNEL_STATUS_3,      0x00},
-	{EP92_AUDIO_INFO_CHANNEL_STATUS_4,      0x00},
-	{EP92_AUDIO_INFO_ADO_INFO_FRAME_0,      0x00},
-	{EP92_AUDIO_INFO_ADO_INFO_FRAME_1,      0x00},
-	{EP92_AUDIO_INFO_ADO_INFO_FRAME_2,      0x00},
-	{EP92_AUDIO_INFO_ADO_INFO_FRAME_3,      0x00},
-	{EP92_AUDIO_INFO_ADO_INFO_FRAME_4,      0x00},
-	{EP92_AUDIO_INFO_ADO_INFO_FRAME_5,      0x00},
-	{EP92_OTHER_PACKETS_HDMI_VS_0,          0x00},
-	{EP92_OTHER_PACKETS_HDMI_VS_1,          0x00},
-	{EP92_OTHER_PACKETS_ACP_PACKET,         0x00},
-	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_0,   0x00},
-	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_1,   0x00},
-	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_2,   0x00},
-	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_3,   0x00},
-	{EP92_OTHER_PACKETS_AVI_INFO_FRAME_4,   0x00},
-	{EP92_OTHER_PACKETS_GC_PACKET_0,        0x00},
-	{EP92_OTHER_PACKETS_GC_PACKET_1,        0x00},
-	{EP92_OTHER_PACKETS_GC_PACKET_2,        0x00},
-};
-
-
 /* shift/masks for register bits
  * GI = General Info
  * GC = General Control
@@ -211,4 +154,6 @@ enum {
 	EP92_KCTL_MAX
 };
 
+int ep92_set_ext_mclk(struct snd_soc_codec *codec, uint32_t mclk_freq);
+
 #endif /* __EP92_H__ */

+ 46 - 0
asoc/msm-pcm-routing-v2.c

@@ -32126,6 +32126,50 @@ static const struct snd_kcontrol_new
 	},
 };
 
+static int msm_routing_put_mclk_src_cfg(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u16 port_id = 0;
+	int32_t mclk_src_id = 0;
+	uint32_t mclk_freq = 0;
+	int be_idx, ret = -EINVAL;
+
+	be_idx = ucontrol->value.integer.value[0];
+	mclk_src_id = ucontrol->value.integer.value[1];
+	mclk_freq = ucontrol->value.integer.value[2];
+
+	if (be_idx < 0 && be_idx >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s: Invalid be id %d\n", __func__, be_idx);
+		return -EINVAL;
+	}
+
+	if (mclk_src_id < MCLK_SRC_INT && mclk_src_id >= MCLK_SRC_MAX) {
+		pr_err("%s: Invalid MCLK src %d\n", __func__, mclk_src_id);
+		return -EINVAL;
+	}
+
+	if (msm_bedais[be_idx].active) {
+		pr_err("%s:BE is active %d, cannot set mclk clock src\n",
+			__func__, be_idx);
+		return -EINVAL;
+	}
+
+	port_id = msm_bedais[be_idx].port_id;
+	pr_debug("%s: be idx %d mclk_src id %d mclk_freq %d port id 0x%x\n",
+		  __func__, be_idx, mclk_src_id, mclk_freq, port_id);
+	ret = afe_set_mclk_src_cfg(port_id, mclk_src_id, mclk_freq);
+	if (ret < 0)
+		pr_err("%s: failed to set mclk src cfg\n", __func__);
+
+	return ret;
+
+}
+
+static const struct snd_kcontrol_new mclk_src_controls[] = {
+	SOC_SINGLE_MULTI_EXT("MCLK_SRC CFG", SND_SOC_NOPM, 0, 24576000, 0, 3,
+					NULL, msm_routing_put_mclk_src_cfg),
+};
+
 static int msm_routing_stereo_channel_reverse_control_get(
 			struct snd_kcontrol *kcontrol,
 			struct snd_ctl_elem_value *ucontrol)
@@ -32418,6 +32462,8 @@ static int msm_routing_probe(struct snd_soc_component *component)
 	snd_soc_add_component_controls(component, pll_clk_drift_controls,
 				      ARRAY_SIZE(pll_clk_drift_controls));
 
+	snd_soc_add_component_controls(component, mclk_src_controls,
+				      ARRAY_SIZE(mclk_src_controls));
 	return 0;
 }
 

+ 312 - 28
dsp/q6afe.c

@@ -252,40 +252,70 @@ struct afe_ctl {
 struct afe_clkinfo_per_port {
 	u16 port_id; /* AFE port ID */
 	uint32_t clk_id; /* Clock ID */
+	uint32_t mclk_src_id; /* MCLK SRC ID */
+	uint32_t mclk_freq; /* MCLK_FREQ */
 };
 
-struct afe_clkinfo_per_port clkinfo_per_port[] = {
-	{ AFE_PORT_ID_PRIMARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT},
-	{ AFE_PORT_ID_SECONDARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT},
-	{ AFE_PORT_ID_TERTIARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT},
-	{ AFE_PORT_ID_QUATERNARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT},
-	{ AFE_PORT_ID_QUINARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT},
-	{ AFE_PORT_ID_SENARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT},
-	{ AFE_PORT_ID_PRIMARY_PCM_RX, Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT},
-	{ AFE_PORT_ID_SECONDARY_PCM_RX, Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT},
-	{ AFE_PORT_ID_TERTIARY_PCM_RX, Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT},
-	{ AFE_PORT_ID_QUATERNARY_PCM_RX, Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT},
-	{ AFE_PORT_ID_QUINARY_PCM_RX, Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT},
-	{ AFE_PORT_ID_SENARY_PCM_RX, Q6AFE_LPASS_CLK_ID_SEN_PCM_IBIT},
-	{ AFE_PORT_ID_PRIMARY_TDM_RX, Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT},
-	{ AFE_PORT_ID_SECONDARY_TDM_RX, Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT},
-	{ AFE_PORT_ID_TERTIARY_TDM_RX, Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT},
-	{ AFE_PORT_ID_QUATERNARY_TDM_RX, Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT},
-	{ AFE_PORT_ID_QUINARY_TDM_RX, Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT},
+struct afe_ext_mclk_cb_info {
+	afe_enable_mclk_and_get_info_cb_func ext_mclk_cb;
+	void *private_data;
+};
+
+static struct afe_clkinfo_per_port clkinfo_per_port[] = {
+	{ AFE_PORT_ID_PRIMARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_SECONDARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_TERTIARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_QUATERNARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_QUINARY_MI2S_RX, Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_SENARY_MI2S_TX, Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_PRIMARY_PCM_RX, Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_SECONDARY_PCM_RX, Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_TERTIARY_PCM_RX, Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_QUATERNARY_PCM_RX, Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_QUINARY_PCM_RX, Q6AFE_LPASS_CLK_ID_QUIN_PCM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_SENARY_PCM_RX, Q6AFE_LPASS_CLK_ID_SEN_PCM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX, Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX, Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX, Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX, Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_QUINARY_TDM_RX, Q6AFE_LPASS_CLK_ID_QUIN_TDM_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
 	{ AFE_PORT_ID_PRIMARY_SPDIF_RX,
-		AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE},
+		AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_OUTPUT_CORE,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
 	{ AFE_PORT_ID_PRIMARY_SPDIF_TX,
-		AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_INPUT_CORE},
+		AFE_CLOCK_SET_CLOCK_ID_PRI_SPDIF_INPUT_CORE,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
 	{ AFE_PORT_ID_SECONDARY_SPDIF_RX,
-		AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_CORE},
+		AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_OUTPUT_CORE,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
 	{ AFE_PORT_ID_SECONDARY_SPDIF_TX,
-		AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_INPUT_CORE},
-	{ AFE_PORT_ID_PRIMARY_META_MI2S_RX,
-		Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT},
-	{ AFE_PORT_ID_SECONDARY_META_MI2S_RX,
-		Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT},
+		AFE_CLOCK_SET_CLOCK_ID_SEC_SPDIF_INPUT_CORE,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_PRIMARY_META_MI2S_RX, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
+	{ AFE_PORT_ID_SECONDARY_META_MI2S_RX, Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+		MCLK_SRC_INT, Q6AFE_EXT_MCLK_FREQ_DEFAULT},
 };
 
+static struct afe_ext_mclk_cb_info afe_ext_mclk;
+
 static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX];
 static unsigned long afe_configured_cmd;
 
@@ -425,6 +455,37 @@ static int afe_get_cal_hw_delay(int32_t path,
 				struct audio_cal_hw_delay_entry *entry);
 static int remap_cal_data(struct cal_block_data *cal_block, int cal_index);
 
+/**
+ * afe_register_ext_mclk_cb - register callback for external mclk
+ *
+ * @fn - external mclk callback function
+ * @private_data - external mclk callback specific data
+ *
+ * Returns 0 in case of success and -EINVAL for failure
+ */
+int afe_register_ext_mclk_cb(afe_enable_mclk_and_get_info_cb_func fn,
+				void *private_data)
+{
+	if (fn && private_data) {
+		afe_ext_mclk.ext_mclk_cb = fn;
+		afe_ext_mclk.private_data = private_data;
+		return 0;
+	}
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL(afe_register_ext_mclk_cb);
+
+/**
+ * afe_unregister_ext_mclk_cb - unregister external mclk callback
+ */
+void afe_unregister_ext_mclk_cb(void)
+{
+	afe_ext_mclk.ext_mclk_cb = NULL;
+	afe_ext_mclk.private_data = NULL;
+}
+EXPORT_SYMBOL(afe_unregister_ext_mclk_cb);
+
 int afe_get_spk_initial_cal(void)
 {
 	return this_afe.initial_cal;
@@ -3036,6 +3097,63 @@ unlock:
 	return ret;
 }
 
+static int afe_port_topology_deregister(u16 port_id)
+{
+	struct param_hdr_v3 param_info;
+	int ret = 0;
+	uint32_t build_major_version = 0;
+	uint32_t build_minor_version = 0;
+	uint32_t build_branch_version = 0;
+	uint32_t afe_api_version = 0;
+
+	ret = q6core_get_avcs_avs_build_version_info(&build_major_version,
+						     &build_minor_version,
+						     &build_branch_version);
+	if (ret < 0) {
+		pr_err("%s: get AVS build versions failed %d\n",
+		       __func__, ret);
+		goto done;
+	}
+
+	afe_api_version = q6core_get_avcs_api_version_per_service(
+					APRV2_IDS_SERVICE_ID_ADSP_AFE_V);
+	if (afe_api_version < 0) {
+		ret = -EINVAL;
+		goto done;
+	}
+	pr_debug("%s: major: %u, minor: %u, branch: %u, afe_api: %u\n",
+		 __func__, build_major_version, build_minor_version,
+		 build_branch_version, afe_api_version);
+
+	if (build_major_version != AVS_BUILD_MAJOR_VERSION_V2 ||
+	    build_minor_version != AVS_BUILD_MINOR_VERSION_V9 ||
+	    (build_branch_version != AVS_BUILD_BRANCH_VERSION_V0 &&
+	     build_branch_version != AVS_BUILD_BRANCH_VERSION_V3) ||
+	    afe_api_version < AFE_API_VERSION_V9) {
+		ret = -EINVAL;
+		pr_err("%s: AVS build versions mismatched %d\n",
+		       __func__, ret);
+		goto done;
+	}
+
+	memset(&param_info, 0, sizeof(param_info));
+	param_info.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	param_info.instance_id = INSTANCE_ID_0;
+	param_info.param_id = AFE_PARAM_ID_DEREGISTER_TOPOLOGY;
+	param_info.param_size =  0;
+	ret = q6afe_pack_and_set_param_in_band(port_id,
+					q6audio_get_port_index(port_id),
+					param_info, NULL);
+	if (ret < 0)
+		pr_err("%s: AFE deregister topology for port 0x%x failed %d\n",
+		       __func__, port_id, ret);
+
+done:
+	pr_debug("%s: AFE port 0x%x deregister topology, ret %d\n",
+		 __func__, port_id, ret);
+	return ret;
+}
+
 static int afe_send_port_topology_id(u16 port_id)
 {
 	struct afe_param_id_set_topology_cfg topology;
@@ -5517,6 +5635,14 @@ static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
 	if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) {
 		/* One time call: only for first time */
 		afe_send_custom_topology();
+		/*
+		 * Deregister existing afe topology before
+		 * sending a new one for VA use cases only
+		 */
+		if (port_id == AFE_PORT_ID_VA_CODEC_DMA_TX_0 ||
+		    port_id == AFE_PORT_ID_VA_CODEC_DMA_TX_1 ||
+		    port_id == AFE_PORT_ID_VA_CODEC_DMA_TX_2)
+			afe_port_topology_deregister(port_id);
 		afe_send_port_topology_id(port_id);
 		afe_send_cal(port_id);
 		afe_send_hw_delay(port_id, rate);
@@ -8980,6 +9106,89 @@ int afe_set_pll_clk_drift(u16 port_id, int32_t set_clk_drift,
 }
 EXPORT_SYMBOL(afe_set_pll_clk_drift);
 
+static int afe_set_lpass_clk_cfg_ext_mclk(int index, struct afe_clk_set *cfg,
+							uint32_t mclk_freq)
+{
+	struct param_hdr_v3 param_hdr;
+	struct afe_param_id_clock_set_v2_t dyn_mclk_cfg;
+	int ret = 0;
+
+	if (!cfg) {
+		pr_err("%s: clock cfg is NULL\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	if (index < 0 || index >= AFE_MAX_PORTS) {
+		pr_err("%s: index[%d] invalid!\n", __func__, index);
+		return -EINVAL;
+	}
+
+	memset(&param_hdr, 0, sizeof(param_hdr));
+	param_hdr.module_id = AFE_MODULE_CLOCK_SET;
+	param_hdr.instance_id = INSTANCE_ID_0;
+	param_hdr.param_id = AFE_PARAM_ID_CLOCK_SET_V2;
+	param_hdr.param_size = sizeof(struct afe_param_id_clock_set_v2_t);
+
+	memset(&dyn_mclk_cfg, 0, sizeof(dyn_mclk_cfg));
+	dyn_mclk_cfg.clk_freq_in_hz = cfg->clk_freq_in_hz;
+	if (afe_ext_mclk.ext_mclk_cb) {
+		ret =  afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
+			cfg->enable, mclk_freq, &dyn_mclk_cfg);
+		if (ret) {
+			pr_err_ratelimited("%s: get mclk cfg failed %d\n",
+					__func__, ret);
+			return ret;
+		}
+	} else {
+		pr_err_ratelimited("%s: mclk callback not registered\n",
+					__func__);
+		return -EINVAL;
+	}
+
+	dyn_mclk_cfg.clk_set_minor_version = 1;
+	dyn_mclk_cfg.clk_id = cfg->clk_id;
+	dyn_mclk_cfg.clk_attri = cfg->clk_attri;
+	dyn_mclk_cfg.enable = cfg->enable;
+
+	pr_debug("%s: Minor version =0x%x clk id = %d\n", __func__,
+		dyn_mclk_cfg.clk_set_minor_version, dyn_mclk_cfg.clk_id);
+	pr_debug("%s: clk freq (Hz) = %d, clk attri = 0x%x\n", __func__,
+		dyn_mclk_cfg.clk_freq_in_hz, dyn_mclk_cfg.clk_attri);
+	pr_debug("%s: clk root = 0x%x clk enable = 0x%x\n", __func__,
+		dyn_mclk_cfg.clk_root, dyn_mclk_cfg.enable);
+	pr_debug("%s: divider_2x =%d m = %d n = %d, d =%d\n", __func__,
+		dyn_mclk_cfg.divider_2x, dyn_mclk_cfg.m, dyn_mclk_cfg.n,
+		dyn_mclk_cfg.d);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err_ratelimited("%s: Q6 interface prepare failed %d\n",
+				__func__, ret);
+		goto stop_mclk;
+	}
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	ret = q6afe_svc_pack_and_set_param_in_band(index, param_hdr,
+						   (u8 *) &dyn_mclk_cfg);
+	if (ret < 0)
+		pr_err_ratelimited("%s: ext MCLK clk cfg failed with ret %d\n",
+				__func__, ret);
+
+	mutex_unlock(&this_afe.afe_cmd_lock);
+
+	if (ret >= 0)
+		return ret;
+
+stop_mclk:
+	if (afe_ext_mclk.ext_mclk_cb && cfg->enable) {
+		afe_ext_mclk.ext_mclk_cb(afe_ext_mclk.private_data,
+				cfg->enable, mclk_freq, &dyn_mclk_cfg);
+	}
+
+	return ret;
+}
+
 /**
  * afe_set_lpass_clk_cfg - Set AFE clk config
  *
@@ -9052,6 +9261,11 @@ int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg)
 {
 	int index = 0;
 	int ret = 0;
+	u16 idx = 0;
+	uint32_t build_major_version = 0;
+	uint32_t build_minor_version = 0;
+	uint32_t build_branch_version = 0;
+	int afe_api_version = 0;
 
 	index = q6audio_get_port_index(port_id);
 	if (index < 0 || index >= AFE_MAX_PORTS) {
@@ -9068,9 +9282,49 @@ int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg)
 
 	ret = afe_set_clk_id(port_id, cfg->clk_id);
 	if (ret < 0)
-		pr_debug("%s: afe_set_clk_id fail %d\n", __func__, ret);
+		pr_err("%s: afe_set_clk_id fail %d\n", __func__, ret);
+
+	idx = afe_get_port_idx(port_id);
+	if (idx < 0) {
+		pr_err("%s: cannot get clock id for port id 0x%x\n", __func__,
+			port_id);
+		return -EINVAL;
+	}
+
+	if (clkinfo_per_port[idx].mclk_src_id != MCLK_SRC_INT) {
+		pr_debug("%s: ext MCLK src %d\n",
+			__func__, clkinfo_per_port[idx].mclk_src_id);
+
+		ret = q6core_get_avcs_avs_build_version_info(
+			&build_major_version, &build_minor_version,
+						&build_branch_version);
+		if (ret < 0)
+			return ret;
+
+		ret = q6core_get_avcs_api_version_per_service(
+					APRV2_IDS_SERVICE_ID_ADSP_AFE_V);
+		if (ret < 0)
+			return ret;
+
+		afe_api_version = ret;
+
+		pr_debug("%s: mjor: %u, mnor: %u, brnch: %u, afe_api: %u\n",
+			__func__, build_major_version, build_minor_version,
+			build_branch_version, afe_api_version);
+		if ((build_major_version != AVS_BUILD_MAJOR_VERSION_V2) ||
+		    (build_minor_version != AVS_BUILD_MINOR_VERSION_V9) ||
+		    (build_branch_version != AVS_BUILD_BRANCH_VERSION_V3) ||
+		    (afe_api_version < AFE_API_VERSION_V8)) {
+			pr_err("%s: ext mclk not supported by AVS\n", __func__);
+			return -EINVAL;
+		}
+
+		ret = afe_set_lpass_clk_cfg_ext_mclk(index, cfg,
+					clkinfo_per_port[idx].mclk_freq);
+	} else {
+		ret = afe_set_lpass_clk_cfg(index, cfg);
+	}
 
-	ret = afe_set_lpass_clk_cfg(index, cfg);
 	if (ret)
 		pr_err("%s: afe_set_lpass_clk_cfg_v2 failed %d\n",
 			__func__, ret);
@@ -9079,6 +9333,36 @@ int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg)
 }
 EXPORT_SYMBOL(afe_set_lpass_clock_v2);
 
+/**
+ * afe_set_mclk_src_cfg - Set audio interface MCLK source configuration
+ *
+ * @port_id: AFE port id
+ * @mclk_src_id: mclk id to represent internal or one of external MCLK
+ * @mclk_freq: frequency of the MCLK
+ *
+ * Returns 0 on success, appropriate error code otherwise
+ */
+int afe_set_mclk_src_cfg(u16 port_id, uint32_t mclk_src_id, uint32_t mclk_freq)
+{
+	int idx = 0;
+
+	idx = afe_get_port_idx(port_id);
+	if (idx < 0) {
+		pr_err("%s: cannot get clock id for port id 0x%x\n",
+			__func__, port_id);
+		return -EINVAL;
+	}
+
+	clkinfo_per_port[idx].mclk_src_id = mclk_src_id;
+	clkinfo_per_port[idx].mclk_freq = mclk_freq;
+
+	pr_debug("%s: mclk src id 0x%x mclk_freq %d port id 0x%x\n",
+		__func__, mclk_src_id, mclk_freq, port_id);
+
+	return 0;
+}
+EXPORT_SYMBOL(afe_set_mclk_src_cfg);
+
 int afe_set_lpass_internal_digital_codec_clock(u16 port_id,
 			struct afe_digital_clk_cfg *cfg)
 {

+ 38 - 0
dsp/q6core.c

@@ -737,6 +737,44 @@ int q6core_get_avcs_api_version_per_service(uint32_t service_id)
 }
 EXPORT_SYMBOL(q6core_get_avcs_api_version_per_service);
 
+/**
+ * q6core_get_avcs_avs_build_version_info - Get AVS build version information
+ *
+ * @build_major_version - pointer to build major version
+ * @build_minor_version - pointer to build minor version
+ * @build_branch_version - pointer to build branch version
+ *
+ * Returns 0 on success and error on failure
+ */
+int q6core_get_avcs_avs_build_version_info(
+	uint32_t *build_major_version, uint32_t *build_minor_version,
+					uint32_t *build_branch_version)
+{
+
+	struct avcs_fwk_ver_info *cached_ver_info = NULL;
+	int ret = 0;
+
+	if (!build_major_version || !build_minor_version ||
+		!build_branch_version)
+		return -EINVAL;
+
+	ret = q6core_get_avcs_fwk_version();
+	if (ret < 0)
+		return ret;
+
+	cached_ver_info = q6core_lcl.q6core_avcs_ver_info.ver_info;
+
+	*build_major_version =
+			cached_ver_info->avcs_fwk_version.build_major_version;
+	*build_minor_version =
+			cached_ver_info->avcs_fwk_version.build_minor_version;
+	*build_branch_version =
+			cached_ver_info->avcs_fwk_version.build_branch_version;
+
+	return ret;
+}
+EXPORT_SYMBOL(q6core_get_avcs_avs_build_version_info);
+
 /**
  * core_set_license -
  *       command to set license for module

+ 23 - 0
include/dsp/apr_audio-v2.h

@@ -3965,6 +3965,7 @@ struct afe_param_id_device_hw_delay_cfg {
 } __packed;
 
 #define AFE_PARAM_ID_SET_TOPOLOGY    0x0001025A
+#define AFE_PARAM_ID_DEREGISTER_TOPOLOGY	0x000102E8
 #define AFE_API_VERSION_TOPOLOGY_V1 0x1
 
 struct afe_param_id_set_topology_cfg {
@@ -12190,6 +12191,28 @@ struct afe_clk_set {
 	uint32_t enable;
 };
 
+#define AVS_BUILD_MAJOR_VERSION_V2		2
+#define AVS_BUILD_MINOR_VERSION_V9		9
+#define AVS_BUILD_BRANCH_VERSION_V0		0
+#define AVS_BUILD_BRANCH_VERSION_V3		3
+
+#define AFE_PARAM_ID_CLOCK_SET_V2		0x000102E6
+
+#define AFE_API_VERSION_CLOCK_SET_V2		0x1
+
+struct afe_param_id_clock_set_v2_t {
+	uint32_t	clk_set_minor_version;
+	uint32_t	clk_id;
+	uint32_t	clk_freq_in_hz;
+	uint16_t	clk_attri;
+	uint16_t	clk_root;
+	uint32_t	enable;
+	uint32_t	divider_2x;
+	uint32_t	m;
+	uint32_t	n;
+	uint32_t	d;
+};
+
 struct afe_clk_cfg {
 /* Minor version used for tracking the version of the I2S
  * configuration interface.

+ 34 - 0
include/dsp/q6afe-v2.h

@@ -50,6 +50,9 @@
 #define AFE_API_VERSION_V6		6
 /* for Speaker Protection V4 */
 #define AFE_API_VERSION_V9		9
+/* for external mclk dynamic switch */
+#define AFE_API_VERSION_V8		8
+
 
 typedef int (*routing_cb)(int port);
 
@@ -319,6 +322,24 @@ struct vad_config {
 	u32 pre_roll;
 };
 
+enum afe_mclk_src_id {
+	MCLK_SRC_INT = 0x00,
+	MCLK_SRC_EXT_0 = 0x01,
+	MCLK_SRC_MAX,
+};
+
+enum afe_mclk_freq {
+	MCLK_FREQ_MIN = 0,
+	MCLK_FREQ_11P2896_MHZ = MCLK_FREQ_MIN,
+	MCLK_FREQ_12P288_MHZ,
+	MCLK_FREQ_16P384_MHZ,
+	MCLK_FREQ_22P5792_MHZ,
+	MCLK_FREQ_24P576_MHZ,
+	MCLK_FREQ_MAX,
+};
+
+#define Q6AFE_EXT_MCLK_FREQ_DEFAULT 0
+
 struct afe_audio_buffer {
 	dma_addr_t phys;
 	void       *data;
@@ -515,6 +536,19 @@ enum {
 	AFE_LPASS_CORE_HW_DCODEC_BLOCK,
 	AFE_LPASS_CORE_HW_VOTE_MAX
 };
+int afe_set_mclk_src_cfg(u16 port_id, uint32_t mclk_src_id, uint32_t mclk_freq);
+
+typedef int (*afe_enable_mclk_and_get_info_cb_func) (void *private_data,
+			uint32_t enable, uint32_t mclk_freq,
+			struct afe_param_id_clock_set_v2_t *dyn_mclk_cfg);
+
+int afe_register_ext_mclk_cb(afe_enable_mclk_and_get_info_cb_func fn1,
+				void *private_data);
+void afe_unregister_ext_mclk_cb(void);
+
+#define AFE_LPASS_CORE_HW_BLOCK_ID_NONE                        0
+#define AFE_LPASS_CORE_HW_BLOCK_ID_AVTIMER                     2
+#define AFE_LPASS_CORE_HW_MACRO_BLOCK                          3
 
 /* Handles audio-video timer (avtimer) and BTSC vote requests from clients.
  */

+ 3 - 0
include/dsp/q6core.h

@@ -435,4 +435,7 @@ int q6core_destroy_lpass_npa_client(uint32_t client_handle);
 int q6core_request_island_transition(uint32_t client_handle,
 				     uint32_t island_allow_mode);
 
+int q6core_get_avcs_avs_build_version_info(
+	uint32_t *build_major_version, uint32_t *build_minor_version,
+					uint32_t *build_branch_version);
 #endif /* __Q6CORE_H__ */