diff mbox series

[v1,1/4] ASoc: tac5x1x: add codec driver tac5x1x family

Message ID 20250606065136.1821-2-niranjan.hy@ti.com
State New
Headers show
Series ASoc: tac5x1x: mixer-test report | expand

Commit Message

Niranjan H Y June 6, 2025, 6:51 a.m. UTC
tac5x1x family are series of low-power and high performance
mono/stereo audio codecs consists of ADC and DAC combinations.
The patch adds supports for Codecs(DAC & ADC), ADC only and
DAC only configurations available in the tac5x1x family.

Signed-off-by: Niranjan H Y <niranjan.hy@ti.com>
---
 sound/soc/codecs/Kconfig       |   16 +
 sound/soc/codecs/Makefile      |    6 +-
 sound/soc/codecs/tac5x1x-i2c.c |   81 ++
 sound/soc/codecs/tac5x1x.c     | 2389 ++++++++++++++++++++++++++++++++
 sound/soc/codecs/tac5x1x.h     |  302 ++++
 5 files changed, 2793 insertions(+), 1 deletion(-)
 create mode 100644 sound/soc/codecs/tac5x1x-i2c.c
 create mode 100644 sound/soc/codecs/tac5x1x.c
 create mode 100644 sound/soc/codecs/tac5x1x.h
diff mbox series

Patch

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 477d33234fdd..74abc0d68237 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -258,6 +258,8 @@  config SND_SOC_ALL_CODECS
 	imply SND_SOC_STA529
 	imply SND_SOC_STAC9766
 	imply SND_SOC_STI_SAS
+	imply SND_SOC_TAC5X1X
+	imply SND_SOC_TAC5X1X_I2C
 	imply SND_SOC_TAS2552
 	imply SND_SOC_TAS2562
 	imply SND_SOC_TAS2764
@@ -2001,6 +2003,20 @@  config SND_SOC_STAC9766
 config SND_SOC_STI_SAS
 	tristate "codec Audio support for STI SAS codec"
 
+config SND_SOC_TAC5X1X
+	tristate "Texas Instruments TAC5X1X family codecs"
+	depends on I2C && REGMAP_I2C
+
+config SND_SOC_TAC5X1X_I2C
+	tristate "Texas Instruments TAC5X1X family driver based on I2C"
+	depends on I2C && REGMAP_I2C
+	select SND_SOC_TAC5X1X
+	help
+	  Enable support for Texas Instruments TAC5X1X family Audio chips.
+	  The family consists mono/stereo audio codecs, DACs and ADCs.
+	  Includes support for TAC5311-Q1, TAC5411-Q1, TAC5111, TAC5211,
+	  TAA5212, TAA5412-Q1, TAD5112, TAD5212, TAC5312, TAC5412-Q1,
+
 config SND_SOC_TAS2552
 	tristate "Texas Instruments TAS2552 Mono Audio amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 66e2f2381d40..ec315236f2d9 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -312,6 +312,8 @@  snd-soc-tas2770-y := tas2770.o
 snd-soc-tas2781-comlib-y := tas2781-comlib.o
 snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
 snd-soc-tas2781-i2c-y := tas2781-i2c.o
+snd-soc-tac5x1x-y := tac5x1x.o
+snd-soc-tac5x1x-i2c-y := tac5x1x-i2c.o
 snd-soc-tfa9879-y := tfa9879.o
 snd-soc-tfa989x-y := tfa989x.o
 snd-soc-tlv320adc3xxx-y := tlv320adc3xxx.o
@@ -717,6 +719,8 @@  obj-$(CONFIG_SND_SOC_STA32X)   += snd-soc-sta32x.o
 obj-$(CONFIG_SND_SOC_STA350)   += snd-soc-sta350.o
 obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766)	+= snd-soc-stac9766.o
+obj-$(CONFIG_SND_SOC_TAC5X1X)	+= snd-soc-tac5x1x.o
+obj-$(CONFIG_SND_SOC_TAC5X1X_I2C)	+= snd-soc-tac5x1x-i2c.o
 obj-$(CONFIG_SND_SOC_STI_SAS)	+= snd-soc-sti-sas.o
 obj-$(CONFIG_SND_SOC_TAS2552)	+= snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS2562)	+= snd-soc-tas2562.o
@@ -851,4 +855,4 @@  obj-$(CONFIG_SND_SOC_LPASS_RX_MACRO)	+= snd-soc-lpass-rx-macro.o
 obj-$(CONFIG_SND_SOC_LPASS_TX_MACRO)	+= snd-soc-lpass-tx-macro.o
 
 # Mux
