Merge "ASoC: msm: Loopback Driver Reg Stream in Prepare Stage"
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index c6a9d3d..52598ef 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -77,6 +77,8 @@
 #define RX_MACRO_EC_MIX_TX1_MASK 0x0f
 #define RX_MACRO_EC_MIX_TX2_MASK 0x0f
 
+#define COMP_MAX_COEFF 25
+
 struct wcd_imped_val {
 	u32 imped_val;
 	u8 index;
@@ -95,6 +97,75 @@
 	{13, 9},
 };
 
+struct comp_coeff_val {
+	u8 lsb;
+	u8 msb;
+};
+
+enum {
+	HPH_ULP,
+	HPH_LOHIFI,
+	HPH_MODE_MAX,
+};
+
+static const struct comp_coeff_val
+			comp_coeff_table [HPH_MODE_MAX][COMP_MAX_COEFF] = {
+	{
+		{0x40, 0x00},
+		{0x4C, 0x00},
+		{0x5A, 0x00},
+		{0x6B, 0x00},
+		{0x7F, 0x00},
+		{0x97, 0x00},
+		{0xB3, 0x00},
+		{0xD5, 0x00},
+		{0xFD, 0x00},
+		{0x2D, 0x01},
+		{0x66, 0x01},
+		{0xA7, 0x01},
+		{0xF8, 0x01},
+		{0x57, 0x02},
+		{0xC7, 0x02},
+		{0x4B, 0x03},
+		{0xE9, 0x03},
+		{0xA3, 0x04},
+		{0x7D, 0x05},
+		{0x90, 0x06},
+		{0xD1, 0x07},
+		{0x49, 0x09},
+		{0x00, 0x0B},
+		{0x01, 0x0D},
+		{0x59, 0x0F},
+	},
+	{
+		{0x40, 0x00},
+		{0x4C, 0x00},
+		{0x5A, 0x00},
+		{0x6B, 0x00},
+		{0x80, 0x00},
+		{0x98, 0x00},
+		{0xB4, 0x00},
+		{0xD5, 0x00},
+		{0xFE, 0x00},
+		{0x2E, 0x01},
+		{0x66, 0x01},
+		{0xA9, 0x01},
+		{0xF8, 0x01},
+		{0x56, 0x02},
+		{0xC4, 0x02},
+		{0x4F, 0x03},
+		{0xF0, 0x03},
+		{0xAE, 0x04},
+		{0x8B, 0x05},
+		{0x8E, 0x06},
+		{0xBC, 0x07},
+		{0x56, 0x09},
+		{0x0F, 0x0B},
+		{0x13, 0x0D},
+		{0x6F, 0x0F},
+	},
+};
+
 struct rx_macro_reg_mask_val {
 	u16 reg;
 	u8 mask;
@@ -1526,6 +1597,47 @@
 	return 0;
 }
 
+static int rx_macro_load_compander_coeff(struct snd_soc_component *component,
+					 struct rx_macro_priv *rx_priv,
+					 int interp_n, int event)
+{
+	int comp = 0;
+	u16 comp_coeff_lsb_reg = 0, comp_coeff_msb_reg = 0;
+	int i = 0;
+	int hph_pwr_mode = HPH_LOHIFI;
+
+	if (!rx_priv->comp_enabled[comp])
+		return 0;
+
+	if (interp_n == INTERP_HPHL) {
+		comp_coeff_lsb_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_WR_LSB;
+		comp_coeff_msb_reg = BOLERO_CDC_RX_TOP_HPHL_COMP_WR_MSB;
+	} else if (interp_n == INTERP_HPHR) {
+		comp_coeff_lsb_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_WR_LSB;
+		comp_coeff_msb_reg = BOLERO_CDC_RX_TOP_HPHR_COMP_WR_MSB;
+	} else {
+		/* compander coefficients are loaded only for hph path */
+		return 0;
+	}
+
+	comp = interp_n;
+	hph_pwr_mode = rx_priv->hph_pwr_mode;
+	dev_dbg(component->dev, "%s: event %d compander %d, enabled %d\n",
+		__func__, event, comp + 1, rx_priv->comp_enabled[comp]);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		/* Load Compander Coeff */
+		for (i = 0; i < COMP_MAX_COEFF; i++) {
+			snd_soc_component_write(component, comp_coeff_lsb_reg,
+					comp_coeff_table[hph_pwr_mode][i].lsb);
+			snd_soc_component_write(component, comp_coeff_msb_reg,
+					comp_coeff_table[hph_pwr_mode][i].msb);
+		}
+	}
+
+	return 0;
+}
+
 static void rx_macro_enable_softclip_clk(struct snd_soc_component *component,
 					 struct rx_macro_priv *rx_priv,
 					 bool enable)
@@ -2245,6 +2357,8 @@
 					0x20, 0x20);
 			snd_soc_component_update_bits(component, rx_cfg2_reg,
 					0x03, 0x03);
+			rx_macro_load_compander_coeff(component, rx_priv,
+						      interp_idx, event);
 			rx_macro_idle_detect_control(component, rx_priv,
 					interp_idx, event);
 			if (rx_priv->hph_hd2_mode)
diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c
index fda01e1..8c32043 100644
--- a/asoc/codecs/bolero/tx-macro.c
+++ b/asoc/codecs/bolero/tx-macro.c
@@ -41,6 +41,7 @@
 #define TX_MACRO_TX_PATH_OFFSET 0x80
 #define TX_MACRO_SWR_MIC_MUX_SEL_MASK 0xF
 #define TX_MACRO_ADC_MUX_CFG_OFFSET 0x2
+#define TX_MACRO_ADC_MODE_CFG0_SHIFT 1
 
 #define TX_MACRO_TX_UNMUTE_DELAY_MS	40
 
@@ -163,6 +164,7 @@
 	int va_clk_status;
 	int tx_clk_status;
 	bool bcs_enable;
+	int dec_mode[NUM_DECIMATORS];
 };
 
 static bool tx_macro_get_data(struct snd_soc_component *component,
@@ -609,6 +611,91 @@
 
 	return 0;
 }
+
+static inline int tx_macro_path_get(const char *wname,
+				    unsigned int *path_num)
+{
+	int ret = 0;
+	char *widget_name = NULL;
+	char *w_name = NULL;
+	char *path_num_char = NULL;
+	char *path_name = NULL;
+
+	widget_name = kstrndup(wname, 10, GFP_KERNEL);
+	if (!widget_name)
+		return -EINVAL;
+
+	w_name = widget_name;
+
+	path_name = strsep(&widget_name, " ");
+	if (!path_name) {
+		pr_err("%s: Invalid widget name = %s\n",
+			__func__, widget_name);
+		ret = -EINVAL;
+		goto err;
+	}
+	path_num_char = strpbrk(path_name, "01234567");
+	if (!path_num_char) {
+		pr_err("%s: tx path index not found\n",
+			__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+	ret = kstrtouint(path_num_char, 10, path_num);
+	if (ret < 0)
+		pr_err("%s: Invalid tx path = %s\n",
+			__func__, w_name);
+
+err:
+	kfree(w_name);
+	return ret;
+}
+
+static int tx_macro_dec_mode_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct tx_macro_priv *tx_priv = NULL;
+	struct device *tx_dev = NULL;
+	int ret = 0;
+	int path = 0;
+
+	if (!tx_macro_get_data(component, &tx_dev, &tx_priv, __func__))
+		return -EINVAL;
+
+	ret = tx_macro_path_get(kcontrol->id.name, &path);
+	if (ret)
+		return ret;
+
+	ucontrol->value.integer.value[0] = tx_priv->dec_mode[path];
+
+	return 0;
+}
+
+static int tx_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+			snd_soc_kcontrol_component(kcontrol);
+	struct tx_macro_priv *tx_priv = NULL;
+	struct device *tx_dev = NULL;
+	int value = ucontrol->value.integer.value[0];
+	int ret = 0;
+	int path = 0;
+
+	if (!tx_macro_get_data(component, &tx_dev, &tx_priv, __func__))
+		return -EINVAL;
+
+	ret = tx_macro_path_get(kcontrol->id.name, &path);
+	if (ret)
+		return ret;
+
+	tx_priv->dec_mode[path] = value;
+
+	return 0;
+}
+
 static int tx_macro_get_bcs(struct snd_kcontrol *kcontrol,
                             struct snd_ctl_elem_value *ucontrol)
 {
@@ -759,6 +846,9 @@
 
 	switch (event) {
 	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_component_update_bits(component,
+			dec_cfg_reg, 0x06, tx_priv->dec_mode[decimator] <<
+			TX_MACRO_ADC_MODE_CFG0_SHIFT);
 		/* Enable TX PGA Mute */
 		snd_soc_component_update_bits(component,
 			tx_vol_ctl_reg, 0x10, 0x10);
@@ -840,6 +930,8 @@
 	case SND_SOC_DAPM_POST_PMD:
 		snd_soc_component_update_bits(component, tx_vol_ctl_reg,
 						0x20, 0x00);
+		snd_soc_component_update_bits(component,
+			dec_cfg_reg, 0x06, 0x00);
 		snd_soc_component_update_bits(component, tx_vol_ctl_reg,
 						0x10, 0x00);
 		if (tx_priv->bcs_enable) {
@@ -1111,6 +1203,14 @@
 			0, smic_mux_text, snd_soc_dapm_get_enum_double,
 			tx_macro_put_dec_enum);
 
+static const char * const dec_mode_mux_text[] = {
+	"ADC_DEFAULT", "ADC_LOW_PWR", "ADC_HIGH_PERF",
+};
+
+static const struct soc_enum dec_mode_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(dec_mode_mux_text),
+			    dec_mode_mux_text);
+
 static const struct snd_kcontrol_new tx_aif1_cap_mixer[] = {
 	SOC_SINGLE_EXT("DEC0", SND_SOC_NOPM, TX_MACRO_DEC0, 1, 0,
 			tx_macro_tx_mixer_get, tx_macro_tx_mixer_put),
@@ -1586,6 +1686,30 @@
 			  BOLERO_CDC_TX7_TX_VOL_CTL,
 			  0, -84, 40, digital_gain),
 
+	SOC_ENUM_EXT("DEC0 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC1 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC2 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC3 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC4 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC5 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC6 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
+	SOC_ENUM_EXT("DEC7 MODE", dec_mode_mux_enum,
+			tx_macro_dec_mode_get, tx_macro_dec_mode_put),
+
 	SOC_SINGLE_EXT("DEC0_BCS Switch", SND_SOC_NOPM, 0, 1, 0,
 		       tx_macro_get_bcs, tx_macro_set_bcs),
 };
diff --git a/asoc/codecs/ep92/ep92.c b/asoc/codecs/ep92/ep92.c
index 06d29ff..6077070 100644
--- a/asoc/codecs/ep92/ep92.c
+++ b/asoc/codecs/ep92/ep92.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/init.h>
@@ -25,6 +25,7 @@
 
 #define EP92_POLL_INTERVAL_OFF_MSEC 200
 #define EP92_POLL_INTERVAL_ON_MSEC  20
+#define EP92_POLL_RUNOUT_MSEC       5000
 #define EP92_SYSFS_ENTRY_MAX_LEN 64
 #define EP92_HYST_CNT 5
 
@@ -68,6 +69,9 @@
 	struct timer_list    timer;
 	struct work_struct   read_status_worker;
 	int                  irq;
+	int                  poll_trig;
+	int                  poll_rem;
+	int                  force_inactive;
 
 	int                  hyst_tx_plug;
 	int                  hyst_link_on0;
@@ -560,15 +564,23 @@
 		send_uevent = true;
 	}
 
