Browse Source

Merge "asoc: Add support for META MI2S ports"

qctecmdr 5 years ago
parent
commit
a634c77018
5 changed files with 986 additions and 5 deletions
  1. 726 0
      asoc/msm-dai-q6-v2.c
  2. 12 0
      asoc/msm-dai-q6-v2.h
  3. 237 1
      asoc/msm-pcm-routing-v2.c
  4. 5 0
      asoc/msm-pcm-routing-v2.h
  5. 6 4
      asoc/msm-qti-pp-config.c

+ 726 - 0
asoc/msm-dai-q6-v2.c

@@ -261,6 +261,17 @@ struct msm_dai_q6_mi2s_dai_data {
 	struct msm_dai_q6_mi2s_dai_config rx_dai;
 };
 
+struct msm_dai_q6_meta_mi2s_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	u16 num_member_ports;
+	u16 member_port_id[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+	u16 channel_mode[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+	u32 rate;
+	u32 channels;
+	u32 bitwidth;
+	union afe_port_config port_config;
+};
+
 struct msm_dai_q6_cdc_dma_dai_data {
 	DECLARE_BITMAP(status_mask, STATUS_MAX);
 	DECLARE_BITMAP(hwfree_status, STATUS_MAX);
@@ -6096,6 +6107,42 @@ error_invalid_data:
 	return -EINVAL;
 }
 
+static u16 msm_dai_q6_mi2s_get_num_channels(u16 config)
+{
+	switch (config) {
+	case AFE_PORT_I2S_SD0:
+	case AFE_PORT_I2S_SD1:
+	case AFE_PORT_I2S_SD2:
+	case AFE_PORT_I2S_SD3:
+	case AFE_PORT_I2S_SD4:
+	case AFE_PORT_I2S_SD5:
+	case AFE_PORT_I2S_SD6:
+	case AFE_PORT_I2S_SD7:
+		return 2;
+	case AFE_PORT_I2S_QUAD01:
+	case AFE_PORT_I2S_QUAD23:
+	case AFE_PORT_I2S_QUAD45:
+	case AFE_PORT_I2S_QUAD67:
+		return 4;
+	case AFE_PORT_I2S_6CHS:
+		return 6;
+	case AFE_PORT_I2S_8CHS:
+	case AFE_PORT_I2S_8CHS_2:
+		return 8;
+	case AFE_PORT_I2S_10CHS:
+		return 10;
+	case AFE_PORT_I2S_12CHS:
+		return 12;
+	case AFE_PORT_I2S_14CHS:
+		return 14;
+	case AFE_PORT_I2S_16CHS:
+		return 16;
+	default:
+		pr_err("%s: invalid config\n", __func__);
+		return 0;
+	}
+}
+
 static int msm_dai_q6_mi2s_platform_data_validation(
 	struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
 {
@@ -6261,6 +6308,657 @@ static int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int msm_dai_q6_dai_meta_mi2s_probe(struct snd_soc_dai *dai)
+{
+	struct msm_meta_mi2s_pdata *meta_mi2s_pdata =
+			(struct msm_meta_mi2s_pdata *) dai->dev->platform_data;
+	int rc = 0;
+
+	dai->id = meta_mi2s_pdata->intf_id;
+	rc = msm_dai_q6_dai_add_route(dai);
+	return rc;
+}
+
+static int msm_dai_q6_dai_meta_mi2s_remove(struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static int msm_dai_q6_meta_mi2s_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	return 0;
+}
+
+static int msm_meta_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id)
+{
+	int ret = 0;
+
+	switch (stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		switch (mi2s_id) {
+		case MSM_PRIM_META_MI2S:
+			*port_id = AFE_PORT_ID_PRIMARY_META_MI2S_RX;
+			break;
+		case MSM_SEC_META_MI2S:
+			*port_id = AFE_PORT_ID_SECONDARY_META_MI2S_RX;
+			break;
+		default:
+			pr_err("%s: playback err id 0x%x\n",
+				__func__, mi2s_id);
+			ret = -1;
+			break;
+		}
+		break;
+
+	case SNDRV_PCM_STREAM_CAPTURE:
+		switch (mi2s_id) {
+		default:
+			pr_err("%s: capture err id 0x%x\n", __func__, mi2s_id);
+			ret = -1;
+			break;
+		}
+		break;
+
+	default:
+		pr_err("%s: default err %d\n", __func__, stream);
+		ret = -1;
+		break;
+	}
+	pr_debug("%s: port_id = 0x%x\n", __func__, *port_id);
+	return ret;
+}
+
+static int msm_dai_q6_meta_mi2s_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	u16 port_id = 0;
+	int rc = 0;
+
+	if (msm_meta_mi2s_get_port_id(dai->id, substream->stream,
+		&port_id) != 0) {
+		dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
+			__func__, port_id);
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->dev, "%s: dai id %d, afe port id = 0x%x\n"
+		"dai_data->channels = %u sample_rate = %u\n", __func__,
+		dai->id, port_id, dai_data->channels, dai_data->rate);
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		/* PORT START should be set if prepare called
+		 * in active state.
+		 */
+		rc = afe_port_start(port_id, &dai_data->port_config,
+				    dai_data->rate);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+				dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_meta_mi2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	struct afe_param_id_meta_i2s_cfg *port_cfg =
+		&dai_data->port_config.meta_i2s;
+	int idx = 0;
+	u16 port_channels = 0;
+	u16 channels_left = 0;
+
+	dai_data->channels = params_channels(params);
+	channels_left = dai_data->channels;
+
+	/* map requested channels to channels that member ports provide */
+	for (idx = 0; idx < dai_data->num_member_ports; idx++) {
+		port_channels = msm_dai_q6_mi2s_get_num_channels(
+			dai_data->channel_mode[idx]);
+
+		if (channels_left >= port_channels) {
+			port_cfg->member_port_id[idx] =
+				dai_data->member_port_id[idx];
+			port_cfg->member_port_channel_mode[idx] =
+				dai_data->channel_mode[idx];
+			channels_left -= port_channels;
+		} else {
+			switch (channels_left) {
+			case 15:
+			case 16:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_16CHS;
+					break;
+				default:
+					goto error_invalid_data;
+				};
+				break;
+			case 13:
+			case 14:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_14CHS:
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_14CHS;
+					break;
+				default:
+					goto error_invalid_data;
+				};
+				break;
+			case 11:
+			case 12:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_12CHS:
+				case AFE_PORT_I2S_14CHS:
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_12CHS;
+					break;
+				default:
+					goto error_invalid_data;
+				};
+				break;
+			case 9:
+			case 10:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_10CHS:
+				case AFE_PORT_I2S_12CHS:
+				case AFE_PORT_I2S_14CHS:
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_10CHS;
+					break;
+				default:
+					goto error_invalid_data;
+				};
+				break;
+			case 8:
+			case 7:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_8CHS:
+				case AFE_PORT_I2S_10CHS:
+				case AFE_PORT_I2S_12CHS:
+				case AFE_PORT_I2S_14CHS:
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_8CHS;
+					break;
+				case AFE_PORT_I2S_8CHS_2:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_8CHS_2;
+					break;
+				default:
+					goto error_invalid_data;
+				};
+				break;
+			case 6:
+			case 5:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_6CHS:
+				case AFE_PORT_I2S_8CHS:
+				case AFE_PORT_I2S_10CHS:
+				case AFE_PORT_I2S_12CHS:
+				case AFE_PORT_I2S_14CHS:
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_6CHS;
+					break;
+				default:
+					goto error_invalid_data;
+				};
+				break;
+			case 4:
+			case 3:
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_SD0:
+				case AFE_PORT_I2S_SD1:
+				case AFE_PORT_I2S_SD2:
+				case AFE_PORT_I2S_SD3:
+				case AFE_PORT_I2S_SD4:
+				case AFE_PORT_I2S_SD5:
+				case AFE_PORT_I2S_SD6:
+				case AFE_PORT_I2S_SD7:
+					goto error_invalid_data;
+				case AFE_PORT_I2S_QUAD01:
+				case AFE_PORT_I2S_QUAD23:
+				case AFE_PORT_I2S_QUAD45:
+				case AFE_PORT_I2S_QUAD67:
+					port_cfg->member_port_channel_mode[idx]
+						= dai_data->channel_mode[idx];
+					break;
+				case AFE_PORT_I2S_8CHS_2:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_QUAD45;
+					break;
+				default:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_QUAD01;
+				};
+				break;
+			case 2:
+			case 1:
+				if (dai_data->channel_mode[idx] <
+					AFE_PORT_I2S_SD0)
+					goto error_invalid_data;
+				switch (dai_data->channel_mode[idx]) {
+				case AFE_PORT_I2S_SD0:
+				case AFE_PORT_I2S_SD1:
+				case AFE_PORT_I2S_SD2:
+				case AFE_PORT_I2S_SD3:
+				case AFE_PORT_I2S_SD4:
+				case AFE_PORT_I2S_SD5:
+				case AFE_PORT_I2S_SD6:
+				case AFE_PORT_I2S_SD7:
+					port_cfg->member_port_channel_mode[idx]
+						= dai_data->channel_mode[idx];
+					break;
+				case AFE_PORT_I2S_QUAD01:
+				case AFE_PORT_I2S_6CHS:
+				case AFE_PORT_I2S_8CHS:
+				case AFE_PORT_I2S_10CHS:
+				case AFE_PORT_I2S_12CHS:
+				case AFE_PORT_I2S_14CHS:
+				case AFE_PORT_I2S_16CHS:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_SD0;
+					break;
+				case AFE_PORT_I2S_QUAD23:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_SD2;
+					break;
+				case AFE_PORT_I2S_QUAD45:
+				case AFE_PORT_I2S_8CHS_2:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_SD4;
+					break;
+				case AFE_PORT_I2S_QUAD67:
+					port_cfg->member_port_channel_mode[idx]
+						= AFE_PORT_I2S_SD6;
+					break;
+				}
+				break;
+			case 0:
+				port_cfg->member_port_channel_mode[idx] = 0;
+			}
+			if (port_cfg->member_port_channel_mode[idx] == 0) {
+				port_cfg->member_port_id[idx] =
+					AFE_PORT_ID_INVALID;
+			} else {
+				port_cfg->member_port_id[idx] =
+					dai_data->member_port_id[idx];
+				channels_left -=
+					msm_dai_q6_mi2s_get_num_channels(
+					port_cfg->member_port_channel_mode[idx]);
+			}
+		}
+	}
+
+	if (channels_left > 0) {
+		pr_err("%s: too many channels %d\n",
+			__func__, dai_data->channels);
+		return -EINVAL;
+	}
+
+	dai_data->rate = params_rate(params);
+	port_cfg->sample_rate = dai_data->rate;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		port_cfg->bit_width = 16;
+		dai_data->bitwidth = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		port_cfg->bit_width = 24;
+		dai_data->bitwidth = 24;
+		break;
+	default:
+		pr_err("%s: format %d\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+
+	port_cfg->minor_version = AFE_API_VERSION_META_I2S_CONFIG;
+	port_cfg->data_format = AFE_LINEAR_PCM_DATA;
+
+	dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n"
+		"bit_width = %hu ws_src = 0x%x sample_rate = %u\n"
+		"member_ports 0x%x 0x%x 0x%x 0x%x\n"
+		"sd_lines 0x%x 0x%x 0x%x 0x%x\n",
+		__func__, dai->id, dai_data->channels,
+		port_cfg->bit_width, port_cfg->ws_src, port_cfg->sample_rate,
+		port_cfg->member_port_id[0],
+		port_cfg->member_port_id[1],
+		port_cfg->member_port_id[2],
+		port_cfg->member_port_id[3],
+		port_cfg->member_port_channel_mode[0],
+		port_cfg->member_port_channel_mode[1],
+		port_cfg->member_port_channel_mode[2],
+		port_cfg->member_port_channel_mode[3]);
+	return 0;
+
+error_invalid_data:
+	pr_err("%s: error when assigning member port %d channels (channels_left %d)\n",
+		__func__, idx, channels_left);
+	return -EINVAL;
+}
+
+static int msm_dai_q6_meta_mi2s_set_fmt(struct snd_soc_dai *dai,
+	unsigned int fmt)
+{
+	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		dev_err(dai->dev, "%s: err chg meta i2s mode while dai running",
+			__func__);
+		return -EPERM;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dai_data->port_config.meta_i2s.ws_src = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		dai_data->port_config.meta_i2s.ws_src = 0;
+		break;
+	default:
+		pr_err("%s: fmt %d\n",
+			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void msm_dai_q6_meta_mi2s_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	u16 port_id = 0;
+	int rc = 0;
+
+	if (msm_meta_mi2s_get_port_id(dai->id, substream->stream,
+				 &port_id) != 0) {
+		dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
+			__func__, port_id);
+	}
+
+	dev_dbg(dai->dev, "%s: closing afe port id = 0x%x\n",
+			__func__, port_id);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(port_id);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AFE port\n");
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_meta_mi2s_ops = {
+	.startup	= msm_dai_q6_meta_mi2s_startup,
+	.prepare	= msm_dai_q6_meta_mi2s_prepare,
+	.hw_params	= msm_dai_q6_meta_mi2s_hw_params,
+	.set_fmt	= msm_dai_q6_meta_mi2s_set_fmt,
+	.shutdown	= msm_dai_q6_meta_mi2s_shutdown,
+};
+
+/* Channel min and max are initialized base on platform data */
+static struct snd_soc_dai_driver msm_dai_q6_meta_mi2s_dai[] = {
+	{
+		.playback = {
+			.stream_name = "Primary META MI2S Playback",
+			.aif_name = "PRI_META_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+				SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+				SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+				SNDRV_PCM_RATE_384000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.ops = &msm_dai_q6_meta_mi2s_ops,
+		.name = "Primary META MI2S",
+		.id = AFE_PORT_ID_PRIMARY_META_MI2S_RX,
+		.probe = msm_dai_q6_dai_meta_mi2s_probe,
+		.remove = msm_dai_q6_dai_meta_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary META MI2S Playback",
+			.aif_name = "SEC_META_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.ops = &msm_dai_q6_meta_mi2s_ops,
+		.name = "Secondary META MI2S",
+		.id = AFE_PORT_ID_SECONDARY_META_MI2S_RX,
+		.probe = msm_dai_q6_dai_meta_mi2s_probe,
+		.remove = msm_dai_q6_dai_meta_mi2s_remove,
+	},
+};
+
+static int msm_dai_q6_meta_mi2s_platform_data_validation(
+	struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
+{
+	struct msm_dai_q6_meta_mi2s_dai_data *dai_data =
+		dev_get_drvdata(&pdev->dev);
+	struct msm_meta_mi2s_pdata *meta_mi2s_pdata =
+		(struct msm_meta_mi2s_pdata *) pdev->dev.platform_data;
+	int rc = 0;
+	int idx = 0;
+	u16 channel_mode = 0;
+	unsigned int ch_cnt = 0;
+	unsigned int ch_cnt_sum = 0;
+	struct afe_param_id_meta_i2s_cfg *port_cfg =
+		&dai_data->port_config.meta_i2s;
+
+	if (meta_mi2s_pdata == NULL) {
+		pr_err("%s: meta_mi2s_pdata NULL", __func__);
+		return -EINVAL;
+	}
+
+	dai_data->num_member_ports = meta_mi2s_pdata->num_member_ports;
+	for (idx = 0; idx < meta_mi2s_pdata->num_member_ports; idx++) {
+		rc = msm_dai_q6_mi2s_get_lineconfig(
+			meta_mi2s_pdata->sd_lines[idx],
+			&channel_mode,
+			&ch_cnt);
+		if (rc < 0) {
+			dev_err(&pdev->dev, "invalid META MI2S RX sd line config\n");
+			goto rtn;
+		}
+		if (ch_cnt) {
+			msm_mi2s_get_port_id(meta_mi2s_pdata->member_port[idx],
+				SNDRV_PCM_STREAM_PLAYBACK,
+				&dai_data->member_port_id[idx]);
+			dai_data->channel_mode[idx] = channel_mode;
+			port_cfg->member_port_id[idx] =
+				dai_data->member_port_id[idx];
+			port_cfg->member_port_channel_mode[idx] = channel_mode;
+		}
+		ch_cnt_sum += ch_cnt;
+	}
+
+	if (ch_cnt_sum) {
+		dai_driver->playback.channels_min = 1;
+		dai_driver->playback.channels_max = ch_cnt_sum << 1;
+	} else {
+		dai_driver->playback.channels_min = 0;
+		dai_driver->playback.channels_max = 0;
+	}
+
+	dev_dbg(&pdev->dev, "%s: sdline 0x%x 0x%x 0x%x 0x%x\n", __func__,
+		dai_data->channel_mode[0], dai_data->channel_mode[1],
+		dai_data->channel_mode[2], dai_data->channel_mode[3]);
+	dev_dbg(&pdev->dev, "%s: playback ch_max %d\n",
+		__func__, dai_driver->playback.channels_max);
+rtn:
+	return rc;
+}
+
+static const struct snd_soc_component_driver msm_q6_meta_mi2s_dai_component = {
+	.name		= "msm-dai-q6-meta-mi2s",
+};
+
+static int msm_dai_q6_meta_mi2s_dev_probe(struct platform_device *pdev)
+{
+	struct msm_dai_q6_meta_mi2s_dai_data *dai_data;
+	const char *q6_meta_mi2s_dev_id = "qcom,msm-dai-q6-meta-mi2s-dev-id";
+	u32 dev_id = 0;
+	u32 meta_mi2s_intf = 0;
+	struct msm_meta_mi2s_pdata *meta_mi2s_pdata;
+	int rc;
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_meta_mi2s_dev_id,
+				  &dev_id);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__,
+			q6_meta_mi2s_dev_id);
+		goto rtn;
+	}
+
+	dev_dbg(&pdev->dev, "dev name %s dev id 0x%x\n", dev_name(&pdev->dev),
+		dev_id);
+
+	switch (dev_id) {
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+		meta_mi2s_intf = 0;
+		break;
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+		meta_mi2s_intf = 1;
+		break;
+	default:
+		dev_err(&pdev->dev,
+			"%s: Invalid META MI2S ID 0x%x from Device Tree\n",
+			__func__, dev_id);
+		rc = -ENXIO;
+		goto rtn;
+	}
+
+	pdev->id = dev_id;
+
+	meta_mi2s_pdata = kzalloc(sizeof(struct msm_meta_mi2s_pdata),
+		GFP_KERNEL);
+	if (!meta_mi2s_pdata) {
+		rc = -ENOMEM;
+		goto rtn;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-mi2s-num-members",
+		&meta_mi2s_pdata->num_member_ports);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: invalid num from DT file %s\n",
+			__func__, "qcom,msm-mi2s-num-members");
+		goto free_pdata;
+	}
+
+	if (meta_mi2s_pdata->num_member_ports >
+		MAX_NUM_I2S_META_PORT_MEMBER_PORTS) {
+		dev_err(&pdev->dev, "%s: num-members %d too large from DT file\n",
+			__func__, meta_mi2s_pdata->num_member_ports);
+		goto free_pdata;
+	}
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,msm-mi2s-member-id",
+		meta_mi2s_pdata->member_port,
+		meta_mi2s_pdata->num_member_ports);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: member-id from DT file %s\n",
+			__func__, "qcom,msm-mi2s-member-id");
+		goto free_pdata;
+	}
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,msm-mi2s-rx-lines",
+		meta_mi2s_pdata->sd_lines,
+		meta_mi2s_pdata->num_member_ports);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Rx line from DT file %s\n",
+			__func__, "qcom,msm-mi2s-rx-lines");
+		goto free_pdata;
+	}
+
+	dev_dbg(&pdev->dev, "dev name %s num-members=%d\n",
+		dev_name(&pdev->dev), meta_mi2s_pdata->num_member_ports);
+	dev_dbg(&pdev->dev, "member array (%d, %d, %d, %d)\n",
+		meta_mi2s_pdata->member_port[0],
+		meta_mi2s_pdata->member_port[1],
+		meta_mi2s_pdata->member_port[2],
+		meta_mi2s_pdata->member_port[3]);
+	dev_dbg(&pdev->dev, "sd-lines array (0x%x, 0x%x, 0x%x, 0x%x)\n",
+		meta_mi2s_pdata->sd_lines[0],
+		meta_mi2s_pdata->sd_lines[1],
+		meta_mi2s_pdata->sd_lines[2],
+		meta_mi2s_pdata->sd_lines[3]);
+
+	meta_mi2s_pdata->intf_id = meta_mi2s_intf;
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_meta_mi2s_dai_data),
+			   GFP_KERNEL);
+	if (!dai_data) {
+		rc = -ENOMEM;
+		goto free_pdata;
+	} else
+		dev_set_drvdata(&pdev->dev, dai_data);
+
+	pdev->dev.platform_data = meta_mi2s_pdata;
+
+	rc = msm_dai_q6_meta_mi2s_platform_data_validation(pdev,
+		&msm_dai_q6_meta_mi2s_dai[meta_mi2s_intf]);
+	if (rc < 0)
+		goto free_dai_data;
+
+	rc = snd_soc_register_component(&pdev->dev,
+		&msm_q6_meta_mi2s_dai_component,
+		&msm_dai_q6_meta_mi2s_dai[meta_mi2s_intf], 1);
+	if (rc < 0)
+		goto err_register;
+	return 0;
+
+err_register:
+	dev_err(&pdev->dev, "fail to %s\n", __func__);
+free_dai_data:
+	kfree(dai_data);
+free_pdata:
+	kfree(meta_mi2s_pdata);
+rtn:
+	return rc;
+}
+
+static int msm_dai_q6_meta_mi2s_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
 static const struct snd_soc_component_driver msm_dai_q6_component = {
 	.name		= "msm-dai-q6-dev",
 };
