diff mbox series

[v3] ASoC: rt721-sdca: Add RT721 SDCA driver

Message ID d18b35f8b6934fc6a2be6c4458a63fe5@realtek.com (mailing list archive)
State Accepted
Commit 86ce355c1f9ab943bbe099ea7d0b8a3af2247f65
Headers show
Series [v3] ASoC: rt721-sdca: Add RT721 SDCA driver | expand

Commit Message

Jack Yu Oct. 1, 2024, 9:17 a.m. UTC
This is the initial codec driver for rt721-sdca.
It's a three functions (jack,mic,amp) soundwire driver.

Signed-off-by: Jack Yu <jack.yu@realtek.com>

v2: Fix typo in mbq default registers.
v3: Include soundwire common functions for Realtek.
---
 sound/soc/codecs/Kconfig          |    7 +
 sound/soc/codecs/Makefile         |    2 +
 sound/soc/codecs/rt721-sdca-sdw.c |  551 ++++++++++
 sound/soc/codecs/rt721-sdca-sdw.h |  150 +++
 sound/soc/codecs/rt721-sdca.c     | 1547 +++++++++++++++++++++++++++++
 sound/soc/codecs/rt721-sdca.h     |  268 +++++
 6 files changed, 2525 insertions(+)
 create mode 100644 sound/soc/codecs/rt721-sdca-sdw.c
 create mode 100644 sound/soc/codecs/rt721-sdca-sdw.h
 create mode 100644 sound/soc/codecs/rt721-sdca.c
 create mode 100644 sound/soc/codecs/rt721-sdca.h

Comments

Mark Brown Oct. 7, 2024, 7:34 p.m. UTC | #1
On Tue, 01 Oct 2024 09:17:38 +0000, Jack Yu wrote:
> This is the initial codec driver for rt721-sdca.
> It's a three functions (jack,mic,amp) soundwire driver.
> 
> Signed-off-by: Jack Yu <jack.yu@realtek.com>
> 
> 

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next

Thanks!

[1/1] ASoC: rt721-sdca: Add RT721 SDCA driver
      commit: 86ce355c1f9ab943bbe099ea7d0b8a3af2247f65

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark
Kees Bakker Oct. 8, 2024, 6:42 p.m. UTC | #2
Op 01-10-2024 om 11:17 schreef Jack Yu:
> This is the initial codec driver for rt721-sdca.
> It's a three functions (jack,mic,amp) soundwire driver.
>
> Signed-off-by: Jack Yu <jack.yu@realtek.com>
>
> v2: Fix typo in mbq default registers.
> v3: Include soundwire common functions for Realtek.
> ---
>   sound/soc/codecs/Kconfig          |    7 +
>   sound/soc/codecs/Makefile         |    2 +
>   sound/soc/codecs/rt721-sdca-sdw.c |  551 ++++++++++
>   sound/soc/codecs/rt721-sdca-sdw.h |  150 +++
>   sound/soc/codecs/rt721-sdca.c     | 1547 +++++++++++++++++++++++++++++
>   sound/soc/codecs/rt721-sdca.h     |  268 +++++
>   6 files changed, 2525 insertions(+)
>   create mode 100644 sound/soc/codecs/rt721-sdca-sdw.c
>   create mode 100644 sound/soc/codecs/rt721-sdca-sdw.h
>   create mode 100644 sound/soc/codecs/rt721-sdca.c
>   create mode 100644 sound/soc/codecs/rt721-sdca.h
>
> [...]
> +static int rt721_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
> +		struct snd_ctl_elem_value *ucontrol)
> +{
> +	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
> +	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
> +	struct rt721_sdca_dmic_kctrl_priv *p =
> +		(struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
> +	unsigned int boost_step = 0x0a00;
> +	unsigned int vol_max = 0x1e00;
> +	unsigned int regvalue, ctl, i;
> +	unsigned int adc_vol_flag = 0;
> +	const unsigned int interval_offset = 0xc0;
> +
> +	if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
> +		adc_vol_flag = 1;
> +
> +	/* check all channels */
> +	for (i = 0; i < p->count; i++) {
> +		regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue);
> +
> +		if (!adc_vol_flag) /* boost gain */
> +			ctl = regvalue / boost_step;
> +		else { /* ADC gain */
> +			if (adc_vol_flag)
> +				ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
> +			else
At this point it is dead code.
BTW This was detected by Coverity Scan; CID 1600271
> +				ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset);
> +		}
> +
> +		ucontrol->value.integer.value[i] = ctl;
> +	}
> +
> +	return 0;
> +}
>
Kees Bakker Oct. 8, 2024, 8:35 p.m. UTC | #3
Op 01-10-2024 om 11:17 schreef Jack Yu:
> This is the initial codec driver for rt721-sdca.
> It's a three functions (jack,mic,amp) soundwire driver.
>
> Signed-off-by: Jack Yu <jack.yu@realtek.com>
>
> v2: Fix typo in mbq default registers.
> v3: Include soundwire common functions for Realtek.
> ---
>   sound/soc/codecs/Kconfig          |    7 +
>   sound/soc/codecs/Makefile         |    2 +
>   sound/soc/codecs/rt721-sdca-sdw.c |  551 ++++++++++
>   sound/soc/codecs/rt721-sdca-sdw.h |  150 +++
>   sound/soc/codecs/rt721-sdca.c     | 1547 +++++++++++++++++++++++++++++
>   sound/soc/codecs/rt721-sdca.h     |  268 +++++
>   6 files changed, 2525 insertions(+)
>   create mode 100644 sound/soc/codecs/rt721-sdca-sdw.c
>   create mode 100644 sound/soc/codecs/rt721-sdca-sdw.h
>   create mode 100644 sound/soc/codecs/rt721-sdca.c
>   create mode 100644 sound/soc/codecs/rt721-sdca.h
> [...]
> +static void rt721_sdca_jack_detect_handler(struct work_struct *work)
> +{
> +	struct rt721_sdca_priv *rt721 =
> +		container_of(work, struct rt721_sdca_priv, jack_detect_work.work);
> +	int btn_type = 0, ret;
> +
> +	if (!rt721->hs_jack)
> +		return;
> +
> +	if (!rt721->component->card || !rt721->component->card->instantiated)
> +		return;
> +
> +	/* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */
> +	if (rt721->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6) {
> +		rt721->jack_type = rt_sdca_headset_detect(rt721->regmap,
> +							RT721_SDCA_ENT_GE49);
Variable "ret" is not initialized, no assignment.
> +		if (ret < 0)
> +			return;
> +	}
>
diff mbox series

Patch

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 7092842480ef..74c14da4ae09 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -222,6 +222,7 @@  config SND_SOC_ALL_CODECS
 	imply SND_SOC_RT712_SDCA_DMIC_SDW
 	imply SND_SOC_RT715_SDW
 	imply SND_SOC_RT715_SDCA_SDW
+	imply SND_SOC_RT721_SDCA_SDW
 	imply SND_SOC_RT722_SDCA_SDW
 	imply SND_SOC_RT1308_SDW
 	imply SND_SOC_RT1316_SDW
@@ -1743,6 +1744,12 @@  config SND_SOC_RT712_SDCA_DMIC_SDW
 	select REGMAP_SOUNDWIRE
 	select REGMAP_SOUNDWIRE_MBQ
 
+config SND_SOC_RT721_SDCA_SDW
+	tristate "Realtek RT721 SDCA Codec - SDW"
+	depends on SOUNDWIRE
+	select REGMAP_SOUNDWIRE
+	select REGMAP_SOUNDWIRE_MBQ
+
 config SND_SOC_RT722_SDCA_SDW
 	tristate "Realtek RT722 SDCA Codec - SDW"
 	depends on SOUNDWIRE
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 54cbc3feae32..3477c6352acc 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -259,6 +259,7 @@  snd-soc-rt712-sdca-y := rt712-sdca.o rt712-sdca-sdw.o
 snd-soc-rt712-sdca-dmic-y := rt712-sdca-dmic.o
 snd-soc-rt715-y := rt715.o rt715-sdw.o
 snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o
+snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o
 snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o
 snd-soc-rt9120-y := rt9120.o
 snd-soc-rtq9128-y := rtq9128.o
@@ -662,6 +663,7 @@  obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW)     += snd-soc-rt712-sdca.o
 obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW)     += snd-soc-rt712-sdca-dmic.o
 obj-$(CONFIG_SND_SOC_RT715)     += snd-soc-rt715.o
 obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW)     += snd-soc-rt715-sdca.o
+obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW)     += snd-soc-rt721-sdca.o
 obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW)     += snd-soc-rt722-sdca.o
 obj-$(CONFIG_SND_SOC_RT9120)	+= snd-soc-rt9120.o
 obj-$(CONFIG_SND_SOC_RTQ9128)	+= snd-soc-rtq9128.o
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
new file mode 100644
index 000000000000..c0f8cccae3b2
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -0,0 +1,551 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca-sdw.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt721-sdca.h"
+#include "rt721-sdca-sdw.h"
+#include "rt-sdw-common.h"
+
+static bool rt721_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2f01 ... 0x2f0a:
+	case 0x2f35:
+	case 0x2f50:
+	case 0x2f51:
+	case 0x2f58 ... 0x2f5d:
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+		RT721_SDCA_CTL_XUV, 0):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+		RT721_SDCA_CTL_SELECTED_MODE, 0):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+		RT721_SDCA_CTL_DETECTED_MODE, 0):
+	case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+		RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+		RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+	case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2f01:
+	case 0x2f51:
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+		RT721_SDCA_CTL_DETECTED_MODE, 0):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
+		RT721_SDCA_CTL_XUV, 0):
+	case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+		RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
+		RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+	case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0900007:
+	case 0x0a00005:
+	case 0x0c00005:
+	case 0x0d00014:
+	case 0x0310100:
+	case 0x2000001:
+	case 0x2000002:
+	case 0x2000003:
+	case 0x2000013:
+	case 0x200003c:
+	case 0x2000046:
+	case 0x5810000:
+	case 0x5810036:
+	case 0x5810037:
+	case 0x5810038:
+	case 0x5810039:
+	case 0x5b10018:
+	case 0x5b10019:
+	case 0x5f00045:
+	case 0x5f00048:
+	case 0x6100000:
+	case 0x6100005:
+	case 0x6100006:
+	case 0x610000d:
+	case 0x6100010:
+	case 0x6100011:
+	case 0x6100013:
+	case 0x6100015:
+	case 0x6100017:
+	case 0x6100025:
+	case 0x6100029:
+	case 0x610002c ... 0x610002f:
+	case 0x6100053 ... 0x6100055:
+	case 0x6100057:
+	case 0x610005a:
+	case 0x610005b:
+	case 0x610006a:
+	case 0x610006d:
+	case 0x6100092:
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+			CH_L):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+			CH_R):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+			CH_L):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+			CH_R):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+			RT721_SDCA_CTL_FU_CH_GAIN, CH_L):
+	case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+			RT721_SDCA_CTL_FU_CH_GAIN, CH_R):
+	case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+			CH_01):
+	case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+			CH_02):
+	case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+			CH_03):
+	case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+			CH_04):
+	case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L):
+	case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R):
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x0310100:
+	case 0x0a00005:
+	case 0x0c00005:
+	case 0x0d00014:
+	case 0x2000000:
+	case 0x200000d:
+	case 0x2000019:
+	case 0x2000020:
+	case 0x2000030:
+	case 0x2000046:
+	case 0x2000067:
+	case 0x2000084:
+	case 0x2000086:
+	case 0x5810000:
+	case 0x5810036:
+	case 0x5810037:
+	case 0x5810038:
+	case 0x5810039:
+	case 0x5b10018:
+	case 0x5b10019:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config rt721_sdca_regmap = {
+	.reg_bits = 32,
+	.val_bits = 8,
+	.readable_reg = rt721_sdca_readable_register,
+	.volatile_reg = rt721_sdca_volatile_register,
+	.max_register = 0x44ffffff,
+	.reg_defaults = rt721_sdca_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(rt721_sdca_reg_defaults),
+	.cache_type = REGCACHE_MAPLE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static const struct regmap_config rt721_sdca_mbq_regmap = {
+	.name = "sdw-mbq",
+	.reg_bits = 32,
+	.val_bits = 16,
+	.readable_reg = rt721_sdca_mbq_readable_register,
+	.volatile_reg = rt721_sdca_mbq_volatile_register,
+	.max_register = 0x41000312,
+	.reg_defaults = rt721_sdca_mbq_defaults,
+	.num_reg_defaults = ARRAY_SIZE(rt721_sdca_mbq_defaults),
+	.cache_type = REGCACHE_MAPLE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static int rt721_sdca_update_status(struct sdw_slave *slave,
+				enum sdw_slave_status status)
+{
+	struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+	if (status == SDW_SLAVE_UNATTACHED)
+		rt721->hw_init = false;
+
+	if (status == SDW_SLAVE_ATTACHED) {
+		if (rt721->hs_jack) {
+		/*
+		 * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then
+		 * if the device attached again, we will need to set the setting back.
+		 * It could avoid losing the jack detection interrupt.
+		 * This also could sync with the cache value as the rt721_sdca_jack_init set.
+		 */
+			sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+				SDW_SCP_SDCA_INTMASK_SDCA_6);
+			sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+				SDW_SCP_SDCA_INTMASK_SDCA_8);
+		}
+	}
+
+	/*
+	 * Perform initialization only if slave status is present and
+	 * hw_init flag is false
+	 */
+	if (rt721->hw_init || status != SDW_SLAVE_ATTACHED)
+		return 0;
+
+	/* perform I/O transfers required for Slave initialization */
+	return rt721_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt721_sdca_read_prop(struct sdw_slave *slave)
+{
+	struct sdw_slave_prop *prop = &slave->prop;
+	int nval;
+	int i, j;
+	u32 bit;
+	unsigned long addr;
+	struct sdw_dpn_prop *dpn;
+
+	sdw_slave_read_prop(slave);
+	prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+	prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+	prop->paging_support = true;
+
+	/*
+	 * port = 1 for headphone playback
+	 * port = 2 for headset-mic capture
+	 * port = 3 for speaker playback
+	 * port = 6 for digital-mic capture
+	 */
+	prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */
+	prop->sink_ports = BIT(3) | BIT(1); /* BITMAP:  00001010 */
+
+	nval = hweight32(prop->source_ports);
+	prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+		sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+	if (!prop->src_dpn_prop)
+		return -ENOMEM;
+
+	i = 0;
+	dpn = prop->src_dpn_prop;
+	addr = prop->source_ports;
+	for_each_set_bit(bit, &addr, 32) {
+		dpn[i].num = bit;
+		dpn[i].type = SDW_DPN_FULL;
+		dpn[i].simple_ch_prep_sm = true;
+		dpn[i].ch_prep_timeout = 10;
+		i++;
+	}
+
+	/* do this again for sink now */
+	nval = hweight32(prop->sink_ports);
+	prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+		sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+	if (!prop->sink_dpn_prop)
+		return -ENOMEM;
+
+	j = 0;
+	dpn = prop->sink_dpn_prop;
+	addr = prop->sink_ports;
+	for_each_set_bit(bit, &addr, 32) {
+		dpn[j].num = bit;
+		dpn[j].type = SDW_DPN_FULL;
+		dpn[j].simple_ch_prep_sm = true;
+		dpn[j].ch_prep_timeout = 10;
+		j++;
+	}
+
+	/* set the timeout values */
+	prop->clk_stop_timeout = 900;
+
+	/* wake-up event */
+	prop->wake_capable = 1;
+
+	/* Three data lanes are supported by rt721-sdca codec */
+	prop->lane_control_support = true;
+
+	return 0;
+}
+
+static int rt721_sdca_interrupt_callback(struct sdw_slave *slave,
+					struct sdw_slave_intr_status *status)
+{
+	struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+	int ret, stat;
+	int count = 0, retry = 3;
+	unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+	if (cancel_delayed_work_sync(&rt721->jack_detect_work)) {
+		dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__);
+		/* avoid the HID owner doesn't change to device */
+		if (rt721->scp_sdca_stat2)
+			scp_sdca_stat2 = rt721->scp_sdca_stat2;
+	}
+
+	/*
+	 * The critical section below intentionally protects a rather large piece of code.
+	 * We don't want to allow the system suspend to disable an interrupt while we are
+	 * processing it, which could be problematic given the quirky SoundWire interrupt
+	 * scheme. We do want however to prevent new workqueues from being scheduled if
+	 * the disable_irq flag was set during system suspend.
+	 */
+	mutex_lock(&rt721->disable_irq_lock);
+
+	ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+	if (ret < 0)
+		goto io_error;
+
+	rt721->scp_sdca_stat1 = ret;
+	ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+	if (ret < 0)
+		goto io_error;
+
+	rt721->scp_sdca_stat2 = ret;
+	if (scp_sdca_stat2)
+		rt721->scp_sdca_stat2 |= scp_sdca_stat2;
+	do {
+		/* clear flag */
+		ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+		if (ret < 0)
+			goto io_error;
+		if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+			ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1,
+				SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0);
+			if (ret < 0)
+				goto io_error;
+		} else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) {
+			ret = sdw_update_no_pm(rt721->slave, SDW_SCP_SDCA_INT1,
+				SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6);
+			if (ret < 0)
+				goto io_error;
+		}
+		ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+		if (ret < 0)
+			goto io_error;
+		if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+			ret = sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INT2,
+						SDW_SCP_SDCA_INTMASK_SDCA_8);
+			if (ret < 0)
+				goto io_error;
+		}
+
+		/* check if flag clear or not */
+		ret = sdw_read_no_pm(rt721->slave, SDW_DP0_INT);
+		if (ret < 0)
+			goto io_error;
+		sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+		ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+		if (ret < 0)
+			goto io_error;
+		scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+		ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+		if (ret < 0)
+			goto io_error;
+		scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+		stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade;
+
+		count++;
+	} while (stat != 0 && count < retry);
+
+	if (stat)
+		dev_warn(&slave->dev,
+			"%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+			rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+	ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT1);
+	ret = sdw_read_no_pm(rt721->slave, SDW_SCP_SDCA_INT2);
+
+	if (status->sdca_cascade && !rt721->disable_irq)
+		mod_delayed_work(system_power_efficient_wq,
+			&rt721->jack_detect_work, msecs_to_jiffies(280));
+
+	mutex_unlock(&rt721->disable_irq_lock);
+
+	return 0;
+
+io_error:
+	mutex_unlock(&rt721->disable_irq_lock);
+	pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+	return ret;
+}
+
+static const struct sdw_slave_ops rt721_sdca_slave_ops = {
+	.read_prop = rt721_sdca_read_prop,
+	.interrupt_callback = rt721_sdca_interrupt_callback,
+	.update_status = rt721_sdca_update_status,
+};
+
+static int rt721_sdca_sdw_probe(struct sdw_slave *slave,
+				const struct sdw_device_id *id)
+{
+	struct regmap *regmap, *mbq_regmap;
+
+	/* Regmap Initialization */
+	mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt721_sdca_mbq_regmap);
+	if (IS_ERR(mbq_regmap))
+		return PTR_ERR(mbq_regmap);
+
+	regmap = devm_regmap_init_sdw(slave, &rt721_sdca_regmap);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return rt721_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt721_sdca_sdw_remove(struct sdw_slave *slave)
+{
+	struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev);
+
+	if (rt721->hw_init) {
+		cancel_delayed_work_sync(&rt721->jack_detect_work);
+		cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+	}
+
+	if (rt721->first_hw_init)
+		pm_runtime_disable(&slave->dev);
+
+	mutex_destroy(&rt721->calibrate_mutex);
+	mutex_destroy(&rt721->disable_irq_lock);
+
+	return 0;
+}
+
+static const struct sdw_device_id rt721_sdca_id[] = {
+	SDW_SLAVE_ENTRY_EXT(0x025d, 0x721, 0x3, 0x1, 0),
+	{},
+};
+MODULE_DEVICE_TABLE(sdw, rt721_sdca_id);
+
+static int __maybe_unused rt721_sdca_dev_suspend(struct device *dev)
+{
+	struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+	if (!rt721->hw_init)
+		return 0;
+
+	cancel_delayed_work_sync(&rt721->jack_detect_work);
+	cancel_delayed_work_sync(&rt721->jack_btn_check_work);
+
+	regcache_cache_only(rt721->regmap, true);
+	regcache_cache_only(rt721->mbq_regmap, true);
+
+	return 0;
+}
+
+static int __maybe_unused rt721_sdca_dev_system_suspend(struct device *dev)
+{
+	struct rt721_sdca_priv *rt721_sdca = dev_get_drvdata(dev);
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	int ret1, ret2;
+
+	if (!rt721_sdca->hw_init)
+		return 0;
+
+	/*
+	 * prevent new interrupts from being handled after the
+	 * deferred work completes and before the parent disables
+	 * interrupts on the link
+	 */
+	mutex_lock(&rt721_sdca->disable_irq_lock);
+	rt721_sdca->disable_irq = true;
+	ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1,
+				SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6, 0);
+	ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2,
+				SDW_SCP_SDCA_INTMASK_SDCA_8, 0);
+	mutex_unlock(&rt721_sdca->disable_irq_lock);
+
+	if (ret1 < 0 || ret2 < 0) {
+		/* log but don't prevent suspend from happening */
+		dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__);
+	}
+
+	return rt721_sdca_dev_suspend(dev);
+}
+
+#define RT721_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt721_sdca_dev_resume(struct device *dev)
+{
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+	unsigned long time;
+
+	if (!rt721->first_hw_init)
+		return 0;
+
+	if (!slave->unattach_request) {
+		mutex_lock(&rt721->disable_irq_lock);
+		if (rt721->disable_irq == true) {
+			sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK1, SDW_SCP_SDCA_INTMASK_SDCA_6);
+			sdw_write_no_pm(slave, SDW_SCP_SDCA_INTMASK2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+			rt721->disable_irq = false;
+		}
+		mutex_unlock(&rt721->disable_irq_lock);
+		goto regmap_sync;
+	}
+
+	time = wait_for_completion_timeout(&slave->initialization_complete,
+				msecs_to_jiffies(RT721_PROBE_TIMEOUT));
+	if (!time) {
+		dev_err(&slave->dev, "Initialization not complete, timed out\n");
+		sdw_show_ping_status(slave->bus, true);
+
+		return -ETIMEDOUT;
+	}
+
+regmap_sync:
+	slave->unattach_request = 0;
+	regcache_cache_only(rt721->regmap, false);
+	regcache_sync(rt721->regmap);
+	regcache_cache_only(rt721->mbq_regmap, false);
+	regcache_sync(rt721->mbq_regmap);
+	return 0;
+}
+
+static const struct dev_pm_ops rt721_sdca_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(rt721_sdca_dev_system_suspend, rt721_sdca_dev_resume)
+	SET_RUNTIME_PM_OPS(rt721_sdca_dev_suspend, rt721_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt721_sdca_sdw_driver = {
+	.driver = {
+		.name = "rt721-sdca",
+		.owner = THIS_MODULE,
+		.pm = &rt721_sdca_pm,
+	},
+	.probe = rt721_sdca_sdw_probe,
+	.remove = rt721_sdca_sdw_remove,
+	.ops = &rt721_sdca_slave_ops,
+	.id_table = rt721_sdca_id,
+};
+module_sdw_driver(rt721_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca-sdw.h b/sound/soc/codecs/rt721-sdca-sdw.h
new file mode 100644
index 000000000000..214b31b82583
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca-sdw.h
@@ -0,0 +1,150 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca-sdw.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_SDW_H__
+#define __RT721_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt721_sdca_reg_defaults[] = {
+	{ 0x202d, 0x00 },
+	{ 0x2f01, 0x00 },
+	{ 0x2f02, 0x09 },
+	{ 0x2f03, 0x08 },
+	{ 0x2f04, 0x00 },
+	{ 0x2f05, 0x0e },
+	{ 0x2f06, 0x01 },
+	{ 0x2f09, 0x00 },
+	{ 0x2f0a, 0x00 },
+	{ 0x2f35, 0x00 },
+	{ 0x2f50, 0xf0 },
+	{ 0x2f58, 0x07 },
+	{ 0x2f59, 0x07 },
+	{ 0x2f5a, 0x00 },
+	{ 0x2f5b, 0x07 },
+	{ 0x2f5c, 0x27 },
+	{ 0x2f5d, 0x07 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+		RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+		RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+		RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+		RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+		RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+		RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+		RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+		RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+		RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+		RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+		RT721_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+		RT721_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+		RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+		RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+		RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+		RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+		RT721_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+		RT721_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+		RT721_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+		RT721_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+		RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+		RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+		RT721_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+		RT721_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+};
+
+static const struct reg_default rt721_sdca_mbq_defaults[] = {
+	{ 0x0900007, 0xc004 },
+	{ 0x2000001, 0x0000 },
+	{ 0x2000002, 0x0000 },
+	{ 0x2000003, 0x0000 },
+	{ 0x2000013, 0x8001 },
+	{ 0x200003c, 0x0000 },
+	{ 0x2000046, 0x3400 },
+	{ 0x5f00044, 0x6040 },
+	{ 0x5f00045, 0x3333 },
+	{ 0x5f00048, 0x0000 },
+	{ 0x6100005, 0x0005 },
+	{ 0x6100006, 0x0000 },
+	{ 0x610000d, 0x0051 },
+	{ 0x6100010, 0x0180 },
+	{ 0x6100011, 0x0000 },
+	{ 0x6100013, 0x0000 },
+	{ 0x6100015, 0x0000 },
+	{ 0x6100017, 0x8049 },
+	{ 0x6100025, 0x1000 },
+	{ 0x6100029, 0x0809 },
+	{ 0x610002c, 0x2828 },
+	{ 0x610002d, 0x2929 },
+	{ 0x610002e, 0x3529 },
+	{ 0x610002f, 0x2901 },
+	{ 0x6100053, 0x2630 },
+	{ 0x6100054, 0x2a2a },
+	{ 0x6100055, 0x152f },
+	{ 0x6100057, 0x2200 },
+	{ 0x610005a, 0x2a4b },
+	{ 0x610005b, 0x2a00 },
+	{ 0x610006a, 0x0102 },
+	{ 0x610006d, 0x0102 },
+	{ 0x6100092, 0x4f61 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+		CH_L), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05, RT721_SDCA_CTL_FU_VOLUME,
+		CH_R), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+		CH_L), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F, RT721_SDCA_CTL_FU_VOLUME,
+		CH_R), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+		CH_L), 0xfe00 },
+	{ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44, RT721_SDCA_CTL_FU_CH_GAIN,
+		CH_R), 0xfe00 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+		0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_02),
+		0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_03),
+		0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15, RT721_SDCA_CTL_FU_CH_GAIN, CH_04),
+		0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+		CH_01), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+		CH_02), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+		CH_03), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E, RT721_SDCA_CTL_FU_VOLUME,
+		CH_04), 0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_L),
+		0x0000 },
+	{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06, RT721_SDCA_CTL_FU_VOLUME, CH_R),
+		0x0000 },
+};
+
+#endif /* __RT721_SDW_H__ */
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
new file mode 100644
index 000000000000..36056cb7a3ca
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -0,0 +1,1547 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt721-sdca.c -- rt721 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2024 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <sound/pcm.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm_params.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "rt721-sdca.h"
+#include "rt-sdw-common.h"
+
+static void rt721_sdca_jack_detect_handler(struct work_struct *work)
+{
+	struct rt721_sdca_priv *rt721 =
+		container_of(work, struct rt721_sdca_priv, jack_detect_work.work);
+	int btn_type = 0, ret;
+
+	if (!rt721->hs_jack)
+		return;
+
+	if (!rt721->component->card || !rt721->component->card->instantiated)
+		return;
+
+	/* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */
+	if (rt721->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6) {
+		rt721->jack_type = rt_sdca_headset_detect(rt721->regmap,
+							RT721_SDCA_ENT_GE49);
+		if (ret < 0)
+			return;
+	}
+
+	/* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+	if (rt721->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+		btn_type = rt_sdca_button_detect(rt721->regmap,
+					RT721_SDCA_ENT_HID01, RT721_BUF_ADDR_HID1,
+					RT721_SDCA_HID_ID);
+
+	if (rt721->jack_type == 0)
+		btn_type = 0;
+
+	dev_dbg(&rt721->slave->dev,
+		"in %s, jack_type=%d\n", __func__, rt721->jack_type);
+	dev_dbg(&rt721->slave->dev,
+		"in %s, btn_type=0x%x\n", __func__, btn_type);
+	dev_dbg(&rt721->slave->dev,
+		"in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+		rt721->scp_sdca_stat1, rt721->scp_sdca_stat2);
+
+	snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type,
+			SND_JACK_HEADSET |
+			SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+			SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+	if (btn_type) {
+		/* button released */
+		snd_soc_jack_report(rt721->hs_jack, rt721->jack_type,
+			SND_JACK_HEADSET |
+			SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+			SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+		mod_delayed_work(system_power_efficient_wq,
+			&rt721->jack_btn_check_work, msecs_to_jiffies(200));
+	}
+}
+
+static void rt721_sdca_btn_check_handler(struct work_struct *work)
+{
+	struct rt721_sdca_priv *rt721 =
+		container_of(work, struct rt721_sdca_priv, jack_btn_check_work.work);
+	int btn_type = 0, ret, idx;
+	unsigned int det_mode, offset, val;
+	unsigned char buf[3];
+
+	ret = regmap_read(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
+			RT721_SDCA_CTL_DETECTED_MODE, 0), &det_mode);
+	if (ret < 0)
+		goto io_error;
+
+	/* pin attached */
+	if (det_mode) {
+		/* read UMP message offset */
+		ret = regmap_read(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
+				RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+		if (ret < 0)
+			goto io_error;
+
+		for (idx = 0; idx < sizeof(buf); idx++) {
+			ret = regmap_read(rt721->regmap,
+				RT721_BUF_ADDR_HID1 + offset + idx, &val);
+			if (ret < 0)
+				goto io_error;
+			buf[idx] = val & 0xff;
+		}
+		/* Report ID for HID1 */
+		if (buf[0] == 0x11)
+			btn_type = rt_sdca_btn_type(&buf[1]);
+	} else
+		rt721->jack_type = 0;
+
+	dev_dbg(&rt721->slave->dev, "%s, btn_type=0x%x\n",	__func__, btn_type);
+	snd_soc_jack_report(rt721->hs_jack, rt721->jack_type | btn_type,
+			SND_JACK_HEADSET |
+			SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+			SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+	if (btn_type) {
+		/* button released */
+		snd_soc_jack_report(rt721->hs_jack, rt721->jack_type,
+			SND_JACK_HEADSET |
+			SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+			SND_JACK_BTN_2 | SND_JACK_BTN_3);
+
+		mod_delayed_work(system_power_efficient_wq,
+			&rt721->jack_btn_check_work, msecs_to_jiffies(200));
+	}
+
+	return;
+
+io_error:
+	pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721)
+{
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_MISC_POWER_CTL31, 0x8000);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+		RT721_VREF1_HV_CTRL1, 0xe000);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_MISC_POWER_CTL31, 0x8007);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL9, 0x2a2a);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL10, 0x2a00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL6, 0x2a2a);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL5, 0x2626);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL8, 0x1e00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL7, 0x1515);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_CH_FLOAT_CTL3, 0x0304);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_CH_FLOAT_CTL4, 0x0304);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_HDA_LEGACY_CTL1, 0x0000);
+	regmap_write(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+			RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+	regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+		RT721_RC_CALIB_CTRL0, 0x0b00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+		RT721_RC_CALIB_CTRL0, 0x0b40);
+	regmap_write(rt721->regmap, 0x2f5c, 0x25);
+}
+
+static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721)
+{
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_MISC_POWER_CTL31, 0x8000);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+		RT721_VREF1_HV_CTRL1, 0xe000);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_MISC_POWER_CTL31, 0x8007);
+	regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
+	regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
+	regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_CH_FLOAT_CTL6, 0x5561);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_REG,
+		RT721_GPIO_PAD_CTRL5, 0x8003);
+	regmap_write(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+			RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+	regmap_write(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+			RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+	regmap_write(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+			RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+	regmap_write(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+			RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+	regmap_write(rt721->regmap,
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+			RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+}
+
+static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721)
+{
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_MISC_POWER_CTL31, 0x8000);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+		RT721_VREF1_HV_CTRL1, 0xe000);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_MISC_POWER_CTL31, 0x8007);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_GE_REL_CTRL1, 0x8011);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_UMP_HID_CTRL3, 0xcf00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_UMP_HID_CTRL4, 0x000f);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_UMP_HID_CTRL1, 0x1100);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_UMP_HID_CTRL5, 0x0c12);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
+		RT721_JD_1PIN_GAT_CTRL2, 0xc002);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+		RT721_RC_CALIB_CTRL0, 0x0b00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+		RT721_RC_CALIB_CTRL0, 0x0b40);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_UAJ_TOP_TCON14, 0x3333);
+	regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
+	regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+		RT721_HP_AMP_2CH_CAL1, 0x0140);
+	regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
+	regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+		RT721_HP_AMP_2CH_CAL18, 0x5522);
+	regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
+	regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+		RT721_CBJ_A0_GAT_CTRL1, 0x2a02);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+		RT721_HP_AMP_2CH_CAL4, 0xa105);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_UAJ_TOP_TCON14, 0x3b33);
+	regmap_write(rt721->mbq_regmap, 0x310400, 0x3023);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_UAJ_TOP_TCON14, 0x3f33);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_UAJ_TOP_TCON13, 0x6048);
+	regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
+	regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
+	regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
+	regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
+	regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+		RT721_UAJ_TOP_TCON17, 0x0008);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+		RT721_DAC_2CH_CTRL3, 0x55ff);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+		RT721_DAC_2CH_CTRL4, 0xcc00);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+		RT721_MBIAS_LV_CTRL2, 0x6677);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+		RT721_VREF2_LV_CTRL1, 0x7600);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL2, 0x1234);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL3, 0x3512);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL1, 0x4040);
+	rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_ENT_FLOAT_CTL4, 0x1201);
+	regmap_write(rt721->regmap, 0x2f58, 0x07);
+}
+
+static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721)
+{
+	mutex_lock(&rt721->calibrate_mutex);
+	if (rt721->hs_jack) {
+		sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK1,
+			SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6);
+		sdw_write_no_pm(rt721->slave, SDW_SCP_SDCA_INTMASK2,
+			SDW_SCP_SDCA_INTMASK_SDCA_8);
+		dev_dbg(&rt721->slave->dev, "in %s enable\n", __func__);
+		rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+			RT721_HDA_LEGACY_UAJ_CTL, 0x036E);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU03,
+				RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XU0D,
+				RT721_SDCA_CTL_SELECTED_MODE, 0), 0);
+		rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+			RT721_GE_REL_CTRL1, 0x4000, 0x4000);
+	}
+	mutex_unlock(&rt721->calibrate_mutex);
+}
+
+static int rt721_sdca_set_jack_detect(struct snd_soc_component *component,
+	struct snd_soc_jack *hs_jack, void *data)
+{
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	rt721->hs_jack = hs_jack;
+
+	ret = pm_runtime_resume_and_get(component->dev);
+	if (ret < 0) {
+		if (ret != -EACCES) {
+			dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+			return ret;
+		}
+		/* pm_runtime not enabled yet */
+		dev_dbg(component->dev,	"%s: skipping jack init for now\n", __func__);
+		return 0;
+	}
+
+	rt721_sdca_jack_init(rt721);
+
+	pm_runtime_mark_last_busy(component->dev);
+	pm_runtime_put_autosuspend(component->dev);
+
+	return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt721_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned int read_l, read_r, gain_l_val, gain_r_val;
+	unsigned int adc_vol_flag = 0, changed = 0;
+	unsigned int lvalue, rvalue;
+	const unsigned int interval_offset = 0xc0;
+	const unsigned int tendA = 0x200;
+	const unsigned int tendB = 0xa00;
+
+	if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+		strstr(ucontrol->id.name, "FU0F Capture Volume"))
+		adc_vol_flag = 1;
+
+	regmap_read(rt721->mbq_regmap, mc->reg, &lvalue);
+	regmap_read(rt721->mbq_regmap, mc->rreg, &rvalue);
+
+	/* L Channel */
+	gain_l_val = ucontrol->value.integer.value[0];
+	if (gain_l_val > mc->max)
+		gain_l_val = mc->max;
+
+	if (mc->shift == 8) {
+		/* boost gain */
+		gain_l_val = gain_l_val * tendB;
+	} else if (mc->shift == 1) {
+		/* FU33 boost gain */
+		if (gain_l_val == 0)
+			gain_l_val = 0x8000;
+		else
+			gain_l_val = (gain_l_val - 1) * tendA;
+	} else {
+		/* ADC/DAC gain */
+		if (adc_vol_flag)
+			gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset);
+		else
+			gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset);
+		gain_l_val &= 0xffff;
+	}
+
+	/* R Channel */
+	gain_r_val = ucontrol->value.integer.value[1];
+	if (gain_r_val > mc->max)
+		gain_r_val = mc->max;
+
+	if (mc->shift == 8) {
+		/* boost gain */
+		gain_r_val = gain_r_val * tendB;
+	} else if (mc->shift == 1) {
+		/* FU33 boost gain */
+		if (gain_r_val == 0)
+			gain_r_val = 0x8000;
+		else
+			gain_r_val = (gain_r_val - 1) * tendA;
+	} else {
+		/* ADC/DAC gain */
+		if (adc_vol_flag)
+			gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset);
+		else
+			gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset);
+		gain_r_val &= 0xffff;
+	}
+
+	if (lvalue != gain_l_val || rvalue != gain_r_val)
+		changed = 1;
+	else
+		return 0;
+
+	/* Lch*/
+	regmap_write(rt721->mbq_regmap, mc->reg, gain_l_val);
+
+	/* Rch */
+	regmap_write(rt721->mbq_regmap, mc->rreg, gain_r_val);
+
+	regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+	regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+	if (read_r == gain_r_val && read_l == gain_l_val)
+		return changed;
+
+	return -EIO;
+}
+
+static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
+	unsigned int adc_vol_flag = 0;
+	const unsigned int interval_offset = 0xc0;
+	const unsigned int tendB = 0xa00;
+
+	if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+		strstr(ucontrol->id.name, "FU0F Capture Volume"))
+		adc_vol_flag = 1;
+
+	regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
+	regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
+
+	if (mc->shift == 8) /* boost gain */
+		ctl_l = read_l / tendB;
+	else {
+		if (adc_vol_flag)
+			ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
+		else
+			ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset);
+	}
+
+	if (read_l != read_r) {
+		if (mc->shift == 8) /* boost gain */
+			ctl_r = read_r / tendB;
+		else { /* ADC/DAC gain */
+			if (adc_vol_flag)
+				ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
+			else
+				ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset);
+		}
+	} else {
+		ctl_r = ctl_l;
+	}
+
+	ucontrol->value.integer.value[0] = ctl_l;
+	ucontrol->value.integer.value[1] = ctl_r;
+
+	return 0;
+}
+
+static int rt721_sdca_set_fu1e_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+	int err, i;
+	unsigned int ch_mute;
+
+	for (i = 0; i < ARRAY_SIZE(rt721->fu1e_mixer_mute); i++) {
+		ch_mute = rt721->fu1e_dapm_mute || rt721->fu1e_mixer_mute[i];
+		err = regmap_write(rt721->regmap,
+				SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+				RT721_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+static int rt721_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct rt721_sdca_dmic_kctrl_priv *p =
+		(struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+	unsigned int i;
+
+	for (i = 0; i < p->count; i++)
+		ucontrol->value.integer.value[i] = !rt721->fu1e_mixer_mute[i];
+
+	return 0;
+}
+
+static int rt721_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct rt721_sdca_dmic_kctrl_priv *p =
+		(struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+	int err, changed = 0, i;
+
+	for (i = 0; i < p->count; i++) {
+		if (rt721->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+			changed = 1;
+		rt721->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+	}
+
+	err = rt721_sdca_set_fu1e_capture_ctl(rt721);
+	if (err < 0)
+		return err;
+
+	return changed;
+}
+
+static int rt721_sdca_set_fu0f_capture_ctl(struct rt721_sdca_priv *rt721)
+{
+	int err;
+	unsigned int ch_l, ch_r;
+
+	ch_l = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_l_mute) ? 0x01 : 0x00;
+	ch_r = (rt721->fu0f_dapm_mute || rt721->fu0f_mixer_r_mute) ? 0x01 : 0x00;
+
+	err = regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+			RT721_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+	if (err < 0)
+		return err;
+
+	err = regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+			RT721_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
+static int rt721_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+	ucontrol->value.integer.value[0] = !rt721->fu0f_mixer_l_mute;
+	ucontrol->value.integer.value[1] = !rt721->fu0f_mixer_r_mute;
+	return 0;
+}
+
+static int rt721_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	int err, changed = 0;
+
+	if (rt721->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+		rt721->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1])
+		changed = 1;
+
+	rt721->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0];
+	rt721->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1];
+	err = rt721_sdca_set_fu0f_capture_ctl(rt721);
+	if (err < 0)
+		return err;
+
+	return changed;
+}
+
+static int rt721_sdca_fu_info(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_info *uinfo)
+{
+	struct rt721_sdca_dmic_kctrl_priv *p =
+		(struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+
+	if (p->max == 1)
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = p->count;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = p->max;
+	return 0;
+}
+
+static int rt721_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct rt721_sdca_dmic_kctrl_priv *p =
+		(struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+	unsigned int boost_step = 0x0a00;
+	unsigned int vol_max = 0x1e00;
+	unsigned int regvalue, ctl, i;
+	unsigned int adc_vol_flag = 0;
+	const unsigned int interval_offset = 0xc0;
+
+	if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+		adc_vol_flag = 1;
+
+	/* check all channels */
+	for (i = 0; i < p->count; i++) {
+		regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue);
+
+		if (!adc_vol_flag) /* boost gain */
+			ctl = regvalue / boost_step;
+		else { /* ADC gain */
+			if (adc_vol_flag)
+				ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset);
+			else
+				ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset);
+		}
+
+		ucontrol->value.integer.value[i] = ctl;
+	}
+
+	return 0;
+}
+
+static int rt721_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt721_sdca_dmic_kctrl_priv *p =
+		(struct rt721_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned int boost_step = 0x0a00;
+	unsigned int vol_max = 0x1e00;
+	unsigned int gain_val[4];
+	unsigned int i, adc_vol_flag = 0, changed = 0;
+	unsigned int regvalue[4];
+	const unsigned int interval_offset = 0xc0;
+	int err;
+
+	if (strstr(ucontrol->id.name, "FU1E Capture Volume"))
+		adc_vol_flag = 1;
+
+	/* check all channels */
+	for (i = 0; i < p->count; i++) {
+		regmap_read(rt721->mbq_regmap, p->reg_base + i, &regvalue[i]);
+
+		gain_val[i] = ucontrol->value.integer.value[i];
+		if (gain_val[i] > p->max)
+			gain_val[i] = p->max;
+
+		if (!adc_vol_flag) /* boost gain */
+			gain_val[i] = gain_val[i] * boost_step;
+		else { /* ADC gain */
+			gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset);
+			gain_val[i] &= 0xffff;
+		}
+
+		if (regvalue[i] != gain_val[i])
+			changed = 1;
+	}
+
+	if (!changed)
+		return 0;
+
+	for (i = 0; i < p->count; i++) {
+		err = regmap_write(rt721->mbq_regmap, p->reg_base + i, gain_val[i]);
+		if (err < 0)
+			dev_err(&rt721->slave->dev, "%#08x can't be set\n", p->reg_base + i);
+	}
+
+	return changed;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
+static const DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, -200, 200, 0);
+
+static const struct snd_kcontrol_new rt721_sdca_controls[] = {
+	/* Headphone playback settings */
+	SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+			RT721_SDCA_CTL_FU_VOLUME, CH_L),
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+			RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+		rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+	/* Headset mic capture settings */
+	SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+		rt721_sdca_fu0f_capture_get, rt721_sdca_fu0f_capture_put),
+	SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+			RT721_SDCA_CTL_FU_VOLUME, CH_L),
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU0F,
+			RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0,
+		rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume",
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+			RT721_SDCA_CTL_FU_CH_GAIN, CH_L),
+		SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
+			RT721_SDCA_CTL_FU_CH_GAIN, CH_R), 1, 0x15, 0,
+		rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic2_boost_vol_tlv),
+	/* AMP playback settings */
+	SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+			RT721_SDCA_CTL_FU_VOLUME, CH_L),
+		SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+			RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+		rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+	/* DMIC capture settings */
+	RT_SDCA_FU_CTRL("FU1E Capture Switch",
+		SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+			RT721_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4, rt721_sdca_fu_info,
+			rt721_sdca_fu1e_capture_get, rt721_sdca_fu1e_capture_put),
+	RT_SDCA_EXT_TLV("FU1E Capture Volume",
+		SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
+			RT721_SDCA_CTL_FU_VOLUME, CH_01),
+		rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+			4, 0x3f, mic_vol_tlv, rt721_sdca_fu_info),
+	RT_SDCA_EXT_TLV("FU15 Boost Volume",
+		SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_FU15,
+			RT721_SDCA_CTL_FU_CH_GAIN, CH_01),
+		rt721_sdca_dmic_set_gain_get, rt721_sdca_dmic_set_gain_put,
+			4, 3, boost_vol_tlv, rt721_sdca_fu_info),
+};
+
+static int rt721_sdca_adc_mux_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_kcontrol_component(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned int val = 0, mask_sft, mask;
+
+	if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+		mask_sft = 12;
+		mask = 0x7;
+	} else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+		mask_sft = 10;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+		mask_sft = 8;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+		mask_sft = 6;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+		mask_sft = 4;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+		mask_sft = 2;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+		mask_sft = 0;
+		mask = 0x3;
+	} else
+		return -EINVAL;
+
+	rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_HDA_LEGACY_MUX_CTL0, &val);
+
+	ucontrol->value.enumerated.item[0] = (val >> mask_sft) & mask;
+
+	return 0;
+}
+
+static int rt721_sdca_adc_mux_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_kcontrol_component(kcontrol);
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int *item = ucontrol->value.enumerated.item;
+	unsigned int val, val2 = 0, change, mask_sft, mask;
+	unsigned int check;
+
+	if (item[0] >= e->items)
+		return -EINVAL;
+
+	if (strstr(ucontrol->id.name, "ADC 09 Mux")) {
+		mask_sft = 12;
+		mask = 0x7;
+	} else if (strstr(ucontrol->id.name, "ADC 08 R Mux")) {
+		mask_sft = 10;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 08 L Mux")) {
+		mask_sft = 8;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 10 R Mux")) {
+		mask_sft = 6;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 10 L Mux")) {
+		mask_sft = 4;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 07 R Mux")) {
+		mask_sft = 2;
+		mask = 0x3;
+	} else if (strstr(ucontrol->id.name, "ADC 07 L Mux")) {
+		mask_sft = 0;
+		mask = 0x3;
+	} else
+		return -EINVAL;
+
+	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+	rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+		RT721_HDA_LEGACY_MUX_CTL0, &val2);
+
+	if (strstr(ucontrol->id.name, "ADC 09 Mux"))
+		val2 = (val2 >> mask_sft) & 0x7;
+	else
+		val2 = (val2 >> mask_sft) & 0x3;
+
+	if (val == val2)
+		change = 0;
+	else
+		change = 1;
+
+	if (change) {
+		rt_sdca_index_read(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+			RT721_HDA_LEGACY_MUX_CTL0, &check);
+		rt_sdca_index_update_bits(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+			RT721_HDA_LEGACY_MUX_CTL0, mask << mask_sft,
+			val << mask_sft);
+	}
+
+	snd_soc_dapm_mux_update_power(dapm, kcontrol,
+		item[0], e, NULL);
+
+	return change;
+}
+
+static const char * const adc09_mux_text[] = {
+	"MIC2",
+	"LINE1",
+	"LINE2",
+};
+static const char * const adc07_10_mux_text[] = {
+	"DMIC1 RE",
+	"DMIC1 FE",
+	"DMIC2 RE",
+	"DMIC2 FE",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt721_adc09_enum, SND_SOC_NOPM, 0, adc09_mux_text);
+static SOC_ENUM_SINGLE_DECL(
+	rt721_dmic_enum, SND_SOC_NOPM, 0, adc07_10_mux_text);
+
+static const struct snd_kcontrol_new rt721_sdca_adc09_mux =
+	SOC_DAPM_ENUM_EXT("ADC 09 Mux", rt721_adc09_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_r_mux =
+	SOC_DAPM_ENUM_EXT("ADC 08 R Mux", rt721_dmic_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc08_l_mux =
+	SOC_DAPM_ENUM_EXT("ADC 08 L Mux", rt721_dmic_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_r_mux =
+	SOC_DAPM_ENUM_EXT("ADC 10 R Mux", rt721_dmic_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc10_l_mux =
+	SOC_DAPM_ENUM_EXT("ADC 10 L Mux", rt721_dmic_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_r_mux =
+	SOC_DAPM_ENUM_EXT("ADC 07 R Mux", rt721_dmic_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+static const struct snd_kcontrol_new rt721_sdca_adc07_l_mux =
+	SOC_DAPM_ENUM_EXT("ADC 07 L Mux", rt721_dmic_enum,
+			rt721_sdca_adc_mux_get, rt721_sdca_adc_mux_put);
+
+
+static int rt721_sdca_fu42_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char unmute = 0x0, mute = 0x1;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		msleep(100);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+				RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+				RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+				RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+				RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_fu21_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char unmute = 0x0, mute = 0x1;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+				RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+				RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+				RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+				RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_fu23_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char unmute = 0x0, mute = 0x1;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+				RT721_SDCA_CTL_FU_MUTE, CH_L), unmute);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+				RT721_SDCA_CTL_FU_MUTE, CH_R), unmute);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+				RT721_SDCA_CTL_FU_MUTE, CH_L), mute);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+				RT721_SDCA_CTL_FU_MUTE, CH_R), mute);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_fu113_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		rt721->fu1e_dapm_mute = false;
+		rt721_sdca_set_fu1e_capture_ctl(rt721);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		rt721->fu1e_dapm_mute = true;
+		rt721_sdca_set_fu1e_capture_ctl(rt721);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_fu36_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		rt721->fu0f_dapm_mute = false;
+		rt721_sdca_set_fu0f_capture_ctl(rt721);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		rt721->fu0f_dapm_mute = true;
+		rt721_sdca_set_fu0f_capture_ctl(rt721);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_pde47_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char ps0 = 0x0, ps3 = 0x3;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE40,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_pde41_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char ps0 = 0x0, ps3 = 0x3;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE41,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_pde11_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char ps0 = 0x0, ps3 = 0x3;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_PDE2A,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+		break;
+	}
+	return 0;
+}
+
+static int rt721_sdca_pde34_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_to_component(w->dapm);
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	unsigned char ps0 = 0x0, ps3 = 0x3;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PDE12,
+				RT721_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget rt721_sdca_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("HP"),
+	SND_SOC_DAPM_OUTPUT("SPK"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("LINE1"),
+	SND_SOC_DAPM_INPUT("LINE2"),
+	SND_SOC_DAPM_INPUT("DMIC1_2"),
+	SND_SOC_DAPM_INPUT("DMIC3_4"),
+
+	SND_SOC_DAPM_SUPPLY("PDE 41", SND_SOC_NOPM, 0, 0,
+		rt721_sdca_pde41_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0,
+		rt721_sdca_pde47_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+		rt721_sdca_pde11_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("PDE 34", SND_SOC_NOPM, 0, 0,
+		rt721_sdca_pde34_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0,
+		rt721_sdca_fu21_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_DAC_E("FU 23", NULL, SND_SOC_NOPM, 0, 0,
+		rt721_sdca_fu23_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0,
+		rt721_sdca_fu42_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0,
+		rt721_sdca_fu36_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0,
+		rt721_sdca_fu113_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MUX("ADC 09 Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc09_mux),
+	SND_SOC_DAPM_MUX("ADC 08 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc08_r_mux),
+	SND_SOC_DAPM_MUX("ADC 08 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc08_l_mux),
+	SND_SOC_DAPM_MUX("ADC 10 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc10_r_mux),
+	SND_SOC_DAPM_MUX("ADC 10 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc10_l_mux),
+	SND_SOC_DAPM_MUX("ADC 07 R Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc07_r_mux),
+	SND_SOC_DAPM_MUX("ADC 07 L Mux", SND_SOC_NOPM, 0, 0,
+		&rt721_sdca_adc07_l_mux),
+
+	SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt721_sdca_audio_map[] = {
+	{"FU 42", NULL, "DP1RX"},
+	{"FU 21", NULL, "DP3RX"},
+	{"FU 23", NULL, "DP3RX"},
+
+	{"ADC 09 Mux", "MIC2", "MIC2"},
+	{"ADC 09 Mux", "LINE1", "LINE1"},
+	{"ADC 09 Mux", "LINE2", "LINE2"},
+	{"ADC 07 R Mux", "DMIC1 RE", "DMIC1_2"},
+	{"ADC 07 R Mux", "DMIC1 FE", "DMIC1_2"},
+	{"ADC 07 R Mux", "DMIC2 RE", "DMIC3_4"},
+	{"ADC 07 R Mux", "DMIC2 FE", "DMIC3_4"},
+	{"ADC 07 L Mux", "DMIC1 RE", "DMIC1_2"},
+	{"ADC 07 L Mux", "DMIC1 FE", "DMIC1_2"},
+	{"ADC 07 L Mux", "DMIC2 RE", "DMIC3_4"},
+	{"ADC 07 L Mux", "DMIC2 FE", "DMIC3_4"},
+	{"ADC 08 R Mux", "DMIC1 RE", "DMIC1_2"},
+	{"ADC 08 R Mux", "DMIC1 FE", "DMIC1_2"},
+	{"ADC 08 R Mux", "DMIC2 RE", "DMIC3_4"},
+	{"ADC 08 R Mux", "DMIC2 FE", "DMIC3_4"},
+	{"ADC 08 L Mux", "DMIC1 RE", "DMIC1_2"},
+	{"ADC 08 L Mux", "DMIC1 FE", "DMIC1_2"},
+	{"ADC 08 L Mux", "DMIC2 RE", "DMIC3_4"},
+	{"ADC 08 L Mux", "DMIC2 FE", "DMIC3_4"},
+	{"ADC 10 R Mux", "DMIC1 RE", "DMIC1_2"},
+	{"ADC 10 R Mux", "DMIC1 FE", "DMIC1_2"},
+	{"ADC 10 R Mux", "DMIC2 RE", "DMIC3_4"},
+	{"ADC 10 R Mux", "DMIC2 FE", "DMIC3_4"},
+	{"ADC 10 L Mux", "DMIC1 RE", "DMIC1_2"},
+	{"ADC 10 L Mux", "DMIC1 FE", "DMIC1_2"},
+	{"ADC 10 L Mux", "DMIC2 RE", "DMIC3_4"},
+	{"ADC 10 L Mux", "DMIC2 FE", "DMIC3_4"},
+	{"FU 36", NULL, "PDE 34"},
+	{"FU 36", NULL, "ADC 09 Mux"},
+	{"FU 113", NULL, "PDE 11"},
+	{"FU 113", NULL, "ADC 07 R Mux"},
+	{"FU 113", NULL, "ADC 07 L Mux"},
+	{"FU 113", NULL, "ADC 10 R Mux"},
+	{"FU 113", NULL, "ADC 10 L Mux"},
+	{"DP2TX", NULL, "FU 36"},
+	{"DP6TX", NULL, "FU 113"},
+
+	{"HP", NULL, "PDE 47"},
+	{"HP", NULL, "FU 42"},
+	{"SPK", NULL, "PDE 41"},
+	{"SPK", NULL, "FU 21"},
+	{"SPK", NULL, "FU 23"},
+};
+
+static int rt721_sdca_parse_dt(struct rt721_sdca_priv *rt721, struct device *dev)
+{
+	device_property_read_u32(dev, "realtek,jd-src", &rt721->jd_src);
+
+	return 0;
+}
+
+static int rt721_sdca_probe(struct snd_soc_component *component)
+{
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	int ret;
+
+	rt721_sdca_parse_dt(rt721, &rt721->slave->dev);
+	rt721->component = component;
+
+	ret = pm_runtime_resume(component->dev);
+	if (ret < 0 && ret != -EACCES)
+		return ret;
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt721 = {
+	.probe = rt721_sdca_probe,
+	.controls = rt721_sdca_controls,
+	.num_controls = ARRAY_SIZE(rt721_sdca_controls),
+	.dapm_widgets = rt721_sdca_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(rt721_sdca_dapm_widgets),
+	.dapm_routes = rt721_sdca_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(rt721_sdca_audio_map),
+	.set_jack = rt721_sdca_set_jack_detect,
+	.endianness = 1,
+};
+
+static int rt721_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+				int direction)
+{
+	snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+	return 0;
+}
+
+static void rt721_sdca_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt721_sdca_pcm_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 rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct sdw_stream_config stream_config;
+	struct sdw_port_config port_config;
+	enum sdw_data_direction direction;
+	struct sdw_stream_runtime *sdw_stream;
+	int retval, port, num_channels;
+	unsigned int sampling_rate;
+
+	dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+	sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!sdw_stream)
+		return -EINVAL;
+
+	if (!rt721->slave)
+		return -EINVAL;
+
+	/*
+	 * RT721_AIF1 with port = 1 for headphone playback
+	 * RT721_AIF1 with port = 2 for headset-mic capture
+	 * RT721_AIF2 with port = 3 for speaker playback
+	 * RT721_AIF3 with port = 6 for digital-mic capture
+	 */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		direction = SDW_DATA_DIR_RX;
+		if (dai->id == RT721_AIF1)
+			port = 1;
+		else if (dai->id == RT721_AIF2)
+			port = 3;
+		else
+			return -EINVAL;
+	} else {
+		direction = SDW_DATA_DIR_TX;
+		if (dai->id == RT721_AIF1)
+			port = 2;
+		else if (dai->id == RT721_AIF3)
+			port = 6;
+		else
+			return -EINVAL;
+	}
+	stream_config.frame_rate = params_rate(params);
+	stream_config.ch_count = params_channels(params);
+	stream_config.bps = snd_pcm_format_width(params_format(params));
+	stream_config.direction = direction;
+
+	num_channels = params_channels(params);
+	port_config.ch_mask = GENMASK(num_channels - 1, 0);
+	port_config.num = port;
+
+	retval = sdw_stream_add_slave(rt721->slave, &stream_config,
+					&port_config, 1, sdw_stream);
+	if (retval) {
+		dev_err(dai->dev, "Unable to configure port\n");
+		return retval;
+	}
+
+	if (params_channels(params) > 16) {
+		dev_err(component->dev, "Unsupported channels %d\n",
+			params_channels(params));
+		return -EINVAL;
+	}
+
+	/* sampling rate configuration */
+	switch (params_rate(params)) {
+	case 8000:
+		sampling_rate = RT721_SDCA_RATE_8000HZ;
+		break;
+	case 16000:
+		sampling_rate = RT721_SDCA_RATE_16000HZ;
+		break;
+	case 24000:
+		sampling_rate = RT721_SDCA_RATE_24000HZ;
+		break;
+	case 32000:
+		sampling_rate = RT721_SDCA_RATE_32000HZ;
+		break;
+	case 44100:
+		sampling_rate = RT721_SDCA_RATE_44100HZ;
+		break;
+	case 48000:
+		sampling_rate = RT721_SDCA_RATE_48000HZ;
+		break;
+	case 96000:
+		sampling_rate = RT721_SDCA_RATE_96000HZ;
+		break;
+	case 192000:
+		sampling_rate = RT721_SDCA_RATE_192000HZ;
+		break;
+	case 384000:
+		sampling_rate = RT721_SDCA_RATE_384000HZ;
+		break;
+	case 768000:
+		sampling_rate = RT721_SDCA_RATE_768000HZ;
+		break;
+	default:
+		dev_err(component->dev, "Rate %d is not supported\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	/* set sampling frequency */
+	if (dai->id == RT721_AIF1) {
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS01,
+				RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_CS11,
+				RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+	}
+
+	if (dai->id == RT721_AIF2)
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_CS31,
+				RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+	if (dai->id == RT721_AIF3)
+		regmap_write(rt721->regmap,
+			SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_CS1F,
+				RT721_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate);
+
+	return 0;
+}
+
+static int rt721_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt721_sdca_priv *rt721 = snd_soc_component_get_drvdata(component);
+	struct sdw_stream_runtime *sdw_stream =
+		snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!rt721->slave)
+		return -EINVAL;
+
+	sdw_stream_remove_slave(rt721->slave, sdw_stream);
+	return 0;
+}
+
+#define RT721_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT721_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt721_sdca_ops = {
+	.hw_params	= rt721_sdca_pcm_hw_params,
+	.hw_free	= rt721_sdca_pcm_hw_free,
+	.set_stream	= rt721_sdca_set_sdw_stream,
+	.shutdown	= rt721_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt721_sdca_dai[] = {
+	{
+		.name = "rt721-sdca-aif1",
+		.id = RT721_AIF1,
+		.playback = {
+			.stream_name = "DP1 Headphone Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT721_STEREO_RATES,
+			.formats = RT721_FORMATS,
+		},
+		.capture = {
+			.stream_name = "DP2 Headset Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT721_STEREO_RATES,
+			.formats = RT721_FORMATS,
+		},
+		.ops = &rt721_sdca_ops,
+	},
+	{
+		.name = "rt721-sdca-aif2",
+		.id = RT721_AIF2,
+		.playback = {
+			.stream_name = "DP3 Speaker Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT721_STEREO_RATES,
+			.formats = RT721_FORMATS,
+		},
+		.ops = &rt721_sdca_ops,
+	},
+	{
+		.name = "rt721-sdca-aif3",
+		.id = RT721_AIF3,
+		.capture = {
+			.stream_name = "DP6 DMic Capture",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = RT721_STEREO_RATES,
+			.formats = RT721_FORMATS,
+		},
+		.ops = &rt721_sdca_ops,
+	}
+};
+
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+			struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+	struct rt721_sdca_priv *rt721;
+
+	rt721 = devm_kzalloc(dev, sizeof(*rt721), GFP_KERNEL);
+	if (!rt721)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, rt721);
+	rt721->slave = slave;
+	rt721->regmap = regmap;
+	rt721->mbq_regmap = mbq_regmap;
+
+	regcache_cache_only(rt721->regmap, true);
+	regcache_cache_only(rt721->mbq_regmap, true);
+
+	mutex_init(&rt721->calibrate_mutex);
+	mutex_init(&rt721->disable_irq_lock);
+
+	INIT_DELAYED_WORK(&rt721->jack_detect_work, rt721_sdca_jack_detect_handler);
+	INIT_DELAYED_WORK(&rt721->jack_btn_check_work, rt721_sdca_btn_check_handler);
+
+	/*
+	 * Mark hw_init to false
+	 * HW init will be performed when device reports present
+	 */
+	rt721->hw_init = false;
+	rt721->first_hw_init = false;
+	rt721->fu1e_dapm_mute = true;
+	rt721->fu0f_dapm_mute = true;
+	rt721->fu0f_mixer_l_mute = rt721->fu0f_mixer_r_mute = true;
+	rt721->fu1e_mixer_mute[0] = rt721->fu1e_mixer_mute[1] =
+		rt721->fu1e_mixer_mute[2] = rt721->fu1e_mixer_mute[3] = true;
+
+	return devm_snd_soc_register_component(dev,
+			&soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
+}
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+	struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+
+	rt721->disable_irq = false;
+
+	if (rt721->hw_init)
+		return 0;
+
+	regcache_cache_only(rt721->regmap, false);
+	regcache_cache_only(rt721->mbq_regmap, false);
+	if (rt721->first_hw_init) {
+		regcache_cache_bypass(rt721->regmap, true);
+		regcache_cache_bypass(rt721->mbq_regmap, true);
+	} else {
+		/*
+		 * PM runtime is only enabled when a Slave reports as Attached
+		 */
+
+		/* set autosuspend parameters */
+		pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
+		pm_runtime_use_autosuspend(&slave->dev);
+
+		/* update count of parent 'active' children */
+		pm_runtime_set_active(&slave->dev);
+
+		/* make sure the device does not suspend immediately */
+		pm_runtime_mark_last_busy(&slave->dev);
+
+		pm_runtime_enable(&slave->dev);
+	}
+
+	pm_runtime_get_noresume(&slave->dev);
+	rt721_sdca_dmic_preset(rt721);
+	rt721_sdca_amp_preset(rt721);
+	rt721_sdca_jack_preset(rt721);
+	if (rt721->first_hw_init) {
+		regcache_cache_bypass(rt721->regmap, false);
+		regcache_mark_dirty(rt721->regmap);
+		regcache_cache_bypass(rt721->mbq_regmap, false);
+		regcache_mark_dirty(rt721->mbq_regmap);
+	} else
+		rt721->first_hw_init = true;
+
+	/* Mark Slave initialization complete */
+	rt721->hw_init = true;
+
+	pm_runtime_mark_last_busy(&slave->dev);
+	pm_runtime_put_autosuspend(&slave->dev);
+
+	dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
+	return 0;
+}
+
+MODULE_DESCRIPTION("ASoC RT721 SDCA SDW driver");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h
new file mode 100644
index 000000000000..e2f071909da8
--- /dev/null
+++ b/sound/soc/codecs/rt721-sdca.h
@@ -0,0 +1,268 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt721-sdca.h -- RT721 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2024 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT721_H__
+#define __RT721_H__
+
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+
+struct  rt721_sdca_priv {
+	struct regmap *regmap;
+	struct regmap *mbq_regmap;
+	struct snd_soc_component *component;
+	struct sdw_slave *slave;
+	struct sdw_bus_params params;
+	bool hw_init;
+	bool first_hw_init;
+	struct mutex calibrate_mutex;
+	struct mutex disable_irq_lock;
+	bool disable_irq;
+	/* For Headset jack & Headphone */
+	unsigned int scp_sdca_stat1;
+	unsigned int scp_sdca_stat2;
+	struct snd_soc_jack *hs_jack;
+	struct delayed_work jack_detect_work;
+	struct delayed_work jack_btn_check_work;
+	int jack_type;
+	int jd_src;
+	bool fu0f_dapm_mute;
+	bool fu0f_mixer_l_mute;
+	bool fu0f_mixer_r_mute;
+	/* For DMIC */
+	bool fu1e_dapm_mute;
+	bool fu1e_mixer_mute[4];
+};
+
+struct rt721_sdca_dmic_kctrl_priv {
+	unsigned int reg_base;
+	unsigned int count;
+	unsigned int max;
+	unsigned int invert;
+};
+
+/* NID */
+#define RT721_ANA_POW_PART			0x01
+#define RT721_DAC_CTRL				0x04
+#define RT721_JD_CTRL				0x09
+#define RT721_CBJ_CTRL				0x0a
+#define RT721_CAP_PORT_CTRL			0x0c
+#define RT721_CLASD_AMP_CTRL			0x0d
+#define RT721_VENDOR_REG			0x20
+#define RT721_RC_CALIB_CTRL			0x40
+#define RT721_VENDOR_EQ_L			0x53
+#define RT721_VENDOR_EQ_R			0x54
+#define RT721_VENDOR_HP_CALI			0x56
+#define RT721_VENDOR_CHARGE_PUMP		0x57
+#define RT721_VENDOR_CLASD_CALI			0x58
+#define RT721_VENDOR_IMS_DRE			0x5b
+#define RT721_VENDOR_SPK_EFUSE			0x5c
+#define RT721_VENDOR_LEVEL_CTRL			0x5d
+#define RT721_VENDOR_ANA_CTL			0x5f
+#define RT721_HDA_SDCA_FLOAT			0x61
+
+/* Index (NID:01h) */
+#define RT721_MBIAS_LV_CTRL2			0x07
+#define RT721_VREF1_HV_CTRL1			0x0a
+#define RT721_VREF2_LV_CTRL1			0x0b
+
+/* Index (NID:04h) */
+#define RT721_DAC_2CH_CTRL3			0x02
+#define RT721_DAC_2CH_CTRL4			0x03
+
+/* Index (NID:09h) */
+#define RT721_JD_1PIN_GAT_CTRL2			0x07
+
+/* Index (NID:0ah) */
+#define RT721_CBJ_A0_GAT_CTRL1			0x04
+#define RT721_CBJ_A0_GAT_CTRL2			0x05
+
+/* Index (NID:0Ch) */
+#define RT721_HP_AMP_2CH_CAL1			0x05
+#define RT721_HP_AMP_2CH_CAL4			0x08
+#define RT721_HP_AMP_2CH_CAL18			0x1b
+
+/* Index (NID:0dh) */
+#define RT721_CLASD_AMP_2CH_CAL			0x14
+
+/* Index (NID:20h) */
+#define RT721_JD_PRODUCT_NUM			0x00
+#define RT721_ANALOG_BIAS_CTL3			0x04
+#define RT721_JD_CTRL1				0x09
+#define RT721_LDO2_3_CTL1			0x0e
+#define RT721_GPIO_PAD_CTRL5			0x13
+#define RT721_LDO1_CTL				0x1a
+#define RT721_HP_JD_CTRL			0x24
+#define RT721_VD_HIDDEN_CTRL			0x26
+#define RT721_CLSD_CTRL6			0x3c
+#define RT721_COMBO_JACK_AUTO_CTL1		0x45
+#define RT721_COMBO_JACK_AUTO_CTL2		0x46
+#define RT721_COMBO_JACK_AUTO_CTL3		0x47
+#define RT721_DIGITAL_MISC_CTRL4		0x4a
+#define RT721_VREFO_GAT				0x63
+#define RT721_FSM_CTL				0x67
+#define RT721_SDCA_INTR_REC			0x82
+#define RT721_SW_CONFIG1			0x8a
+#define RT721_SW_CONFIG2			0x8b
+
+/* Index (NID:40h) */
+#define RT721_RC_CALIB_CTRL0			0x00
+
+/* Index (NID:58h) */
+#define RT721_DAC_DC_CALI_CTL1			0x01
+#define RT721_DAC_DC_CALI_CTL2			0x02
+#define RT721_DAC_DC_CALI_CTL3			0x03
+
+/* Index (NID:5fh) */
+#define RT721_MISC_POWER_CTL0			0x00
+#define RT721_MISC_POWER_CTL31			0x31
+#define RT721_UAJ_TOP_TCON13			0x44
+#define RT721_UAJ_TOP_TCON14			0x45
+#define RT721_UAJ_TOP_TCON17			0x48
+
+/* Index (NID:61h) */
+#define RT721_HDA_LEGACY_MUX_CTL0		0x00
+#define RT721_HDA_LEGACY_UAJ_CTL		0x02
+#define RT721_HDA_LEGACY_CTL1			0x05
+#define RT721_HDA_LEGACY_RESET_CTL		0x06
+#define RT721_GE_REL_CTRL1			0x0d
+#define RT721_HDA_LEGACY_GPIO_WAKE_EN_CTL	0x0e
+#define RT721_GE_SDCA_RST_CTRL			0x10
+#define RT721_INT_RST_EN_CTRL			0x11
+#define RT721_XU_EVENT_EN			0x13
+#define RT721_INLINE_CTL2			0x17
+#define RT721_UMP_HID_CTRL1			0x18
+#define RT721_UMP_HID_CTRL2			0x19
+#define RT721_UMP_HID_CTRL3			0x1a
+#define RT721_UMP_HID_CTRL4			0x1b
+#define RT721_UMP_HID_CTRL5			0x1c
+#define RT721_FUNC_FLOAT_CTL0			0x22
+#define RT721_FUNC_FLOAT_CTL1			0x23
+#define RT721_FUNC_FLOAT_CTL2			0x24
+#define RT721_FUNC_FLOAT_CTL3			0x25
+#define RT721_ENT_FLOAT_CTL0			0x29
+#define RT721_ENT_FLOAT_CTL1			0x2c
+#define RT721_ENT_FLOAT_CTL2			0x2d
+#define RT721_ENT_FLOAT_CTL3			0x2e
+#define RT721_ENT_FLOAT_CTL4			0x2f
+#define RT721_CH_FLOAT_CTL1			0x45
+#define RT721_CH_FLOAT_CTL2			0x46
+#define RT721_ENT_FLOAT_CTL5			0x53
+#define RT721_ENT_FLOAT_CTL6			0x54
+#define RT721_ENT_FLOAT_CTL7			0x55
+#define RT721_ENT_FLOAT_CTL8			0x57
+#define RT721_ENT_FLOAT_CTL9			0x5a
+#define RT721_ENT_FLOAT_CTL10			0x5b
+#define RT721_CH_FLOAT_CTL3			0x6a
+#define RT721_CH_FLOAT_CTL4			0x6d
+#define RT721_CH_FLOAT_CTL5			0x70
+#define RT721_CH_FLOAT_CTL6			0x92
+
+/* Parameter & Verb control 01 (0x26)(NID:20h) */
+#define RT721_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* Buffer address for HID */
+#define RT721_BUF_ADDR_HID1			0x44030000
+#define RT721_BUF_ADDR_HID2			0x44030020
+
+/* RT721 SDCA Control - function number */
+#define FUNC_NUM_JACK_CODEC			0x01
+#define FUNC_NUM_MIC_ARRAY			0x02
+#define FUNC_NUM_HID				0x03
+#define FUNC_NUM_AMP				0x04
+
+/* RT721 SDCA entity */
+#define RT721_SDCA_ENT_HID01			0x01
+#define RT721_SDCA_ENT_XUV			0x03
+#define RT721_SDCA_ENT_GE49			0x49
+#define RT721_SDCA_ENT_USER_FU05		0x05
+#define RT721_SDCA_ENT_USER_FU06		0x06
+#define RT721_SDCA_ENT_USER_FU0F		0x0f
+#define RT721_SDCA_ENT_USER_FU10		0x19
+#define RT721_SDCA_ENT_USER_FU1E		0x1e
+#define RT721_SDCA_ENT_FU15			0x15
+#define RT721_SDCA_ENT_PDE23			0x23
+#define RT721_SDCA_ENT_PDE40			0x40
+#define RT721_SDCA_ENT_PDE41			0x41
+#define RT721_SDCA_ENT_PDE11			0x11
+#define RT721_SDCA_ENT_PDE12			0x12
+#define RT721_SDCA_ENT_PDE2A			0x2a
+#define RT721_SDCA_ENT_CS01			0x01
+#define RT721_SDCA_ENT_CS11			0x11
+#define RT721_SDCA_ENT_CS1F			0x1f
+#define RT721_SDCA_ENT_CS1C			0x1c
+#define RT721_SDCA_ENT_CS31			0x31
+#define RT721_SDCA_ENT_OT23			0x42
+#define RT721_SDCA_ENT_IT26			0x26
+#define RT721_SDCA_ENT_IT09			0x09
+#define RT721_SDCA_ENT_PLATFORM_FU15		0x15
+#define RT721_SDCA_ENT_PLATFORM_FU44		0x44
+#define RT721_SDCA_ENT_XU03			0x03
+#define RT721_SDCA_ENT_XU0D			0x0d
+#define RT721_SDCA_ENT_FU55			0x55
+
+/* RT721 SDCA control */
+#define RT721_SDCA_CTL_SAMPLE_FREQ_INDEX		0x10
+#define RT721_SDCA_CTL_FU_MUTE				0x01
+#define RT721_SDCA_CTL_FU_VOLUME			0x02
+#define RT721_SDCA_CTL_HIDTX_CURRENT_OWNER		0x10
+#define RT721_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE	0x11
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_OFFSET		0x12
+#define RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH		0x13
+#define RT721_SDCA_CTL_SELECTED_MODE			0x01
+#define RT721_SDCA_CTL_DETECTED_MODE			0x02
+#define RT721_SDCA_CTL_REQ_POWER_STATE			0x01
+#define RT721_SDCA_CTL_VENDOR_DEF			0x30
+#define RT721_SDCA_CTL_XUV				0x34
+#define RT721_SDCA_CTL_FU_CH_GAIN			0x0b
+
+/* RT721 SDCA channel */
+#define CH_L	0x01
+#define CH_R	0x02
+#define CH_01	0x01
+#define CH_02	0x02
+#define CH_03	0x03
+#define CH_04	0x04
+#define CH_08	0x08
+#define CH_09	0x09
+#define CH_0A	0x0a
+
+/* sample frequency index */
+#define RT721_SDCA_RATE_8000HZ		0x01
+#define RT721_SDCA_RATE_11025HZ		0x02
+#define RT721_SDCA_RATE_12000HZ		0x03
+#define RT721_SDCA_RATE_16000HZ		0x04
+#define RT721_SDCA_RATE_22050HZ		0x05
+#define RT721_SDCA_RATE_24000HZ		0x06
+#define RT721_SDCA_RATE_32000HZ		0x07
+#define RT721_SDCA_RATE_44100HZ		0x08
+#define RT721_SDCA_RATE_48000HZ		0x09
+#define RT721_SDCA_RATE_88200HZ		0x0a
+#define RT721_SDCA_RATE_96000HZ		0x0b
+#define RT721_SDCA_RATE_176400HZ	0x0c
+#define RT721_SDCA_RATE_192000HZ	0x0d
+#define RT721_SDCA_RATE_384000HZ	0x0e
+#define RT721_SDCA_RATE_768000HZ	0x0f
+
+/* RT721 HID ID */
+#define RT721_SDCA_HID_ID		0x11
+
+enum {
+	RT721_AIF1, /* For headset mic and headphone */
+	RT721_AIF2, /* For speaker */
+	RT721_AIF3, /* For dmic */
+	RT721_AIFS,
+};
+
+int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt721_sdca_init(struct device *dev, struct regmap *regmap,
+			struct regmap *mbq_regmap, struct sdw_slave *slave);
+#endif /* __RT721_H__ */