+	old = ep92->ai.cs[0];
+	ep92->ai.cs[0] = snd_soc_component_read32(component,
+		EP92_AUDIO_INFO_CHANNEL_STATUS_0);
+	if (ep92->ai.cs[0] == 0xff) {
+		dev_dbg(component->dev, "ep92 EP92_AUDIO_INFO_CHANNEL_STATUS_0 read 0xff\n");
+		ep92->ai.cs[0] = old;
+	}
+	change = ep92->ai.cs[0] ^ old;
+	if (change & EP92_AI_PREEMPH_MASK) {
+		dev_dbg(component->dev, "ep92 preemph changed to %d\n",
+			(ep92->ai.cs[0] & EP92_AI_PREEMPH_MASK) >>
+			EP92_AI_PREEMPH_SHIFT);
+		send_uevent = true;
+	}
+
 	new_mode = ep92->old_mode;
 	if (ep92->ai.audio_status & EP92_AI_STD_ADO_MASK) {
-		old = ep92->ai.cs[0];
-		ep92->ai.cs[0] = snd_soc_component_read32(component,
-			EP92_AUDIO_INFO_CHANNEL_STATUS_0);
-		if (ep92->ai.cs[0] == 0xff) {
-			dev_dbg(component->dev, "ep92 EP92_AUDIO_INFO_CHANNEL_STATUS_0 read 0xff\n");
-			ep92->ai.cs[0] = old;
-		}
 		if (ep92->ai.cs[0] & EP92_AI_NPCM_MASK)
 			new_mode = 1; /* Compr */
 		else
@@ -648,10 +660,8 @@
 	ep92_init(component, ep92);
 
 	/* start polling when codec is registered */
-	if (ep92->irq == 0) {
-		mod_timer(&ep92->timer, jiffies +
-			msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC));
-	}
+	mod_timer(&ep92->timer, jiffies +
+		msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC));
 
 	return 0;
 }
@@ -690,6 +700,9 @@
 	if (component == NULL)
 		return;
 
+	if (ep92->force_inactive)
+		return;
+
 	/* check ADO_CHF that is set when audio format has changed */
 	val = snd_soc_component_read32(component, EP92_BI_GENERAL_INFO_1);
 	if (val == 0xff) {
@@ -724,6 +737,10 @@
 
 	dev_dbg(component->dev, "ep92_interrupt\n");
 
+	ep92->poll_trig = 1;
+	mod_timer(&ep92->timer, jiffies +
+		msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+
 	schedule_work(&ep92->read_status_worker);
 
 	return IRQ_HANDLED;
@@ -732,14 +749,45 @@
 void ep92_poll_status(struct timer_list *t)
 {
 	struct ep92_pdata *ep92 = from_timer(ep92, t, timer);
-	u32 poll_msec;
+	struct snd_soc_component *component = ep92->component;
 
-	if ((ep92->gc.ctl & EP92_GC_POWER_MASK) == 0)
-		poll_msec = EP92_POLL_INTERVAL_OFF_MSEC;
-	else
-		poll_msec = EP92_POLL_INTERVAL_ON_MSEC;
+	if (ep92->force_inactive)
+		return;
 
-	mod_timer(&ep92->timer, jiffies + msecs_to_jiffies(poll_msec));
+	/* if no IRQ is configured, always keep on polling */
+	if (ep92->irq == 0)
+		ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
+
+	/* on interrupt, start polling for some time */
+	if (ep92->poll_trig) {
+		if (ep92->poll_rem == 0)
+			dev_info(component->dev, "status checking activated\n");
+
+		ep92->poll_trig = 0;
+		ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
+	}
+
+	/*
+	 * If power_on == 0, poll only until poll_rem reaches zero and stop.
+	 * This allows to system to go to low power sleep mode.
+	 * Otherwise (power_on == 1) always re-arm timer to keep on polling.
+	 */
+	if ((ep92->gc.ctl & EP92_GC_POWER_MASK) == 0) {
+		if (ep92->poll_rem) {
+			mod_timer(&ep92->timer, jiffies +
+				msecs_to_jiffies(EP92_POLL_INTERVAL_OFF_MSEC));
+			if (ep92->poll_rem > EP92_POLL_INTERVAL_OFF_MSEC) {
+				ep92->poll_rem -= EP92_POLL_INTERVAL_OFF_MSEC;
+			} else {
+				dev_info(component->dev, "status checking stopped\n");
+				ep92->poll_rem = 0;
+			}
+		}
+	} else {
+		ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
+		mod_timer(&ep92->timer, jiffies +
+			msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+	}
 
 	schedule_work(&ep92->read_status_worker);
 }
@@ -933,7 +981,7 @@
 	return ret;
 }
 
-static ssize_t ep92_sysfs_rda_avmute(struct device *dev,
+static ssize_t ep92_sysfs_rda_audio_preemph(struct device *dev,
 	struct device_attribute *attr, char *buf)
 {
 	ssize_t ret;
@@ -945,6 +993,26 @@
 		return -ENODEV;
 	}
 
+	val = (ep92->ai.cs[0] & EP92_AI_PREEMPH_MASK) >>
+		EP92_AI_PREEMPH_SHIFT;
+
+	ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val);
+	dev_dbg(dev, "%s: '%d'\n", __func__, val);
+
+	return ret;
+}
+
+static ssize_t ep92_sysfs_rda_avmute(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	int val;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
 
 	val = (ep92->ai.system_status_0 >> EP92_AI_AVMUTE_SHIFT) &
 		EP92_2CHOICE_MASK;
@@ -1162,6 +1230,11 @@
 	ep92->gc.ctl &= ~EP92_GC_POWER_MASK;
 	ep92->gc.ctl |= (val << EP92_GC_POWER_SHIFT) & EP92_GC_POWER_MASK;
 
+	if (val == 1) {
+		ep92->poll_trig = 1;
+		mod_timer(&ep92->timer, jiffies +
+			msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+	}
 	rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN);
 end:
 	return rc;
@@ -1321,12 +1394,12 @@
 	}
 
 	reg = snd_soc_component_read32(ep92->component, EP92_GENERAL_CONTROL_0);
-	reg &= ~EP92_GC_AUDIO_PATH_MASK;
-	reg |= (val << EP92_GC_AUDIO_PATH_SHIFT) & EP92_GC_AUDIO_PATH_MASK;
+	reg &= ~EP92_GC_ARC_EN_MASK;
+	reg |= (val << EP92_GC_ARC_EN_SHIFT) & EP92_GC_ARC_EN_MASK;
 	snd_soc_component_write(ep92->component, EP92_GENERAL_CONTROL_0, reg);
-	ep92->gc.ctl &= ~EP92_GC_AUDIO_PATH_MASK;
-	ep92->gc.ctl |= (val << EP92_GC_AUDIO_PATH_SHIFT) &
-		EP92_GC_AUDIO_PATH_MASK;
+	ep92->gc.ctl &= ~EP92_GC_ARC_EN_MASK;
+	ep92->gc.ctl |= (val << EP92_GC_ARC_EN_SHIFT) &
+		EP92_GC_ARC_EN_MASK;
 
 	rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN);
 end:
@@ -1440,6 +1513,83 @@
 	return rc;
 }
 