@@ -6627,6 +7325,24 @@ static struct platform_driver msm_dai_q6_mi2s_driver = {
 	},
 };
 
+static const struct of_device_id msm_dai_q6_meta_mi2s_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-q6-meta-mi2s", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_meta_mi2s_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_meta_mi2s_driver = {
+	.probe  = msm_dai_q6_meta_mi2s_dev_probe,
+	.remove  = msm_dai_q6_meta_mi2s_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-meta-mi2s",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_meta_mi2s_dev_dt_match,
+		.suppress_bind_attrs = true,
+	},
+};
+
 static int msm_dai_q6_spdif_dev_probe(struct platform_device *pdev)
 {
 	int rc, id;
@@ -12175,6 +12891,13 @@ int __init msm_dai_q6_init(void)
 		goto dai_q6_mi2s_drv_fail;
 	}
 
+	rc = platform_driver_register(&msm_dai_q6_meta_mi2s_driver);
+	if (rc) {
+		pr_err("%s: fail to register dai META MI2S dev drv\n",
+			__func__);
+		goto dai_q6_meta_mi2s_drv_fail;
+	}
+
 	rc = platform_driver_register(&msm_dai_mi2s_q6);
 	if (rc) {
 		pr_err("%s: fail to register dai MI2S\n", __func__);
@@ -12224,6 +12947,8 @@ dai_q6_tdm_drv_fail:
 dai_spdif_q6_fail:
 	platform_driver_unregister(&msm_dai_mi2s_q6);
 dai_mi2s_q6_fail:
+	platform_driver_unregister(&msm_dai_q6_meta_mi2s_driver);
+dai_q6_meta_mi2s_drv_fail:
 	platform_driver_unregister(&msm_dai_q6_mi2s_driver);
 dai_q6_mi2s_drv_fail:
 	platform_driver_unregister(&msm_dai_q6_dev);
@@ -12243,6 +12968,7 @@ void msm_dai_q6_exit(void)
 	platform_driver_unregister(&msm_dai_q6_tdm_driver);
 	platform_driver_unregister(&msm_dai_q6_spdif_driver);
 	platform_driver_unregister(&msm_dai_mi2s_q6);
+	platform_driver_unregister(&msm_dai_q6_meta_mi2s_driver);
 	platform_driver_unregister(&msm_dai_q6_mi2s_driver);
 	platform_driver_unregister(&msm_dai_q6_dev);
 	platform_driver_unregister(&msm_dai_q6);

+ 12 - 0
asoc/msm-dai-q6-v2.h

@@ -38,6 +38,11 @@
 #define MSM_DISPLAY_PORT	0
 #define MSM_DISPLAY_PORT1	1
 
+#define MSM_PRIM_META_MI2S 0
+#define MSM_SEC_META_MI2S  1
+#define MSM_META_MI2S_MIN  MSM_PRIM_META_MI2S
+#define MSM_META_MI2S_MAX  MSM_SEC_META_MI2S
+
 struct msm_dai_auxpcm_config {
 	u16 mode;
 	u16 sync;
@@ -60,6 +65,13 @@ struct msm_mi2s_pdata {
 	u16 intf_id;
 };
 
+struct msm_meta_mi2s_pdata {
+	u32 num_member_ports;
+	u32 member_port[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+	u32 sd_lines[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+	u16 intf_id;
+};
+
 struct msm_i2s_data {
 	u32 capability; /* RX or TX */
 	u16 sd_lines;

+ 237 - 1
asoc/msm-pcm-routing-v2.c

@@ -689,6 +689,10 @@ struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
 	{ SLIMBUS_9_RX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_9_RX},
 	{ SLIMBUS_9_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_SLIMBUS_9_TX},
 	{ AFE_LOOPBACK_TX, 0, {0}, {0}, 0, 0, 0, 0, LPASS_BE_AFE_LOOPBACK_TX},
+	{ AFE_PORT_ID_PRIMARY_META_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0,
+	  LPASS_BE_PRI_META_MI2S_RX},
+	{ AFE_PORT_ID_SECONDARY_META_MI2S_RX, 0, {0}, {0}, 0, 0, 0, 0,
+	  LPASS_BE_SEC_META_MI2S_RX},
 };
 
 /* Track ASM playback & capture sessions of DAI
@@ -3435,7 +3439,8 @@ static const char *const be_name[] = {
 "RX_CDC_DMA_RX_4", "TX_CDC_DMA_TX_4", "RX_CDC_DMA_RX_5", "TX_CDC_DMA_TX_5",
 "RX_CDC_DMA_RX_6", "RX_CDC_DMA_RX_7",
 "PRI_SPDIF_TX", "SEC_SPDIF_RX", "SEC_SPDIF_TX",
-"SLIM_9_RX", "SLIM_9_TX", "AFE_LOOPBACK_TX"
+"SLIM_9_RX", "SLIM_9_TX", "AFE_LOOPBACK_TX", "PRI_META_MI2S_RX",
+"SEC_META_MI2S_RX"
 };
 
 static SOC_ENUM_SINGLE_DECL(mm1_channel_mux,
@@ -7143,6 +7148,189 @@ static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = {
 	msm_routing_put_audio_mixer),
 };
 
+static const struct snd_kcontrol_new pri_meta_mi2s_rx_mixer_controls[] = {
+	SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+
+};
+
+static const struct snd_kcontrol_new sec_meta_mi2s_rx_mixer_controls[] = {
+	SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia2", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia3", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia4", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia5", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia6", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia7", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia8", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia9", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia10", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia11", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia12", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia13", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia14", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia15", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia16", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia17", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia18", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia19", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia26", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA26, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia28", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA28, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_DOUBLE_EXT("MultiMedia29", SND_SOC_NOPM,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA29, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
 static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
 	SOC_DOUBLE_EXT("MultiMedia1", SND_SOC_NOPM,
 	MSM_BACKEND_DAI_HDMI_RX,
@@ -23169,6 +23357,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
 				0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("SEN_TDM_TX_7", "Senary TDM7 Capture",
 				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_META_MI2S_RX", "Primary META MI2S Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_META_MI2S_RX", "Secondary META MI2S Playback",
+			     0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_OUT("WSA_CDC_DMA_RX_0", "WSA CDC DMA0 Playback",
 				0, 0, 0, 0),
 	SND_SOC_DAPM_AIF_IN("WSA_CDC_DMA_TX_0", "WSA CDC DMA0 Capture",
@@ -23479,6 +23671,12 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
 	SND_SOC_DAPM_MIXER("SEN_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
 				sen_tdm_rx_3_mixer_controls,
 				ARRAY_SIZE(sen_tdm_rx_3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PRI_META_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+				pri_meta_mi2s_rx_mixer_controls,
+				ARRAY_SIZE(pri_meta_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_META_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+				sec_meta_mi2s_rx_mixer_controls,
+				ARRAY_SIZE(sec_meta_mi2s_rx_mixer_controls)),
 	SND_SOC_DAPM_MIXER("WSA_CDC_DMA_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
 				wsa_cdc_dma_rx_0_mixer_controls,
 				ARRAY_SIZE(wsa_cdc_dma_rx_0_mixer_controls)),
@@ -24702,6 +24900,44 @@ static const struct snd_soc_dapm_route intercon[] = {
 	{"SEN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
 	{"SEN_MI2S_RX", NULL, "SEN_MI2S_RX Audio Mixer"},
 
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"PRI_META_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+	{"PRI_META_MI2S_RX", NULL, "PRI_META_MI2S_RX Audio Mixer"},
+
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SEC_META_MI2S_RX Audio Mixer", "MultiMedia26", "MM_DL26"},
+	{"SEC_META_MI2S_RX", NULL, "SEC_META_MI2S_RX Audio Mixer"},
+
 	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
 	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
 	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},

+ 5 - 0
asoc/msm-pcm-routing-v2.h

@@ -81,6 +81,9 @@
 #define LPASS_BE_SENARY_MI2S_TX "SENARY_MI2S_TX"
 #define LPASS_BE_SENARY_MI2S_RX "SENARY_MI2S_RX"
 
+#define LPASS_BE_PRI_META_MI2S_RX "PRI_META_MI2S_RX"
+#define LPASS_BE_SEC_META_MI2S_RX "SEC_META_MI2S_RX"
+
 #define LPASS_BE_PRI_TDM_RX_0 "PRI_TDM_RX_0"
 #define LPASS_BE_PRI_TDM_TX_0 "PRI_TDM_TX_0"
 #define LPASS_BE_PRI_TDM_RX_1 "PRI_TDM_RX_1"
@@ -494,6 +497,8 @@ enum {
 	MSM_BACKEND_DAI_SLIMBUS_9_RX,
 	MSM_BACKEND_DAI_SLIMBUS_9_TX,
 	MSM_BACKEND_DAI_AFE_LOOPBACK_TX,
+	MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+	MSM_BACKEND_DAI_SEC_META_MI2S_RX,
 	MSM_BACKEND_DAI_MAX,
 };
 

+ 6 - 4
asoc/msm-qti-pp-config.c

@@ -1484,14 +1484,16 @@ static const struct snd_kcontrol_new sec_auxpcm_lb_vol_mixer_controls[] = {
 };
 
 static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
-	SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 47,
-	0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_mixer,
+	SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM,
+	0, PCM_MAX_CHMAP_ID, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8,
+	msm_qti_pp_get_channel_map_mixer,
 	msm_qti_pp_put_channel_map_mixer),
 };
 
 static const struct snd_kcontrol_new multi_ch_channel_map_capture_controls[] = {
-	SOC_SINGLE_MULTI_EXT("Capture Device Channel Map", SND_SOC_NOPM, 0, 47,
-	0, PCM_FORMAT_MAX_NUM_CHANNEL_V8, msm_qti_pp_get_channel_map_capture,
+	SOC_SINGLE_MULTI_EXT("Capture Device Channel Map", SND_SOC_NOPM,
+	0, PCM_MAX_CHMAP_ID, 0, PCM_FORMAT_MAX_NUM_CHANNEL_V8,
+	msm_qti_pp_get_channel_map_capture,
 	msm_qti_pp_put_channel_map_capture),
 };