-obj-$(CONFIG_SND_SOC_SIMPLE_MUX)	+= snd-soc-simple-mux.o
\ No newline at end of file
+obj-$(CONFIG_SND_SOC_SIMPLE_MUX)	+= snd-soc-simple-mux.o
diff --git a/sound/soc/codecs/tac5x1x-i2c.c b/sound/soc/codecs/tac5x1x-i2c.c
new file mode 100644
index 000000000000..26af55a29b03
--- /dev/null
+++ b/sound/soc/codecs/tac5x1x-i2c.c
@@ -0,0 +1,81 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAC5x1x Audio Codec
+//
+// Copyright (C) 2020 - 2025 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// TAC5x1x are a series of low-power and high performance mono or stereo
+// audio codecs, as well as multiple inputs and outputs programmable in
+// single-ended or fully differential configurations. Device supports both
+// Microphone and Line In input on ADC Channel. DAC Output can be configured
+// for either Line Out or Head Phone Load.
+//
+// Author: Kevin Lu <kevin-lu@ti.com>
+// Author: Kokila Karuppusamy <kokila.karuppusamy@ti.com>
+// Author: Niranjan H Y <niranjan.hy@ti.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+
+#include "tac5x1x.h"
+
+static const struct i2c_device_id tac5x1x_id[] = {
+	{ "taa5212", TAA5212 },
+	{ "taa5412", TAA5412 },
+	{ "tac5111", TAC5111 },
+	{ "tac5112", TAC5112 },
+	{ "tac5211", TAC5211 },
+	{ "tac5212", TAC5212 },
+	{ "tac5311", TAC5311 },
+	{ "tac5312", TAC5312 },
+	{ "tac5411", TAC5411 },
+	{ "tac5412", TAC5412 },
+	{ "tad5112", TAD5112 },
+	{ "tad5212", TAD5212 },
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tac5x1x_id);
+
+static int tac5x1x_i2c_probe(struct i2c_client *i2c)
+{
+	int ret;
+	enum tac5x1x_type type;
+	struct regmap *regmap;
+	const struct regmap_config *config = &tac5x1x_regmap;
+
+	regmap = devm_regmap_init_i2c(i2c, config);
+	type = (uintptr_t)i2c_get_match_data(i2c);
+
+	dev_info(&i2c->dev, "probing %s codec_type = %d\n",
+		 i2c->name, type);
+
+	ret = tac5x1x_probe(&i2c->dev, regmap, type);
+	if (ret)
+		dev_err(&i2c->dev, "probe failed");
+
+	return ret;
+}
+
+static void tac5x1x_i2c_remove(struct i2c_client *client)
+{
+	tac5x1x_remove(&client->dev);
+}
+
+static struct i2c_driver tac5x1x_i2c_driver = {
+	.driver = {
+		.name		= "tac5x1x-codec",
+		.of_match_table	= of_match_ptr(tac5x1x_of_match),
+	},
+	.probe		= tac5x1x_i2c_probe,
+	.remove		= tac5x1x_i2c_remove,
+	.id_table	= tac5x1x_id,
+};
+
+module_i2c_driver(tac5x1x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC tac5x1x codec driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tac5x1x.c b/sound/soc/codecs/tac5x1x.c
new file mode 100644
index 000000000000..f0481a2aebd0
--- /dev/null
+++ b/sound/soc/codecs/tac5x1x.c
@@ -0,0 +1,2389 @@ 
+// SPDX-License-Identifier: GPL-2.0
+//
+// tac5x1x.c
+//
+// Copyright (C) 2022 - 2025 Texas Instruments Incorporated
+//
+// Author: Kevin Lu <kevin-lu@ti.com>
+// Author: Kokila Karuppusamy <kokila.karuppusamy@ti.com>
+// Author: Niranjan H Y <niranjan.hy@ti.com>
+//
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include <sound/tlv.h>
+
+#include "tac5x1x.h"
+
+struct tac5x1x_setup_gpio {
+	s32 gpio_func[3];
+	s32 gpio_drive[3];
+	s32 gpi1_func;
+	s32 gpa_gpio;
+};
+
+struct tac5x1x_input_diag_config {
+	s32 in_ch_en;
+	s32 out_ch_en;
+	s32 incl_se_inm;
+	s32 incl_ac_coup;
+};
+
+struct tac5x1x_irqinfo {
+	struct delayed_work irq_work;
+	s32 irq_gpio;
+	s32 irq;
+	bool irq_enable;
+};
+
+struct tac5x1x_dev {
+	u32 addr;
+	bool is_loading;
+};
+
+struct interrupt_info {
+	const char * const *names;
+	size_t size;
+};
+
+struct tac5x1x_priv {
+	struct snd_soc_component *component;
+	struct regmap *regmap;
+	struct device *dev;
+	enum tac5x1x_type codec_type;
+	bool pll_setup;
+	s32 irq;
+	s32 vref_vg;
+	s32 adc_en;
+	s32 dac_en;
+	s32 micbias_en;
+	s32 micbias_vg;
+	s32 agc_en;
+	s32 uad_en;
+	s32 vad_en;
+	s32 uag_en;
+	s32 dac_mute;
+	s32 dac_dig_vol[4];
+	s32 dac_chns;
+	u32 sysclk;
+	s32 ndev;
+	s32 micbias_threshold[2];
+	s32 gpa_threshold[2];
+	u8 dev_name[20];
+	struct snd_soc_jack *jack;
+	struct regulator *supply_avdd;
+	struct regulator *supply_iovdd;
+	struct tac5x1x_setup_gpio *gpio_setup;
+	struct tac5x1x_irqinfo irqinfo;
+	/* mutex to prevent simulataneous device read */
+	struct mutex dev_lock;
+	struct tac5x1x_dev tac5x1x_dev[2];
+	struct tac5x1x_input_diag_config input_diag_config;
+	void (*irq_work_func)(struct tac5x1x_priv *tac5x1x);
+};
+
+static const char * const int_ltch0[] = {
+	"Clock Error",
+	"PLL Lock",
+	"Boost Over Temperature",
+	"Boost Over Current",
+	"Boost Mode",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+};
+
+static const char * const chx_ltch[] = {
+	"Input Channel1 fault",
+	"Input Channel2 fault",
+	"Output Channel1 fault",
+	"Output Channel2 fault",
+	"Short to VBAT_IN",
+	"Reserved",
+	"Reserved",
+	"Reserved",
+};
+
+static const char * const in_ch1_ltch[] = {
+	"IN_CH1 open Input",
+	"IN_CH1 Input shorted",
+	"IN_CH1 INP shorted to GND",
+	"IN_CH1 INM shorted to GND",
+	"IN_CH1 INP shorted to MICBIAS",
+	"IN_CH1 INM shorted to MICBIAS",
+	"IN_CH1 INP shorted to VBAT_IN",
+	"IN_CH1 INM shorted to VBAT_IN",
+};
+
+static const char * const in_ch2_ltch[] = {
+	"IN_CH2 open Input",
+	"IN_CH2 Input shorted",
+	"IN_CH2 INP shorted to GND",
+	"IN_CH2 INM shorted to GND",
+	"IN_CH2 INP shorted to MICBIAS",
+	"IN_CH2 INM shorted to MICBIAS",
+	"IN_CH2 INP shorted to VBAT_IN",
+	"IN_CH2 INM shorted to VBAT_IN",
+};
+
+static const char * const out_ch1_ltch[] = {
+	"OUT_CH1 OUT1P Short circuit Fault",
+	"OUT_CH1 OUT1M Short circuit Fault",
+	"OUT_CH1 DRVRP Virtual Ground Fault",
+	"OUT_CH1 DRVRM Virtual ground Fault",
+	"OUT_CH1 ADC CH1 Mask",
+	"OUT_CH1 ADC CH2 MASK",
+	"Reserved",
+	"Reserved",
+};
+
+static const char * const out_ch2_ltch[] = {
+	"OUT_CH2 OUT2P Short circuit Fault",
+	"OUT_CH2 OUT2M Short circuit Fault",
+	"OUT_CH2 DRVRP Virtual Ground Fault",
+	"OUT_CH2 DRVRM Virtual ground Fault",
+	"Reserved",
+	"Reserved",
+	"AREG SC Fault Mask",
+	"AREG SC Fault",
+};
+
+static const char * const int_ltch1[] = {
+	"CH1 INP Over Voltage",
+	"CH1 INM Over Voltage",
+	"CH2 INP over Voltage",
+	"CH2 INM Over Voltage",
+	"Headset Insert Detection",
+	"Headset Remove Detection",
+	"Headset Hook",
+	"MIPS Overload",
+};
+
+static const char * const int_ltch2[] = {
+	"GPA Up threashold Fault",
+	"GPA low threashold Fault",
+	"VAD Power up detect",
+	"VAD power down detect",
+	"Micbias short circuit",
+	"Micbias high current fault",
+	"Micbias low current fault",
+	"Micbias Over voltage fault",
+};
+
+static u32 int_reg_array[] = {
+	TAC5X1X_REG_INT_LTCH0,
+	TAC5X1X_REG_CHX_LTCH,
+	TAC5X1X_REG_IN_CH1_LTCH,
+	TAC5X1X_REG_IN_CH2_LTCH,
+	TAC5X1X_REG_OUT_CH1_LTCH,
+	TAC5X1X_REG_OUT_CH2_LTCH,
+	TAC5X1X_REG_INT_LTCH1,
+	TAC5X1X_REG_INT_LTCH2,
+};
+
+static const char * const int_reg_arr_name[] = {
+	"TAC5X1X_REG_INT_LTCH0",
+	"TAC5X1X_REG_CHX_LTCH",
+	"TAC5X1X_REG_IN_CH1_LTCH",
+	"TAC5X1X_REG_IN_CH2_LTCH",
+	"TAC5X1X_REG_OUT_CH1_LTCH",
+	"TAC5X1X_REG_OUT_CH2_LTCH",
+	"TAC5X1X_REG_INT_LTCH1",
+	"TAC5X1X_REG_INT_LTCH2",
+};
+
+static const struct interrupt_info interrupts[] = {
+	{ int_ltch0, ARRAY_SIZE(int_ltch0) },
+	{ chx_ltch, ARRAY_SIZE(chx_ltch) },
+	{ in_ch1_ltch, ARRAY_SIZE(in_ch1_ltch) },
+	{ in_ch2_ltch, ARRAY_SIZE(in_ch2_ltch) },
+	{ out_ch1_ltch, ARRAY_SIZE(out_ch1_ltch) },
+	{ out_ch2_ltch, ARRAY_SIZE(out_ch2_ltch) },
+	{ int_ltch1, ARRAY_SIZE(int_ltch1) },
+	{ int_ltch2, ARRAY_SIZE(int_ltch2) },
+};
+
+static s32 tac5x1x_regmap_write(struct tac5x1x_priv *tac5x1x,
+				u32 reg, u32 value)
+{
+	s32 ret;
+	s32 retry_count = 5;
+
+	while (retry_count--) {
+		ret = regmap_write(tac5x1x->regmap, reg,
+				   value);
+		if (ret >= 0)
+			break;
+		usleep_range(5000, 5050);
+	}
+	if (retry_count == -1)
+		return 3;
+	else
+		return ret;
+}
+
+static s32 tac5x1x_regmap_read(struct tac5x1x_priv *tac5x1x,
+			       u32 reg, u32 *value)
+{
+	s32 ret;
+	s32 retry_count = 5;
+
+	ret = regmap_reinit_cache(tac5x1x->regmap, &tac5x1x_regmap);
+	if (ret) {
+		dev_err(tac5x1x->dev, "Failed to reinit reg cache\n");
+		return ret;
+	}
+
+	while (retry_count--) {
+		ret = regmap_read(tac5x1x->regmap, reg,
+				  value);
+		if (ret >= 0)
+			break;
+		usleep_range(5000, 5050);
+	}
+	if (retry_count == -1)
+		return 3;
+	else
+		return ret;
+}
+
+static s32 tac5x1x_dev_read(struct tac5x1x_priv *tac5x1x,
+			    u32 dev_no, u32 reg,
+			    u32 *ref_value)
+{
+	s32 ret;
+
+	guard(mutex)(&tac5x1x->dev_lock);
+	if (dev_no < tac5x1x->ndev) {
+		ret = tac5x1x_regmap_write(tac5x1x,
+					   TAC_PAGE_SELECT, 0);
+		if (ret < 0) {
+			dev_err(tac5x1x->dev, "%s, E=%d\n",
+				__func__, ret);
+			return ret;
+		}
+
+		ret = tac5x1x_regmap_read(tac5x1x, reg, ref_value);
+		if (ret < 0)
+			dev_err(tac5x1x->dev, "read, ERROR, E=%d\n",
+				ret);
+		else
+			dev_dbg(tac5x1x->dev,
+				"read PAGE:REG 0x%02x:0x%02x,0x%02x\n",
+					TAC_PAGE_ID(reg),
+					TAC_PAGE_REG(reg), *ref_value);
+	} else {
+		dev_err(tac5x1x->dev, "%s, ERROR: no such device(%d)\n",
+			__func__, dev_no);
+	}
+
+	return 0;
+}
+
+static void tac5x1x_enable_irq(struct tac5x1x_priv *tac5x1x, bool is_enable)
+{
+	if (is_enable == tac5x1x->irqinfo.irq_enable &&
+	    !gpio_is_valid(tac5x1x->irqinfo.irq_gpio))
+		return;
+	if (is_enable)
+		enable_irq(tac5x1x->irqinfo.irq);
+	else
+		disable_irq_nosync(tac5x1x->irqinfo.irq);
+	tac5x1x->irqinfo.irq_enable = is_enable;
+}
+
+static void tac5x1x_irq_work_func(struct tac5x1x_priv *tac5x1x)
+{
+	u32 reg_val, array_size, i, index = 0, bit = 0;
+	s32 rc;
+
+	tac5x1x_enable_irq(tac5x1x, false);
+	array_size =  ARRAY_SIZE(int_reg_array);
+	for (i = 0; i < array_size; i++) {
+		rc = tac5x1x_dev_read(tac5x1x, index,
+				      int_reg_array[i], &reg_val);
+		if (!rc) {
+			for (s32 j = 0; j < interrupts[i].size; j++) {
+				bit = interrupts[i].size - 1 - j;
+				if (reg_val & (1 << bit))
+					dev_info(tac5x1x->dev,
+						 "Reg: %s || %s\n",
+						int_reg_arr_name[i],
+						interrupts[i].names[j]);
+			}
+		} else {
+			dev_err(tac5x1x->dev,
+				"%s DEV_NO%d Read Reg 0x%04x error(rc=%d)\n",
+					tac5x1x->dev_name, index,
+					int_reg_array[i], rc);
+		}
+	}
+	tac5x1x_enable_irq(tac5x1x, true);
+}
+
+static const struct regmap_range_cfg tac5x1x_ranges[] = {
+	{
+		.range_min = 0,
+		.range_max = 12 * 128,
+		.selector_reg = TAC_PAGE_SELECT,
+		.selector_mask = GENMASK(7, 0),
+		.selector_shift = 0,
+		.window_start = 0,
+		.window_len = 128,
+	},
+};
+
+const struct regmap_config tac5x1x_regmap = {
+	.max_register = 12 * 128,
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_MAPLE,
+	.ranges = tac5x1x_ranges,
+	.num_ranges = ARRAY_SIZE(tac5x1x_ranges),
+};
+EXPORT_SYMBOL(tac5x1x_regmap);
+
+static void irq_work_routine(struct work_struct *work)
+{
+	struct tac5x1x_priv *tac5x1x =
+		container_of(work, struct tac5x1x_priv, irqinfo.irq_work.work);
+
+	if (tac5x1x->irq_work_func) {
+		dev_dbg(tac5x1x->dev, "Calling irq_work_func\n");
+		tac5x1x->irq_work_func(tac5x1x);
+	} else {
+		dev_dbg(tac5x1x->dev,
+			"%s, irq_work_func is NULL\n", __func__);
+	}
+}
+
+static irqreturn_t tac5x1x_irq_handler(s32 irq, void *dev_id)
+{
+	struct tac5x1x_priv *tac5x1x = (struct tac5x1x_priv *)dev_id;
+
+	/* get IRQ status after 100 ms */
+	schedule_delayed_work(&tac5x1x->irqinfo.irq_work,
+			      msecs_to_jiffies(100));
+	return IRQ_HANDLED;
+}
+
+static s32 tac5x1x_register_interrupt(struct tac5x1x_priv *tac5x1x)
+{
+	struct device_node *np = tac5x1x->dev->of_node;
+	s32 ret;
+
+	mutex_init(&tac5x1x->dev_lock);
+	tac5x1x->irqinfo.irq = of_irq_get(np, 0);
+	INIT_DELAYED_WORK(&tac5x1x->irqinfo.irq_work, irq_work_routine);
+
+	dev_dbg(tac5x1x->dev, "irq = %d\n", tac5x1x->irqinfo.irq);
+	ret = request_threaded_irq(tac5x1x->irqinfo.irq, tac5x1x_irq_handler,
+				   NULL, IRQF_TRIGGER_FALLING, "TAC-IRQ", tac5x1x);
+	if (ret) {
+		dev_err(tac5x1x->dev, "request irq failed, ret %d\n", ret);
+		return ret;
+	}
+	tac5x1x->irq_work_func = tac5x1x_irq_work_func;
+	return 0;
+};
+
+static s32 tac5x1x_set_GPO1_gpio(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	s32 gpio_check, val;
+
+	val = snd_soc_component_read(component, TAC5X1X_GPO1);
+	gpio_check = ((val & TAC5X1X_GPIOX_CFG_MASK) >> 0);
+	if (gpio_check != TAC5X1X_GPIO_GPO) {
+		dev_err(component->dev,
+			"%s: GPO1 is not configure as a GPO output\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (ucontrol->value.integer.value[0])
+		val = 0;
+	else
+		val = TAC5X1X_GPO1_VAL;
+
+	ucontrol->value.integer.value[0] = (val >> 0);
+	snd_soc_component_update_bits(component, TAC5X1X_GPIOVAL,
+				      TAC5X1X_GPO1_VAL, val);
+
+	return 1;
+};
+
+static s32 tac5x1x_get_GPIO1_gpio(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	s32 val;
+
+	val = snd_soc_component_read(component, TAC5X1X_GPIOVAL);
+	ucontrol->value.integer.value[0] = ((val & TAC5X1X_GPIO1_MON) >> 0);
+
+	return 0;
+};
+
+static s32 tac5x1x_set_GPIO1_gpio(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	s32 gpio_check, val;
+
+	val = snd_soc_component_read(component, TAC5X1X_GPIO1);
+	gpio_check = ((val & TAC5X1X_GPIOX_CFG_MASK) >> 0);
+	if (gpio_check == TAC5X1X_GPIO_DISABLE) {
+		dev_err(component->dev,
+			"%s: GPIO1 is not configure as a GPO output\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (ucontrol->value.integer.value[0])
+		val = 0;
+	else
+		val = TAC5X1X_GPIO1_VAL;
+
+	ucontrol->value.integer.value[0] = (val >> 0);
+	snd_soc_component_update_bits(component, TAC5X1X_GPIOVAL,
+				      TAC5X1X_GPIO1_VAL, val);
+
+	return 1;
+};
+
+static s32 tac5x1x_get_GPIO2_gpio(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	s32 val;
+
+	val = snd_soc_component_read(component, TAC5X1X_GPIOVAL);
+	ucontrol->value.integer.value[0] = !!(val & TAC5X1X_GPIO2_MON);
+
+	return 0;
+};
+
+static s32 tac5x1x_set_GPIO2_gpio(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	s32 gpio_check, val;
+
+	val = snd_soc_component_read(component, TAC5X1X_GPIO2);
+	gpio_check = ((val & TAC5X1X_GPIOX_CFG_MASK) >> 0);
+	if (gpio_check == TAC5X1X_GPIO_DISABLE) {
+		dev_err(component->dev,
+			"%s: GPIO2 is not configure as a GPO output\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (ucontrol->value.integer.value[0])
+		val = 0;
+	else
+		val = TAC5X1X_GPIO2_VAL;
+
+	ucontrol->value.integer.value[0] = (val >> 0);
+	snd_soc_component_update_bits(component, TAC5X1X_GPIOVAL,
+				      TAC5X1X_GPIO2_VAL, val);
+
+	return 1;
+};
+
+static s32 tac5x1x_get_GPI1_gpio(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	s32 val;
+
+	val = snd_soc_component_read(component, TAC5X1X_GPIOVAL);
+	ucontrol->value.integer.value[0] = !!(val & TAC5X1X_GPI1_MON);
+
+	return 0;
+};
+
+static const struct snd_kcontrol_new tac5x1x_GPO1[] = {
+	SOC_SINGLE_BOOL_EXT("GPO1 GPO", 0, NULL, tac5x1x_set_GPO1_gpio),
+};
+
+static const struct snd_kcontrol_new tac5x1x_GPIO1_I[] = {
+	SOC_SINGLE_BOOL_EXT("GPIO1 GPI", 0, tac5x1x_get_GPIO1_gpio, NULL),
+};
+
+static const struct snd_kcontrol_new tac5x1x_GPIO1_O[] = {
+	SOC_SINGLE_BOOL_EXT("GPIO1 GPO", 0, NULL, tac5x1x_set_GPIO1_gpio),
+};
+
+static const struct snd_kcontrol_new tac5x1x_GPIO2_I[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "GPIO2 GPI",
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.get = tac5x1x_get_GPIO2_gpio,
+		.info = snd_soc_info_bool_ext,
+	}
+};
+
+static const struct snd_kcontrol_new tac5x1x_GPIO2_O[] = {
+	SOC_SINGLE_BOOL_EXT("GPIO2 GPO", 0, NULL, tac5x1x_set_GPIO2_gpio),
+};
+
+static const struct snd_kcontrol_new tac5x1x_GPI1[] = {
+	SOC_SINGLE_BOOL_EXT("GPI1 GPI", 0, tac5x1x_get_GPI1_gpio, NULL),
+};
+
+/* Record */
+/* ADC Analog/PDM Selection */
+static const char *const tac5x1x_input_source_text[] = {"Analog", "PDM"};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_in1_source_enum, TAC5X1X_INTF4, 7,
+		tac5x1x_input_source_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_in2_source_enum, TAC5X1X_INTF4, 6,
+		tac5x1x_input_source_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_in1_source_control[] = {
+	SOC_DAPM_ENUM("CH1 Source MUX", tac5x1x_in1_source_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_in2_source_control[] = {
+	SOC_DAPM_ENUM("CH2 Source MUX", tac5x1x_in2_source_enum),
+};
+
+static const char *const tad5x1x_input_source_text[] = {"Disable", "PDM"};
+static SOC_ENUM_SINGLE_DECL(tad5x1x_in1_source_enum, TAC5X1X_INTF4, 7,
+		tad5x1x_input_source_text);
+static SOC_ENUM_SINGLE_DECL(tad5x1x_in2_source_enum, TAC5X1X_INTF4, 6,
+		tad5x1x_input_source_text);
+
+static const struct snd_kcontrol_new tad5x1x_dapm_in1_source_control[] = {
+	SOC_DAPM_ENUM("CH1 Source MUX", tad5x1x_in1_source_enum),
+};
+
+static const struct snd_kcontrol_new tad5x1x_dapm_in2_source_control[] = {
+	SOC_DAPM_ENUM("CH2 Source MUX", tad5x1x_in2_source_enum),
+};
+
+/* ADC Analog source Selection */
+static const char *const tac5x1x_input_analog_sel_text[] = {
+	"Differential",
+	"Single-ended",
+	"Single-ended mux INxP",
+	"Single-ended mux INxM",
+};
+
+static const char *const tac5x1x_input_analog2_sel_text[] = {
+	"Differential",
+	"Single-ended",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_config_enum, TAC5X1X_ADCCH1C0, 6,
+		tac5x1x_input_analog_sel_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_config_enum, TAC5X1X_ADCCH2C0, 6,
+		tac5x1x_input_analog2_sel_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc1_config_control[] = {
+	SOC_DAPM_ENUM("ADC1 Analog MUX", tac5x1x_adc1_config_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc2_config_control[] = {
+	SOC_DAPM_ENUM("ADC2 Analog MUX", tac5x1x_adc2_config_enum),
+};
+
+/*
+ * ADC full-scale selection
+ * 2/10-VRMS is for TAX52xx/TAX51xx devices
+ * 4/5-VRMS is for TAX54xx/TAX53xx devices
+ */
+static const char *const tac5x1x_adc_fscale_text[] = {"2/10-VRMS",
+	"4/5-VRMS"};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_fscale_enum, TAC5X1X_ADCCH1C0, 1,
+		tac5x1x_adc_fscale_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_fscale_enum, TAC5X1X_ADCCH2C0, 1,
+		tac5x1x_adc_fscale_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc1_fscale_control[] = {
+	SOC_DAPM_ENUM("ADC1 FSCALE MUX", tac5x1x_adc1_fscale_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_adc2_fscale_control[] = {
+	SOC_DAPM_ENUM("ADC2 FSCALE MUX", tac5x1x_adc2_fscale_enum),
+};
+
+/* Impedance Selection */
+static const char *const resistor_text[] = {
+	"5 kOhm",
+	"10 kOhm",
+	"40 kOhm",
+};
+
+static SOC_ENUM_SINGLE_DECL(adc1_resistor_enum, TAC5X1X_ADCCH1C0, 4,
+		resistor_text);
+static SOC_ENUM_SINGLE_DECL(adc2_resistor_enum, TAC5X1X_ADCCH2C0, 4,
+		resistor_text);
+
+/* PDM Data input pin Selection */
+static const char *const pdm_pin_text[] = {
+	"Disable",
+	"GPIO1",
+	"GPIO2",
+	"GPI1",
+};
+
+static SOC_ENUM_SINGLE_DECL(pdm_12_pin_enum, TAC5X1X_INTF4, 2, pdm_pin_text);
+static SOC_ENUM_SINGLE_DECL(pdm_34_pin_enum, TAC5X1X_INTF4, 0, pdm_pin_text);
+static const struct snd_kcontrol_new pdm_12_pin_controls[] = {
+	SOC_DAPM_ENUM("PDM chn12 Datain Select", pdm_12_pin_enum),
+};
+
+static const struct snd_kcontrol_new pdm_34_pin_controls[] = {
+	SOC_DAPM_ENUM("PDM chn34 Datain Select", pdm_34_pin_enum),
+};
+
+static const char *const pdmclk_text[] = {
+	"2.8224 MHz or 3.072 MHz", "1.4112 MHz or 1.536 MHz",
+	"705.6 kHz or 768 kHz", "5.6448 MHz or 6.144 MHz"};
+
+static SOC_ENUM_SINGLE_DECL(pdmclk_select_enum, TAC5X1X_CNTCLK0, 6,
+		pdmclk_text);
+
+/* Digital Volume control. From -80 to 47 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(record_dig_vol_tlv, -8000, 50, 0);
+
+/* Gain Calibration control. From -0.8db to 0.7db dB in 0.1 dB steps */
+static DECLARE_TLV_DB_MINMAX(record_gain_cali_tlv, -80, 70);
+
+/* Analog Level control. From -12 to 24 dB in 6 dB steps */
+static DECLARE_TLV_DB_SCALE(playback_analog_level_tlv, -1200, 600, 0);
+
+/* Digital Volume control. From -100 to 27 dB in 0.5 dB steps */
+static DECLARE_TLV_DB_SCALE(dac_dig_vol_tlv, -10000, 50, 0); // mute ?
+
+/* Gain Calibration control. From -0.8db to 0.7db dB in 0.1 dB steps */
+static DECLARE_TLV_DB_MINMAX(playback_gain_cali_tlv, -80, 70);
+
+/* Output Source Selection */
+static const char *const tac5x1x_output_source_text[] = {
+	"Disabled",
+	"DAC Input",
+	"Analog Bypass",
+	"DAC + Analog Bypass Mix",
+	"DAC -> OUTxP, INxP -> OUTxM",
+	"INxM -> OUTxP, DAC -> OUTxM",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out1_source_enum, TAC5X1X_OUT1CFG0, 5,
+		tac5x1x_output_source_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out2_source_enum, TAC5X1X_OUT2CFG0, 5,
+		tac5x1x_output_source_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out1_source_control[] = {
+	SOC_DAPM_ENUM("OUT1X MUX", tac5x1x_out1_source_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out2_source_control[] = {
+	SOC_DAPM_ENUM("OUT2X MUX", tac5x1x_out2_source_enum),
+};
+
+/* Output Config Selection */
+static const char *const tac5x1x_output_config_text[] = {
+	"Differential",
+	"Stereo Single-ended",
+	"Mono Single-ended at OUTxP only",
+	"Mono Single-ended at OUTxM only",
+	"Pseudo differential with OUTxM as VCOM",
+	"Pseudo differential with OUTxM as external sensing",
+	"Pseudo differential with OUTxP as VCOM",
+};
+
+static const char *const tac5x1x_output2_config_text[] = {
+	"Differential",
+	"Stereo Single-ended",
+	"Mono Single-ended at OUTxP only",
+	"Mono Single-ended at OUTxM only",
+	"Pseudo differential with OUTxM as VCOM",
+	"Reserved",
+	"Pseudo differential with OUTxP as VCOM",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out1_config_enum, TAC5X1X_OUT1CFG0, 2,
+			tac5x1x_output_config_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_out2_config_enum, TAC5X1X_OUT2CFG0, 2,
+			tac5x1x_output2_config_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out1_config_control[] = {
+	SOC_DAPM_ENUM("OUT1X Config MUX", tac5x1x_out1_config_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out2_config_control[] = {
+	SOC_DAPM_ENUM("OUT2X Config MUX", tac5x1x_out2_config_enum),
+};
+
+static const char *const tac5x1x_wideband_text[] = {
+	"Audio BW 24-kHz",
+	"Wide BW 96-kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_wideband_enum, TAC5X1X_ADCCH1C0, 0,
+		tac5x1x_wideband_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_wideband_enum, TAC5X1X_ADCCH2C0, 0,
+		tac5x1x_wideband_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_dac1_wideband_enum, TAC5X1X_OUT1CFG1, 0,
+		tac5x1x_wideband_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_dac2_wideband_enum, TAC5X1X_OUT2CFG1, 0,
+		tac5x1x_wideband_text);
+
+static const char *const tac5x1x_tolerance_text[] = {
+	"AC Coupled with 100mVpp",
+	"AC/DC Coupled with 1Vpp",
+	"AC/DC Coupled with Rail-to-rail",
+};
+
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc1_tolerance_enum, TAC5X1X_ADCCH1C0, 2,
+		tac5x1x_tolerance_text);
+static SOC_ENUM_SINGLE_DECL(tac5x1x_adc2_tolerance_enum, TAC5X1X_ADCCH2C0, 2,
+		tac5x1x_tolerance_text);
+
+/* Output Drive Selection */
+static const char *const tac5x1x_output_driver_text[] = {
+	"Line-out",
+	"Headphone",
+	"4 ohm",
+	"FD Receiver/Debug",
+};
+
+static SOC_ENUM_SINGLE_DECL(out1p_driver_enum, TAC5X1X_OUT1CFG1, 6,
+		tac5x1x_output_driver_text);
+
+static SOC_ENUM_SINGLE_DECL(out2p_driver_enum, TAC5X1X_OUT2CFG1, 6,
+		tac5x1x_output_driver_text);
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out1_driver_control[] = {
+	SOC_DAPM_ENUM("OUT1 driver MUX", out1p_driver_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_dapm_out2_driver_control[] = {
+	SOC_DAPM_ENUM("OUT2 driver MUX", out2p_driver_enum),
+};
+
+/* Decimation Filter Selection */
+static const char *const decimation_filter_text[] = {
+	"Linear Phase", "Low Latency", "Ultra-low Latency"};
+
+static SOC_ENUM_SINGLE_DECL(decimation_filter_record_enum, TAC5X1X_DSP0, 6,
+			decimation_filter_text);
+static SOC_ENUM_SINGLE_DECL(decimation_filter_playback_enum, TAC5X1X_DSP1, 6,
+			    decimation_filter_text);
+
+static const struct snd_kcontrol_new tx_ch1_asi_switch =
+	SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH1, 5, 1, 0);
+static const struct snd_kcontrol_new tx_ch2_asi_switch =
+	SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH2, 5, 1, 0);
+static const struct snd_kcontrol_new tx_ch3_asi_switch =
+	SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH3, 5, 1, 0);
+static const struct snd_kcontrol_new tx_ch4_asi_switch =
+	SOC_DAPM_SINGLE("Capture Switch", TAC5X1X_PASITXCH4, 5, 1, 0);
+
+static const struct snd_kcontrol_new rx_ch1_asi_switch =
+	SOC_DAPM_SINGLE("Switch", TAC5X1X_PASIRXCH1, 5, 1, 0);
+static const struct snd_kcontrol_new rx_ch2_asi_switch =
+	SOC_DAPM_SINGLE("Switch", TAC5X1X_PASIRXCH2, 5, 1, 0);
+
+static const char *const rx_ch3_asi_cfg_text[] = {
+		"Disable",
+		"DAC channel data",
+};
+
+static const char *const rx_ch5_asi_cfg_text[] = {
+		"Disable",
+		"DAC channel data",
+		"ADC channel output loopback",
+};
+
+static const char *const rx_ch6_asi_cfg_text[] = {
+		"Disable",
+		"DAC channel data",
+		"ADC channel output loopback",
+		"Channel Input to ICLA device",
+};
+
+static const char *const tx_ch5_asi_cfg_text[] = {
+		"Tristate",
+		"Input Channel Loopback data",
+		"Echo reference Channel data",
+};
+
+static const char *const tx_ch7_asi_cfg_text[] = {
+		"Tristate",
+		"Vbat_Wlby2,Temp_Wlby2",
+		"echo_ref_ch1,echo_ref_ch2",
+};
+
+static const char *const tx_ch8_asi_cfg_text[] = {
+		"Tristate",
+		"ICLA data",
+};
+
+static const char *const tac5x1x_slot_select_text[] = {
+	"Slot 0", "Slot 1", "Slot 2", "Slot 3",
+	"Slot 4", "Slot 5", "Slot 6", "Slot 7",
+	"Slot 8", "Slot 9", "Slot 10", "Slot 11",
+	"Slot 12", "Slot 13", "Slot 14", "Slot 15",
+	"Slot 16", "Slot 17", "Slot 18", "Slot 19",
+	"Slot 20", "Slot 21", "Slot 22", "Slot 23",
+	"Slot 24", "Slot 25", "Slot 26", "Slot 27",
+	"Slot 28", "Slot 29", "Slot 30", "Slot 31",
+};
+
+static const char *const out2x_vcom_text[] = {
+	"0.6 * Vref",
+	"AVDD by 2",
+};
+
+static const char *const diag_cfg_text[] = {
+	"0mv", "30mv", "60mv", "90mv",
+	"120mv", "150mv", "180mv", "210mv",
+	"240mv", "270mv", "300mv", "330mv",
+	"360mv", "390mv", "420mv", "450mv",
+};
+
+static const char *const diag_cfg_gnd_text[] = {
+	"0mv", "60mv", "120mv", "180mv",
+	"240mv", "300mv", "360mv", "420mv",
+	"480mv", "540mv", "600mv", "660mv",
+	"720mv", "780mv", "840mv", "900mv",
+};
+
+static SOC_ENUM_SINGLE_DECL(out2x_vcom_enum, TAC5X1X_OUT2CFG0, 1,
+		out2x_vcom_text);
+
+static SOC_ENUM_SINGLE_DECL(tx_ch5_asi_cfg_enum, TAC5X1X_PASITXCH5, 5,
+		tx_ch5_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch6_asi_cfg_enum, TAC5X1X_PASITXCH6, 5,
+		tx_ch5_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch7_asi_cfg_enum, TAC5X1X_PASITXCH7, 5,
+		tx_ch7_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch8_asi_cfg_enum, TAC5X1X_PASITXCH8, 5,
+		tx_ch8_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch1_slot_select_enum, TAC5X1X_PASITXCH1, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch2_slot_select_enum, TAC5X1X_PASITXCH2, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch3_slot_select_enum, TAC5X1X_PASITXCH3, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch4_slot_select_enum, TAC5X1X_PASITXCH4, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch5_slot_select_enum, TAC5X1X_PASITXCH5, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch6_slot_select_enum, TAC5X1X_PASITXCH6, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch7_slot_select_enum, TAC5X1X_PASITXCH7, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(tx_ch8_slot_select_enum, TAC5X1X_PASITXCH8, 0,
+		tac5x1x_slot_select_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_ch3_asi_cfg_enum, TAC5X1X_PASIRXCH3, 5,
+		rx_ch3_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch4_asi_cfg_enum, TAC5X1X_PASIRXCH4, 5,
+		rx_ch3_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch5_asi_cfg_enum, TAC5X1X_PASIRXCH5, 5,
+		rx_ch5_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch6_asi_cfg_enum, TAC5X1X_PASIRXCH6, 5,
+		rx_ch6_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch7_asi_cfg_enum, TAC5X1X_PASIRXCH7, 5,
+		rx_ch6_asi_cfg_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch8_asi_cfg_enum, TAC5X1X_PASIRXCH8, 5,
+		rx_ch6_asi_cfg_text);
+
+static SOC_ENUM_SINGLE_DECL(rx_ch1_slot_select_enum, TAC5X1X_PASIRXCH1, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch2_slot_select_enum, TAC5X1X_PASIRXCH2, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch3_slot_select_enum, TAC5X1X_PASIRXCH3, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch4_slot_select_enum, TAC5X1X_PASIRXCH4, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch5_slot_select_enum, TAC5X1X_PASIRXCH5, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch6_slot_select_enum, TAC5X1X_PASIRXCH6, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch7_slot_select_enum, TAC5X1X_PASIRXCH7, 0,
+		tac5x1x_slot_select_text);
+static SOC_ENUM_SINGLE_DECL(rx_ch8_slot_select_enum, TAC5X1X_PASIRXCH8, 0,
+		tac5x1x_slot_select_text);
+
+static SOC_ENUM_SINGLE_DECL(diag_cfg1_sht_term_enum, TAC5X1X_DIAG_CFG1, 4,
+		diag_cfg_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg1_vbat_in_enum, TAC5X1X_DIAG_CFG1, 0,
+		diag_cfg_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg2_sht_gnd_enum, TAC5X1X_DIAG_CFG2, 4,
+		diag_cfg_gnd_text);
+static SOC_ENUM_SINGLE_DECL(diag_cfg2_micbias, TAC5X1X_DIAG_CFG2, 0,
+		diag_cfg_text);
+
+static const struct snd_kcontrol_new taa5x1x_controls[] = {
+	SOC_ENUM("Record Decimation Filter",
+		 decimation_filter_record_enum),
+	SOC_ENUM("ADC1 Audio BW", tac5x1x_adc1_wideband_enum),
+
+	SOC_SINGLE_TLV("ADC1 Digital Capture Volume", TAC5X1X_ADCCH1C2,
+		       0, 0xff, 0, record_dig_vol_tlv),
+
+	SOC_SINGLE_TLV("ADC1 Fine Capture Volume", TAC5X1X_ADCCH1C3,
+		       0, 0xff, 0, record_gain_cali_tlv),
+
+	SOC_SINGLE_RANGE("ADC1 Phase Capture Volume", TAC5X1X_ADCCH1C4,
+			 2, 0, 63, 0),
+
+	SOC_ENUM("ASI_TX_CH5_CFG", tx_ch5_asi_cfg_enum),
+	SOC_ENUM("ASI_TX_CH6_CFG", tx_ch6_asi_cfg_enum),
+	SOC_ENUM("ASI_TX_CH7_CFG", tx_ch7_asi_cfg_enum),
+	SOC_ENUM("ASI_TX_CH8_CFG", tx_ch8_asi_cfg_enum),
+	SOC_ENUM("ASI_TX_CH1_SLOT", tx_ch1_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH2_SLOT", tx_ch2_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH3_SLOT", tx_ch3_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH4_SLOT", tx_ch4_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH5_SLOT", tx_ch5_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH6_SLOT", tx_ch6_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH7_SLOT", tx_ch7_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH8_SLOT", tx_ch8_slot_select_enum),
+	SOC_SINGLE("IN_CH1_EN Capture Switch", TAC5X1X_CH_EN, 7, 1, 0),
+	SOC_SINGLE("IN_CH2_EN Capture Switch", TAC5X1X_CH_EN, 6, 1, 0),
+	SOC_SINGLE("IN_CH3_EN Capture Switch", TAC5X1X_CH_EN, 5, 1, 0),
+	SOC_SINGLE("IN_CH4_EN Capture Switch", TAC5X1X_CH_EN, 4, 1, 0),
+};
+
+static const struct snd_kcontrol_new tad5x1x_controls[] = {
+	SOC_ENUM("Playback Decimation Filter",
+		 decimation_filter_playback_enum),
+	SOC_ENUM("DAC1 Audio BW", tac5x1x_dac1_wideband_enum),
+	SOC_SINGLE_TLV("OUT1P Analog Level Playback Volume", TAC5X1X_OUT1CFG1,
+		       3, 6, 1, playback_analog_level_tlv),
+	SOC_SINGLE_TLV("OUT1M Analog Level Playback Volume", TAC5X1X_OUT1CFG2,
+		       3, 6, 1, playback_analog_level_tlv),
+	SOC_SINGLE_TLV("DAC1 CHA Digital Playback Volume", TAC5X1X_DACCH1A0,
+		       0, 0xff, 0, dac_dig_vol_tlv),
+	SOC_SINGLE_TLV("DAC1 CHB Digital Playback Volume", TAC5X1X_DACCH1B0,
+		       0, 0xff, 0, dac_dig_vol_tlv),
+	SOC_SINGLE_TLV("DAC1 CHA Gain Calibration Playback Volume",
+		       TAC5X1X_DACCH1A1, 4, 0xf, 0,
+			playback_gain_cali_tlv),
+	SOC_SINGLE_TLV("DAC1 CHB Gain Calibration Playback Volume",
+		       TAC5X1X_DACCH1B1, 4, 0xf, 0,
+			playback_gain_cali_tlv),
+
+	SOC_ENUM("ASI_RX_CH3_EN Playback", rx_ch3_asi_cfg_enum),
+	SOC_ENUM("ASI_RX_CH4_EN Playback", rx_ch4_asi_cfg_enum),
+	SOC_ENUM("ASI_RX_CH5_EN Playback", rx_ch5_asi_cfg_enum),
+	SOC_ENUM("ASI_RX_CH6_EN Playback", rx_ch6_asi_cfg_enum),
+	SOC_ENUM("ASI_RX_CH7_EN Playback", rx_ch7_asi_cfg_enum),
+	SOC_ENUM("ASI_RX_CH8_EN Playback", rx_ch8_asi_cfg_enum),
+	SOC_ENUM("ASI_RX_CH1_SLOT Playback", rx_ch1_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH2_SLOT Playback", rx_ch2_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH3_SLOT Playback", rx_ch3_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH4_SLOT Playback", rx_ch4_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH5_SLOT Playback", rx_ch5_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH6_SLOT Playback", rx_ch6_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH7_SLOT Playback", rx_ch7_slot_select_enum),
+	SOC_ENUM("ASI_RX_CH8_SLOT Playback", rx_ch8_slot_select_enum),
+
+	SOC_SINGLE("OUT_CH1_EN Playback Switch", TAC5X1X_CH_EN, 3, 1, 0),
+	SOC_SINGLE("OUT_CH2_EN Playback Switch", TAC5X1X_CH_EN, 2, 1, 0),
+	SOC_SINGLE("OUT_CH3_EN Playback Switch", TAC5X1X_CH_EN, 1, 1, 0),
+	SOC_SINGLE("OUT_CH4_EN Playback Switch", TAC5X1X_CH_EN, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new tac5x11_controls[] = {
+	SOC_ENUM("ADC1 Common-mode Tolerance", tac5x1x_adc1_tolerance_enum),
+	SOC_ENUM("ADC1 Impedance Select", adc1_resistor_enum),
+};
+
+static const struct snd_kcontrol_new tad5x12_controls[] = {
+	SOC_SINGLE_TLV("OUT2P Analog Level Playback Volume", TAC5X1X_OUT2CFG1,
+		       3, 6, 1, playback_analog_level_tlv),
+	SOC_SINGLE_TLV("OUT2M Analog Level Playback Volume", TAC5X1X_OUT2CFG2,
+		       3, 6, 1, playback_analog_level_tlv),
+	SOC_SINGLE_TLV("DAC2 CHA Digital Playback Volume", TAC5X1X_DACCH2A0,
+		       0, 0xff, 0, dac_dig_vol_tlv),
+	SOC_SINGLE_TLV("DAC2 CHB Digital Playback Volume", TAC5X1X_DACCH2B0,
+		       0, 0xff, 0, dac_dig_vol_tlv),
+	SOC_SINGLE_TLV("DAC2 CHA Gain Calibration Playback Volume",
+		       TAC5X1X_DACCH2A1, 4, 0xf, 0,
+			playback_gain_cali_tlv),
+	SOC_SINGLE_TLV("DAC2 CHB Gain Calibration Playback Volume",
+		       TAC5X1X_DACCH2B1, 4, 0xf, 0,
+			playback_gain_cali_tlv),
+	SOC_ENUM("DAC2 Audio BW", tac5x1x_dac2_wideband_enum),
+	SOC_ENUM("OUT2X_VCOM", out2x_vcom_enum),
+};
+
+static const struct snd_kcontrol_new taa5x12_controls[] = {
+	SOC_ENUM("ADC2 Audio BW", tac5x1x_adc2_wideband_enum),
+
+	SOC_SINGLE_TLV("ADC2 Digital Capture Volume", TAC5X1X_ADCCH2C2,
+		       0, 0xff, 0, record_dig_vol_tlv),
+
+	SOC_SINGLE_TLV("ADC2 Fine Capture Volume", TAC5X1X_ADCCH2C3,
+		       0, 0xff, 0, record_gain_cali_tlv),
+
+	SOC_SINGLE_RANGE("ADC2 Phase Capture Volume", TAC5X1X_ADCCH2C4,
+			 2, 0, 63, 0),
+};
+
+static const struct snd_kcontrol_new tac5x12_impedance_controls[] = {
+	SOC_ENUM("ADC1 Impedance Select", adc1_resistor_enum),
+	SOC_ENUM("ADC2 Impedance Select", adc2_resistor_enum),
+};
+
+static const struct snd_kcontrol_new tac5x12_tolerance_controls[] = {
+	SOC_ENUM("ADC1 Common-mode Tolerance", tac5x1x_adc1_tolerance_enum),
+	SOC_ENUM("ADC2 Common-mode Tolerance", tac5x1x_adc2_tolerance_enum),
+};
+
+static const struct snd_kcontrol_new tac5x1x_pdm_controls[] = {
+	SOC_ENUM("PDM Clk Divider", pdmclk_select_enum),
+
+	SOC_SINGLE_TLV("PDM1 Digital Capture Volume", TAC5X1X_ADCCH1C2,
+		       0, 0xff, 0, record_dig_vol_tlv),
+	SOC_SINGLE_TLV("PDM2 Digital Capture Volume", TAC5X1X_ADCCH2C2,
+		       0, 0xff, 0, record_dig_vol_tlv),
+
+	SOC_SINGLE_TLV("PDM1 Fine Capture Volume", TAC5X1X_ADCCH1C3,
+		       0, 0xff, 0, record_gain_cali_tlv),
+	SOC_SINGLE_TLV("PDM2 Fine Capture Volume", TAC5X1X_ADCCH2C3,
+		       0, 0xff, 0, record_gain_cali_tlv),
+
+	SOC_SINGLE_RANGE("PDM1 Phase Capture Volume", TAC5X1X_ADCCH1C4,
+			 2, 0, 63, 0),
+	SOC_SINGLE_RANGE("PDM2 Phase Capture Volume", TAC5X1X_ADCCH2C4,
+			 2, 0, 63, 0),
+
+	SOC_SINGLE_TLV("PDM3 Digital Capture Volume", TAC5X1X_ADCCH3C2,
+		       0, 0xff, 0, record_dig_vol_tlv),
+	SOC_SINGLE_TLV("PDM4 Digital Capture Volume", TAC5X1X_ADCCH4C2,
+		       0, 0xff, 0, record_dig_vol_tlv),
+
+	SOC_SINGLE_TLV("PDM3 Fine Capture Volume", TAC5X1X_ADCCH3C3,
+		       0, 0xff, 0, record_gain_cali_tlv),
+	SOC_SINGLE_TLV("PDM4 Fine Capture Volume", TAC5X1X_ADCCH4C3,
+		       0, 0xff, 0, record_gain_cali_tlv),
+
+	SOC_SINGLE_RANGE("PDM3 Phase Capture Volume", TAC5X1X_ADCCH3C4,
+			 2, 0, 63, 0),
+	SOC_SINGLE_RANGE("PDM4 Phase Capture Volume", TAC5X1X_ADCCH4C4,
+			 2, 0, 63, 0),
+};
+
+static const struct snd_kcontrol_new taa5x1x_ip_controls[] = {
+	SOC_ENUM("DIAG_SHT_TERM", diag_cfg1_sht_term_enum),
+	SOC_ENUM("DIAG_SHT_VBAT_IN", diag_cfg1_vbat_in_enum),
+	SOC_ENUM("DIAG_SHT_GND", diag_cfg2_sht_gnd_enum),
+	SOC_ENUM("DIAG_SHT_MICBIAS", diag_cfg2_micbias),
+};
+
+static const struct snd_soc_dapm_widget taa5x1x_dapm_widgets[] = {
+	/* ADC1 */
+	SND_SOC_DAPM_INPUT("AIN1"),
+
+	SND_SOC_DAPM_MUX("ADC1 Full-Scale", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_adc1_fscale_control),
+
+	SND_SOC_DAPM_MUX("ADC1 Config", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_adc1_config_control),
+
+	SND_SOC_DAPM_ADC("CH1_ADC_EN", "CH1 Capture", TAC5X1X_CH_EN, 7, 0),
+
+	SND_SOC_DAPM_SWITCH("ASI_TX_CH1_EN", SND_SOC_NOPM, 0, 0,
+			    &tx_ch1_asi_switch),
+
+	SND_SOC_DAPM_MICBIAS("Mic Bias", TAC5X1X_PWR_CFG, 5, 0),
+	SND_SOC_DAPM_SWITCH("ASI_TX_CH2_EN", SND_SOC_NOPM, 0, 0,
+			    &tx_ch2_asi_switch),
+};
+
+static const struct snd_kcontrol_new tad5x1x_pdm_controls[] = {
+	SOC_ENUM("ASI_TX_CH1_SLOT", tx_ch1_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH2_SLOT", tx_ch2_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH3_SLOT", tx_ch3_slot_select_enum),
+	SOC_ENUM("ASI_TX_CH4_SLOT", tx_ch4_slot_select_enum),
+};
+
+static const struct snd_soc_dapm_widget tad5xx_dapm_widgets[] = {
+	/* pdm capture */
+	SND_SOC_DAPM_SWITCH("ASI_TX_CH1_EN", SND_SOC_NOPM, 0, 0,
+			    &tx_ch1_asi_switch),
+	SND_SOC_DAPM_SWITCH("ASI_TX_CH2_EN", SND_SOC_NOPM, 0, 0,
+			    &tx_ch2_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tad5x1x_dapm_widgets[] = {
+	/* DAC1 */
+	SND_SOC_DAPM_AIF_IN("ASI IN1", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_OUTPUT("OUT1"),
+
+	SND_SOC_DAPM_MUX("OUT1x Source", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_out1_source_control),
+
+	SND_SOC_DAPM_MUX("OUT1x Config", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_out1_config_control),
+
+	SND_SOC_DAPM_MUX("OUT1x Driver", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_out1_driver_control),
+
+	SND_SOC_DAPM_DAC("Left DAC Enable", "Left Playback", TAC5X1X_CH_EN, 3,
+			 0),
+	SND_SOC_DAPM_PGA("Left DAC Power", TAC5X1X_PWR_CFG, 6, 0, NULL, 0),
+
+	SND_SOC_DAPM_SWITCH("ASI_RX_CH1_EN", SND_SOC_NOPM, 0, 0,
+			    &rx_ch1_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget taa5x12_dapm_widgets[] = {
+	/* ADC2 */
+	SND_SOC_DAPM_INPUT("AIN2"),
+
+	SND_SOC_DAPM_MUX("ADC2 Full-Scale", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_adc2_fscale_control),
+
+	SND_SOC_DAPM_MUX("ADC2 Config", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_adc2_config_control),
+
+	SND_SOC_DAPM_ADC("CH2_ADC_EN", "CH2 Capture", TAC5X1X_CH_EN, 6, 0),
+
+};
+
+static const struct snd_soc_dapm_widget tad5x12_dapm_widgets[] = {
+	/* DAC2 */
+	SND_SOC_DAPM_OUTPUT("OUT2"),
+
+	SND_SOC_DAPM_AIF_IN("ASI IN2", "ASI Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MUX("OUT2x Source", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_out2_source_control),
+
+	SND_SOC_DAPM_MUX("OUT2x Config", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_out2_config_control),
+
+	SND_SOC_DAPM_MUX("OUT2x Driver", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_out2_driver_control),
+
+	SND_SOC_DAPM_DAC("Right DAC Enable",
+			 "Right Playback", TAC5X1X_CH_EN, 2, 0),
+	SND_SOC_DAPM_PGA("Right DAC Power", TAC5X1X_PWR_CFG, 6, 0, NULL, 0),
+
+	SND_SOC_DAPM_SWITCH("ASI_RX_CH2_EN", SND_SOC_NOPM, 0, 0,
+			    &rx_ch2_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tac5x1x_dapm_pdm_widgets[] = {
+	/* PDM */
+	SND_SOC_DAPM_INPUT("DIN1"),
+	SND_SOC_DAPM_INPUT("DIN2"),
+	SND_SOC_DAPM_INPUT("DIN3"),
+	SND_SOC_DAPM_INPUT("DIN4"),
+
+	SND_SOC_DAPM_MUX("PDM ch1 & ch2 Datain Select",
+			 SND_SOC_NOPM, 0, 0, pdm_12_pin_controls),
+	SND_SOC_DAPM_MUX("PDM ch3 & ch4 Datain Select",
+			 SND_SOC_NOPM, 0, 0, pdm_34_pin_controls),
+
+	SND_SOC_DAPM_ADC("CH1_PDM_EN", "PDM CH1 Capture", TAC5X1X_CH_EN, 7, 0),
+	SND_SOC_DAPM_ADC("CH2_PDM_EN", "PDM CH2 Capture", TAC5X1X_CH_EN, 6, 0),
+	SND_SOC_DAPM_ADC("CH3_PDM_EN", "PDM CH3 Capture", TAC5X1X_CH_EN, 5, 0),
+	SND_SOC_DAPM_ADC("CH4_PDM_EN", "PDM CH4 Capture", TAC5X1X_CH_EN, 4, 0),
+
+	SND_SOC_DAPM_SWITCH("ASI_TX_CH3_EN", SND_SOC_NOPM, 0, 0,
+			    &tx_ch3_asi_switch),
+	SND_SOC_DAPM_SWITCH("ASI_TX_CH4_EN", SND_SOC_NOPM, 0, 0,
+			    &tx_ch4_asi_switch),
+};
+
+static const struct snd_soc_dapm_widget tac5x1x_common_dapm_widgets[] = {
+	SND_SOC_DAPM_MUX("IN1 Source Mux", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_in1_source_control),
+	SND_SOC_DAPM_MUX("IN2 Source Mux", SND_SOC_NOPM, 0, 0,
+			 tac5x1x_dapm_in2_source_control),
+	SND_SOC_DAPM_PGA("ADC Power", TAC5X1X_PWR_CFG, 7, 0, NULL, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_widget tad5x1x_common_dapm_widgets[] = {
+	SND_SOC_DAPM_MUX("IN1 Source Mux", SND_SOC_NOPM, 0, 0,
+			 tad5x1x_dapm_in1_source_control),
+	SND_SOC_DAPM_MUX("IN2 Source Mux", SND_SOC_NOPM, 0, 0,
+			 tad5x1x_dapm_in2_source_control),
+	SND_SOC_DAPM_PGA("ADC Power", TAC5X1X_PWR_CFG, 7, 0, NULL, 0),
+	SND_SOC_DAPM_AIF_OUT("AIF OUT", "ASI Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route taa5x1x_dapm_routes[] = {
+	/* ADC channel1 */
+	{"IN1 Source Mux", "Analog", "AIN1"},
+	{"IN2 Source Mux", "Analog",
+		"IN1 Source Mux"},
+
+	{"CH1_ADC_EN", NULL, "IN2 Source Mux"},
+	{"ASI_TX_CH1_EN", "Capture Switch", "CH1_ADC_EN"},
+
+	{"ADC1 Config", "Differential", "ASI_TX_CH1_EN"},
+	{"ADC1 Config", "Single-ended", "ASI_TX_CH1_EN"},
+	{"ADC1 Config", "Single-ended mux INxP",
+		"ASI_TX_CH1_EN"},
+	{"ADC1 Config", "Single-ended mux INxM",
+		"ASI_TX_CH1_EN"},
+
+	{"ADC1 Full-Scale", "2/10-VRMS",
+		"ADC1 Config"},
+	{"ADC1 Full-Scale", "4/5-VRMS",
+		"ADC1 Config"},
+	{"Mic Bias", NULL, "ADC1 Full-Scale"},
+
+};
+
+static const struct snd_soc_dapm_route tad5x1x_dapm_routes[] = {
+	/* Left Output */
+	{"ASI_RX_CH1_EN", "Switch", "ASI IN1"},
+
+	{"OUT1x Source", "DAC + Analog Bypass Mix", "ASI_RX_CH1_EN"},
+	{"OUT1x Source", "DAC -> OUTxP, INxP -> OUTxM", "ASI_RX_CH1_EN"},
+	{"OUT1x Source", "INxM -> OUTxP, DAC -> OUTxM", "ASI_RX_CH1_EN"},
+
+	{"OUT1x Config", "Differential", "OUT1x Source"},
+	// {"OUT1x Config", "Stereo Single-ended", "OUT1x Source"},
+	{"OUT1x Config", "Mono Single-ended at OUTxP only", "OUT1x Source"},
+	{"OUT1x Config", "Mono Single-ended at OUTxM only", "OUT1x Source"},
+	{"OUT1x Config", "Pseudo differential with OUTxM as VCOM",
+		"OUT1x Source"},
+	{"OUT1x Config", "Pseudo differential with OUTxM as external sensing",
+		"OUT1x Source"},
+	{"OUT1x Config", "Pseudo differential with OUTxP as VCOM",
+		"OUT1x Source"},
+
+	{"OUT1x Driver", "Line-out", "OUT1x Config"},
+	{"OUT1x Driver", "Headphone", "OUT1x Config"},
+
+	{"Left DAC Enable", NULL, "OUT1x Driver"},
+	{"Left DAC Power", NULL, "Left DAC Enable"},
+
+	{"OUT1", NULL, "Left DAC Power"},
+};
+
+static const struct snd_soc_dapm_route taa5x12_dapm_routes[] = {
+	/* ADC channel2 */
+	{"CH2_ADC_EN", NULL, "AIN2"},
+
+	{"ASI_TX_CH2_EN", "Capture Switch", "CH2_ADC_EN"},
+
+	{"ADC2 Config", "Differential", "ASI_TX_CH2_EN"},
+	{"ADC2 Config", "Single-ended", "ASI_TX_CH2_EN"},
+	{"ADC2 Full-Scale", "2/10-VRMS",
+		"ADC2 Config"},
+	{"ADC2 Full-Scale", "4/5-VRMS",
+		"ADC2 Config"},
+
+	{"Mic Bias", NULL, "ADC2 Full-Scale"},
+};
+
+static const struct snd_soc_dapm_route tad5x12_dapm_routes[] = {
+	/* Right Output */
+	{"ASI_RX_CH2_EN", "Switch", "ASI IN2"},
+
+	{"OUT2x Source", "DAC + Analog Bypass Mix", "ASI_RX_CH1_EN"},
+	{"OUT2x Source", "DAC -> OUTxP, INxP -> OUTxM", "ASI_RX_CH1_EN"},
+	{"OUT2x Source", "INxM -> OUTxP, DAC -> OUTxM", "ASI_RX_CH1_EN"},
+
+	{"OUT2x Config", "Differential", "OUT2x Source"},
+	// {"OUT2x Config", "Stereo Single-ended", "OUT2x Source"},
+	{"OUT2x Config", "Mono Single-ended at OUTxP only", "OUT2x Source"},
+	{"OUT2x Config", "Mono Single-ended at OUTxM only", "OUT2x Source"},
+	{"OUT2x Config", "Pseudo differential with OUTxM as VCOM",
+		"OUT2x Source"},
+	{"OUT2x Config", "Pseudo differential with OUTxP as VCOM",
+		"OUT2x Source"},
+
+	{"OUT2x Driver", "Line-out", "OUT2x Config"},
+	{"OUT2x Driver", "Headphone", "OUT2x Config"},
+
+	{"Right DAC Enable", NULL, "OUT2x Driver"},
+
+	{"Right DAC Power", NULL, "Right DAC Enable"},
+
+	{"OUT2", NULL, "Right DAC Power"},
+};
+
+static const struct snd_soc_dapm_route tac5x1x_dapm_pdm_routes[] = {
+	/* PDM channel1 & Channel2 */
+	{"IN1 Source Mux", "PDM", "DIN1"},
+	{"IN2 Source Mux", "PDM", "DIN2"},
+
+	{"ASI_TX_CH1_EN", "Capture Switch",
+		"IN1 Source Mux"},
+	{"ASI_TX_CH2_EN", "Capture Switch",
+		"IN2 Source Mux"},
+
+	{"CH1_PDM_EN", NULL, "ASI_TX_CH1_EN"},
+	{"CH2_PDM_EN", NULL, "ASI_TX_CH2_EN"},
+
+	{"PDM ch1 & ch2 Datain Select", "GPIO1", "CH1_PDM_EN"},
+	{"PDM ch1 & ch2 Datain Select", "GPIO2", "CH1_PDM_EN"},
+	{"PDM ch1 & ch2 Datain Select", "GPI1", "CH1_PDM_EN"},
+
+	{"ADC Power", NULL, "PDM ch1 & ch2 Datain Select"},
+
+	/* PDM channel3 & Channel4 */
+	{"ASI_TX_CH3_EN", "Capture Switch", "DIN3"},
+	{"ASI_TX_CH4_EN", "Capture Switch", "DIN4"},
+
+	{"CH3_PDM_EN", NULL, "ASI_TX_CH3_EN"},
+	{"CH4_PDM_EN", NULL, "ASI_TX_CH4_EN"},
+
+	{"PDM ch3 & ch4 Datain Select", "GPIO1", "CH3_PDM_EN"},
+	{"PDM ch3 & ch4 Datain Select", "GPIO2", "CH3_PDM_EN"},
+	{"PDM ch3 & ch4 Datain Select", "GPI1", "CH3_PDM_EN"},
+
+	{"ADC Power", NULL, "PDM ch3 & ch4 Datain Select"},
+	{"AIF OUT", NULL, "ADC Power"},
+};
+
+static const struct snd_soc_dapm_route tac5x1x_common_dapm_routes[] = {
+	{"ADC Power", NULL, "Mic Bias"},
+	{"AIF OUT", NULL, "ADC Power"},
+};
+
+static const struct reg_default tac5x1x_reg_defaults[] = {
+	{TAC5X1X_PGSEL, 0x00},
+	{TAC5X1X_INT, 0x10},
+	{TAC5X1X_ADCCH1C0, 0x04},
+	{TAC5X1X_ADCCH2C0, 0x04},
+	{TAC5X1X_OUT1CFG0, 0x20},
+	{TAC5X1X_OUT2CFG0, 0x20},
+	{TAC5X1X_CH_EN, 0x00},
+	{TAC5X1X_PASITXCH1, 0x00},
+	{TAC5X1X_PASITXCH2, 0x01},
+	{TAC5X1X_PASIRXCH1, 0x00},
+	{TAC5X1X_PASIRXCH2, 0x01},
+	{},
+};
+
+static s32 tac5x1x_pwr_ctrl(struct snd_soc_component *component,
+			    bool power_state)
+{
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+	s32 active_ctrl, ret;
+	s32 pwr_ctrl = 0;
+
+	if (power_state) {
+		active_ctrl = TAC5X1X_VREF_SLEEP_ACTIVE_MASK;
+		snd_soc_component_update_bits(component, TAC5X1X_VREFCFG,
+					      TAC5X1X_VREFCFG_MICBIAS_VAL_MASK,
+					      tac5x1x->micbias_vg << 2);
+		snd_soc_component_update_bits(component, TAC5X1X_VREFCFG,
+					      TAC5X1X_VREFCFG_VREF_FSCALE_MASK,
+					      tac5x1x->vref_vg);
+
+		if (tac5x1x->uad_en)
+			pwr_ctrl |= TAC5X1X_PWR_CFG_UAD_EN;
+		if (tac5x1x->vad_en)
+			pwr_ctrl |= TAC5X1X_PWR_CFG_VAD_EN;
+		if (tac5x1x->uag_en)
+			pwr_ctrl |= TAC5X1X_PWR_CFG_UAG_EN;
+	} else {
+		active_ctrl = 0x0;
+	}
+
+	ret = snd_soc_component_update_bits(component, TAC5X1X_VREF,
+					    TAC5X1X_VREF_SLEEP_EXIT_VREF_EN |
+					    TAC5X1X_VREF_SLEEP_ACTIVE_MASK, active_ctrl);
+	if (ret < 0) {
+		dev_err(tac5x1x->dev,
+			"%s, device active or sleep failed!, ret %d/n",
+			__func__, ret);
+		return ret;
+	}
+
+	ret = snd_soc_component_update_bits(component, TAC5X1X_PWR_CFG,
+					    TAC5X1X_PWR_CFG_UAD_EN | TAC5X1X_PWR_CFG_UAG_EN |
+					    TAC5X1X_PWR_CFG_VAD_EN, pwr_ctrl);
+	if (ret < 0)
+		dev_err(tac5x1x->dev,
+			"%s, Power control set failed!, ret %d/n",
+			__func__, ret);
+	return ret;
+}
+
+static s32 tac5x1x_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	s32 iface_reg_1 = 0;
+	s32 iface_reg_2 = 0;
+	s32 iface_reg_3 = 0;
+
+	dev_err(component->dev, "[tac5x1x] %s(), fmt=%d\n", __func__, fmt);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBP_CFP:
+		iface_reg_1 |= TAC5X1X_PASI_MODE_MASK;
+		break;
+	case SND_SOC_DAIFMT_CBC_CFC:
+		break;
+	default:
+		dev_err(component->dev,
+			"%s: invalid DAI master/slave interface\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		iface_reg_2 |= TAC5X1X_PASI_FMT_I2S;
+		break;
+	case SND_SOC_DAIFMT_DSP_A:
+		iface_reg_2 |= TAC5X1X_PASI_FMT_TDM;
+		iface_reg_3 |= BIT(0); /* add offset 1 */
+		break;
+	case SND_SOC_DAIFMT_DSP_B:
+		iface_reg_2 |= TAC5X1X_PASI_FMT_TDM;
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		iface_reg_2 |= TAC5X1X_PASI_FMT_LJ;
+		break;
+	default:
+		dev_err(component->dev,
+			"%s: invalid DAI interface format\n", __func__);
+		return -EINVAL;
+	}
+
+	snd_soc_component_update_bits(component, TAC5X1X_CNTCLK2,
+				      TAC5X1X_PASI_MODE_MASK, iface_reg_1);
+	snd_soc_component_update_bits(component, TAC5X1X_PASI0,
+				      TAC5X1X_PASI_FMT_MASK, iface_reg_2);
+	snd_soc_component_update_bits(component, TAC5X1X_PASITX1,
+				      TAC5X1X_PASITX_OFFSET_MASK, iface_reg_3);
+	snd_soc_component_update_bits(component, TAC5X1X_PASIRX0,
+				      TAC5X1X_PASIRX_OFFSET_MASK, iface_reg_3);
+	return 0;
+}
+
+static s32 tac5x1x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+	s32 sample_rate, word_length = 0;
+
+	switch (params_rate(params)) {
+	case 24000:
+		sample_rate = 25;
+		break;
+	case 32000:
+		sample_rate = 23;
+		break;
+	case 44100:
+	case 48000:
+		sample_rate = 20;
+		break;
+	case 64000:
+		sample_rate = 18;
+		break;
+	case 96000:
+		sample_rate = 15;
+		break;
+	case 192000:
+		sample_rate = 10;
+		break;
+	default:
+		/* Auto detect sample rate */
+		sample_rate = 0;
+		break;
+	}
+
+	switch (params_physical_width(params)) {
+	case 16:
+		word_length |= TAC5X1X_WORD_LEN_16BITS;
+		break;
+	case 20:
+		word_length |= TAC5X1X_WORD_LEN_20BITS;
+		break;
+	case 24:
+		word_length |= TAC5X1X_WORD_LEN_24BITS;
+		break;
+	case 32:
+		word_length |= TAC5X1X_WORD_LEN_32BITS;
+		break;
+	default:
+		dev_err(tac5x1x->dev, "%s, set word length failed\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	snd_soc_component_update_bits(component, TAC5X1X_CLK0, TAC5X1X_PASI_SAMP_RATE_MASK,
+				      sample_rate << 2);
+	snd_soc_component_update_bits(component, TAC5X1X_PASI0,
+				      TAC5X1X_PASI_DATALEN_MASK, word_length);
+
+	tac5x1x_pwr_ctrl(component, true);
+	return 0;
+}
+
+static s32 tac5x1x_set_bias_level(struct snd_soc_component *component,
+				  enum snd_soc_bias_level level)
+{
+	s32 ret;
+	struct tac5x1x_priv *tac5x1x = snd_soc_component_get_drvdata(component);
+
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		ret = tac5x1x_pwr_ctrl(component, true);
+		if (ret < 0)
+			dev_err(tac5x1x->dev, "%s, power up failed!/n", __func__);
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		break;
+	case SND_SOC_BIAS_OFF:
+		ret = tac5x1x_pwr_ctrl(component, false);
+		if (ret < 0)
+			dev_err(tac5x1x->dev, "%s, power down failed!/n", __func__);
+		break;
+	}
+
+	return ret;
+}
+
+static const struct snd_soc_dai_ops tac5x1x_ops = {
+	.hw_params = tac5x1x_hw_params,
+	.set_fmt = tac5x1x_set_dai_fmt,
+	.no_capture_mute = 1,
+};
+
+static struct snd_soc_dai_driver tac5x1x_dai = {
+	.name = "tac5x1x-hifi",
+	.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = TAC5X1X_RATES,
+			.formats = TAC5X1X_FORMATS,},
+	.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = TAC5X1X_RATES,
+			.formats = TAC5X1X_FORMATS,
+			},
+	.ops = &tac5x1x_ops,
+	.symmetric_rate = 1,
+};
+
+static struct snd_soc_dai_driver taa5x1x_dai = {
+	.name = "taa5x1x-hifi",
+	.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = TAC5X1X_RATES,
+			.formats = TAC5X1X_FORMATS,
+			},
+	.ops = &tac5x1x_ops,
+	.symmetric_rate = 1,
+};
+
+static struct snd_soc_dai_driver tad5x1x_dai = {
+	.name = "tad5x1x-hifi",
+	.playback = {
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = TAC5X1X_RATES,
+			.formats = TAC5X1X_FORMATS,
+			},
+	.capture = {
+			.stream_name = "Capture",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = TAC5X1X_RATES,
+			.formats = TAC5X1X_FORMATS,
+			},
+	.ops = &tac5x1x_ops,
+	.symmetric_rate = 1,
+};
+
+static void tac5x1x_setup_gpios(struct snd_soc_component *component)
+{
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+	s32 *gpio_drive = tac5x1x->gpio_setup->gpio_drive;
+	s32 *gpio_func = tac5x1x->gpio_setup->gpio_func;
+
+	/* setup GPIO functions */
+	/* GPIO1 */
+	if (gpio_func[0] <= TAC5X1X_GPIO_DAISY_OUT) {
+		snd_soc_component_update_bits(component, TAC5X1X_GPIO1, TAC5X1X_GPIOX_CFG_MASK,
+					      gpio_func[0] << 4);
+		snd_soc_component_update_bits(component, TAC5X1X_GPIO1, TAC5X1X_GPIOX_DRV_MASK,
+					      gpio_drive[0]);
+
+		if (gpio_func[0] == TAC5X1X_GPIO_GPI)
+			snd_soc_add_component_controls(component, tac5x1x_GPIO1_I,
+						       ARRAY_SIZE(tac5x1x_GPIO1_I));
+		else if (gpio_func[0] == TAC5X1X_GPIO_GPO)
+			snd_soc_add_component_controls(component, tac5x1x_GPIO1_O,
+						       ARRAY_SIZE(tac5x1x_GPIO1_O));
+	}
+	/* GPIO2 */
+	if (gpio_func[1] <= TAC5X1X_GPIO_DAISY_OUT) {
+		snd_soc_component_update_bits(component, TAC5X1X_GPIO2, TAC5X1X_GPIOX_CFG_MASK,
+					      gpio_func[1] << 4);
+		snd_soc_component_update_bits(component, TAC5X1X_GPIO2, TAC5X1X_GPIOX_DRV_MASK,
+					      gpio_drive[1]);
+
+		if (gpio_func[1] == TAC5X1X_GPIO_GPI)
+			snd_soc_add_component_controls(component, tac5x1x_GPIO2_I,
+						       ARRAY_SIZE(tac5x1x_GPIO2_I));
+		else if (gpio_func[1] == TAC5X1X_GPIO_GPO)
+			snd_soc_add_component_controls(component, tac5x1x_GPIO2_O,
+						       ARRAY_SIZE(tac5x1x_GPIO2_O));
+	}
+	/* GPO1 */
+	if (gpio_func[2] <= TAC5X1X_GPIO_DAISY_OUT) {
+		snd_soc_component_update_bits(component, TAC5X1X_GPO1, TAC5X1X_GPIOX_CFG_MASK,
+					      gpio_func[2] << 4);
+		snd_soc_component_update_bits(component, TAC5X1X_GPO1, TAC5X1X_GPIOX_DRV_MASK,
+					      gpio_drive[2]);
+
+		if (gpio_func[2] == TAC5X1X_GPIO_GPO)
+			snd_soc_add_component_controls(component, tac5x1x_GPO1,
+						       ARRAY_SIZE(tac5x1x_GPO1));
+	}
+	/* GPI1 */
+	if (tac5x1x->gpio_setup->gpi1_func) {
+		snd_soc_component_update_bits(component, TAC5X1X_GPI1, TAC5X1X_GPI1_CFG_MASK,
+					      TAC5X1X_GPI1_CFG_MASK);
+		snd_soc_add_component_controls(component, tac5x1x_GPI1, ARRAY_SIZE(tac5x1x_GPI1));
+	}
+	/*GPA GPIO*/
+	if (tac5x1x->gpio_setup->gpa_gpio)
+		snd_soc_component_update_bits(component, TAC5X1X_INTF5, TAC5X1X_GPA_CFG_MASK,
+					      TAC5X1X_GPA_CFG_MASK);
+}
+
+static s32 tac5x1x_reset(struct snd_soc_component *component)
+{
+	s32 ret, index;
+
+	ret = snd_soc_component_write(component, TAC5X1X_RESET, 1);
+	if (ret < 0)
+		return ret;
+	/* Wait >= 10 ms after entering sleep mode. */
+	usleep_range(10000, 100000);
+
+	for (index = 0; index < ARRAY_SIZE(tac5x1x_reg_defaults); index++) {
+		ret = snd_soc_component_write(component, tac5x1x_reg_defaults[index].reg,
+					      tac5x1x_reg_defaults[index].def);
+		if (ret < 0)
+			return ret;
+	}
+	return ret;
+}
+
+static s32 tac5x1x_add_controls(struct snd_soc_component *component)
+{
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+	s32 *gpio_func = tac5x1x->gpio_setup->gpio_func;
+	s32 ret;
+
+	switch (tac5x1x->codec_type) {
+	case TAA5212:
+		ret = snd_soc_add_component_controls(component, tac5x12_impedance_controls,
+						     ARRAY_SIZE(tac5x12_impedance_controls));
+		if (ret)
+			return ret;
+		fallthrough;
+	case TAA5412:
+		ret = snd_soc_add_component_controls(component, tac5x12_tolerance_controls,
+						     ARRAY_SIZE(tac5x12_tolerance_controls));
+		if (ret)
+			return ret;
+		ret = snd_soc_add_component_controls(component, taa5x1x_ip_controls,
+						     ARRAY_SIZE(taa5x1x_ip_controls));
+		if (ret)
+			return ret;
+		break;
+	/* For Mono */
+	case TAC5111:
+	case TAC5211:
+		ret = snd_soc_add_component_controls(component, tac5x11_controls,
+						     ARRAY_SIZE(tac5x11_controls));
+		if (ret)
+			return ret;
+	fallthrough;
+	case TAC5311:
+	case TAC5411:
+		ret = snd_soc_add_component_controls(component, tad5x1x_controls,
+						     ARRAY_SIZE(tad5x1x_controls));
+		if (ret)
+			return ret;
+		break;
+	/* For Stereo */
+	case TAC5112:
+	case TAC5212:
+		ret = snd_soc_add_component_controls(component, tac5x12_impedance_controls,
+						     ARRAY_SIZE(tac5x12_impedance_controls));
+		if (ret)
+			return ret;
+		fallthrough;
+	case TAC5312:
+	case TAC5412:
+		ret = snd_soc_add_component_controls(component, tac5x12_tolerance_controls,
+						     ARRAY_SIZE(tac5x12_tolerance_controls));
+		if (ret)
+			return ret;
+		ret = snd_soc_add_component_controls(component, tad5x1x_controls,
+						     ARRAY_SIZE(tad5x1x_controls));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_add_component_controls(component, taa5x12_controls,
+						     ARRAY_SIZE(taa5x12_controls));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_add_component_controls(component, tad5x12_controls,
+						     ARRAY_SIZE(tad5x12_controls));
+		if (ret)
+			return ret;
+		break;
+	case TAD5212:
+	case TAD5112:
+		ret = snd_soc_add_component_controls(component, tad5x12_controls,
+						     ARRAY_SIZE(tad5x12_controls));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_add_component_controls(component, tad5x1x_pdm_controls,
+						     ARRAY_SIZE(tad5x1x_pdm_controls));
+		if (ret)
+			return ret;
+		break;
+	default:
+		break;
+	}
+
+	/* If enabled PDM GPIO*/
+	if (memchr(gpio_func, TAC5X1X_GPIO_PDMCLK,
+		   sizeof(*gpio_func) / sizeof(gpio_func[0]))) {
+		ret = snd_soc_add_component_controls(component, tac5x1x_pdm_controls,
+						     ARRAY_SIZE(tac5x1x_pdm_controls));
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static s32 tac5x1x_add_ip_diag_controls(struct snd_soc_component *component)
+{
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+	s32 ret;
+
+	switch (tac5x1x->codec_type) {
+	case TAA5212:
+		break;
+	case TAA5412:
+	case TAC5311:
+	case TAC5312:
+	case TAC5411:
+	case TAC5412:
+		if (tac5x1x->input_diag_config.in_ch_en) {
+			ret = snd_soc_add_component_controls(component, taa5x1x_ip_controls,
+							     ARRAY_SIZE(taa5x1x_ip_controls));
+			if (ret)
+				return ret;
+			snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+						      TAC5X1X_IN_CH_DIAG_EN_MASK,
+						      TAC5X1X_IN_CH_DIAG_EN_MASK);
+		}
+		if (tac5x1x->input_diag_config.out_ch_en) {
+			snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+						      TAC5X1X_OUT1P_DIAG_EN_MASK,
+						      TAC5X1X_OUT1P_DIAG_EN_MASK);
+		}
+		if (tac5x1x->input_diag_config.incl_se_inm) {
+			snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+						      TAC5X1X_INCL_SE_INM_MASK,
+						      TAC5X1X_INCL_SE_INM_MASK);
+		}
+		if (tac5x1x->input_diag_config.incl_ac_coup) {
+			snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG0,
+						      TAC5X1X_INCL_AC_COUP_MASK,
+						      TAC5X1X_INCL_AC_COUP_MASK);
+		}
+		snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG7, 0xff,
+					      tac5x1x->micbias_threshold[0]);
+		snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG6, 0xff,
+					      tac5x1x->micbias_threshold[1]);
+		ret = tac5x1x_register_interrupt(tac5x1x);
+		if (!ret)
+			dev_info(tac5x1x->dev, "Interrupt registration Done");
+		fallthrough;
+	case TAC5111:
+	case TAC5112:
+	case TAC5211:
+	case TAC5212:
+	case TAD5112:
+	case TAD5212:
+		snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG9, 0xff,
+					      tac5x1x->gpa_threshold[0]);
+		snd_soc_component_update_bits(component, TAC5X1X_DIAG_CFG8, 0xff,
+					      tac5x1x->gpa_threshold[1]);
+		break;
+	default:
+		break;
+	};
+
+	return ret;
+};
+
+static s32 tac5x1x_add_widgets(struct snd_soc_component *component)
+{
+	struct tac5x1x_priv *tac5x1x = snd_soc_component_get_drvdata(component);
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+	s32 *gpio_func = tac5x1x->gpio_setup->gpio_func;
+	s32 ret;
+
+	switch (tac5x1x->codec_type) {
+	case TAC5111:
+	case TAC5211:
+	case TAC5311:
+	case TAC5411:
+		ret = snd_soc_dapm_new_controls(dapm, tad5x1x_dapm_widgets,
+						ARRAY_SIZE(tad5x1x_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(dapm, tad5x1x_dapm_routes,
+					      ARRAY_SIZE(tad5x1x_dapm_routes));
+		if (ret)
+			return ret;
+		break;
+	case TAC5112:
+	case TAC5212:
+	case TAC5312:
+	case TAC5412:
+		ret = snd_soc_dapm_new_controls(dapm, tad5x1x_dapm_widgets,
+						ARRAY_SIZE(tad5x1x_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(dapm, tad5x1x_dapm_routes,
+					      ARRAY_SIZE(tad5x1x_dapm_routes));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_new_controls(dapm, tad5x12_dapm_widgets,
+						ARRAY_SIZE(tad5x12_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(dapm, tad5x12_dapm_routes,
+					      ARRAY_SIZE(tad5x12_dapm_routes));
+		if (ret)
+			return ret;
+		fallthrough;
+	case TAA5212:
+	case TAA5412:
+		ret = snd_soc_dapm_new_controls(dapm, taa5x12_dapm_widgets,
+						ARRAY_SIZE(taa5x12_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(dapm, taa5x12_dapm_routes,
+					      ARRAY_SIZE(taa5x12_dapm_routes));
+		if (ret)
+			return ret;
+		break;
+	case TAD5212:
+	case TAD5112:
+		ret = snd_soc_dapm_new_controls(dapm, tad5xx_dapm_widgets,
+						ARRAY_SIZE(tad5xx_dapm_widgets));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_dapm_new_controls(dapm, tad5x12_dapm_widgets,
+						ARRAY_SIZE(tad5x12_dapm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(dapm, tad5x12_dapm_routes,
+					      ARRAY_SIZE(tad5x12_dapm_routes));
+		if (ret)
+			return ret;
+
+		break;
+	default:
+		break;
+	}
+
+	if (!(tac5x1x->codec_type == TAD5212 || tac5x1x->codec_type == TAD5112)) {
+		ret = snd_soc_dapm_new_controls(dapm, tac5x1x_common_dapm_widgets,
+						ARRAY_SIZE(tac5x1x_common_dapm_widgets));
+		if (ret)
+			return ret;
+
+		ret = snd_soc_dapm_add_routes(dapm, tac5x1x_common_dapm_routes,
+					      ARRAY_SIZE(tac5x1x_common_dapm_routes));
+		if (ret)
+			return ret;
+	} else {
+		ret = snd_soc_dapm_new_controls(dapm, tad5x1x_common_dapm_widgets,
+						ARRAY_SIZE(tad5x1x_common_dapm_widgets));
+		if (ret)
+			return ret;
+	}
+	/* If enabled PDM GPIO*/
+	if (memchr(gpio_func, TAC5X1X_GPIO_PDMCLK,
+		   sizeof(*gpio_func) / sizeof(gpio_func[0]))) {
+		ret = snd_soc_dapm_new_controls(dapm, tac5x1x_dapm_pdm_widgets,
+						ARRAY_SIZE(tac5x1x_dapm_pdm_widgets));
+		if (ret)
+			return ret;
+		ret = snd_soc_dapm_add_routes(dapm, tac5x1x_dapm_pdm_routes,
+					      ARRAY_SIZE(tac5x1x_dapm_pdm_routes));
+		if (ret)
+			return ret;
+	}
+	return 0;
+}
+
+static s32 tac5x1x_component_probe(struct snd_soc_component *component)
+{
+	s32 ret;
+	struct tac5x1x_priv *tac5x1x = snd_soc_component_get_drvdata(component);
+
+	ret = tac5x1x_add_controls(component);
+	if (ret < 0) {
+		dev_err(tac5x1x->dev, "%s, add control failed\n", __func__);
+		return ret;
+	}
+
+	ret = tac5x1x_add_widgets(component);
+	if (ret < 0) {
+		dev_err(tac5x1x->dev, "%s, device widget addition failed\n", __func__);
+		return ret;
+	}
+
+	ret = tac5x1x_reset(component);
+	if (ret < 0) {
+		dev_err(tac5x1x->dev, "%s, device reset failed\n", __func__);
+		return ret;
+	}
+
+	if (tac5x1x->gpio_setup)
+		tac5x1x_setup_gpios(component);
+
+	ret = tac5x1x_add_ip_diag_controls(component);
+	if (ret < 0) {
+		dev_err(tac5x1x->dev, "%s, add Ip diag control failed\n", __func__);
+		return ret;
+	}
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static s32 tac5x1x_soc_suspend(struct snd_soc_component *component)
+{
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+
+	regcache_cache_only(tac5x1x->regmap, true);
+	regcache_mark_dirty(tac5x1x->regmap);
+
+	regulator_disable(tac5x1x->supply_avdd);
+	regulator_disable(tac5x1x->supply_iovdd);
+
+	return 0;
+}
+
+static s32 tac5x1x_soc_resume(struct snd_soc_component *component)
+{
+	struct tac5x1x_priv *tac5x1x =
+		snd_soc_component_get_drvdata(component);
+	s32 ret;
+
+	regcache_cache_only(tac5x1x->regmap, false);
+	snd_soc_component_cache_sync(component);
+
+	ret = regulator_enable(tac5x1x->supply_avdd);
+	if (ret) {
+		regulator_disable(tac5x1x->supply_avdd);
+		return ret;
+	}
+
+	ret = regulator_enable(tac5x1x->supply_iovdd);
+	if (ret) {
+		regulator_disable(tac5x1x->supply_iovdd);
+		return ret;
+	}
+	return 0;
+}
+#else
+#define tac5x1x_soc_suspend	NULL
+#define tac5x1x_soc_resume	NULL
+#endif /* CONFIG_PM */
+
+static const struct snd_soc_component_driver soc_component_dev_tac5x1x = {
+	.probe			= tac5x1x_component_probe,
+	.set_bias_level		= tac5x1x_set_bias_level,
+	.suspend		= tac5x1x_soc_suspend,
+	.resume			= tac5x1x_soc_resume,
+	.controls		= taa5x1x_controls,
+	.num_controls		= ARRAY_SIZE(taa5x1x_controls),
+	.dapm_widgets		= taa5x1x_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(taa5x1x_dapm_widgets),
+	.dapm_routes		= taa5x1x_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(taa5x1x_dapm_routes),
+	.suspend_bias_off	= 1,
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_taa5x1x = {
+	.probe			= tac5x1x_component_probe,
+	.set_bias_level		= tac5x1x_set_bias_level,
+	.suspend		= tac5x1x_soc_suspend,
+	.resume			= tac5x1x_soc_resume,
+	.controls		= taa5x1x_controls,
+	.num_controls		= ARRAY_SIZE(taa5x1x_controls),
+	.dapm_widgets		= taa5x1x_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(taa5x1x_dapm_widgets),
+	.dapm_routes		= taa5x1x_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(taa5x1x_dapm_routes),
+	.suspend_bias_off	= 1,
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+};
+
+static const struct snd_soc_component_driver soc_component_dev_tad5x1x = {
+	.probe			= tac5x1x_component_probe,
+	.set_bias_level		= tac5x1x_set_bias_level,
+	.suspend		= tac5x1x_soc_suspend,
+	.resume			= tac5x1x_soc_resume,
+	.controls		= tad5x1x_controls,
+	.num_controls		= ARRAY_SIZE(tad5x1x_controls),
+	.dapm_widgets		= tad5x1x_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(tad5x1x_dapm_widgets),
+	.dapm_routes		= tad5x1x_dapm_routes,
+	.num_dapm_routes	= ARRAY_SIZE(tad5x1x_dapm_routes),
+	.suspend_bias_off	= 1,
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+};
+
+static s32 tac5x1x_parse_dt(struct tac5x1x_priv *tac5x1x,
+			    struct device_node *np)
+{
+	struct tac5x1x_input_diag_config input_config;
+	struct tac5x1x_setup_gpio *tac5x1x_setup;
+	s32 micbias_value = TAC5X1X_MICBIAS_VREF;
+	s32 vref_value = TAC5X1X_VERF_2_5V;
+	s32 ret;
+
+	tac5x1x_setup = devm_kzalloc(tac5x1x->dev, sizeof(*tac5x1x_setup),
+				     GFP_KERNEL);
+	if (!tac5x1x_setup)
+		return -ENOMEM;
+
+	ret = fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,vref", &vref_value);
+	if (ret) {
+		dev_err(tac5x1x->dev, "Fail to get verf E:%d\n", ret);
+		goto out;
+	}
+	ret = fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,micbias-vg", &micbias_value);
+	if (ret) {
+		dev_err(tac5x1x->dev, "Fail to get micbias-vg E:%d\n", ret);
+		goto out;
+	}
+
+	if (micbias_value == TAC5X1X_MICBIAS_AVDD) {
+		tac5x1x->micbias_vg = micbias_value;
+		tac5x1x->vref_vg = TAC5X1X_VERF_2_75V;
+		tac5x1x->micbias_en = true;
+	} else {
+		switch (vref_value) {
+		case TAC5X1X_VERF_2_75V:
+		case TAC5X1X_VERF_2_5V:
+			switch (micbias_value) {
+			case TAC5X1X_MICBIAS_VREF:
+			case TAC5X1X_MICBIAS_0_5VREF:
+				tac5x1x->micbias_vg = micbias_value;
+				break;
+			default:
+				dev_err(tac5x1x->dev, "Bad tac5x1x-micbias-vg value %d\n",
+					micbias_value);
+				tac5x1x->micbias_vg = TAC5X1X_MICBIAS_AVDD;
+				break;
+			}
+			tac5x1x->vref_vg = vref_value;
+			tac5x1x->micbias_en = true;
+			break;
+		case TAC5X1X_VERF_1_375V:
+			if (micbias_value == TAC5X1X_MICBIAS_VREF) {
+				tac5x1x->micbias_vg = micbias_value;
+			} else {
+				dev_err(tac5x1x->dev, "Bad tac5x1x-micbias-vg value %d\n",
+					micbias_value);
+				tac5x1x->micbias_vg = TAC5X1X_MICBIAS_AVDD;
+			}
+			tac5x1x->micbias_en = true;
+			tac5x1x->vref_vg = vref_value;
+			break;
+		default:
+			dev_err(tac5x1x->dev, "Bad tac5x1x-vref-vg value %d\n", vref_value);
+			tac5x1x->vref_vg = TAC5X1X_VERF_2_5V;
+			tac5x1x->micbias_vg = TAC5X1X_MICBIAS_AVDD;
+			tac5x1x->micbias_en = true;
+			break;
+		}
+	}
+
+	if (fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,gpi1-func",
+				     &tac5x1x_setup->gpi1_func))
+		dev_err(tac5x1x->dev, "%s, Fail to get gpi1-func value\n", __func__);
+
+	if (fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,gpa-gpio",
+				     &tac5x1x_setup->gpa_gpio))
+		dev_err(tac5x1x->dev, "%s, Fail to get gpa-gpio value\n", __func__);
+
+	if (fwnode_property_read_u32_array(tac5x1x->dev->fwnode, "ti,gpios-func",
+					   tac5x1x_setup->gpio_func, 3))
+		dev_err(tac5x1x->dev, "%s, Fail to get gpios-func value\n", __func__);
+	if (fwnode_property_read_u32_array(tac5x1x->dev->fwnode, "ti,gpios-drive",
+					   tac5x1x_setup->gpio_drive, 3))
+		dev_err(tac5x1x->dev, "%s, Fail to get gpios-drive value\n", __func__);
+
+	tac5x1x->gpa_threshold[0] = TAC5X1X_GPA_LOW_THRESHOLD;
+	tac5x1x->gpa_threshold[1] = TAC5X1X_GPA_HIGH_THRESHOLD;
+	if (fwnode_property_read_u32_array(tac5x1x->dev->fwnode, "ti,gpa-threshold",
+					   tac5x1x->gpa_threshold, 2))
+		dev_err(tac5x1x->dev, "%s, Fail to get ti,gpa-threshold value\n", __func__);
+
+	tac5x1x->gpio_setup = tac5x1x_setup;
+
+	switch (tac5x1x->codec_type) {
+	case TAA5212:
+	case TAC5111:
+	case TAC5112:
+	case TAC5211:
+	case TAC5212:
+	case TAD5112:
+	case TAD5212:
+		break;
+	case TAA5412:
+	case TAC5411:
+	case TAC5412:
+	case TAC5311:
+	case TAC5312:
+		tac5x1x->input_diag_config.in_ch_en = 0;
+		if (fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,in-ch-en",
+					     &input_config.in_ch_en))
+			dev_err(tac5x1x->dev, "%s, Fail to get ti,in-ch-en value\n", __func__);
+		tac5x1x->input_diag_config.out_ch_en = 0;
+		if (fwnode_property_read_u32(tac5x1x->dev->fwnode,
+					     "ti,out-ch-en", &input_config.in_ch_en))
+			dev_err(tac5x1x->dev, "%s, Fail to get ti,out-ch-en value\n", __func__);
+		tac5x1x->input_diag_config.incl_se_inm = 0;
+		if (fwnode_property_read_u32(tac5x1x->dev->fwnode, "ti,incl-se-inm",
+					     &input_config.incl_se_inm))
+			dev_err(tac5x1x->dev, "%s, Fail to get ti,incl-se-inm value\n", __func__);
+		tac5x1x->input_diag_config.incl_ac_coup = 0;
+		if (fwnode_property_read_u32(tac5x1x->dev->fwnode,
+					     "ti,incl-ac-coup", &input_config.incl_ac_coup))
+			dev_err(tac5x1x->dev, "%s, Fail to get ti,incl-ac-coup value\n", __func__);
+		tac5x1x->input_diag_config = input_config;
+
+		tac5x1x->micbias_threshold[0] = TAC5X1X_MICBIAS_LOW_THRESHOLD;
+		tac5x1x->micbias_threshold[1] =
+					TAC5X1X_MICBIAS_HIGH_THRESHOLD;
+		if (fwnode_property_read_u32_array(tac5x1x->dev->fwnode, "ti,micbias-threshold",
+						   tac5x1x->micbias_threshold, 2))
+			dev_err(tac5x1x->dev, "%s, Fail to get ti,micbias-threshold value\n",
+				__func__);
+		break;
+	}
+out:
+	return ret;
+}
+
+static void tac5x1x_disable_regulators(struct tac5x1x_priv *tac5x1x)
+{
+	if (!IS_ERR(tac5x1x->supply_iovdd))
+		regulator_disable(tac5x1x->supply_iovdd);
+
+	if (!IS_ERR(tac5x1x->supply_avdd))
+		regulator_disable(tac5x1x->supply_avdd);
+}
+
+static s32 tac5x1x_setup_regulators(struct device *dev,
+				    struct tac5x1x_priv *tac5x1x)
+{
+	s32 ret;
+
+	tac5x1x->supply_iovdd = devm_regulator_get(dev, "iovdd");
+	tac5x1x->supply_avdd = devm_regulator_get(dev, "avdd");
+
+	/* Check if the regulator requirements are fulfilled */
+	if (IS_ERR(tac5x1x->supply_iovdd)) {
+		dev_err(dev, "Missing supply 'iovdd'\n");
+		return PTR_ERR(tac5x1x->supply_iovdd);
+	}
+
+	if (IS_ERR(tac5x1x->supply_avdd)) {
+		dev_err(dev, "Missing supply 'avdd'\n");
+		return PTR_ERR(tac5x1x->supply_avdd);
+	}
+
+	ret = regulator_enable(tac5x1x->supply_iovdd);
+	if (ret) {
+		dev_err(dev, "Failed to enable regulator iovdd\n");
+		regulator_disable(tac5x1x->supply_iovdd);
+		return ret;
+	}
+
+	ret = regulator_enable(tac5x1x->supply_avdd);
+	if (ret) {
+		dev_err(dev, "Failed to enable regulator avdd\n");
+		regulator_disable(tac5x1x->supply_avdd);
+		return ret;
+	}
+
+	return 0;
+}
+
+s32 tac5x1x_probe(struct device *dev, struct regmap *regmap,
+		  enum tac5x1x_type type)
+{
+	struct device_node *np = dev->of_node;
+	struct tac5x1x_priv *tac5x1x;
+	s32 ret;
+
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	tac5x1x = devm_kzalloc(dev, sizeof(struct tac5x1x_priv),
+			       GFP_KERNEL);
+	if (!tac5x1x)
+		return -ENOMEM;
+
+	tac5x1x->dev = dev;
+	tac5x1x->codec_type = type;
+	tac5x1x->regmap = regmap;
+	tac5x1x->ndev = 1;
+
+	dev_set_drvdata(dev, tac5x1x);
+	if (np) {
+		ret = tac5x1x_parse_dt(tac5x1x, np);
+		if (ret) {
+			dev_err(dev, "Failed to parse DT node\n");
+			return ret;
+		}
+	} else {
+		dev_err(dev, "%s: Fail to get device node\n", __func__);
+	}
+
+	ret = tac5x1x_setup_regulators(dev, tac5x1x);
+	if (ret) {
+		dev_err(dev, "Failed to setup regulators\n");
+		return ret;
+	}
+
+	switch (tac5x1x->codec_type) {
+	case TAA5212:
+	case TAA5412:
+		ret = devm_snd_soc_register_component(dev,
+						      &soc_component_dev_taa5x1x, &taa5x1x_dai, 1);
+		if (ret) {
+			dev_err(dev, "Failed to register component\n");
+			goto err_disable_regulators;
+		}
+		break;
+	case TAC5111:
+	case TAC5112:
+	case TAC5211:
+	case TAC5212:
+	case TAC5311:
+	case TAC5312:
+	case TAC5411:
+	case TAC5412:
+		ret = devm_snd_soc_register_component(dev,
+						      &soc_component_dev_tac5x1x, &tac5x1x_dai, 1);
+		if (ret) {
+			dev_err(dev, "Failed to register component\n");
+			goto err_disable_regulators;
+		}
+		break;
+	case TAD5112:
+	case TAD5212:
+		ret = devm_snd_soc_register_component(dev,
+						      &soc_component_dev_tad5x1x, &tad5x1x_dai, 1);
+		if (ret) {
+			dev_err(dev, "Failed to register component\n");
+			goto err_disable_regulators;
+		}
+		break;
+	}
+	return 0;
+
+err_disable_regulators:
+	tac5x1x_disable_regulators(tac5x1x);
+
+	return ret;
+}
+EXPORT_SYMBOL(tac5x1x_probe);
+
+s32 tac5x1x_remove(struct device *dev)
+{
+	struct tac5x1x_priv *tac5x1x = dev_get_drvdata(dev);
+
+	tac5x1x_disable_regulators(tac5x1x);
+	if (tac5x1x->irq_work_func) {
+		dev_info(tac5x1x->dev, "Cancelled IRQ\n");
+		free_irq(tac5x1x->irqinfo.irq, tac5x1x);
+		mutex_destroy(&tac5x1x->dev_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(tac5x1x_remove);
+
+const struct of_device_id tac5x1x_of_match[] = {
+	{ .compatible = "ti,taa5212", .data = (void *)TAA5212 },
+	{ .compatible = "ti,taa5412", .data = (void *)TAA5412 },
+	{ .compatible = "ti,tac5111", .data = (void *)TAC5111 },
+	{ .compatible = "ti,tac5112", .data = (void *)TAC5112 },
+	{ .compatible = "ti,tac5211", .data = (void *)TAC5211 },
+	{ .compatible = "ti,tac5212", .data = (void *)TAC5212 },
+	{ .compatible = "ti,tac5311", .data = (void *)TAC5311 },
+	{ .compatible = "ti,tac5312", .data = (void *)TAC5312 },
+	{ .compatible = "ti,tac5411", .data = (void *)TAC5411 },
+	{ .compatible = "ti,tac5412", .data = (void *)TAC5412 },
+	{ .compatible = "ti,tad5112", .data = (void *)TAD5112 },
+	{ .compatible = "ti,tad5212", .data = (void *)TAD5212 },
+	{}
+};
+EXPORT_SYMBOL_GPL(tac5x1x_of_match);
+MODULE_DEVICE_TABLE(of, tac5x1x_of_match);
+
+MODULE_DESCRIPTION("ASoC tac5x1x codec driver");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tac5x1x.h b/sound/soc/codecs/tac5x1x.h
new file mode 100644
index 000000000000..d3f2d5b4769c
--- /dev/null
+++ b/sound/soc/codecs/tac5x1x.h
@@ -0,0 +1,302 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Audio Codec Driver Supporting Devices
+ * TAA5X1X, TAC5X1X, TAD5X1X
+ *
+ * Copyright (C) 2024-2025 Texas Instruments Incorporated - https://www.ti.com
+ *
+ * Author: Kevin Lu <kevin-lu@ti.com>
+ * Author: Kokila Karuppusamy <kokila.karuppusamy@ti.com>
+ * Author: Niranjan H Y <niranjan.hy@ti.com>
+ */
+
+#ifndef _TAC5X1X_H
+#define _TAC5X1X_H
+
+#define	TAC5X1X_RATES	SNDRV_PCM_RATE_8000_192000
+#define	TAC5X1X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE \
+			 | SNDRV_PCM_FMTBIT_S20_3LE \
+			 | SNDRV_PCM_FMTBIT_S24_LE \
+			 | SNDRV_PCM_FMTBIT_S24_3LE \
+			 | SNDRV_PCM_FMTBIT_S32_LE)
+
+/*PAGE Control Register (available in page0 of each book) */
+#define	TAC_PAGE_SELECT		0x00
+#define	TAC_PAGE_ID(reg)	((reg) / 128)
+#define	TAC_PAGE_REG(reg)	((reg) % 128)
+#define	TAC5X1X_REG(page, reg)	(((page) * 128) + (reg))
+
+#define	TAC_PAGE1_SELECT	0x01
+#define	TAC5X1X_PGSEL		TAC5X1X_REG(0, 0x0)
+
+#define	TAC5X1X_RESET		TAC5X1X_REG(0, 0x1)
+#define	TAC5X1X_VREF		TAC5X1X_REG(0, 0x2)
+#define	TAC5X1X_VDDSTS		TAC5X1X_REG(0, 0x3)
+#define	TAC5X1X_MISC		TAC5X1X_REG(0, 0x4)
+#define	TAC5X1X_MISC1		TAC5X1X_REG(0, 0x5)
+#define	TAC5X1X_DACA0		TAC5X1X_REG(0, 0x6)
+#define	TAC5X1X_MISC0		TAC5X1X_REG(0, 0x7)
+#define	TAC5X1X_GPIO1		TAC5X1X_REG(0, 0xa)
+#define	TAC5X1X_GPIO2		TAC5X1X_REG(0, 0xb)
+#define	TAC5X1X_GPO1		TAC5X1X_REG(0, 0xc)
+#define	TAC5X1X_GPI1		TAC5X1X_REG(0, 0xd)
+#define	TAC5X1X_GPIOVAL		TAC5X1X_REG(0, 0xe)
+#define	TAC5X1X_INTF0		TAC5X1X_REG(0, 0xf)
+#define	TAC5X1X_INTF1		TAC5X1X_REG(0, 0x10)
+#define	TAC5X1X_INTF2		TAC5X1X_REG(0, 0x11)
+#define	TAC5X1X_INTF3		TAC5X1X_REG(0, 0x12)
+#define	TAC5X1X_INTF4		TAC5X1X_REG(0, 0x13)
+#define	TAC5X1X_INTF5		TAC5X1X_REG(0, 0x14)
+#define	TAC5X1X_INTF6		TAC5X1X_REG(0, 0x15)
+#define	TAC5X1X_ASI0		TAC5X1X_REG(0, 0x18)
+#define	TAC5X1X_ASI1		TAC5X1X_REG(0, 0x19)
+#define	TAC5X1X_PASI0		TAC5X1X_REG(0, 0x1a)
+#define	TAC5X1X_PASITX0		TAC5X1X_REG(0, 0x1b)
+#define	TAC5X1X_PASITX1		TAC5X1X_REG(0, 0x1c)
+#define	TAC5X1X_PASITX2		TAC5X1X_REG(0, 0x1d)
+#define	TAC5X1X_PASITXCH1	TAC5X1X_REG(0, 0x1e)
+#define	TAC5X1X_PASITXCH2	TAC5X1X_REG(0, 0x1f)
+#define	TAC5X1X_PASITXCH3	TAC5X1X_REG(0, 0x20)
+#define	TAC5X1X_PASITXCH4	TAC5X1X_REG(0, 0x21)
+#define	TAC5X1X_PASITXCH5	TAC5X1X_REG(0, 0x22)
+#define	TAC5X1X_PASITXCH6	TAC5X1X_REG(0, 0x23)
+#define	TAC5X1X_PASITXCH7	TAC5X1X_REG(0, 0x24)
+#define	TAC5X1X_PASITXCH8	TAC5X1X_REG(0, 0x25)
+#define	TAC5X1X_PASIRX0		TAC5X1X_REG(0, 0x26)
+#define	TAC5X1X_PASIRX1		TAC5X1X_REG(0, 0x27)
+#define	TAC5X1X_PASIRXCH1	TAC5X1X_REG(0, 0x28)
+#define	TAC5X1X_PASIRXCH2	TAC5X1X_REG(0, 0x29)
+#define	TAC5X1X_PASIRXCH3	TAC5X1X_REG(0, 0x2a)
+#define	TAC5X1X_PASIRXCH4	TAC5X1X_REG(0, 0x2b)
+#define	TAC5X1X_PASIRXCH5	TAC5X1X_REG(0, 0x2c)
+#define	TAC5X1X_PASIRXCH6	TAC5X1X_REG(0, 0x2d)
+#define	TAC5X1X_PASIRXCH7	TAC5X1X_REG(0, 0x2e)
+#define	TAC5X1X_PASIRXCH8	TAC5X1X_REG(0, 0x2f)
+#define	TAC5X1X_CLK0		TAC5X1X_REG(0, 0x32)
+#define	TAC5X1X_CLK1		TAC5X1X_REG(0, 0x33)
+#define	TAC5X1X_CLK2		TAC5X1X_REG(0, 0x34)
+#define	TAC5X1X_CNTCLK0		TAC5X1X_REG(0, 0x35)
+#define	TAC5X1X_CNTCLK1		TAC5X1X_REG(0, 0x36)
+#define	TAC5X1X_CNTCLK2		TAC5X1X_REG(0, 0x37)
+#define	TAC5X1X_CNTCLK3		TAC5X1X_REG(0, 0x38)
+#define	TAC5X1X_CNTCLK4		TAC5X1X_REG(0, 0x39)
+#define	TAC5X1X_CNTCLK5		TAC5X1X_REG(0, 0x3a)
+#define	TAC5X1X_CNTCLK6		TAC5X1X_REG(0, 0x3b)
+#define	TAC5X1X_CLKERRSTS0	TAC5X1X_REG(0, 0x3c)
+#define	TAC5X1X_CLKERRSTS1	TAC5X1X_REG(0, 0x3d)
+#define	TAC5X1X_CLKDETSTS0	TAC5X1X_REG(0, 0x3e)
+#define	TAC5X1X_CLKDETSTS1	TAC5X1X_REG(0, 0x3f)
+#define	TAC5X1X_CLKDETSTS2	TAC5X1X_REG(0, 0x40)
+#define	TAC5X1X_CLKDETSTS3	TAC5X1X_REG(0, 0x41)
+#define	TAC5X1X_INT		TAC5X1X_REG(0, 0x42)
+#define	TAC5X1X_DAC_FLT		TAC5X1X_REG(0, 0x43)
+#define	TAC5X1X_ADCDACMISC	TAC5X1X_REG(0, 0x4b)
+#define	TAC5X1X_IADC		TAC5X1X_REG(0, 0x4c)
+#define	TAC5X1X_VREFCFG		TAC5X1X_REG(0, 0x4d)
+#define	TAC5X1X_PWRTUNE0	TAC5X1X_REG(0, 0x4e)
+#define	TAC5X1X_PWRTUNE1	TAC5X1X_REG(0, 0x4f)
+#define	TAC5X1X_ADCCH1C0	TAC5X1X_REG(0, 0x50)
+#define	TAC5X1X_ADCCH		TAC5X1X_REG(0, 0x51)
+#define	TAC5X1X_ADCCH1C2	TAC5X1X_REG(0, 0x52)
+#define	TAC5X1X_ADCCH1C3	TAC5X1X_REG(0, 0x53)
+#define	TAC5X1X_ADCCH1C4	TAC5X1X_REG(0, 0x54)
+#define	TAC5X1X_ADCCH2C0	TAC5X1X_REG(0, 0x55)
+#define	TAC5X1X_ADCCH2C2	TAC5X1X_REG(0, 0x57)
+#define	TAC5X1X_ADCCH2C3	TAC5X1X_REG(0, 0x58)
+#define	TAC5X1X_ADCCH2C4	TAC5X1X_REG(0, 0x59)
+#define	TAC5X1X_ADCCH3C0	TAC5X1X_REG(0, 0x5a)
+#define	TAC5X1X_ADCCH3C2	TAC5X1X_REG(0, 0x5b)
+#define	TAC5X1X_ADCCH3C3	TAC5X1X_REG(0, 0x5c)
+#define	TAC5X1X_ADCCH3C4	TAC5X1X_REG(0, 0x5d)
+#define	TAC5X1X_ADCCH4C0	TAC5X1X_REG(0, 0x5e)
+#define	TAC5X1X_ADCCH4C2	TAC5X1X_REG(0, 0x5f)
+#define	TAC5X1X_ADCCH4C3	TAC5X1X_REG(0, 0x60)
+#define	TAC5X1X_ADCCH4C4	TAC5X1X_REG(0, 0x61)
+#define	TAC5X1X_ADCCFG1		TAC5X1X_REG(0, 0x62)
+#define	TAC5X1X_OUT1CFG0	TAC5X1X_REG(0, 0x64)
+#define	TAC5X1X_OUT1CFG1	TAC5X1X_REG(0, 0x65)
+#define	TAC5X1X_OUT1CFG2	TAC5X1X_REG(0, 0x66)
+#define	TAC5X1X_DACCH1A0	TAC5X1X_REG(0, 0x67)
+#define	TAC5X1X_DACCH1A1	TAC5X1X_REG(0, 0x68)
+#define	TAC5X1X_DACCH1B0	TAC5X1X_REG(0, 0x69)
+#define	TAC5X1X_DACCH1B1	TAC5X1X_REG(0, 0x6a)
+#define	TAC5X1X_OUT2CFG0	TAC5X1X_REG(0, 0x6b)
+#define	TAC5X1X_OUT2CFG1	TAC5X1X_REG(0, 0x6c)
+#define	TAC5X1X_OUT2CFG2	TAC5X1X_REG(0, 0x6d)
+#define	TAC5X1X_DACCH2A0	TAC5X1X_REG(0, 0x6e)
+#define	TAC5X1X_DACCH2A1	TAC5X1X_REG(0, 0x6f)
+#define	TAC5X1X_DACCH2B0	TAC5X1X_REG(0, 0x70)
+#define	TAC5X1X_DACCH2B1	TAC5X1X_REG(0, 0x71)
+#define	TAC5X1X_DSP0		TAC5X1X_REG(0, 0x72)
+#define	TAC5X1X_DSP1		TAC5X1X_REG(0, 0x73)
+#define	TAC5X1X_CH_EN		TAC5X1X_REG(0, 0x76)
+#define	TAC5X1X_DYN_PUPD	TAC5X1X_REG(0, 0x77)
+#define	TAC5X1X_PWR_CFG		TAC5X1X_REG(0, 0x78)
+#define	TAC5X1X_DEVSTS0		TAC5X1X_REG(0, 0x79)
+#define	TAC5X1X_DEVSTS1		TAC5X1X_REG(0, 0x7a)
+
+#define	TAC5X1X_CLKCFG0		TAC5X1X_REG(1, 0xd)
+#define	TAC5X1X_MICBIAS1	TAC5X1X_REG(1, 0x16)
+#define	TAC5X1X_AGC_DRC		TAC5X1X_REG(1, 0x24)
+#define	TAC5X1X_PLIM		TAC5X1X_REG(1, 0x2b)
+#define	TAC5X1X_MIXER		TAC5X1X_REG(1, 0x2c)
+
+#define	TAC5X1X_DIAG_CFG0	TAC5X1X_REG(1, 0x46)
+#define	TAC5X1X_DIAG_CFG1	TAC5X1X_REG(1, 0x47)
+#define	TAC5X1X_DIAG_CFG2	TAC5X1X_REG(1, 0x48)
+#define	TAC5X1X_DIAG_CFG8	TAC5X1X_REG(1, 0x4e)
+#define	TAC5X1X_DIAG_CFG9	TAC5X1X_REG(1, 0x4b)
+#define	 TAC5X1X_DIAG_CFG6	TAC5X1X_REG(1, 0x4c)
+#define	TAC5X1X_DIAG_CFG7	TAC5X1X_REG(1, 0x4d)
+/* Bits, masks, and shifts */
+
+#define	TAC5X1X_REG_INT_LTCH0	TAC5X1X_REG(0x1, 0x34)
+#define	TAC5X1X_REG_CHX_LTCH	TAC5X1X_REG(0x1, 0x35)
+#define	TAC5X1X_REG_IN_CH1_LTCH		TAC5X1X_REG(0x1, 0x36)
+#define	TAC5X1X_REG_IN_CH2_LTCH		TAC5X1X_REG(0x1, 0x37)
+#define	TAC5X1X_REG_OUT_CH1_LTCH	TAC5X1X_REG(0x1, 0x38)
+#define	TAC5X1X_REG_OUT_CH2_LTCH	TAC5X1X_REG(0x1, 0x39)
+#define	TAC5X1X_REG_INT_LTCH1	TAC5X1X_REG(0x1, 0x3A)
+#define	TAC5X1X_REG_INT_LTCH2	TAC5X1X_REG(0x1, 0x3B)
+
+/* TAC5X1X_CH_EN */
+
+#define	TAC5X1X_CH_EN_ADC_MASK	GENMASK(7, 4)
+#define	TAC5X1X_CH_EN_ADC_CH1	BIT(7)
+#define	TAC5X1X_CH_EN_ADC_CH2	BIT(6)
+#define	TAC5X1X_CH_EN_ADC_CH3	BIT(5)
+#define	TAC5X1X_CH_EN_ADC_CH4	BIT(4)
+
+#define	TAC5X1X_CH_EN_DAC_MASK	GENMASK(3, 0)
+#define	TAC5X1X_CH_EN_DAC_CH1	BIT(3)
+#define	TAC5X1X_CH_EN_DAC_CH2	BIT(2)
+#define	TAC5X1X_CH_EN_DAC_CH3	BIT(1)
+#define	TAC5X1X_CH_EN_DAC_CH4	BIT(0)
+
+/* TAC5X1X_GPIOVAL */
+#define	TAC5X1X_GPIO1_VAL	BIT(7)
+#define	TAC5X1X_GPIO2_VAL	BIT(6)
+#define	TAC5X1X_GPO1_VAL	BIT(5)
+#define	TAC5X1X_GPIO1_MON	BIT(3)
+#define	TAC5X1X_GPIO2_MON	BIT(2)
+#define	TAC5X1X_GPI1_MON	BIT(1)
+
+/* TAC5X1X_DIAG_CFG0 */
+#define	TAC5X1X_IN_CH_DIAG_EN_MASK	0xc0
+#define	TAC5X1X_INCL_SE_INM_MASK	0x20
+#define	TAC5X1X_INCL_AC_COUP_MASK	0x10
+#define	TAC5X1X_OUT1P_DIAG_EN_MASK	0x0f
+#define	TAC5X1X_MICBIAS_LOW_THRESHOLD	0x48
+#define	TAC5X1X_MICBIAS_HIGH_THRESHOLD	0xa2
+#define	TAC5X1X_GPA_LOW_THRESHOLD	0x4b
+#define	TAC5X1X_GPA_HIGH_THRESHOLD	0xba
+
+/* TAC5X1X_PASI */
+#define	TAC5X1X_PASI_SAMP_RATE_MASK	GENMASK(7, 2)
+#define	TAC5X1X_PASI_FMT_MASK		GENMASK(7, 6)
+#define	TAC5X1X_PASI_FMT_TDM		0x00
+#define	TAC5X1X_PASI_FMT_I2S		0x40
+#define	TAC5X1X_PASI_FMT_LJ		0x80
+
+#define	TAC5X1X_PASI_DATALEN_MASK	GENMASK(5, 4)
+#define	TAC5X1X_WORD_LEN_16BITS		0x00
+#define	TAC5X1X_WORD_LEN_20BITS		0x10
+#define	TAC5X1X_WORD_LEN_24BITS		0x20
+#define	TAC5X1X_WORD_LEN_32BITS		0x30
+
+/* TAC5X1X_CNTCLK2 */
+#define	TAC5X1X_PASI_MODE_MASK		0x10
+#define	TAC5X1X_SASI_MODE_MASK		0x08
+#define	TAC5X1X_ASI_RATE_MASK		0x01
+
+#define	TAC5X1X_PASI_RATE_48000		0x00
+#define	TAC5X1X_PASI_RATE_44100		0x01
+
+/* TAC5X1X_PASITX0 */
+#define	TAC5X1X_PASITX_OFFSET_MASK	0x1f
+
+/* TAC5X1X_PASIRX0 */
+#define	TAC5X1X_PASIRX_OFFSET_MASK	0x1f
+
+/* TAC5X1X_VREF */
+#define	TAC5X1X_VREF_SLEEP_EXIT_VREF_EN		0x80
+#define	TAC5X1X_VREF_SLEEP_ACTIVE_MASK		0x01
+
+/* TAC5X1X_PWRCFG */
+#define	TAC5X1X_PWR_CFG_ADC_PDZ	BIT(7)
+#define	TAC5X1X_PWR_CFG_DAC_PDZ	BIT(6)
+#define	TAC5X1X_PWR_CFG_MICBIAS	BIT(5)
+#define	TAC5X1X_PWR_CFG_UAD_EN	BIT(3)
+#define	TAC5X1X_PWR_CFG_VAD_EN	BIT(2)
+#define	TAC5X1X_PWR_CFG_UAG_EN	BIT(1)
+
+/* TAC5X1X_GPIOx */
+#define	TAC5X1X_GPIO1_DEFAULT_VAL	0x32
+#define	TAC5X1X_GPIO2_DEFAULT_VAL	0x00
+#define	TAC5X1X_GPI1_DEFAULT_VAL	0x00
+#define	TAC5X1X_GPO1_DEFAULT_VAL	0x00
+
+#define	TAC5X1X_GPIOX_CFG_MASK	0xf0
+#define	TAC5X1X_GPIOX_DRV_MASK	0x07
+
+#define	TAC5X1X_GPIO_DISABLE	0
+#define	TAC5X1X_GPIO_GPI	1
+#define	TAC5X1X_GPIO_GPO	2
+#define	TAC5X1X_GPIO_IRQ	3
+#define	TAC5X1X_GPIO_PDMCLK	4
+#define	TAC5X1X_GPIO_P_DOUT	5
+#define	TAC5X1X_GPIO_P_DOUT2	6
+#define	TAC5X1X_GPIO_S_DOUT	7
+#define	TAC5X1X_GPIO_S_DOUT2	8
+#define	TAC5X1X_GPIO_S_BCLK	9
+#define	TAC5X1X_GPIO_S_FSYNC	10
+#define	TAC5X1X_GPIO_CLKOUT	11
+#define	TAC5X1X_GPIO_DOUT_MUX	12
+#define	TAC5X1X_GPIO_DAISY_OUT	13
+
+#define	TAC5X1X_GPIO_DRV_HIZ	0
+#define	TAC5X1X_GPIO_DRV_ALAH	1
+#define	TAC5X1X_GPIO_DRV_ALWH	2
+#define	TAC5X1X_GPIO_DRV_ALHIZ	3
+#define	TAC5X1X_GPIO_DRV_WLAH	4
+#define	TAC5X1X_GPIO_DRV_HIZAH	5
+
+/* TAC5X1X_GPI1 */
+#define	TAC5X1X_GPI1_CFG_MASK	BIT(1)
+#define	TAC5X1X_GPA_CFG_MASK	BIT(0)
+
+/* TAC5X1X_VREFCFG */
+#define	TAC5X1X_VREFCFG_MICBIAS_VAL_MASK	GENMASK(3, 2)
+#define	TAC5X1X_VREFCFG_VREF_FSCALE_MASK	GENMASK(1, 0)
+
+#define	TAC5X1X_MICBIAS_VREF	0
+#define	TAC5X1X_MICBIAS_0_5VREF	1
+#define	TAC5X1X_MICBIAS_AVDD	3
+
+#define	TAC5X1X_VERF_2_75V	0
+#define	TAC5X1X_VERF_2_5V	1
+#define	TAC5X1X_VERF_1_375V	2
+
+enum tac5x1x_type {
+	TAA5212 = 0,
+	TAA5412,
+	TAC5111,
+	TAC5112,
+	TAC5211,
+	TAC5212,
+	TAC5311,
+	TAC5312,
+	TAC5411,
+	TAC5412,
+	TAD5112,
+	TAD5212,
+};
+
+extern const struct regmap_config tac5x1x_regmap;
+extern const struct of_device_id tac5x1x_of_match[];
+
+int tac5x1x_probe(struct device *dev, struct regmap *regmap,
+		  enum tac5x1x_type type);
+int tac5x1x_remove(struct device *dev);
+
+#endif	/* _TAC5X1X_H */