+static ssize_t ep92_sysfs_rda_runout(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	int val;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
+
+	val = ep92->poll_rem;
+
+	ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val);
+	dev_dbg(dev, "%s: '%d'\n", __func__, val);
+
+	return ret;
+}
+
+static ssize_t ep92_sysfs_rda_force_inactive(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	int val;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
+
+	val = ep92->force_inactive;
+
+	ret = snprintf(buf, EP92_SYSFS_ENTRY_MAX_LEN, "%d\n", val);
+	dev_dbg(dev, "%s: '%d'\n", __func__, val);
+
+	return ret;
+}
+
+static ssize_t ep92_sysfs_wta_force_inactive(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	int val, rc;
+	struct ep92_pdata *ep92 = dev_get_drvdata(dev);
+
+	if (!ep92 || !ep92->component) {
+		dev_err(dev, "%s: device error\n", __func__);
+		return -ENODEV;
+	}
+
+	rc = kstrtoint(buf, 10, &val);
+	if (rc) {
+		dev_err(dev, "%s: kstrtoint failed. rc=%d\n", __func__, rc);
+		goto end;
+	}
+	if ((val < 0) || (val > 1)) {
+		dev_err(dev, "%s: value out of range.\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (val == 0) {
+		ep92->force_inactive = 0;
+		ep92->poll_trig = 1;
+		mod_timer(&ep92->timer, jiffies +
+			msecs_to_jiffies(EP92_POLL_INTERVAL_ON_MSEC));
+	} else {
+		ep92->force_inactive = 1;
+		ep92->poll_rem = 0;
+	}
+
+	rc = strnlen(buf, EP92_SYSFS_ENTRY_MAX_LEN);
+end:
+	return rc;
+}
+
 static DEVICE_ATTR(chipid, 0444, ep92_sysfs_rda_chipid, NULL);
 static DEVICE_ATTR(version, 0444, ep92_sysfs_rda_version, NULL);
 static DEVICE_ATTR(audio_state, 0444, ep92_sysfs_rda_audio_state, NULL);
@@ -1448,6 +1598,7 @@
 static DEVICE_ATTR(audio_layout, 0444, ep92_sysfs_rda_audio_layout, NULL);
 static DEVICE_ATTR(audio_ch_count, 0444, ep92_sysfs_rda_audio_ch_count, NULL);
 static DEVICE_ATTR(audio_ch_alloc, 0444, ep92_sysfs_rda_audio_ch_alloc, NULL);
+static DEVICE_ATTR(audio_preemph, 0444, ep92_sysfs_rda_audio_preemph, NULL);
 static DEVICE_ATTR(audio_avmute, 0444, ep92_sysfs_rda_avmute, NULL);
 static DEVICE_ATTR(link_on0, 0444, ep92_sysfs_rda_link_on0, NULL);
 static DEVICE_ATTR(link_on1, 0444, ep92_sysfs_rda_link_on1, NULL);
@@ -1467,6 +1618,9 @@
 	ep92_sysfs_wta_cec_mute);
 static DEVICE_ATTR(cec_volume, 0644, ep92_sysfs_rda_cec_volume,
 	ep92_sysfs_wta_cec_volume);
+static DEVICE_ATTR(runout, 0444, ep92_sysfs_rda_runout, NULL);
+static DEVICE_ATTR(force_inactive, 0644, ep92_sysfs_rda_force_inactive,
+	ep92_sysfs_wta_force_inactive);
 
 static struct attribute *ep92_fs_attrs[] = {
 	&dev_attr_chipid.attr,
@@ -1477,6 +1631,7 @@
 	&dev_attr_audio_layout.attr,
 	&dev_attr_audio_ch_count.attr,
 	&dev_attr_audio_ch_alloc.attr,
+	&dev_attr_audio_preemph.attr,
 	&dev_attr_audio_avmute.attr,
 	&dev_attr_link_on0.attr,
 	&dev_attr_link_on1.attr,
@@ -1490,6 +1645,8 @@
 	&dev_attr_arc_enable.attr,
 	&dev_attr_cec_mute.attr,
 	&dev_attr_cec_volume.attr,
+	&dev_attr_runout.attr,
+	&dev_attr_force_inactive.attr,
 	NULL,
 };
 
@@ -1552,9 +1709,9 @@
 			ep92->irq = 0;
 		}
 	}
-	/* poll status if IRQ is not configured */
-	if (ep92->irq == 0)
-		timer_setup(&ep92->timer, ep92_poll_status, 0);
+	/* prepare timer */
+	timer_setup(&ep92->timer, ep92_poll_status, 0);
+	ep92->poll_rem = EP92_POLL_RUNOUT_MSEC;
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 	/* debugfs interface */
@@ -1610,8 +1767,7 @@
 err_sysfs:
 	snd_soc_unregister_component(&client->dev);
 err_reg:
-	if (ep92->irq == 0)
-		del_timer(&ep92->timer);
+	del_timer(&ep92->timer);
 
 	return ret;
 }
@@ -1622,8 +1778,7 @@
 
 	ep92 = i2c_get_clientdata(client);
 	if (ep92) {
-		if (ep92->irq == 0)
-			del_timer(&ep92->timer);
+		del_timer(&ep92->timer);
 
 #if IS_ENABLED(CONFIG_DEBUG_FS)
 		debugfs_remove_recursive(ep92->debugfs_dir);
diff --git a/asoc/codecs/ep92/ep92.h b/asoc/codecs/ep92/ep92.h
index bfc8d80..18cb010 100644
--- a/asoc/codecs/ep92/ep92.h
+++ b/asoc/codecs/ep92/ep92.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
  */
 
 #ifndef __EP92_H__
@@ -171,6 +171,8 @@
 #define EP92_AI_STD_ADO_MASK     0x08
 #define EP92_AI_RATE_MASK        0x07
 #define EP92_AI_NPCM_MASK        0x02
+#define EP92_AI_PREEMPH_SHIFT    3
+#define EP92_AI_PREEMPH_MASK     0x38
 #define EP92_AI_CH_COUNT_MASK    0x07
 #define EP92_AI_CH_ALLOC_MASK    0xff
 
diff --git a/asoc/codecs/wcd-irq.c b/asoc/codecs/wcd-irq.c
index 3c8e777..d4890a6 100644
--- a/asoc/codecs/wcd-irq.c
+++ b/asoc/codecs/wcd-irq.c
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0-only
-/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/kernel.h>
@@ -178,7 +178,8 @@
 		return -EINVAL;
 	}
 
-	regmap_del_irq_chip(irq_find_mapping(virq, 0), irq_info->irq_chip);
+	devm_regmap_del_irq_chip(irq_info->dev, irq_find_mapping(virq, 0),
+				 irq_info->irq_chip);
 
 	return 0;
 }
diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c
index f89a2aa..036f97a 100644
--- a/asoc/codecs/wcd938x/wcd938x.c
+++ b/asoc/codecs/wcd938x/wcd938x.c
@@ -2779,6 +2779,40 @@
 }
 EXPORT_SYMBOL(wcd938x_info_create_codec_entry);
 
+static int wcd938x_set_micbias_data(struct wcd938x_priv *wcd938x,
+			      struct wcd938x_pdata *pdata)
+{
+	int vout_ctl_1 = 0, vout_ctl_2 = 0, vout_ctl_3 = 0, vout_ctl_4 = 0;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(wcd938x->dev, "%s: NULL pdata\n", __func__);
+		return -ENODEV;
+	}
+
+	/* set micbias voltage */
+	vout_ctl_1 = wcd938x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv);
+	vout_ctl_2 = wcd938x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv);
+	vout_ctl_3 = wcd938x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv);
+	vout_ctl_4 = wcd938x_get_micb_vout_ctl_val(pdata->micbias.micb4_mv);
+	if (vout_ctl_1 < 0 || vout_ctl_2 < 0 || vout_ctl_3 < 0 ||
+	    vout_ctl_4 < 0) {
+		rc = -EINVAL;
+		goto done;
+	}
+	regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB1, 0x3F,
+			   vout_ctl_1);
+	regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB2, 0x3F,
+			   vout_ctl_2);
+	regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB3, 0x3F,
+			   vout_ctl_3);
+	regmap_update_bits(wcd938x->regmap, WCD938X_ANA_MICB4, 0x3F,
+			   vout_ctl_4);
+
+done:
+	return rc;
+}
+
 static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
 {
 	struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
@@ -3026,6 +3060,19 @@
 		dev_info(dev, "%s: Micbias3 DT property not found\n",
 			__func__);
 	}
+
+	/* MB4 */
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias4-mv",
+				    NULL)) {
+		rc = wcd938x_read_of_property_u32(dev,
+						  "qcom,cdc-micbias4-mv",
+						  &prop_val);
+		if (!rc)
+			mb->micb4_mv = prop_val;
+	} else {
+		dev_info(dev, "%s: Micbias4 DT property not found\n",
+			__func__);
+	}
 }
 
 static int wcd938x_reset_low(struct device *dev)
@@ -3164,6 +3211,12 @@
 	}
 	wcd938x->tx_swr_dev->slave_irq = wcd938x->virq;
 
+	ret = wcd938x_set_micbias_data(wcd938x, pdata);
+	if (ret < 0) {
+		dev_err(dev, "%s: bad micbias pdata\n", __func__);
+		goto err_irq;
+	}
+
 	/* Request for watchdog interrupt */
 	wcd_request_irq(&wcd938x->irq_info, WCD938X_IRQ_HPHR_PDM_WD_INT,
 			"HPHR PDM WD INT", wcd938x_wd_handle_irq, NULL);
diff --git a/asoc/msm-compress-q6-v2.c b/asoc/msm-compress-q6-v2.c
index 75b217f..c4bf720 100644
--- a/asoc/msm-compress-q6-v2.c
+++ b/asoc/msm-compress-q6-v2.c
@@ -978,6 +978,7 @@
 	struct asm_ape_cfg ape_cfg;
 	struct asm_dsd_cfg dsd_cfg;
 	struct aptx_dec_bt_addr_cfg aptx_cfg;
+	struct asm_amrwbplus_cfg amrwbplus_cfg;
 	union snd_codec_options *codec_options;
 
 	int ret = 0;
@@ -1266,6 +1267,26 @@
 					 __func__, ret);
 		}
 		break;
+	case FORMAT_AMRNB:
+		pr_debug("SND_AUDIOCODEC_AMR\n");
+		/* no media format block needed */
+		break;
+	case FORMAT_AMRWB:
+		pr_debug("SND_AUDIOCODEC_AMRWB\n");
+		/* no media format block needed */
+		break;
+	case FORMAT_AMR_WB_PLUS:
+		pr_debug("SND_AUDIOCODEC_AMRWBPLUS\n");
+		memset(&amrwbplus_cfg, 0x0, sizeof(struct asm_amrwbplus_cfg));
+		amrwbplus_cfg.amr_frame_fmt =
+			codec_options->amrwbplus.bit_stream_fmt;
+		ret =  q6asm_media_format_block_amrwbplus(
+				prtd->audio_client,
+				&amrwbplus_cfg);
+		if (ret < 0)
+			pr_err("%s: CMD AMRWBPLUS Format block failed ret %d\n",
+				__func__, ret);
+		break;
 	default:
 		pr_debug("%s, unsupported format, skip", __func__);
 		break;
@@ -2193,6 +2214,24 @@
 		break;
 	}
 
+	case SND_AUDIOCODEC_AMR: {
+		pr_debug("%s:SND_AUDIOCODEC_AMR\n", __func__);
+		prtd->codec = FORMAT_AMRNB;
+		break;
+	}
+
+	case SND_AUDIOCODEC_AMRWB: {
+		pr_debug("%s:SND_AUDIOCODEC_AMRWB\n", __func__);
+		prtd->codec = FORMAT_AMRWB;
+		break;
+	}
+
+	case SND_AUDIOCODEC_AMRWBPLUS: {
+		pr_debug("%s:SND_AUDIOCODEC_AMRWBPLUS\n", __func__);
+		prtd->codec = FORMAT_AMR_WB_PLUS;
+		break;
+	}
+
 	default:
 		pr_err("codec not supported, id =%d\n", params->codec.id);
 		return -EINVAL;
@@ -3267,6 +3306,9 @@
 	case FORMAT_VORBIS:
 	case FORMAT_ALAC:
 	case FORMAT_APE:
+	case FORMAT_AMRNB:
+	case FORMAT_AMRWB:
+	case FORMAT_AMR_WB_PLUS:
 		memcpy(&(prtd->gapless_state.codec_options),
 			codec_options,
 			sizeof(union snd_codec_options));
@@ -3641,6 +3683,9 @@
 	case FORMAT_TRUEHD:
 	case FORMAT_IEC61937:
 	case FORMAT_APTX:
+	case FORMAT_AMRNB:
+	case FORMAT_AMRWB:
+	case FORMAT_AMR_WB_PLUS:
 		pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
 			 prtd->codec);
 		break;
diff --git a/asoc/msm-dai-q6-v2.c b/asoc/msm-dai-q6-v2.c
index c6f61e4..ab3445a 100644
--- a/asoc/msm-dai-q6-v2.c
+++ b/asoc/msm-dai-q6-v2.c
@@ -35,6 +35,9 @@
 #define CHANNEL_STATUS_SIZE 24
 #define CHANNEL_STATUS_MASK_INIT 0x0
 #define CHANNEL_STATUS_MASK 0x4
+#define PREEMPH_MASK 0x38
+#define PREEMPH_SHIFT 3
+#define GET_PREEMPH(b) ((b & PREEMPH_MASK) >> PREEMPH_SHIFT)
 #define AFE_API_VERSION_CLOCK_SET 1
 #define MSM_DAI_SYSFS_ENTRY_MAX_LEN 64
 
@@ -261,6 +264,17 @@
 	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);
@@ -1728,22 +1742,41 @@
 {
 	struct msm_dai_q6_spdif_event_msg *evt;
 	struct msm_dai_q6_spdif_dai_data *dai_data;
+	int preemph_old = 0;
+	int preemph_new = 0;
 
 	evt = (struct msm_dai_q6_spdif_event_msg *)payload;
 	dai_data = (struct msm_dai_q6_spdif_dai_data *)private_data;
 
-	pr_debug("%s: old state %d, fmt %d, rate %d\n",
+	preemph_old = GET_PREEMPH(dai_data->fmt_event.channel_status[0]);
+	preemph_new = GET_PREEMPH(evt->fmt_event.channel_status[0]);
+
+	pr_debug("%s: old state %d, fmt %d, rate %d, preemph %d\n",
 			__func__, dai_data->fmt_event.status,
 			dai_data->fmt_event.data_format,
-			dai_data->fmt_event.sample_rate);
-	pr_debug("%s: new state %d, fmt %d, rate %d\n",
+			dai_data->fmt_event.sample_rate,
+			preemph_old);
+	pr_debug("%s: new state %d, fmt %d, rate %d, preemph %d\n",
 			__func__, evt->fmt_event.status,
 			evt->fmt_event.data_format,
-			evt->fmt_event.sample_rate);
+			evt->fmt_event.sample_rate,
+			preemph_new);
 
 	dai_data->fmt_event.status = evt->fmt_event.status;
 	dai_data->fmt_event.data_format = evt->fmt_event.data_format;
 	dai_data->fmt_event.sample_rate = evt->fmt_event.sample_rate;
+	dai_data->fmt_event.channel_status[0] =
+		evt->fmt_event.channel_status[0];
+	dai_data->fmt_event.channel_status[1] =
+		evt->fmt_event.channel_status[1];
+	dai_data->fmt_event.channel_status[2] =
+		evt->fmt_event.channel_status[2];
+	dai_data->fmt_event.channel_status[3] =
+		evt->fmt_event.channel_status[3];
+	dai_data->fmt_event.channel_status[4] =
+		evt->fmt_event.channel_status[4];
+	dai_data->fmt_event.channel_status[5] =
+		evt->fmt_event.channel_status[5];
 }
 
 static int msm_dai_q6_spdif_hw_params(struct snd_pcm_substream *substream,
@@ -1887,17 +1920,40 @@
 	return ret;
 }
 
+static ssize_t msm_dai_q6_spdif_sysfs_rda_audio_preemph(struct device *dev,
+	struct device_attribute *attr, char *buf)
+{
+	ssize_t ret;
+	struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dev);
+	int preemph = 0;
+
+	if (!dai_data) {
+		pr_err("%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	preemph = GET_PREEMPH(dai_data->fmt_event.channel_status[0]);
+
+	ret = snprintf(buf, MSM_DAI_SYSFS_ENTRY_MAX_LEN, "%d\n", preemph);
+	pr_debug("%s: '%d'\n", __func__, preemph);
+
+	return ret;
+}
+
 static DEVICE_ATTR(audio_state, 0444, msm_dai_q6_spdif_sysfs_rda_audio_state,
 	NULL);
 static DEVICE_ATTR(audio_format, 0444, msm_dai_q6_spdif_sysfs_rda_audio_format,
 	NULL);
 static DEVICE_ATTR(audio_rate, 0444, msm_dai_q6_spdif_sysfs_rda_audio_rate,
 	NULL);
+static DEVICE_ATTR(audio_preemph, 0444,
+	msm_dai_q6_spdif_sysfs_rda_audio_preemph, NULL);
 
 static struct attribute *msm_dai_q6_spdif_fs_attrs[] = {
 	&dev_attr_audio_state.attr,
 	&dev_attr_audio_format.attr,
 	&dev_attr_audio_rate.attr,
+	&dev_attr_audio_preemph.attr,
 	NULL,
 };
 static struct attribute_group msm_dai_q6_spdif_fs_attrs_group = {
@@ -6096,6 +6152,42 @@
 	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 +6353,657 @@
 	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 +7370,24 @@
 	},
 };
 
+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 +12936,13 @@
 		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 +12992,8 @@
 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 +13013,7 @@
 	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);
diff --git a/asoc/msm-dai-q6-v2.h b/asoc/msm-dai-q6-v2.h
index b65e4e1..3ad049d 100644
--- a/asoc/msm-dai-q6-v2.h
+++ b/asoc/msm-dai-q6-v2.h
@@ -23,8 +23,8 @@
 #define MSM_TERT_MI2S 2
 #define MSM_QUAT_MI2S  3
 #define MSM_QUIN_MI2S  4
-#define MSM_SEC_MI2S_SD1  5
-#define MSM_SENARY_MI2S  6
+#define MSM_SENARY_MI2S  5
+#define MSM_SEC_MI2S_SD1  6
 #define MSM_INT0_MI2S  7
 #define MSM_INT1_MI2S  8
 #define MSM_INT2_MI2S  9
@@ -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 @@
 	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;
diff --git a/asoc/msm-pcm-routing-v2.c b/asoc/msm-pcm-routing-v2.c
index ffd09e8..f5e9896 100644
--- a/asoc/msm-pcm-routing-v2.c
+++ b/asoc/msm-pcm-routing-v2.c
@@ -80,6 +80,7 @@
 static bool is_custom_stereo_on;
 static bool is_ds2_on;
 static bool swap_ch;
+static bool hifi_filter_enabled;
 static int aanc_level;
 static int num_app_cfg_types;
 static int msm_ec_ref_port_id;
@@ -689,6 +690,10 @@
 	{ 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
@@ -1520,6 +1525,9 @@
 				topology = COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY;
 			pr_debug("%s: Before adm open topology %d\n", __func__,
 				topology);
+			if (hifi_filter_enabled)
+				bit_width = msm_routing_get_bit_width(
+						SNDRV_PCM_FORMAT_S32_LE);
 
 			copp_idx =
 				adm_open(port_id, path_type, sample_rate,
@@ -1865,6 +1873,9 @@
 			topology = msm_routing_get_adm_topology(fedai_id,
 								session_type,
 								i);
+			if (hifi_filter_enabled)
+				bits_per_sample = msm_routing_get_bit_width(
+							SNDRV_PCM_FORMAT_S32_LE);
 			copp_idx = adm_open(port_id, path_type,
 					    sample_rate, channels, topology,
 					    perf_mode, bits_per_sample,
@@ -2132,6 +2143,9 @@
 								reg);
 			acdb_dev_id =
 			fe_dai_app_type_cfg[val][session_type][reg].acdb_dev_id;
+			if (hifi_filter_enabled)
+				bits_per_sample = msm_routing_get_bit_width(
+							SNDRV_PCM_FORMAT_S32_LE);
 			copp_idx = adm_open(port_id, path_type,
 					    sample_rate, channels, topology,
 					    fdai->perf_mode, bits_per_sample,
@@ -3435,7 +3449,8 @@
 "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,
@@ -5410,6 +5425,10 @@
 		*index = 36;
 		port_id = AFE_PORT_ID_SECONDARY_TDM_TX;
 		break;
+	case 37:
+		*index = 37;
+		port_id = AFE_PORT_ID_HDMI_OVER_DP_RX;
+		break;
 	default:
 		*index = 0; /* NONE */
 		pr_err("%s: Invalid value %d\n", __func__, value);
@@ -5450,9 +5469,10 @@
 	"32000", "44100", "48000", "96000", "192000", "384000"};
 
 static const struct soc_enum msm_route_ec_ref_params_enum[] = {
-	SOC_ENUM_SINGLE_EXT(17, ec_ref_ch_text),
-	SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text),
-	SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_ch_text), ec_ref_ch_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_bit_format_text),
+				ec_ref_bit_format_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_rate_text), ec_ref_rate_text),
 };
 
 static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_RX",
@@ -5462,11 +5482,11 @@
 	"QUAT_TDM_RX_0", "QUAT_TDM_RX_1", "QUAT_TDM_RX_2", "SLIM_6_RX",
 	"TERT_MI2S_RX", "QUAT_MI2S_RX", "TERT_TDM_TX_0", "USB_AUDIO_RX",
 	"INT0_MI2S_RX", "INT4_MI2S_RX", "INT3_MI2S_TX", "DISPLAY_PORT",
-	"DISPLAY_PORT1",
 	"WSA_CDC_DMA_RX_0", "WSA_CDC_DMA_RX_1",
 	"WSA_CDC_DMA_TX_0", "WSA_CDC_DMA_TX_1", "WSA_CDC_DMA_TX_2",
 	"SLIM_7_RX", "RX_CDC_DMA_RX_0", "RX_CDC_DMA_RX_1", "RX_CDC_DMA_RX_2",
 	"RX_CDC_DMA_RX_3", "TX_CDC_DMA_TX_0", "TERT_TDM_RX_2", "SEC_TDM_TX_0",
+	"DISPLAY_PORT1",
 };
 
 static const struct soc_enum msm_route_ec_ref_rx_enum[] = {
@@ -7138,6 +7158,189 @@
 	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,
@@ -21553,6 +21756,28 @@
 	msm_routing_put_use_ds1_or_ds2_control),
 };
 
+static int msm_routing_get_hifi_filter_control(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hifi_filter_enabled;
+	return 0;
+}
+
+static int msm_routing_put_hifi_filter_control(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	hifi_filter_enabled = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_kcontrol_new hifi_filter_controls[] = {
+	SOC_SINGLE_EXT("HiFi Filter", SND_SOC_NOPM, 0,
+		1, 0, msm_routing_get_hifi_filter_control,
+		msm_routing_put_hifi_filter_control),
+};
+
 int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol) {
 	int rc = 0;
@@ -23164,6 +23389,10 @@
 				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",
@@ -23474,6 +23703,12 @@
 	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)),
@@ -24697,6 +24932,44 @@
 	{"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"},
@@ -28209,6 +28482,9 @@
 				|| (fdai->passthr_mode == COMPRESSED_PASSTHROUGH_IEC61937))
 				topology = COMPRESSED_PASSTHROUGH_NONE_TOPOLOGY;
 
+			if (hifi_filter_enabled)
+				bits_per_sample = msm_routing_get_bit_width(
+							SNDRV_PCM_FORMAT_S32_LE);
 			copp_idx = adm_open(port_id, path_type,
 					    sample_rate, channels, topology,
 					    fdai->perf_mode, bits_per_sample,
@@ -28809,6 +29085,10 @@
 			ARRAY_SIZE(use_ds1_or_ds2_controls));
 
 	snd_soc_add_component_controls(component,
+			hifi_filter_controls,
+			ARRAY_SIZE(hifi_filter_controls));
+
+	snd_soc_add_component_controls(component,
 				device_pp_params_mixer_controls,
 				ARRAY_SIZE(device_pp_params_mixer_controls));
 
diff --git a/asoc/msm-pcm-routing-v2.h b/asoc/msm-pcm-routing-v2.h
index c9544cb..aca00bb 100644
--- a/asoc/msm-pcm-routing-v2.h
+++ b/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 @@
 	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,
 };
 
diff --git a/asoc/msm-qti-pp-config.c b/asoc/msm-qti-pp-config.c
index d2da56e..89386c4 100644
--- a/asoc/msm-qti-pp-config.c
+++ b/asoc/msm-qti-pp-config.c
@@ -1484,14 +1484,16 @@
 };
 
 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),
 };
 
diff --git a/asoc/qcs405.c b/asoc/qcs405.c
index cadcd40..36fbfad 100644
--- a/asoc/qcs405.c
+++ b/asoc/qcs405.c
@@ -106,6 +106,12 @@
 };
 
 enum {
+	PRIM_META_MI2S = 0,
+	SEC_META_MI2S,
+	META_MI2S_MAX,
+};
+
+enum {
 	PRIM_AUX_PCM = 0,
 	SEC_AUX_PCM,
 	TERT_AUX_PCM,
@@ -157,6 +163,12 @@
 	Q6AFE_LPASS_CLK_ID_SEN_MI2S_EBIT
 };
 
+struct meta_mi2s_conf {
+	u32 num_member_ports;
+	u32 member_port[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+	bool clk_enable[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+};
+
 struct dev_config {
 	u32 sample_rate;
 	u32 bit_format;
@@ -402,6 +414,11 @@
 	[SEN_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
 };
 
+static struct dev_config meta_mi2s_rx_cfg[] = {
+	[PRIM_META_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[SEC_META_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
 /* Default configuration of SPDIF channels */
 static struct dev_config spdif_rx_cfg[] = {
 	[PRIM_SPDIF_RX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
@@ -487,6 +504,14 @@
 		"Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen",
 		"Fourteen", "Fifteen", "Sixteen"
 };
+static const char *const meta_mi2s_ch_text[] = {
+		"One", "Two", "Three", "Four", "Five", "Six", "Seven",
+		"Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen",
+		"Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen",
+		"Nineteen", "Twenty", "TwentyOne", "TwentyTwo", "TwentyThree",
+		"TwentyFour", "TwentyFive", "TwentySix", "TwentySeven",
+		"TwentyEight", "TwentyNine", "Thirty", "ThirtyOne", "ThirtyTwo"
+};
 static const char *const qos_text[] = {"Disable", "Enable"};
 
 static const char *const cdc_dma_rx_ch_text[] = {"One", "Two"};
@@ -576,6 +601,12 @@
 static SOC_ENUM_SINGLE_EXT_DECL(mi2s_tx_format, bit_format_text);
 static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_rx_format, bit_format_text);
 static SOC_ENUM_SINGLE_EXT_DECL(aux_pcm_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_meta_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_meta_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_meta_mi2s_rx_chs, meta_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_meta_mi2s_rx_chs, meta_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_meta_mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_meta_mi2s_rx_format, bit_format_text);
 static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_0_chs, cdc_dma_rx_ch_text);
 static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_rx_1_chs, cdc_dma_rx_ch_text);
 static SOC_ENUM_SINGLE_EXT_DECL(wsa_cdc_dma_tx_0_chs, cdc_dma_tx_ch_text);
@@ -689,6 +720,8 @@
 
 static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
 
+static struct meta_mi2s_conf meta_mi2s_intf_conf[META_MI2S_MAX];
+
 static int msm_island_vad_get_portid_from_beid(int32_t be_id, int *port_id)
 {
 	*port_id = 0xFFFF;
@@ -3070,6 +3103,143 @@
 	return 0;
 }
 
+static int msm_meta_mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx = 0;
+
+	if (strnstr(kcontrol->id.name, "PRIM_META_MI2S_RX",
+	    sizeof("PRIM_META_MI2S_RX"))) {
+		idx = PRIM_META_MI2S;
+	} else if (strnstr(kcontrol->id.name, "SEC_META_MI2S_RX",
+		   sizeof("SEC_META_MI2S_RX"))) {
+		idx = SEC_META_MI2S;
+	} else {
+		pr_err("%s: unsupported port: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int msm_meta_mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = msm_meta_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(meta_mi2s_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, meta_mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_meta_mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = msm_meta_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	meta_mi2s_rx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, meta_mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_meta_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = msm_meta_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] = meta_mi2s_rx_cfg[idx].channels - 1;
+
+	pr_debug("%s: meta_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, meta_mi2s_rx_cfg[idx].channels);
+
+	return 0;
+}
+
+static int msm_meta_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = msm_meta_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	meta_mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+
+	pr_debug("%s: meta_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, meta_mi2s_rx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int msm_meta_mi2s_rx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = msm_meta_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_auxpcm_get_format_value(meta_mi2s_rx_cfg[idx].bit_format);
+
+	pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+		idx, meta_mi2s_rx_cfg[idx].bit_format,
+		ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_meta_mi2s_rx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct snd_soc_card *card = NULL;
+	int idx = msm_meta_mi2s_get_port_idx(kcontrol);
+
+	card = kcontrol->private_data;
+	pdata = snd_soc_card_get_drvdata(card);
+
+	if (idx < 0)
+		return idx;
+
+	/* check for PRIM_META_MI2S and CSRAx to allow 24bit BE config only */
+	if ((idx == PRIM_META_MI2S) && pdata->codec_is_csra) {
+		meta_mi2s_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		pr_debug("%s: Keeping default format idx[%d]_rx_format = %d, item = %d\n",
+			__func__, idx, meta_mi2s_rx_cfg[idx].bit_format,
+			ucontrol->value.enumerated.item[0]);
+	} else {
+		meta_mi2s_rx_cfg[idx].bit_format =
+			mi2s_auxpcm_get_format(
+			ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: idx[%d]_rx_format = %d, item = %d\n", __func__,
+			idx, meta_mi2s_rx_cfg[idx].bit_format,
+			ucontrol->value.enumerated.item[0]);
+	}
+
+	return 0;
+}
+
 static int msm_aux_pcm_rx_format_get(struct snd_kcontrol *kcontrol,
 				struct snd_ctl_elem_value *ucontrol)
 {
@@ -3979,6 +4149,26 @@
 			msm_mi2s_rx_format_get, msm_mi2s_rx_format_put),
 	SOC_ENUM_EXT("SEN_MI2S_TX Format", mi2s_tx_format,
 			msm_mi2s_tx_format_get, msm_mi2s_tx_format_put),
+	SOC_ENUM_EXT("PRIM_META_MI2S_RX SampleRate",
+			prim_meta_mi2s_rx_sample_rate,
+			msm_meta_mi2s_rx_sample_rate_get,
+			msm_meta_mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_META_MI2S_RX SampleRate",
+			sec_meta_mi2s_rx_sample_rate,
+			msm_meta_mi2s_rx_sample_rate_get,
+			msm_meta_mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_META_MI2S_RX Channels", prim_meta_mi2s_rx_chs,
+			msm_meta_mi2s_rx_ch_get,
+			msm_meta_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("SEC_META_MI2S_RX Channels", sec_meta_mi2s_rx_chs,
+			msm_meta_mi2s_rx_ch_get,
+			msm_meta_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("PRIM_META_MI2S_RX Format", mi2s_rx_format,
+			msm_meta_mi2s_rx_format_get,
+			msm_meta_mi2s_rx_format_put),
+	SOC_ENUM_EXT("SEC_META_MI2S_RX Format", mi2s_rx_format,
+			msm_meta_mi2s_rx_format_get,
+			msm_meta_mi2s_rx_format_put),
 	SOC_ENUM_EXT("PRIM_AUX_PCM_RX Format", aux_pcm_rx_format,
 			msm_aux_pcm_rx_format_get, msm_aux_pcm_rx_format_put),
 	SOC_ENUM_EXT("PRIM_AUX_PCM_TX Format", aux_pcm_tx_format,
@@ -4761,6 +4951,25 @@
 		channels->min = channels->max =
 			mi2s_tx_cfg[SEN_MI2S].channels;
 		break;
+
+	case MSM_BACKEND_DAI_PRI_META_MI2S_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			meta_mi2s_rx_cfg[PRIM_META_MI2S].bit_format);
+		rate->min = rate->max =
+			meta_mi2s_rx_cfg[PRIM_META_MI2S].sample_rate;
+		channels->min = channels->max =
+			meta_mi2s_rx_cfg[PRIM_META_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_META_MI2S_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			meta_mi2s_rx_cfg[SEC_META_MI2S].bit_format);
+		rate->min = rate->max =
+			meta_mi2s_rx_cfg[SEC_META_MI2S].sample_rate;
+		channels->min = channels->max =
+			meta_mi2s_rx_cfg[SEC_META_MI2S].channels;
+		break;
+
 	case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_0:
 	case MSM_BACKEND_DAI_WSA_CDC_DMA_RX_1:
 		idx = msm_cdc_dma_get_idx_from_beid(dai_link->id);
@@ -5996,6 +6205,172 @@
 	mutex_unlock(&mi2s_intf_conf[index].lock);
 }
 
+static int msm_meta_mi2s_set_sclk(struct snd_pcm_substream *substream,
+	int member_id, bool enable)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int be_id = 0;
+	int port_id = 0;
+	int index = cpu_dai->id;
+	u32 bit_per_sample = 0;
+
+	switch (member_id) {
+	case PRIM_MI2S:
+		be_id = MSM_BACKEND_DAI_PRI_MI2S_RX;
+		break;
+	case SEC_MI2S:
+		be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX;
+		break;
+	case TERT_MI2S:
+		be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX;
+		break;
+	case QUAT_MI2S:
+		be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX;
+		break;
+	default:
+		dev_err(rtd->card->dev, "%s: Invalid member_id\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	port_id = msm_get_port_id(be_id);
+	if (port_id < 0) {
+		dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+		ret = port_id;
+		goto err;
+	}
+
+	if (enable) {
+		bit_per_sample =
+			get_mi2s_bits_per_sample(
+				meta_mi2s_rx_cfg[index].bit_format);
+		mi2s_clk[member_id].clk_freq_in_hz =
+			meta_mi2s_rx_cfg[index].sample_rate * 2 *
+			bit_per_sample;
+
+		dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+			mi2s_clk[member_id].clk_freq_in_hz);
+	}
+
+	mi2s_clk[member_id].enable = enable;
+	ret = afe_set_lpass_clock_v2(port_id, &mi2s_clk[member_id]);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+			"%s: afe lpass clock failed for port 0x%x , err:%d\n",
+			__func__, port_id, ret);
+		goto err;
+	}
+
+err:
+	return ret;
+}
+
+static int msm_meta_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	int i = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id;
+	int member_port = 0;
+	unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+	struct snd_soc_card *card = rtd->card;
+	struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_META_MI2S || index >= META_MI2S_MAX) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto err;
+	}
+
+	for (i = 0; i < meta_mi2s_intf_conf[index].num_member_ports; i++) {
+		member_port = meta_mi2s_intf_conf[index].member_port[i];
+
+		if (!mi2s_intf_conf[member_port].msm_is_mi2s_master) {
+			mi2s_clk[member_port].clk_id =
+				mi2s_ebit_clk[member_port];
+			fmt = SND_SOC_DAIFMT_CBM_CFM;
+		}
+
+		ret = msm_meta_mi2s_set_sclk(substream, member_port, true);
+		if (ret < 0) {
+			dev_err(rtd->card->dev,
+				"%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+				__func__, ret);
+			goto clk_off;
+		}
+		meta_mi2s_intf_conf[index].clk_enable[i] = true;
+
+		if (i == 0) {
+			ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+			if (ret < 0) {
+				pr_err("%s: set fmt cpu dai failed for META_MI2S (%d), err:%d\n",
+					__func__, index, ret);
+				goto clk_off;
+			}
+		}
+		if (pdata->mi2s_gpio_p[member_port])
+			msm_cdc_pinctrl_select_active_state(
+					pdata->mi2s_gpio_p[member_port]);
+	}
+	return 0;
+
+clk_off:
+	for (i = 0; i < meta_mi2s_intf_conf[index].num_member_ports; i++) {
+		member_port = meta_mi2s_intf_conf[index].member_port[i];
+		if (pdata->mi2s_gpio_p[member_port])
+			msm_cdc_pinctrl_select_sleep_state(
+					pdata->mi2s_gpio_p[member_port]);
+
+		if (meta_mi2s_intf_conf[index].clk_enable[i]) {
+			msm_meta_mi2s_set_sclk(substream, member_port, false);
+			meta_mi2s_intf_conf[index].clk_enable[i] = false;
+		}
+	}
+err:
+	return ret;
+}
+
+static void msm_meta_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	int i = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id;
+	int member_port = 0;
+	struct snd_soc_card *card = rtd->card;
+	struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+	if (index < PRIM_MI2S || index >= MI2S_MAX) {
+		pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+		return;
+	}
+
+	for (i = 0; i < meta_mi2s_intf_conf[index].num_member_ports; i++) {
+		member_port = meta_mi2s_intf_conf[index].member_port[i];
+
+		if (pdata->mi2s_gpio_p[member_port])
+			msm_cdc_pinctrl_select_sleep_state(
+					pdata->mi2s_gpio_p[member_port]);
+
+		ret = msm_meta_mi2s_set_sclk(substream, member_port, false);
+		if (ret < 0)
+			pr_err("%s:clock disable failed for META MI2S (%d); ret=%d\n",
+				__func__, index, ret);
+	}
+}
+
 static int msm_spdif_set_clk(struct snd_pcm_substream *substream, bool enable)
 {
 	int ret = 0;
@@ -6131,6 +6506,11 @@
 	.shutdown = msm_mi2s_snd_shutdown,
 };
 
+static struct snd_soc_ops msm_meta_mi2s_be_ops = {
+	.startup = msm_meta_mi2s_snd_startup,
+	.shutdown = msm_meta_mi2s_snd_shutdown,
+};
+
 static struct snd_soc_ops msm_auxpcm_be_ops = {
 	.startup = msm_snd_auxpcm_startup,
 };
@@ -7567,6 +7947,39 @@
 
 };
 
+static struct snd_soc_dai_link msm_meta_mi2s_be_dai_links[] = {
+	{
+		.name = LPASS_BE_PRI_META_MI2S_RX,
+		.stream_name = "Primary META MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-meta-mi2s.4864",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_PRI_META_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_meta_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_META_MI2S_RX,
+		.stream_name = "Secondary META MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-meta-mi2s.4866",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SEC_META_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_meta_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+};
+
 static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
 	/* Primary AUX PCM Backend DAI Links */
 	{
@@ -7885,6 +8298,7 @@
 			 ARRAY_SIZE(msm_tasha_be_dai_links) +
 			 ARRAY_SIZE(msm_wcn_be_dai_links) +
 			 ARRAY_SIZE(msm_mi2s_be_dai_links) +
+			 ARRAY_SIZE(msm_meta_mi2s_be_dai_links) +
 			 ARRAY_SIZE(msm_auxpcm_be_dai_links) +
 			 ARRAY_SIZE(msm_va_cdc_dma_be_dai_links) +
 			 ARRAY_SIZE(msm_wsa_cdc_dma_be_dai_links) +
@@ -8140,7 +8554,7 @@
 	uint32_t tasha_codec = 0, auxpcm_audio_intf = 0;
 	uint32_t va_bolero_codec = 0, wsa_bolero_codec = 0, mi2s_audio_intf = 0;
 	uint32_t spdif_audio_intf = 0, wcn_audio_intf = 0;
-	uint32_t afe_loopback_intf = 0;
+	uint32_t afe_loopback_intf = 0, meta_mi2s_intf = 0;
 	const struct of_device_id *match;
 	char __iomem *spdif_cfg, *spdif_pin_ctl;
 	int rc = 0;
@@ -8248,6 +8662,22 @@
 				ARRAY_SIZE(msm_mi2s_be_dai_links);
 			}
 		}
+
+		rc = of_property_read_u32(dev->of_node, "qcom,meta-mi2s-intf",
+					  &meta_mi2s_intf);
+		if (rc) {
+			dev_dbg(dev, "%s: No DT match META-MI2S interface\n",
+				__func__);
+		} else {
+			if (meta_mi2s_intf) {
+				memcpy(msm_qcs405_dai_links + total_links,
+				msm_meta_mi2s_be_dai_links,
+				sizeof(msm_meta_mi2s_be_dai_links));
+				total_links +=
+				ARRAY_SIZE(msm_meta_mi2s_be_dai_links);
+			}
+		}
+
 		rc = of_property_read_u32(dev->of_node,
 					  "qcom,auxpcm-audio-intf",
 					  &auxpcm_audio_intf);
@@ -8807,6 +9237,81 @@
 	}
 }
 
+static void msm_meta_mi2s_init(struct platform_device *pdev)
+{
+	int rc = 0;
+	int i = 0;
+	int index = 0;
+	bool parse_of = false;
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+
+	dev_dbg(&pdev->dev, "%s: read from DT\n", __func__);
+
+	for (index = 0; index < META_MI2S_MAX; index++) {
+		meta_mi2s_intf_conf[index].num_member_ports = 0;
+		meta_mi2s_intf_conf[index].member_port[0] = 0;
+		meta_mi2s_intf_conf[index].member_port[1] = 0;
+		meta_mi2s_intf_conf[index].member_port[2] = 0;
+		meta_mi2s_intf_conf[index].member_port[3] = 0;
+		meta_mi2s_intf_conf[index].clk_enable[0] = false;
+		meta_mi2s_intf_conf[index].clk_enable[1] = false;
+		meta_mi2s_intf_conf[index].clk_enable[2] = false;
+		meta_mi2s_intf_conf[index].clk_enable[3] = false;
+	}
+
+	/* get member port info to set matching clocks for involved ports */
+	for (i = 0; i < card->num_links; i++) {
+		if (dai_link[i].id == MSM_BACKEND_DAI_PRI_META_MI2S_RX) {
+			parse_of = true;
+			index = PRIM_META_MI2S;
+		} else if (dai_link[i].id == MSM_BACKEND_DAI_SEC_META_MI2S_RX) {
+			parse_of = true;
+			index = SEC_META_MI2S;
+		} else {
+			parse_of = false;
+		}
+		if (parse_of && dai_link[i].cpu_of_node) {
+			rc = of_property_read_u32(dai_link[i].cpu_of_node,
+				"qcom,msm-mi2s-num-members",
+				&meta_mi2s_intf_conf[index].num_member_ports);
+			if (rc) {
+				dev_err(&pdev->dev, "%s: invalid num from DT file %s\n",
+					__func__, "qcom,msm-mi2s-num-members");
+			}
+
+			if (meta_mi2s_intf_conf[index].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_intf_conf[index].num_member_ports);
+			}
+
+			if (meta_mi2s_intf_conf[index].num_member_ports > 0) {
+				rc = of_property_read_u32_array(
+					dai_link[i].cpu_of_node,
+					"qcom,msm-mi2s-member-id",
+					meta_mi2s_intf_conf[index].member_port,
+					meta_mi2s_intf_conf[index].num_member_ports);
+				if (rc) {
+					dev_err(&pdev->dev, "%s: member-id from DT file %s\n",
+						__func__,
+						"qcom,msm-mi2s-member-id");
+				}
+			}
+
+			dev_dbg(&pdev->dev, "dev name %s num-members=%d\n",
+				dev_name(&pdev->dev),
+				meta_mi2s_intf_conf[index].num_member_ports);
+			dev_dbg(&pdev->dev, "member array (%d, %d, %d, %d)\n",
+				meta_mi2s_intf_conf[index].member_port[0],
+				meta_mi2s_intf_conf[index].member_port[1],
+				meta_mi2s_intf_conf[index].member_port[2],
+				meta_mi2s_intf_conf[index].member_port[3]);
+		}
+	}
+}
+
 static int msm_scan_i2c_addr(struct platform_device *pdev,
 		uint32_t busnum, uint32_t addr)
 {
@@ -8984,6 +9489,8 @@
 	if (val) {
 		pdata->codec_is_csra = true;
 		mi2s_rx_cfg[PRIM_MI2S].bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		meta_mi2s_rx_cfg[PRIM_META_MI2S].bit_format =
+			SNDRV_PCM_FORMAT_S24_LE;
 		ret = msm_init_csra_dev(pdev, card);
 		if (ret)
 			goto err;
@@ -9065,6 +9572,8 @@
 
 	msm_i2s_auxpcm_init(pdev);
 
+	msm_meta_mi2s_init(pdev);
+
 	is_initial_boot = true;
 	return 0;
 err:
diff --git a/dsp/q6afe.c b/dsp/q6afe.c
index 20ff039..9709772 100644
--- a/dsp/q6afe.c
+++ b/dsp/q6afe.c
@@ -915,6 +915,10 @@
 	case AFE_PORT_ID_SENARY_MI2S_TX:
 		ret_size = SIZEOF_CFG_CMD(afe_param_id_i2s_cfg);
 		break;
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_meta_i2s_cfg);
+		break;
 	case HDMI_RX:
 	case DISPLAY_PORT_RX:
 		ret_size =
@@ -4556,6 +4560,10 @@
 	case AFE_PORT_ID_INT6_MI2S_TX:
 		cfg_type = AFE_PARAM_ID_I2S_CONFIG;
 		break;
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+		cfg_type = AFE_PARAM_ID_META_I2S_CONFIG;
+		break;
 	case HDMI_RX:
 	case DISPLAY_PORT_RX:
 		cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
@@ -5091,6 +5099,10 @@
 		return IDX_AFE_PORT_ID_INT6_MI2S_RX;
 	case AFE_PORT_ID_INT6_MI2S_TX:
 		return IDX_AFE_PORT_ID_INT6_MI2S_TX;
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_META_MI2S_RX;
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_META_MI2S_RX;
 	case AFE_PORT_ID_VA_CODEC_DMA_TX_0:
 		return IDX_AFE_PORT_ID_VA_CODEC_DMA_TX_0;
 	case AFE_PORT_ID_VA_CODEC_DMA_TX_1:
@@ -5254,6 +5266,10 @@
 	case AFE_PORT_ID_SENARY_MI2S_TX:
 		cfg_type = AFE_PARAM_ID_I2S_CONFIG;
 		break;
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+		cfg_type = AFE_PARAM_ID_META_I2S_CONFIG;
+		break;
 	case HDMI_RX:
 	case DISPLAY_PORT_RX:
 		cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
@@ -7231,6 +7247,8 @@
 	case AFE_PORT_ID_QUINARY_MI2S_TX:
 	case AFE_PORT_ID_SENARY_MI2S_RX:
 	case AFE_PORT_ID_SENARY_MI2S_TX:
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
 	case AFE_PORT_ID_PRIMARY_TDM_RX:
 	case AFE_PORT_ID_PRIMARY_TDM_TX:
 	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
diff --git a/dsp/q6asm.c b/dsp/q6asm.c
index 37ea0d0..11b307a 100644
--- a/dsp/q6asm.c
+++ b/dsp/q6asm.c
@@ -3542,6 +3542,15 @@
 	case FORMAT_WMA_V10PRO:
 		open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2;
 		break;
+	case FORMAT_AMRNB:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AMRNB_FS;
+		break;
+	case FORMAT_AMRWB:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AMRWB_FS;
+		break;
+	case FORMAT_AMR_WB_PLUS:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AMR_WB_PLUS_V2;
+		break;
 	case FORMAT_MP3:
 		open.dec_fmt_id = ASM_MEDIA_FMT_MP3;
 		break;
diff --git a/dsp/q6audio-v2.c b/dsp/q6audio-v2.c
index 8de6060..68b3736 100644
--- a/dsp/q6audio-v2.c
+++ b/dsp/q6audio-v2.c
@@ -337,6 +337,10 @@
 		return IDX_AFE_PORT_ID_INT6_MI2S_RX;
 	case AFE_PORT_ID_INT6_MI2S_TX:
 		return IDX_AFE_PORT_ID_INT6_MI2S_TX;
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_META_MI2S_RX;
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_META_MI2S_RX;
 	case AFE_PORT_ID_WSA_CODEC_DMA_RX_0:
 		return IDX_AFE_PORT_ID_WSA_CODEC_DMA_RX_0;
 	case AFE_PORT_ID_WSA_CODEC_DMA_TX_0:
@@ -487,6 +491,10 @@
 			return AUDIO_PORT_ID_I2S_RX;
 	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
 			return AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+			return AFE_PORT_ID_PRIMARY_META_MI2S_RX;
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
+			return AFE_PORT_ID_SECONDARY_META_MI2S_RX;
 	case AFE_PORT_ID_PRIMARY_TDM_RX:
 		return AFE_PORT_ID_PRIMARY_TDM_RX;
 	case AFE_PORT_ID_PRIMARY_TDM_TX:
@@ -820,6 +828,8 @@
 	case AFE_PORT_ID_SECONDARY_MI2S_TX:
 	case AUDIO_PORT_ID_I2S_RX:
 	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
 	case AFE_PORT_ID_PRIMARY_TDM_RX:
 	case AFE_PORT_ID_PRIMARY_TDM_TX:
 	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
@@ -1048,6 +1058,8 @@
 	case AFE_PORT_ID_QUINARY_MI2S_RX:
 	case AFE_PORT_ID_QUINARY_MI2S_TX:
 	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+	case AFE_PORT_ID_PRIMARY_META_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_META_MI2S_RX:
 	case AFE_PORT_ID_PRIMARY_TDM_RX:
 	case AFE_PORT_ID_PRIMARY_TDM_TX:
 	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
diff --git a/dsp/q6lsm.c b/dsp/q6lsm.c
index d55dfc3..d27bbac 100644
--- a/dsp/q6lsm.c
+++ b/dsp/q6lsm.c
@@ -372,9 +372,9 @@
 		pr_err("%s: Invalid Session %d\n", __func__, client->session);
 		return;
 	}
-	mutex_lock(&session_lock);
 	apr_deregister(client->apr);
 	client->mmap_apr = NULL;
+	mutex_lock(&session_lock);
 	q6lsm_session_free(client);
 	q6lsm_mmap_apr_dereg();
 	mutex_destroy(&client->cmd_lock);
diff --git a/include/dsp/apr_audio-v2.h b/include/dsp/apr_audio-v2.h
index a9a552e..0fcc76a 100644
--- a/include/dsp/apr_audio-v2.h
+++ b/include/dsp/apr_audio-v2.h
@@ -1505,6 +1505,9 @@
 /* ID of the senary auxiliary PCM Tx port. */
 #define AFE_PORT_ID_SENARY_PCM_TX                0x103F
 
+#define AFE_PORT_ID_PRIMARY_META_MI2S_RX         0x1300
+#define AFE_PORT_ID_SECONDARY_META_MI2S_RX       0x1302
+
 #define AFE_PORT_ID_PRIMARY_SPDIF_RX             0x5000
 #define AFE_PORT_ID_PRIMARY_SPDIF_TX             0x5001
 #define AFE_PORT_ID_SECONDARY_SPDIF_RX           0x5002
@@ -2650,6 +2653,98 @@
 	/* This field must be set to zero. */
 } __packed;
 
+/* This param id is used to configure META I2S interface */
+#define AFE_PARAM_ID_META_I2S_CONFIG 0x000102C5
+#define AFE_API_VERSION_META_I2S_CONFIG 0x1
+#define MAX_NUM_I2S_META_PORT_MEMBER_PORTS 4
+
+/*  Payload of the #AFE_PARAM_ID_META_I2S_CONFIG
+ * command's (I2S configuration
+ * parameter).
+ */
+struct afe_param_id_meta_i2s_cfg {
+	u32     minor_version;
+/* Minor version used for tracking the version of the I2S
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_META_I2S_CONFIG
+ */
+
+	u16     bit_width;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+
+	u16     ws_src;
+/* Word select source: internal or external.
+ * Supported values:
+ * - #AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL
+ * - #AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL
+ */
+
+	u32     sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ * - #AFE_PORT_SAMPLE_RATE_96K
+ * - #AFE_PORT_SAMPLE_RATE_192K
+ */
+
+	u16     member_port_id[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+/* Array of member port IDs in this meta device.
+ * Supported values:
+ * - #AFE_PORT_ID_PRIMARY_MI2S_RX
+ * - #AFE_PORT_ID_SECONDARY_MI2S_RX
+ * - #AFE_PORT_ID_TERTIARY_MI2S_RX
+ * - #AFE_PORT_ID_QUATERNY_MI2S_RX
+ * - #AFE_PORT_ID_INVALID
+ *
+ * Fill these values from index 0. Set unused index to AFE_PORT_ID_INVALID.
+ *
+ * Note:
+ * the first member port will act as WS master in case
+ * meta port ws_src is configured as AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL.
+ * In all other cases member ports will act as slave.
+ * This must be considered when HLOS enables the interface clocks
+ */
+
+	u16     member_port_channel_mode[MAX_NUM_I2S_META_PORT_MEMBER_PORTS];
+/* I2S lines and multichannel operation per member port.
+ * The sequence matches the sequence in member_port_id,
+ * value will be ignored if member port is set to AFE_PORT_ID_INVALID
+ *
+ * Supported values:
+ * - #AFE_PORT_I2S_SD0
+ * - #AFE_PORT_I2S_SD1
+ * - #AFE_PORT_I2S_SD2
+ * - #AFE_PORT_I2S_SD3
+ * - #AFE_PORT_I2S_QUAD01
+ * - #AFE_PORT_I2S_QUAD23
+ * - #AFE_PORT_I2S_6CHS
+ * - #AFE_PORT_I2S_8CHS
+ * - #AFE_PORT_I2S_10CHS
+ * - #AFE_PORT_I2S_12CHS
+ * - #AFE_PORT_I2S_14CHS
+ * - #AFE_PORT_I2S_16CHS
+ * - #AFE_PORT_I2S_SD4
+ * - #AFE_PORT_I2S_SD5
+ * - #AFE_PORT_I2S_SD6
+ * - #AFE_PORT_I2S_SD7
+ * - #AFE_PORT_I2S_QUAD45
+ * - #AFE_PORT_I2S_QUAD67
+ * - #AFE_PORT_I2S_8CHS_2
+ */
+
+	u16     data_format;
+/* data format
+ * Supported values:
+ * - #LINEAR_PCM_DATA
+ */
+	u16     reserved;
+	/* This field must be set to zero. */
+} __packed;
+
 /*
  * This param id is used to configure PCM interface
  */
@@ -4846,6 +4941,7 @@
 union afe_port_config {
 	struct afe_param_id_pcm_cfg               pcm;
 	struct afe_param_id_i2s_cfg               i2s;
+	struct afe_param_id_meta_i2s_cfg          meta_i2s;
 	struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch;
 	struct afe_param_id_slimbus_cfg           slim_sch;
 	struct afe_param_id_rt_proxy_port_cfg     rtproxy;
@@ -5663,9 +5759,7 @@
 /* Left side direct channel. */
 #define PCM_CHANNEL_LSD  33
 
-/* Right side direct channel. Update PCM_MAX_CHMAP_ID when
- * this list is extended.
- */
+/* Right side direct channel. */
 #define PCM_CHANNEL_RSD  34
 
 /* Mark unused channel. */
@@ -5690,7 +5784,7 @@
 #define PCM_MAX_CHANNEL_MAP   63
 
 /* Max valid channel map index */
-#define PCM_MAX_CHMAP_ID PCM_CHANNEL_RSD
+#define PCM_MAX_CHMAP_ID PCM_MAX_CHANNEL_MAP
 
 #define PCM_FORMAT_MAX_NUM_CHANNEL  8
 #define PCM_FORMAT_MAX_CHANNELS_9   9
diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h
index 9504ad6..d1b4f3c 100644
--- a/include/dsp/q6afe-v2.h
+++ b/include/dsp/q6afe-v2.h
@@ -281,6 +281,9 @@
 	IDX_AFE_PORT_ID_SENARY_TDM_TX_6,
 	IDX_AFE_PORT_ID_SENARY_TDM_RX_7,
 	IDX_AFE_PORT_ID_SENARY_TDM_TX_7,
+	/* IDX 208-> 209 */
+	IDX_AFE_PORT_ID_PRIMARY_META_MI2S_RX,
+	IDX_AFE_PORT_ID_SECONDARY_META_MI2S_RX,
 	AFE_MAX_PORTS
 };
 
diff --git a/include/soc/swr-wcd.h b/include/soc/swr-wcd.h
index 25c5339..4ec5094 100644
--- a/include/soc/swr-wcd.h
+++ b/include/soc/swr-wcd.h
@@ -22,6 +22,8 @@
 	SWR_REGISTER_WAKE_IRQ,
 	SWR_SET_PORT_MAP,
 	SWR_REQ_CLK_SWITCH,
+	SWR_REGISTER_WAKEUP,
+	SWR_DEREGISTER_WAKEUP,
 };
 
 struct swr_mstr_port {
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index c4deb20..9ad849e 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -144,6 +144,11 @@
 		i <= SWR_MSTR_MAX_REG_ADDR; i += 4) {
 		reg_val = dbgswrm->read(dbgswrm->handle, i);
 		len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, reg_val);
+		if (len < 0) {
+			pr_err("%s: fail to fill the buffer\n", __func__);
+			total = -EFAULT;
+			goto copy_err;
+		}
 		if ((total + len) >= count - 1)
 			break;
 		if (copy_to_user((ubuf + total), tmp_buf, len)) {
@@ -397,7 +402,11 @@
 		mutex_lock(&swrm->iolock);
 		for (i = 0; i < length; i++) {
 		/* wait for FIFO WR command to complete to avoid overflow */
-			usleep_range(100, 105);
+		/*
+		 * Reduce sleep from 100us to 10us to meet KPIs
+		 * This still meets the hardware spec
+		 */
+			usleep_range(10, 12);
 			swr_master_write(swrm, reg_addr[i], val[i]);
 		}
 		mutex_unlock(&swrm->iolock);
@@ -559,7 +568,7 @@
 	 * skip delay if write is handled in platform driver.
 	 */
 	if(!swrm->write)
-		usleep_range(250, 255);
+		usleep_range(150, 155);
 	if (cmd_id == 0xF) {
 		/*
 		 * sleep for 10ms for MSM soundwire variant to allow broadcast
@@ -1407,6 +1416,7 @@
 					continue;
 				if (swr_dev->slave_irq) {
 					do {
+						swr_dev->slave_irq_pending = 0;
 						handle_nested_irq(
 							irq_find_mapping(
 							swr_dev->slave_irq, 0));
@@ -2880,6 +2890,14 @@
 			mutex_unlock(&swrm->mlock);
 		}
 		break;
+	case SWR_REGISTER_WAKEUP:
+		msm_aud_evt_blocking_notifier_call_chain(
+					SWR_WAKE_IRQ_REGISTER, (void *)swrm);
+		break;
+	case SWR_DEREGISTER_WAKEUP:
+		msm_aud_evt_blocking_notifier_call_chain(
+					SWR_WAKE_IRQ_DEREGISTER, (void *)swrm);
+		break;
 	case SWR_SET_PORT_MAP:
 		if (!data) {
 			dev_err(swrm->dev, "%s: data is NULL for id=%d\n",