linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
@ 2020-11-30 14:40 Bard Liao
  2020-11-30 14:40 ` [PATCH v2 1/5] soundwire: SDCA: add helper macro to access controls Bard Liao
                   ` (6 more replies)
  0 siblings, 7 replies; 16+ messages in thread
From: Bard Liao @ 2020-11-30 14:40 UTC (permalink / raw)
  To: alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

The MIPI SoundWire Device Class standard will define audio functionality
beyond the scope of the existing SoundWire 1.2 standard, which is limited
to the bus and interface.

The description is inspired by the USB Audio Class, with "functions",
"entities", "control selectors", "audio clusters". The main difference
with the USB Audio class is that the devices are typically on a motherboard
and descriptors stored in platform firmware instead of being retrieved
from the device.

The current set of devices managed in this patchset are conformant with the
SDCA 0.6 specification and require dedicated drivers since the descriptors
and platform firmware specification is not complete at this time. They do
however rely on the hierarchical addressing required by the SDCA standard.
Future devices conformant with SDCA 1.0 should rely on a class driver.

This series adds support for the hierarchical SDCA addressing and extends
regmap. It then provides 3 codecs for RT711-sdca headset codec, RT1316
amplifier and RT715-scda microphone codec.

Note that the release of this code before the formal adoption of the
SDCA 1.0 specification was formally endorsed by the MIPI Board to make
sure there is no delay for Linux-based support of this specification.

v2:
- rt715-sdca: Use rt715_sdca prefix to avoid compiling issue.
- rt715-sdca: Merge multiple mute/volume operation into single mute/volume
  operation
- rt711-sdca: Initial ret = 0 as it could be used uninitialized.

Jack Yu (1):
  ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec
    driver

Pierre-Louis Bossart (2):
  soundwire: SDCA: add helper macro to access controls
  regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ

Shuming Fan (2):
  ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver
  ASoC/SoundWire: rt711-sdca: Add RT711 SDCA vendor-specific driver

 drivers/base/regmap/Kconfig             |    6 +-
 drivers/base/regmap/Makefile            |    1 +
 drivers/base/regmap/regmap-sdw-mbq.c    |  101 ++
 include/linux/regmap.h                  |   35 +
 include/linux/soundwire/sdw_registers.h |   32 +
 sound/soc/codecs/Kconfig                |   20 +
 sound/soc/codecs/Makefile               |    6 +
 sound/soc/codecs/rt1316-sdw.c           |  756 ++++++++++++
 sound/soc/codecs/rt1316-sdw.h           |  115 ++
 sound/soc/codecs/rt711-sdca-sdw.c       |  424 +++++++
 sound/soc/codecs/rt711-sdca-sdw.h       |  101 ++
 sound/soc/codecs/rt711-sdca.c           | 1482 +++++++++++++++++++++++
 sound/soc/codecs/rt711-sdca.h           |  246 ++++
 sound/soc/codecs/rt715-sdca-sdw.c       |  278 +++++
 sound/soc/codecs/rt715-sdca-sdw.h       |  170 +++
 sound/soc/codecs/rt715-sdca.c           |  947 +++++++++++++++
 sound/soc/codecs/rt715-sdca.h           |  124 ++
 17 files changed, 4843 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/regmap/regmap-sdw-mbq.c
 create mode 100644 sound/soc/codecs/rt1316-sdw.c
 create mode 100644 sound/soc/codecs/rt1316-sdw.h
 create mode 100644 sound/soc/codecs/rt711-sdca-sdw.c
 create mode 100644 sound/soc/codecs/rt711-sdca-sdw.h
 create mode 100644 sound/soc/codecs/rt711-sdca.c
 create mode 100644 sound/soc/codecs/rt711-sdca.h
 create mode 100644 sound/soc/codecs/rt715-sdca-sdw.c
 create mode 100644 sound/soc/codecs/rt715-sdca-sdw.h
 create mode 100644 sound/soc/codecs/rt715-sdca.c
 create mode 100644 sound/soc/codecs/rt715-sdca.h

-- 
2.17.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v2 1/5] soundwire: SDCA: add helper macro to access controls
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
@ 2020-11-30 14:40 ` Bard Liao
  2020-11-30 14:40 ` [PATCH v2 2/5] regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ Bard Liao
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Bard Liao @ 2020-11-30 14:40 UTC (permalink / raw)
  To: alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>

The upcoming SDCA (SoundWire Device Class Audio) specification defines
a hierarchical encoding to interface with Class-defined capabilities.

The specification is not yet accessible to the general public but this
information is released with explicit permission from the MIPI Board
to avoid delays with SDCA support on Linux platforms.

A block of 64 MBytes of register addresses are allocated to SDCA
controls, starting at address 0x40000000. The 26 LSBs which identify
individual controls are set based on the following variables:

- Function Number. An SCDA device can be split in up to 8 independent
  Functions. Each of these Functions is described in the SDCA
  specification, e.g. Smart Amplifier, Smart Microphone, Simple
  Microphone, Jack codec, HID, etc.

- Entity Number.  Within each Function,  an Entity is  an identifiable
  block.  Up   to  127  Entities   are  connected  in   a  pre-defined
  graph  (similar to  USB), with  Entity0 reserved  for Function-level
  configurations.  In  contrast  to  USB, the  SDCA  spec  pre-defines
  Function Types, topologies, and allowed  options, i.e. the degree of
  freedom  is not  unlimited to  limit  the possibility  of errors  in
  descriptors leading to software quirks.

- Control Selector. Within each Entity, the SDCA specification defines
  48 controls such as Mute, Gain, AGC, etc, and 16 implementation
  defined ones. Some Control Selectors might be used for low-level
  platform setup, and other exposed to applications and users. Note
  that the same Control Selector capability, e.g. Latency control,
  might be located at different offsets in different entities, the
  Control Selector mapping is Entity-specific.

- Control Number. Some Control Selectors allow channel-specific values
  to be set, with up to 64 channels allowed. This is mostly used for
  volume control.

- Current/Next values. Some Control Selectors are
  'Dual-Ranked'. Software may either update the Current value directly
  for immediate effect. Alternatively, software may write into the
  'Next' values and update the SoundWire 1.2 'Commit Groups' register
  to copy 'Next' values into 'Current' ones in a synchronized
  manner. This is different from bank switching which is typically
  used to change the bus configuration only.

- MBQ. the Multi-Byte Quantity bit is used to provide atomic updates
  when accessing more that one byte, for example a 16-bit volume
  control would be updated consistently, the intermediate values
  mixing old MSB with new LSB are not applied.

These 6 parameters are used to build a 32-bit address to access the
desired Controls. Because of address range, paging is required, but
the most often used parameter values are placed in the lower 16 bits
of the address. This helps to keep the paging registers constant while
updating Controls for a specific Device/Function.

Reviewed-by: Rander Wang <rander.wang@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
---
 include/linux/soundwire/sdw_registers.h | 32 +++++++++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/include/linux/soundwire/sdw_registers.h b/include/linux/soundwire/sdw_registers.h
index 0cb1a22685b8..138bec908c40 100644
--- a/include/linux/soundwire/sdw_registers.h
+++ b/include/linux/soundwire/sdw_registers.h
@@ -309,4 +309,36 @@
 #define SDW_CASC_PORT_MASK_INTSTAT3		1
 #define SDW_CASC_PORT_REG_OFFSET_INTSTAT3	2
 
+/*
+ * v1.2 device - SDCA address mapping
+ *
+ * Spec definition
+ *	Bits		Contents
+ *	31		0 (required by addressing range)
+ *	30:26		0b10000 (Control Prefix)
+ *	25		0 (Reserved)
+ *	24:22		Function Number [2:0]
+ *	21		Entity[6]
+ *	20:19		Control Selector[5:4]
+ *	18		0 (Reserved)
+ *	17:15		Control Number[5:3]
+ *	14		Next
+ *	13		MBQ
+ *	12:7		Entity[5:0]
+ *	6:3		Control Selector[3:0]
+ *	2:0		Control Number[2:0]
+ */
+
+#define SDW_SDCA_CTL(fun, ent, ctl, ch)		(BIT(30) |			\
+						 (((fun) & 0x7) << 22) |	\
+						 (((ent) & 0x40) << 15) |	\
+						 (((ent) & 0x3f) << 7) |	\
+						 (((ctl) & 0x30) << 15) |	\
+						 (((ctl) & 0x0f) << 3) |	\
+						 (((ch) & 0x38) << 12) |	\
+						 ((ch) & 0x07))
+
+#define SDW_SDCA_MBQ_CTL(reg)			((reg) | BIT(13))
+#define SDW_SDCA_NEXT_CTL(reg)			((reg) | BIT(14))
+
 #endif /* __SDW_REGISTERS_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 2/5] regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
  2020-11-30 14:40 ` [PATCH v2 1/5] soundwire: SDCA: add helper macro to access controls Bard Liao
@ 2020-11-30 14:40 ` Bard Liao
  2020-12-01 11:54   ` Mark Brown
  2020-11-30 14:40 ` [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver Bard Liao
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Bard Liao @ 2020-11-30 14:40 UTC (permalink / raw)
  To: alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>

The SoundWire 1.1 specification only allowed for reads and writes of
bytes. The SoundWire 1.2 specification adds a new capability to
transfer "Multi-Byte Quantities" (MBQ) across the bus. The transfers
still happens one-byte-at-a-time, but the update is atomic.

For example when writing a 16-bit volume, the first byte transferred
is only taken into account when the second byte is successfully
transferred.

The mechanism is symmetrical for read and writes:
- On a read, the address of the last byte to be read is modified by
setting the MBQ bit
- On a write, the address of all but the last byte to be written are
modified by setting the MBQ bit. The address for the last byte relies
on the MBQ bit being cleared.

The current definitions for MBQ-based controls in the SDCA draft
standard are limited to 16 bits for volumes, so for now this is the
only supported format. An update will be provided if and when support
for 24-bit and 32-bit values is specified by the SDCA standard.

One possible objection is that this code could have been handled with
regmap-sdw.c. However this is a new spec addition not handled by every
SoundWire 1.1 and non-SDCA device, so there's no reason to load code
that will never be used.

Also in practice it's extremely unlikely that CONFIG_REGMAP would not
be selected with CONFIG_REGMAP_MBQ selected. However there's no
functional dependency between the two modules so they can be selected
separately.

Reviewed-by: Rander Wang <rander.wang@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
---
 drivers/base/regmap/Kconfig          |   6 +-
 drivers/base/regmap/Makefile         |   1 +
 drivers/base/regmap/regmap-sdw-mbq.c | 101 +++++++++++++++++++++++++++
 include/linux/regmap.h               |  35 ++++++++++
 4 files changed, 142 insertions(+), 1 deletion(-)
 create mode 100644 drivers/base/regmap/regmap-sdw-mbq.c

diff --git a/drivers/base/regmap/Kconfig b/drivers/base/regmap/Kconfig
index bcb90d8c3960..50b1e2d06a25 100644
--- a/drivers/base/regmap/Kconfig
+++ b/drivers/base/regmap/Kconfig
@@ -4,7 +4,7 @@
 # subsystems should select the appropriate symbols.
 
 config REGMAP
-	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM)
+	default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ || REGMAP_SOUNDWIRE || REGMAP_SOUNDWIRE_MBQ || REGMAP_SCCB || REGMAP_I3C || REGMAP_SPI_AVMM)
 	select IRQ_DOMAIN if REGMAP_IRQ
 	bool
 
@@ -46,6 +46,10 @@ config REGMAP_SOUNDWIRE
 	tristate
 	depends on SOUNDWIRE
 
+config REGMAP_SOUNDWIRE_MBQ
+	tristate
+	depends on SOUNDWIRE
+
 config REGMAP_SCCB
 	tristate
 	depends on I2C
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile
index ac1b69ee4051..33f63adb5b3d 100644
--- a/drivers/base/regmap/Makefile
+++ b/drivers/base/regmap/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
 obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
 obj-$(CONFIG_REGMAP_W1) += regmap-w1.o
 obj-$(CONFIG_REGMAP_SOUNDWIRE) += regmap-sdw.o
+obj-$(CONFIG_REGMAP_SOUNDWIRE_MBQ) += regmap-sdw-mbq.o
 obj-$(CONFIG_REGMAP_SCCB) += regmap-sccb.o
 obj-$(CONFIG_REGMAP_I3C) += regmap-i3c.o
 obj-$(CONFIG_REGMAP_SPI_AVMM) += regmap-spi-avmm.o
diff --git a/drivers/base/regmap/regmap-sdw-mbq.c b/drivers/base/regmap/regmap-sdw-mbq.c
new file mode 100644
index 000000000000..8ce30650b97c
--- /dev/null
+++ b/drivers/base/regmap/regmap-sdw-mbq.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright(c) 2020 Intel Corporation.
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include "internal.h"
+
+static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
+{
+	struct device *dev = context;
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	int ret;
+
+	ret = sdw_write(slave, SDW_SDCA_MBQ_CTL(reg), (val >> 8) & 0xff);
+	if (ret < 0)
+		return ret;
+
+	return sdw_write(slave, reg, val & 0xff);
+}
+
+static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
+{
+	struct device *dev = context;
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	int read0;
+	int read1;
+
+	read0 = sdw_read(slave, reg);
+	if (read0 < 0)
+		return read0;
+
+	read1 = sdw_read(slave, SDW_SDCA_MBQ_CTL(reg));
+	if (read1 < 0)
+		return read1;
+
+	*val = (read1 << 8) | read0;
+
+	return 0;
+}
+
+static struct regmap_bus regmap_sdw_mbq = {
+	.reg_read = regmap_sdw_mbq_read,
+	.reg_write = regmap_sdw_mbq_write,
+	.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
+{
+	/* MBQ-based controls are only 16-bits for now */
+	if (config->val_bits != 16)
+		return -ENOTSUPP;
+
+	/* Registers are 32 bits wide */
+	if (config->reg_bits != 32)
+		return -ENOTSUPP;
+
+	if (config->pad_bits != 0)
+		return -ENOTSUPP;
+
+	return 0;
+}
+
+struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
+				     const struct regmap_config *config,
+				     struct lock_class_key *lock_key,
+				     const char *lock_name)
+{
+	int ret;
+
+	ret = regmap_sdw_mbq_config_check(config);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return __regmap_init(&sdw->dev, &regmap_sdw_mbq,
+			&sdw->dev, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);
+
+struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
+					  const struct regmap_config *config,
+					  struct lock_class_key *lock_key,
+					  const char *lock_name)
+{
+	int ret;
+
+	ret = regmap_sdw_mbq_config_check(config);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq,
+			&sdw->dev, config, lock_key, lock_name);
+}
+EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);
+
+MODULE_DESCRIPTION("Regmap SoundWire MBQ Module");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index e7834d98207f..a652d1474d6a 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -570,6 +570,10 @@ struct regmap *__regmap_init_sdw(struct sdw_slave *sdw,
 				 const struct regmap_config *config,
 				 struct lock_class_key *lock_key,
 				 const char *lock_name);
+struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
+				     const struct regmap_config *config,
+				     struct lock_class_key *lock_key,
+				     const char *lock_name);
 struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
 				      const struct regmap_config *config,
 				      struct lock_class_key *lock_key,
@@ -619,6 +623,10 @@ struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
 				 const struct regmap_config *config,
 				 struct lock_class_key *lock_key,
 				 const char *lock_name);
+struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
+					  const struct regmap_config *config,
+					  struct lock_class_key *lock_key,
+					  const char *lock_name);
 struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
 				 const struct regmap_config *config,
 				 struct lock_class_key *lock_key,
@@ -817,6 +825,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 	__regmap_lockdep_wrapper(__regmap_init_sdw, #config,		\
 				sdw, config)
 
+/**
+ * regmap_init_sdw_mbq() - Initialise register map
+ *
+ * @sdw: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+#define regmap_init_sdw_mbq(sdw, config)					\
+	__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config,		\
+				sdw, config)
+
 /**
  * regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave
  * to AVMM Bus Bridge
@@ -989,6 +1010,20 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 	__regmap_lockdep_wrapper(__devm_regmap_init_sdw, #config,	\
 				sdw, config)
 
+/**
+ * devm_regmap_init_sdw_mbq() - Initialise managed register map
+ *
+ * @sdw: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap. The regmap will be automatically freed by the
+ * device management code.
+ */
+#define devm_regmap_init_sdw_mbq(sdw, config)			\
+	__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, #config,   \
+				sdw, config)
+
 /**
  * devm_regmap_init_slimbus() - Initialise managed register map
  *
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
  2020-11-30 14:40 ` [PATCH v2 1/5] soundwire: SDCA: add helper macro to access controls Bard Liao
  2020-11-30 14:40 ` [PATCH v2 2/5] regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ Bard Liao
@ 2020-11-30 14:40 ` Bard Liao
  2020-12-02 16:07   ` Mark Brown
  2020-11-30 14:40 ` [PATCH v2 4/5] ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver Bard Liao
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 16+ messages in thread
From: Bard Liao @ 2020-11-30 14:40 UTC (permalink / raw)
  To: alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

From: Jack Yu <jack.yu@realtek.com>

First version of rt715 sdw sdca codec driver.

Signed-off-by: Jack Yu <jack.yu@realtek.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
---
 sound/soc/codecs/Kconfig          |   7 +
 sound/soc/codecs/Makefile         |   2 +
 sound/soc/codecs/rt715-sdca-sdw.c | 278 +++++++++
 sound/soc/codecs/rt715-sdca-sdw.h | 170 ++++++
 sound/soc/codecs/rt715-sdca.c     | 947 ++++++++++++++++++++++++++++++
 sound/soc/codecs/rt715-sdca.h     | 124 ++++
 6 files changed, 1528 insertions(+)
 create mode 100644 sound/soc/codecs/rt715-sdca-sdw.c
 create mode 100644 sound/soc/codecs/rt715-sdca-sdw.h
 create mode 100644 sound/soc/codecs/rt715-sdca.c
 create mode 100644 sound/soc/codecs/rt715-sdca.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 34c6dd04b85a..e7797f08e057 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -177,6 +177,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_RT700_SDW
 	imply SND_SOC_RT711_SDW
 	imply SND_SOC_RT715_SDW
+	imply SND_SOC_RT715_SDCA_SDW
 	imply SND_SOC_RT1308_SDW
 	imply SND_SOC_SGTL5000
 	imply SND_SOC_SI476X
@@ -1216,6 +1217,12 @@ config SND_SOC_RT715_SDW
 	select SND_SOC_RT715
 	select REGMAP_SOUNDWIRE
 
+config SND_SOC_RT715_SDCA_SDW
+	tristate "Realtek RT715 SDCA Codec - SDW"
+	depends on SOUNDWIRE
+	select REGMAP_SOUNDWIRE
+	select REGMAP_SOUNDWIRE_MBQ
+
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
 	tristate "Freescale SGTL5000 CODEC"
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 11ce98c25d6c..b1683403afb3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -188,6 +188,7 @@ snd-soc-rt5682-i2c-objs := rt5682-i2c.o
 snd-soc-rt700-objs := rt700.o rt700-sdw.o
 snd-soc-rt711-objs := rt711.o rt711-sdw.o
 snd-soc-rt715-objs := rt715.o rt715-sdw.o
+snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
 snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
@@ -498,6 +499,7 @@ obj-$(CONFIG_SND_SOC_RT5682_SDW)	+= snd-soc-rt5682-sdw.o
 obj-$(CONFIG_SND_SOC_RT700)     += snd-soc-rt700.o
 obj-$(CONFIG_SND_SOC_RT711)     += snd-soc-rt711.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_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SIGMADSP)	+= snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SIGMADSP_I2C)	+= snd-soc-sigmadsp-i2c.o
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
new file mode 100644
index 000000000000..e73a826ee8e3
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca-sdw.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "rt715-sdca.h"
+#include "rt715-sdca-sdw.h"
+
+static bool rt715_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x201a ... 0x2027:
+	case 0x2029 ... 0x202a:
+	case 0x202d ... 0x2034:
+	case 0x2200 ... 0x2204:
+	case 0x2206 ... 0x2212:
+	case 0x2230 ... 0x2239:
+	case 0x2f5b:
+	case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+		RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00):
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt715_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x201b:
+	case 0x201c:
+	case 0x201d:
+	case 0x201f:
+	case 0x2021:
+	case 0x2023:
+	case 0x2230:
+	case 0x202d ... 0x202f: /* BRA */
+	case 0x2200 ... 0x2212: /* i2c debug */
+	case 0x2f07:
+	case 0x2f1b ... 0x2f1e:
+	case 0x2f30 ... 0x2f34:
+	case 0x2f50 ... 0x2f51:
+	case 0x2f53 ... 0x2f59:
+	case 0x2f5c ... 0x2f5f:
+	case SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+		RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00): /* VAD Searching status */
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt715_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2000000:
+	case 0x200002b:
+	case 0x2000036:
+	case 0x2000037:
+	case 0x2000039:
+	case 0x6100000:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt715_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2000000:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config rt715_sdca_regmap = {
+	.reg_bits = 32,
+	.val_bits = 8,
+	.readable_reg = rt715_sdca_readable_register,
+	.volatile_reg = rt715_sdca_volatile_register,
+	.max_register = 0x43ffffff,
+	.reg_defaults = rt715_reg_defaults_sdca,
+	.num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca),
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static const struct regmap_config rt715_sdca_mbq_regmap = {
+	.name = "sdw-mbq",
+	.reg_bits = 32,
+	.val_bits = 16,
+	.readable_reg = rt715_sdca_mbq_readable_register,
+	.volatile_reg = rt715_sdca_mbq_volatile_register,
+	.max_register = 0x43ffffff,
+	.reg_defaults = rt715_mbq_reg_defaults_sdca,
+	.num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca),
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static int rt715_sdca_update_status(struct sdw_slave *slave,
+				enum sdw_slave_status status)
+{
+	struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
+
+	/* Update the status */
+	rt715->status = status;
+
+	/*
+	 * Perform initialization only if slave status is present and
+	 * hw_init flag is false
+	 */
+	if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED)
+		return 0;
+
+	/* perform I/O transfers required for Slave initialization */
+	return rt715_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt715_sdca_read_prop(struct sdw_slave *slave)
+{
+	struct sdw_slave_prop *prop = &slave->prop;
+	int nval, i;
+	u32 bit;
+	unsigned long addr;
+	struct sdw_dpn_prop *dpn;
+
+	prop->paging_support = true;
+
+	/* first we need to allocate memory for set bits in port lists */
+	prop->source_ports = 0x50;/* BITMAP: 01010000 */
+	prop->sink_ports = 0x0;	/* BITMAP:  00000000 */
+
+	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;
+
+	dpn = prop->src_dpn_prop;
+	i = 0;
+	addr = prop->source_ports;
+	for_each_set_bit(bit, &addr, 32) {
+		dpn[i].num = bit;
+		dpn[i].simple_ch_prep_sm = true;
+		dpn[i].ch_prep_timeout = 10;
+		i++;
+	}
+
+	/* set the timeout values */
+	prop->clk_stop_timeout = 20;
+
+	return 0;
+}
+
+static struct sdw_slave_ops rt715_sdca_slave_ops = {
+	.read_prop = rt715_sdca_read_prop,
+	.update_status = rt715_sdca_update_status,
+};
+
+static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
+			   const struct sdw_device_id *id)
+{
+	struct regmap *mbq_regmap, *regmap;
+
+	slave->ops = &rt715_sdca_slave_ops;
+
+	/* Regmap Initialization */
+	mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt715_sdca_mbq_regmap);
+	if (!mbq_regmap)
+		return -EINVAL;
+
+	regmap = devm_regmap_init_sdw(slave, &rt715_sdca_regmap);
+	if (!regmap)
+		return -EINVAL;
+
+	return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave);
+}
+
+static const struct sdw_device_id rt715_sdca_id[] = {
+	SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x3, 0x1, 0),
+	SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x3, 0x1, 0),
+	{},
+};
+MODULE_DEVICE_TABLE(sdw, rt715_sdca_id);
+
+static int __maybe_unused rt715_dev_suspend(struct device *dev)
+{
+	struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+
+	if (!rt715->hw_init)
+		return 0;
+
+	regcache_cache_only(rt715->regmap, true);
+	regcache_mark_dirty(rt715->regmap);
+	regcache_cache_only(rt715->mbq_regmap, true);
+	regcache_mark_dirty(rt715->mbq_regmap);
+
+	return 0;
+}
+
+#define RT715_PROBE_TIMEOUT 2000
+
+static int __maybe_unused rt715_dev_resume(struct device *dev)
+{
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+	unsigned long time;
+
+	if (!rt715->hw_init)
+		return 0;
+
+	if (!slave->unattach_request)
+		goto regmap_sync;
+
+	time = wait_for_completion_timeout(&slave->enumeration_complete,
+					   msecs_to_jiffies(RT715_PROBE_TIMEOUT));
+	if (!time) {
+		dev_err(&slave->dev, "Enumeration not complete, timed out\n");
+		return -ETIMEDOUT;
+	}
+
+regmap_sync:
+	slave->unattach_request = 0;
+	regcache_cache_only(rt715->regmap, false);
+	regcache_sync_region(rt715->regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
+			CH_00),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+			RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
+	regcache_cache_only(rt715->mbq_regmap, false);
+	regcache_sync_region(rt715->mbq_regmap, 0x2000000, 0x61020ff);
+	regcache_sync_region(rt715->mbq_regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
+			CH_00),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+			RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00));
+
+	return 0;
+}
+
+static const struct dev_pm_ops rt715_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume)
+	SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL)
+};
+
+static struct sdw_driver rt715_sdw_driver = {
+	.driver = {
+		.name = "rt715-sdca",
+		.owner = THIS_MODULE,
+		.pm = &rt715_pm,
+	},
+	.probe = rt715_sdca_sdw_probe,
+	.ops = &rt715_sdca_slave_ops,
+	.id_table = rt715_sdca_id,
+};
+module_sdw_driver(rt715_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdca-sdw.h b/sound/soc/codecs/rt715-sdca-sdw.h
new file mode 100644
index 000000000000..cd365bb60747
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca-sdw.h
@@ -0,0 +1,170 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt715-sdca-sdw.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDW_SDCA_H__
+#define __RT715_SDW_SDCA_H__
+
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt715_reg_defaults_sdca[] = {
+	{ 0x201a, 0x00 },
+	{ 0x201e, 0x00 },
+	{ 0x2020, 0x00 },
+	{ 0x2021, 0x00 },
+	{ 0x2022, 0x00 },
+	{ 0x2023, 0x00 },
+	{ 0x2024, 0x00 },
+	{ 0x2025, 0x01 },
+	{ 0x2026, 0x00 },
+	{ 0x2027, 0x00 },
+	{ 0x2029, 0x00 },
+	{ 0x202a, 0x00 },
+	{ 0x202d, 0x00 },
+	{ 0x202e, 0x00 },
+	{ 0x202f, 0x00 },
+	{ 0x2030, 0x00 },
+	{ 0x2031, 0x00 },
+	{ 0x2032, 0x00 },
+	{ 0x2033, 0x00 },
+	{ 0x2034, 0x00 },
+	{ 0x2230, 0x00 },
+	{ 0x2231, 0x2f },
+	{ 0x2232, 0x80 },
+	{ 0x2233, 0x00 },
+	{ 0x2234, 0x00 },
+	{ 0x2235, 0x00 },
+	{ 0x2236, 0x00 },
+	{ 0x2237, 0x00 },
+	{ 0x2238, 0x00 },
+	{ 0x2239, 0x00 },
+	{ 0x2f01, 0x00 },
+	{ 0x2f02, 0x09 },
+	{ 0x2f03, 0x0b },
+	{ 0x2f04, 0x00 },
+	{ 0x2f05, 0x0e },
+	{ 0x2f06, 0x01 },
+	{ 0x2f08, 0x00 },
+	{ 0x2f09, 0x00 },
+	{ 0x2f0a, 0x00 },
+	{ 0x2f0b, 0x00 },
+	{ 0x2f0c, 0x00 },
+	{ 0x2f0d, 0x00 },
+	{ 0x2f0e, 0x12 },
+	{ 0x2f0f, 0x00 },
+	{ 0x2f10, 0x00 },
+	{ 0x2f11, 0x00 },
+	{ 0x2f12, 0x00 },
+	{ 0x2f13, 0x00 },
+	{ 0x2f14, 0x00 },
+	{ 0x2f15, 0x00 },
+	{ 0x2f16, 0x00 },
+	{ 0x2f17, 0x00 },
+	{ 0x2f18, 0x00 },
+	{ 0x2f19, 0x03 },
+	{ 0x2f1a, 0x00 },
+	{ 0x2f1f, 0x10 },
+	{ 0x2f20, 0x00 },
+	{ 0x2f21, 0x00 },
+	{ 0x2f22, 0x00 },
+	{ 0x2f23, 0x00 },
+	{ 0x2f24, 0x00 },
+	{ 0x2f25, 0x00 },
+	{ 0x2f52, 0x01 },
+	{ 0x2f5a, 0x02 },
+	{ 0x2f5b, 0x05 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
+		RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_03), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_04), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+		RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x02 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+		RT715_SDCA_SMPU_TRIG_ST_CTRL, CH_00), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_01), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+		RT715_SDCA_FU_MUTE_CTRL, CH_02), 0x01 },
+};
+
+static const struct reg_default rt715_mbq_reg_defaults_sdca[] = {
+	{ 0x200002b, 0x0420 },
+	{ 0x2000036, 0x0000 },
+	{ 0x2000037, 0x0000 },
+	{ 0x2000039, 0xaa81 },
+	{ 0x6100000, 0x0100 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_03), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_04), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_01), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+		RT715_SDCA_FU_VOL_CTRL, CH_02), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_01), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_02), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_03), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_04), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_05), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_06), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_07), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+		RT715_SDCA_FU_DMIC_GAIN_CTRL, CH_08), 0x00 },
+};
+#endif /* __RT715_SDW_SDCA_H__ */
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
new file mode 100644
index 000000000000..b43ac8559e45
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -0,0 +1,947 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt715-sdca.c -- rt715 ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <linux/soundwire/sdw_registers.h>
+
+#include "rt715-sdca.h"
+
+static int rt715_sdca_index_write(struct rt715_sdca_priv *rt715,
+		unsigned int nid, unsigned int reg, unsigned int value)
+{
+	struct regmap *regmap = rt715->mbq_regmap;
+	unsigned int addr;
+	int ret;
+
+	addr = (nid << 20) | reg;
+
+	ret = regmap_write(regmap, addr, value);
+	if (ret < 0)
+		dev_err(&rt715->slave->dev,
+				"Failed to set private value: %08x <= %04x %d\n", ret, addr,
+				value);
+
+	return ret;
+}
+
+static int rt715_sdca_index_read(struct rt715_sdca_priv *rt715,
+		unsigned int nid, unsigned int reg, unsigned int *value)
+{
+	struct regmap *regmap = rt715->mbq_regmap;
+	unsigned int addr;
+	int ret;
+
+	addr = (nid << 20) | reg;
+
+	ret = regmap_read(regmap, addr, value);
+	if (ret < 0)
+		dev_err(&rt715->slave->dev,
+				"Failed to get private value: %06x => %04x ret=%d\n",
+				addr, *value, ret);
+
+	return ret;
+}
+
+static int rt715_sdca_index_update_bits(struct rt715_sdca_priv *rt715,
+	unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret = rt715_sdca_index_read(rt715, nid, reg, &tmp);
+	if (ret < 0)
+		return ret;
+
+	set_mask_bits(&tmp, mask, val);
+
+	return rt715_sdca_index_write(rt715, nid, reg, tmp);
+}
+
+/* SDCA Volume/Boost control */
+static int rt715_sdca_set_amp_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+	unsigned int val_l, val_r, gain_l_val, gain_r_val, loop_cnt, i, reg;
+	int ret;
+
+	if (strstr(ucontrol->id.name, "FU02 Capture Volume") ||
+		strstr(ucontrol->id.name, "FU06 Capture Volume"))
+		loop_cnt = 2;
+	else if (strstr(ucontrol->id.name, "FU0E Boost") ||
+		strstr(ucontrol->id.name, "FU0C Boost"))
+		loop_cnt = 4;
+	else
+		loop_cnt = 1;
+
+	/* control value to 2s complement */
+	/* L channel */
+	gain_l_val = ucontrol->value.integer.value[0];
+	if (gain_l_val > mc->max)
+		gain_l_val = mc->max;
+	val_l = gain_l_val;
+
+	if (mc->shift == 8) {
+		gain_l_val = (gain_l_val * 10) << mc->shift;
+	} else {
+		gain_l_val =
+			((abs(gain_l_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000;
+		if (val_l <= mc->shift) {
+			gain_l_val = ~gain_l_val;
+			gain_l_val += 1;
+		}
+		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;
+	val_r = gain_r_val;
+
+	if (mc->shift == 8) {
+		gain_r_val = (gain_r_val * 10) << mc->shift;
+	} else {
+		gain_r_val =
+			((abs(gain_r_val - mc->shift) * RT715_SDCA_DB_STEP) << 8) / 1000;
+		if (val_r <= mc->shift) {
+			gain_r_val = ~gain_r_val;
+			gain_r_val += 1;
+		}
+		gain_r_val &= 0xffff;
+	}
+
+	/* Lch*/
+	for (i = 0; i < loop_cnt; i++) {
+		ret = regmap_write(rt715->mbq_regmap, mc->reg + i * 2, gain_l_val);
+		if (ret != 0) {
+			dev_err(component->dev, "Failed to write 0x%x=0x%x\n",
+				mc->reg + i * 2, gain_l_val);
+			return ret;
+		}
+	}
+
+	/* Rch */
+	for (i = 0; i < loop_cnt; i++) {
+		reg = (i == 3) ? (mc->rreg - 2) | BIT(15) : mc->rreg + i * 2;
+		ret = regmap_write(rt715->mbq_regmap, reg, gain_r_val);
+		if (ret != 0) {
+			dev_err(component->dev, "Failed to write 0x%x=0x%x\n",
+				reg, gain_r_val);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int rt715_sdca_set_amp_gain_get(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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+	unsigned int val_l, val_r, ctl_l, ctl_r, neg_flag = 0;
+	int ret;
+
+	ret = regmap_read(rt715->mbq_regmap, mc->reg, &val_l);
+	if (ret < 0)
+		dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->reg, ret);
+	ret = regmap_read(rt715->mbq_regmap, mc->rreg, &val_r);
+	if (ret < 0)
+		dev_err(component->dev, "Failed to read 0x%x, ret=%d\n", mc->rreg,
+				ret);
+
+	/* L channel */
+	if (mc->shift == 8) {
+		ctl_l = (val_l >> mc->shift) / 10;
+	} else {
+		ctl_l = val_l;
+		if (ctl_l & BIT(15)) {
+			ctl_l = ~(val_l - 1) & 0xffff;
+			neg_flag = 1;
+		}
+		ctl_l *= 1000;
+		ctl_l >>= 8;
+		if (neg_flag)
+			ctl_l = mc->shift - ctl_l / RT715_SDCA_DB_STEP;
+		else
+			ctl_l = mc->shift + ctl_l / RT715_SDCA_DB_STEP;
+	}
+
+	neg_flag = 0;
+	/* R channel */
+	if (mc->shift == 8) {
+		ctl_r = (val_r >> mc->shift) / 10;
+	} else {
+		ctl_r = val_r;
+		if (ctl_r & BIT(15)) {
+			ctl_r = ~(val_r - 1) & 0xffff;
+			neg_flag = 1;
+		}
+		ctl_r *= 1000;
+		ctl_r >>= 8;
+		if (neg_flag)
+			ctl_r = mc->shift - ctl_r / RT715_SDCA_DB_STEP;
+		else
+			ctl_r = mc->shift + ctl_r / RT715_SDCA_DB_STEP;
+	}
+
+	ucontrol->value.integer.value[0] = ctl_l;
+	ucontrol->value.integer.value[1] = ctl_r;
+
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static int rt715_sdca_fu_info(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+
+	return 0;
+}
+
+static int rt715_sdca_get_volsw(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;
+	unsigned int invert = mc->invert;
+	unsigned int max = mc->max;
+	int val;
+
+	val = snd_soc_component_read(component, mc->reg);
+	if (val < 0)
+		return -EINVAL;
+	ucontrol->value.integer.value[0] = invert ? max - val : val;
+
+	val = snd_soc_component_read(component, mc->rreg);
+	if (val < 0)
+		return -EINVAL;
+	ucontrol->value.integer.value[1] = invert ? max - val : val;
+
+	return 0;
+}
+
+static int rt715_sdca_put_volsw(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;
+	unsigned int val, val2, loop_cnt = 2, i;
+	unsigned int invert = mc->invert;
+	unsigned int reg2 = mc->rreg;
+	unsigned int reg = mc->reg;
+	unsigned int max = mc->max;
+	int err;
+
+	val = ucontrol->value.integer.value[0];
+	if (invert)
+		val = max - val;
+
+	val2 = ucontrol->value.integer.value[1];
+	if (invert)
+		val2 = max - val2;
+
+	for (i = 0; i < loop_cnt; i++) {
+		err = snd_soc_component_write(component, reg + i * 2, val);
+		if (err < 0)
+			return err;
+		err = snd_soc_component_write(component, reg2 + i * 2, val2);
+		if (err < 0)
+			return err;
+	}
+
+	return 0;
+}
+
+#define RT715_SDCA_FU_VALUE(xlreg, xrreg, xshift, xmax, xinvert) \
+	((unsigned long)&(struct soc_mixer_control) \
+	{.reg = xlreg, .rreg = xrreg, .shift = xshift, \
+	.max = xmax, .invert = xinvert})
+
+#define RT715_SDCA_FU_CTRL(xname, reg_left, reg_right, xshift, xmax, xinvert) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.info = rt715_sdca_fu_info, \
+	.get = rt715_sdca_get_volsw, \
+	.put = rt715_sdca_put_volsw, \
+	.private_value = RT715_SDCA_FU_VALUE(reg_left, reg_right, xshift, \
+					    xmax, xinvert)}
+
+#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\
+	 xhandler_get, xhandler_put) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+	.info = snd_soc_info_volsw, \
+	.get = xhandler_get, .put = xhandler_put, \
+	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
+					    xmax, xinvert) }
+
+static const struct snd_kcontrol_new rt715_sdca_snd_controls[] = {
+	/* Capture switch */
+	SOC_DOUBLE_R("FU0A Capture Switch",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+			RT715_SDCA_FU_MUTE_CTRL, CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+			RT715_SDCA_FU_MUTE_CTRL, CH_02),
+			0, 1, 1),
+	RT715_SDCA_FU_CTRL("FU02 Capture Switch",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+			RT715_SDCA_FU_MUTE_CTRL, CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+			RT715_SDCA_FU_MUTE_CTRL, CH_02),
+			0, 1, 1),
+	RT715_SDCA_FU_CTRL("FU06 Capture Switch",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+			RT715_SDCA_FU_MUTE_CTRL, CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+			RT715_SDCA_FU_MUTE_CTRL, CH_02),
+			0, 1, 1),
+	/* Volume Control */
+	SOC_DOUBLE_R_EXT_TLV("FU0A Capture Volume",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+			RT715_SDCA_FU_VOL_CTRL, CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC7_27_VOL,
+			RT715_SDCA_FU_VOL_CTRL, CH_02),
+			0x2f, 0x7f, 0,
+		rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+		in_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU02 Capture Volume",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+			RT715_SDCA_FU_VOL_CTRL, CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC8_9_VOL,
+			RT715_SDCA_FU_VOL_CTRL, CH_02),
+			0x2f, 0x7f, 0,
+		rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+		in_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU06 Capture Volume",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+			RT715_SDCA_FU_VOL_CTRL,
+			CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_ADC10_11_VOL,
+			RT715_SDCA_FU_VOL_CTRL,
+			CH_02), 0x2f, 0x7f, 0,
+		rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+		in_vol_tlv),
+	/* MIC Boost Control */
+	SOC_DOUBLE_R_EXT_TLV("FU0E Boost",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+			RT715_SDCA_FU_DMIC_GAIN_CTRL,
+			CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_DMIC_GAIN_EN,
+			RT715_SDCA_FU_DMIC_GAIN_CTRL,
+			CH_02), 8, 3, 0,
+		rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+		mic_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU0C Boost",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+			RT715_SDCA_FU_DMIC_GAIN_CTRL,
+			CH_01),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_FU_AMIC_GAIN_EN,
+			RT715_SDCA_FU_DMIC_GAIN_CTRL,
+			CH_02), 8, 3, 0,
+		rt715_sdca_set_amp_gain_get, rt715_sdca_set_amp_gain_put,
+		mic_vol_tlv),
+};
+
+static int rt715_sdca_mux_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_kcontrol_component(kcontrol);
+	struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+	unsigned int val, mask_sft;
+
+	if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+		mask_sft = 12;
+	else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+		mask_sft = 8;
+	else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
+		mask_sft = 4;
+	else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+		mask_sft = 0;
+	else
+		return -EINVAL;
+
+	rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL,
+		RT715_HDA_LEGACY_MUX_CTL1, &val);
+	val = (val >> mask_sft) & 0xf;
+
+	/*
+	 * The first two indices of ADC Mux 24/25 are routed to the same
+	 * hardware source. ie, ADC Mux 24 0/1 will both connect to MIC2.
+	 * To have a unique set of inputs, we skip the index1 of the muxes.
+	 */
+	if ((strstr(ucontrol->id.name, "ADC 24 Mux") ||
+		strstr(ucontrol->id.name, "ADC 25 Mux")) && val > 0)
+		val -= 1;
+	ucontrol->value.enumerated.item[0] = val;
+
+	return 0;
+}
+
+static int rt715_sdca_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 rt715_sdca_priv *rt715 = 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;
+
+	if (item[0] >= e->items)
+		return -EINVAL;
+
+	if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+		mask_sft = 12;
+	else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+		mask_sft = 8;
+	else if (strstr(ucontrol->id.name, "ADC 24 Mux"))
+		mask_sft = 4;
+	else if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+		mask_sft = 0;
+	else
+		return -EINVAL;
+
+	/* Verb ID = 0x701h, nid = e->reg */
+	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+	rt715_sdca_index_read(rt715, RT715_VENDOR_HDA_CTL,
+		RT715_HDA_LEGACY_MUX_CTL1, &val2);
+	val2 = (val2 >> mask_sft) & 0xf;
+
+	change = val != val2;
+
+	if (change)
+		rt715_sdca_index_update_bits(rt715, RT715_VENDOR_HDA_CTL,
+			RT715_HDA_LEGACY_MUX_CTL1, 0xf << mask_sft, val << mask_sft);
+
+	snd_soc_dapm_mux_update_power(dapm, kcontrol, item[0], e, NULL);
+
+	return change;
+}
+
+static const char * const adc_22_23_mux_text[] = {
+	"MIC1",
+	"MIC2",
+	"LINE1",
+	"LINE2",
+	"DMIC1",
+	"DMIC2",
+	"DMIC3",
+	"DMIC4",
+};
+
+/*
+ * Due to mux design for nid 24 (MUX_IN3)/25 (MUX_IN4), connection index 0 and
+ * 1 will be connected to the same dmic source, therefore we skip index 1 to
+ * avoid misunderstanding on usage of dapm routing.
+ */
+static int rt715_adc_24_25_values[] = {
+	0,
+	2,
+	3,
+	4,
+	5,
+};
+
+static const char * const adc_24_mux_text[] = {
+	"MIC2",
+	"DMIC1",
+	"DMIC2",
+	"DMIC3",
+	"DMIC4",
+};
+
+static const char * const adc_25_mux_text[] = {
+	"MIC1",
+	"DMIC1",
+	"DMIC2",
+	"DMIC3",
+	"DMIC4",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt715_adc22_enum, SND_SOC_NOPM, 0,
+	adc_22_23_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(rt715_adc23_enum, SND_SOC_NOPM, 0,
+	adc_22_23_mux_text);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc24_enum,
+	SND_SOC_NOPM, 0, 0xf,
+	adc_24_mux_text, rt715_adc_24_25_values);
+static SOC_VALUE_ENUM_SINGLE_DECL(rt715_adc25_enum,
+	SND_SOC_NOPM, 0, 0xf,
+	adc_25_mux_text, rt715_adc_24_25_values);
+
+static const struct snd_kcontrol_new rt715_adc22_mux =
+	SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt715_adc22_enum,
+			rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc23_mux =
+	SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt715_adc23_enum,
+			rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc24_mux =
+	SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt715_adc24_enum,
+			rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt715_adc25_mux =
+	SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt715_adc25_enum,
+			rt715_sdca_mux_get, rt715_sdca_mux_put);
+
+static int rt715_sdca_pde23_24_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt715->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
+				RT715_SDCA_REQ_POW_CTRL,
+				CH_00), 0x00);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt715->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CREQ_POW_EN,
+				RT715_SDCA_REQ_POW_CTRL,
+				CH_00), 0x03);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget rt715_sdca_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("DMIC1"),
+	SND_SOC_DAPM_INPUT("DMIC2"),
+	SND_SOC_DAPM_INPUT("DMIC3"),
+	SND_SOC_DAPM_INPUT("DMIC4"),
+	SND_SOC_DAPM_INPUT("MIC1"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("LINE1"),
+	SND_SOC_DAPM_INPUT("LINE2"),
+
+	SND_SOC_DAPM_SUPPLY("PDE23_24", SND_SOC_NOPM, 0, 0,
+		rt715_sdca_pde23_24_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_ADC("ADC 07", NULL, SND_SOC_NOPM, 4, 0),
+	SND_SOC_DAPM_ADC("ADC 08", NULL, SND_SOC_NOPM, 4, 0),
+	SND_SOC_DAPM_ADC("ADC 09", NULL, SND_SOC_NOPM, 4, 0),
+	SND_SOC_DAPM_ADC("ADC 27", NULL, SND_SOC_NOPM, 4, 0),
+	SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+		&rt715_adc22_mux),
+	SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+		&rt715_adc23_mux),
+	SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0,
+		&rt715_adc24_mux),
+	SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+		&rt715_adc25_mux),
+	SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = {
+	{"DP6TX", NULL, "ADC 09"},
+	{"DP6TX", NULL, "ADC 08"},
+	{"DP4TX", NULL, "ADC 07"},
+	{"DP4TX", NULL, "ADC 27"},
+	{"DP4TX", NULL, "ADC 09"},
+	{"DP4TX", NULL, "ADC 08"},
+
+	{"LINE1", NULL, "PDE23_24"},
+	{"LINE2", NULL, "PDE23_24"},
+	{"MIC1", NULL, "PDE23_24"},
+	{"MIC2", NULL, "PDE23_24"},
+	{"DMIC1", NULL, "PDE23_24"},
+	{"DMIC2", NULL, "PDE23_24"},
+	{"DMIC3", NULL, "PDE23_24"},
+	{"DMIC4", NULL, "PDE23_24"},
+
+	{"ADC 09", NULL, "ADC 22 Mux"},
+	{"ADC 08", NULL, "ADC 23 Mux"},
+	{"ADC 07", NULL, "ADC 24 Mux"},
+	{"ADC 27", NULL, "ADC 25 Mux"},
+	{"ADC 22 Mux", "MIC1", "MIC1"},
+	{"ADC 22 Mux", "MIC2", "MIC2"},
+	{"ADC 22 Mux", "LINE1", "LINE1"},
+	{"ADC 22 Mux", "LINE2", "LINE2"},
+	{"ADC 22 Mux", "DMIC1", "DMIC1"},
+	{"ADC 22 Mux", "DMIC2", "DMIC2"},
+	{"ADC 22 Mux", "DMIC3", "DMIC3"},
+	{"ADC 22 Mux", "DMIC4", "DMIC4"},
+	{"ADC 23 Mux", "MIC1", "MIC1"},
+	{"ADC 23 Mux", "MIC2", "MIC2"},
+	{"ADC 23 Mux", "LINE1", "LINE1"},
+	{"ADC 23 Mux", "LINE2", "LINE2"},
+	{"ADC 23 Mux", "DMIC1", "DMIC1"},
+	{"ADC 23 Mux", "DMIC2", "DMIC2"},
+	{"ADC 23 Mux", "DMIC3", "DMIC3"},
+	{"ADC 23 Mux", "DMIC4", "DMIC4"},
+	{"ADC 24 Mux", "MIC2", "MIC2"},
+	{"ADC 24 Mux", "DMIC1", "DMIC1"},
+	{"ADC 24 Mux", "DMIC2", "DMIC2"},
+	{"ADC 24 Mux", "DMIC3", "DMIC3"},
+	{"ADC 24 Mux", "DMIC4", "DMIC4"},
+	{"ADC 25 Mux", "MIC1", "MIC1"},
+	{"ADC 25 Mux", "DMIC1", "DMIC1"},
+	{"ADC 25 Mux", "DMIC2", "DMIC2"},
+	{"ADC 25 Mux", "DMIC3", "DMIC3"},
+	{"ADC 25 Mux", "DMIC4", "DMIC4"},
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
+	.controls = rt715_sdca_snd_controls,
+	.num_controls = ARRAY_SIZE(rt715_sdca_snd_controls),
+	.dapm_widgets = rt715_sdca_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(rt715_sdca_dapm_widgets),
+	.dapm_routes = rt715_sdca_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(rt715_sdca_audio_map),
+};
+
+static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+				int direction)
+{
+	struct rt715_sdw_stream_data *stream;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return -ENOMEM;
+
+	stream->sdw_stream = sdw_stream;
+
+	/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		dai->playback_dma_data = stream;
+	else
+		dai->capture_dma_data = stream;
+
+	return 0;
+}
+
+static void rt715_sdca_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+
+{
+	struct rt715_sdw_stream_data *stream;
+
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+	if (!stream)
+		return;
+
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+	kfree(stream);
+}
+
+static int rt715_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 rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+	struct sdw_stream_config stream_config;
+	struct sdw_port_config port_config;
+	enum sdw_data_direction direction;
+	struct rt715_sdw_stream_data *stream;
+	int retval, port, num_channels;
+	unsigned int val;
+
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!stream)
+		return -EINVAL;
+
+	if (!rt715->slave)
+		return -EINVAL;
+
+	switch (dai->id) {
+	case RT715_AIF1:
+		direction = SDW_DATA_DIR_TX;
+		port = 6;
+		rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
+			0xa500);
+		break;
+	case RT715_AIF2:
+		direction = SDW_DATA_DIR_TX;
+		port = 4;
+		rt715_sdca_index_write(rt715, RT715_VENDOR_REG, RT715_SDW_INPUT_SEL,
+			0xaf00);
+		break;
+	default:
+		dev_err(component->dev, "Invalid DAI id %d\n", dai->id);
+		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(rt715->slave, &stream_config,
+					&port_config, 1, stream->sdw_stream);
+	if (retval) {
+		dev_err(component->dev, "Unable to configure port, retval:%d\n",
+			retval);
+		return retval;
+	}
+
+	switch (params_rate(params)) {
+	case 8000:
+		val = 0x1;
+		break;
+	case 11025:
+		val = 0x2;
+		break;
+	case 12000:
+		val = 0x3;
+		break;
+	case 16000:
+		val = 0x4;
+		break;
+	case 22050:
+		val = 0x5;
+		break;
+	case 24000:
+		val = 0x6;
+		break;
+	case 32000:
+		val = 0x7;
+		break;
+	case 44100:
+		val = 0x8;
+		break;
+	case 48000:
+		val = 0x9;
+		break;
+	case 88200:
+		val = 0xa;
+		break;
+	case 96000:
+		val = 0xb;
+		break;
+	case 176400:
+		val = 0xc;
+		break;
+	case 192000:
+		val = 0xd;
+		break;
+	case 384000:
+		val = 0xe;
+		break;
+	case 768000:
+		val = 0xf;
+		break;
+	default:
+		dev_err(component->dev, "Unsupported sample rate %d\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	regmap_write(rt715->regmap,
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CS_FREQ_IND_EN,
+			RT715_SDCA_FREQ_IND_CTRL, CH_00), val);
+
+	return 0;
+}
+
+static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
+	struct rt715_sdw_stream_data *stream =
+		snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!rt715->slave)
+		return -EINVAL;
+
+	sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
+	return 0;
+}
+
+#define RT715_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define RT715_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+static struct snd_soc_dai_ops rt715_sdca_ops = {
+	.hw_params	= rt715_sdca_pcm_hw_params,
+	.hw_free	= rt715_sdca_pcm_hw_free,
+	.set_sdw_stream	= rt715_sdca_set_sdw_stream,
+	.shutdown	= rt715_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt715_sdca_dai[] = {
+	{
+		.name = "rt715-aif1",
+		.id = RT715_AIF1,
+		.capture = {
+			.stream_name = "DP6 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT715_STEREO_RATES,
+			.formats = RT715_FORMATS,
+		},
+		.ops = &rt715_sdca_ops,
+	},
+	{
+		.name = "rt715-aif2",
+		.id = RT715_AIF2,
+		.capture = {
+			.stream_name = "DP4 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT715_STEREO_RATES,
+			.formats = RT715_FORMATS,
+		},
+		.ops = &rt715_sdca_ops,
+	},
+};
+
+/* Bus clock frequency */
+#define RT715_CLK_FREQ_9600000HZ 9600000
+#define RT715_CLK_FREQ_12000000HZ 12000000
+#define RT715_CLK_FREQ_6000000HZ 6000000
+#define RT715_CLK_FREQ_4800000HZ 4800000
+#define RT715_CLK_FREQ_2400000HZ 2400000
+#define RT715_CLK_FREQ_12288000HZ 12288000
+
+int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
+	struct regmap *regmap, struct sdw_slave *slave)
+{
+	struct rt715_sdca_priv *rt715;
+	int ret;
+
+	rt715 = devm_kzalloc(dev, sizeof(*rt715), GFP_KERNEL);
+	if (!rt715)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, rt715);
+	rt715->slave = slave;
+	rt715->regmap = regmap;
+	rt715->mbq_regmap = mbq_regmap;
+	rt715->hw_sdw_ver = slave->id.sdw_version;
+	/*
+	 * Mark hw_init to false
+	 * HW init will be performed when device reports present
+	 */
+	rt715->hw_init = false;
+	rt715->first_init = false;
+
+	ret = devm_snd_soc_register_component(dev,
+			&soc_codec_dev_rt715_sdca,
+			rt715_sdca_dai,
+			ARRAY_SIZE(rt715_sdca_dai));
+
+	return ret;
+}
+
+int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+	struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
+	unsigned int hw_ver;
+
+	if (rt715->hw_init)
+		return 0;
+
+	/*
+	 * PM runtime is only enabled when a Slave reports as Attached
+	 */
+	if (!rt715->first_init) {
+		/* 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);
+
+		rt715->first_init = true;
+	}
+
+	pm_runtime_get_noresume(&slave->dev);
+
+	rt715_sdca_index_read(rt715, RT715_VENDOR_REG,
+		RT715_PRODUCT_NUM, &hw_ver);
+	hw_ver = hw_ver & 0x000f;
+
+	/* set clock selector = external */
+	regmap_write(rt715->regmap,
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_CX_CLK_SEL_EN,
+			RT715_SDCA_CX_CLK_SEL_CTRL, CH_00), 0x1);
+	/* set GPIO_4/5/6 to be 3rd/4th DMIC usage */
+	if (hw_ver == 0x0)
+		rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+			RT715_AD_FUNC_EN, 0x54, 0x54);
+	else if (hw_ver == 0x1) {
+		rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+			RT715_AD_FUNC_EN, 0x55, 0x55);
+		rt715_sdca_index_update_bits(rt715, RT715_VENDOR_REG,
+			RT715_REV_1, 0x40, 0x40);
+	}
+	/* trigger mode = VAD enable */
+	regmap_write(rt715->regmap,
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT715_SDCA_SMPU_TRIG_ST_EN,
+			RT715_SDCA_SMPU_TRIG_EN_CTRL, CH_00), 0x2);
+	/* SMPU-1 interrupt enable mask */
+	regmap_update_bits(rt715->regmap, RT715_INT_MASK, 0x1, 0x1);
+
+	/* Mark Slave initialization complete */
+	rt715->hw_init = true;
+
+	pm_runtime_mark_last_busy(&slave->dev);
+	pm_runtime_put_autosuspend(&slave->dev);
+
+	return 0;
+}
+
+MODULE_DESCRIPTION("ASoC rt715 driver SDW SDCA");
+MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h
new file mode 100644
index 000000000000..840c237895dd
--- /dev/null
+++ b/sound/soc/codecs/rt715-sdca.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt715-sdca.h -- RT715 ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT715_SDCA_H__
+#define __RT715_SDCA_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+
+struct rt715_sdca_priv {
+	struct regmap *regmap;
+	struct regmap *mbq_regmap;
+	struct snd_soc_codec *codec;
+	struct sdw_slave *slave;
+	struct delayed_work adc_mute_work;
+	int dbg_nid;
+	int dbg_vid;
+	int dbg_payload;
+	enum sdw_slave_status status;
+	struct sdw_bus_params params;
+	bool hw_init;
+	bool first_init;
+	int l_is_unmute;
+	int r_is_unmute;
+	int hw_sdw_ver;
+};
+
+struct rt715_sdw_stream_data {
+	struct sdw_stream_runtime *sdw_stream;
+};
+
+/* MIPI Register */
+#define RT715_INT_CTRL					0x005a
+#define RT715_INT_MASK					0x005e
+
+/* NID */
+#define RT715_AUDIO_FUNCTION_GROUP			0x01
+#define RT715_MIC_ADC					0x07
+#define RT715_LINE_ADC					0x08
+#define RT715_MIX_ADC					0x09
+#define RT715_DMIC1					0x12
+#define RT715_DMIC2					0x13
+#define RT715_MIC1					0x18
+#define RT715_MIC2					0x19
+#define RT715_LINE1					0x1a
+#define RT715_LINE2					0x1b
+#define RT715_DMIC3					0x1d
+#define RT715_DMIC4					0x29
+#define RT715_VENDOR_REG				0x20
+#define RT715_MUX_IN1					0x22
+#define RT715_MUX_IN2					0x23
+#define RT715_MUX_IN3					0x24
+#define RT715_MUX_IN4					0x25
+#define RT715_MIX_ADC2					0x27
+#define RT715_INLINE_CMD				0x55
+#define RT715_VENDOR_HDA_CTL				0x61
+
+/* Index (NID:20h) */
+#define RT715_PRODUCT_NUM				0x0
+#define RT715_IRQ_CTRL					0x2b
+#define RT715_AD_FUNC_EN				0x36
+#define RT715_REV_1					0x37
+#define RT715_SDW_INPUT_SEL				0x39
+#define RT715_EXT_DMIC_CLK_CTRL2			0x54
+
+/* Index (NID:61h) */
+#define RT715_HDA_LEGACY_MUX_CTL1			0x00
+
+/* SDCA (Function) */
+#define FUN_JACK_CODEC				0x01
+#define FUN_MIC_ARRAY				0x02
+#define FUN_HID						0x03
+/* SDCA (Entity) */
+#define RT715_SDCA_ST_EN							0x00
+#define RT715_SDCA_CS_FREQ_IND_EN					0x01
+#define RT715_SDCA_FU_ADC8_9_VOL					0x02
+#define RT715_SDCA_SMPU_TRIG_ST_EN					0x05
+#define RT715_SDCA_FU_ADC10_11_VOL					0x06
+#define RT715_SDCA_FU_ADC7_27_VOL					0x0a
+#define RT715_SDCA_FU_AMIC_GAIN_EN					0x0c
+#define RT715_SDCA_FU_DMIC_GAIN_EN					0x0e
+#define RT715_SDCA_CX_CLK_SEL_EN					0x10
+#define RT715_SDCA_CREQ_POW_EN						0x18
+/* SDCA (Control) */
+#define RT715_SDCA_ST_CTRL							0x00
+#define RT715_SDCA_CX_CLK_SEL_CTRL					0x01
+#define RT715_SDCA_REQ_POW_CTRL					0x01
+#define RT715_SDCA_FU_MUTE_CTRL					0x01
+#define RT715_SDCA_FU_VOL_CTRL						0x02
+#define RT715_SDCA_FU_DMIC_GAIN_CTRL				0x0b
+#define RT715_SDCA_FREQ_IND_CTRL					0x10
+#define RT715_SDCA_SMPU_TRIG_EN_CTRL				0x10
+#define RT715_SDCA_SMPU_TRIG_ST_CTRL				0x11
+/* SDCA (Channel) */
+#define CH_00						0x00
+#define CH_01						0x01
+#define CH_02						0x02
+#define CH_03						0x03
+#define CH_04						0x04
+#define CH_05						0x05
+#define CH_06						0x06
+#define CH_07						0x07
+#define CH_08						0x08
+
+#define RT715_SDCA_DB_STEP			375
+
+enum {
+	RT715_AIF1,
+	RT715_AIF2,
+};
+
+int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
+	struct regmap *regmap, struct sdw_slave *slave);
+
+#endif /* __RT715_SDCA_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 4/5] ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
                   ` (2 preceding siblings ...)
  2020-11-30 14:40 ` [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver Bard Liao
@ 2020-11-30 14:40 ` Bard Liao
  2020-11-30 14:40 ` [PATCH v2 5/5] ASoC/SoundWire: rt711-sdca: Add RT711 " Bard Liao
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 16+ messages in thread
From: Bard Liao @ 2020-11-30 14:40 UTC (permalink / raw)
  To: alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

From: Shuming Fan <shumingf@realtek.com>

This is the initial amplifier driver for rt1316 SDCA version.

Signed-off-by: Shuming Fan <shumingf@realtek.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
---
 sound/soc/codecs/Kconfig      |   6 +
 sound/soc/codecs/Makefile     |   2 +
 sound/soc/codecs/rt1316-sdw.c | 756 ++++++++++++++++++++++++++++++++++
 sound/soc/codecs/rt1316-sdw.h | 115 ++++++
 4 files changed, 879 insertions(+)
 create mode 100644 sound/soc/codecs/rt1316-sdw.c
 create mode 100644 sound/soc/codecs/rt1316-sdw.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index e7797f08e057..ebc124142f90 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -179,6 +179,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_RT715_SDW
 	imply SND_SOC_RT715_SDCA_SDW
 	imply SND_SOC_RT1308_SDW
+	imply SND_SOC_RT1316_SDW
 	imply SND_SOC_SGTL5000
 	imply SND_SOC_SI476X
 	imply SND_SOC_SIMPLE_AMPLIFIER
@@ -1110,6 +1111,11 @@ config SND_SOC_RT1308_SDW
 	depends on I2C && SOUNDWIRE
 	select REGMAP_SOUNDWIRE
 
+config SND_SOC_RT1316_SDW
+	tristate "Realtek RT1316 Codec - SDW"
+	depends on SOUNDWIRE
+	select REGMAP_SOUNDWIRE
+
 config SND_SOC_RT5514
 	tristate
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index b1683403afb3..601bbb8b46e7 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -164,6 +164,7 @@ snd-soc-rt1015p-objs := rt1015p.o
 snd-soc-rt1305-objs := rt1305.o
 snd-soc-rt1308-objs := rt1308.o
 snd-soc-rt1308-sdw-objs := rt1308-sdw.o
+snd-soc-rt1316-sdw-objs := rt1316-sdw.o
 snd-soc-rt274-objs := rt274.o
 snd-soc-rt286-objs := rt286.o
 snd-soc-rt298-objs := rt298.o
@@ -474,6 +475,7 @@ obj-$(CONFIG_SND_SOC_RT1015P)	+= snd-soc-rt1015p.o
 obj-$(CONFIG_SND_SOC_RT1305)	+= snd-soc-rt1305.o
 obj-$(CONFIG_SND_SOC_RT1308)	+= snd-soc-rt1308.o
 obj-$(CONFIG_SND_SOC_RT1308_SDW)	+= snd-soc-rt1308-sdw.o
+obj-$(CONFIG_SND_SOC_RT1316_SDW)	+= snd-soc-rt1316-sdw.o
 obj-$(CONFIG_SND_SOC_RT274)	+= snd-soc-rt274.o
 obj-$(CONFIG_SND_SOC_RT286)	+= snd-soc-rt286.o
 obj-$(CONFIG_SND_SOC_RT298)	+= snd-soc-rt298.o
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
new file mode 100644
index 000000000000..145ffb8cd1ca
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -0,0 +1,756 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1316-sdw.c -- rt1316 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include "rt1316-sdw.h"
+
+static bool rt1316_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2f0a:
+	case 0x2f36:
+	case 0x3203 ... 0x320e:
+	case 0xc000 ... 0xc7b4:
+	case 0xcf00 ... 0xcf03:
+	case 0xd101 ... 0xd103:
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0):
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L):
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R):
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE27, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+	case SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0):
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt1316_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0xc000:
+	case 0xc093:
+	case 0xc09d:
+	case 0xc0a3:
+	case 0xc201:
+	case 0xc427 ... 0xc428:
+	case 0xd102:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config rt1316_sdw_regmap = {
+	.reg_bits = 32,
+	.val_bits = 8,
+	.readable_reg = rt1316_readable_register,
+	.volatile_reg = rt1316_volatile_register,
+	.max_register = 0x4108ffff,
+	.reg_defaults = rt1316_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static int rt1316_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;
+
+	prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+	prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+	prop->is_sdca = true;
+
+	prop->paging_support = true;
+
+	/* first we need to allocate memory for set bits in port lists */
+	prop->source_ports = 0x04; /* BITMAP: 00000100 */
+	prop->sink_ports = 0x2; /* BITMAP:  00000010 */
+
+	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 = 20;
+
+	dev_dbg(&slave->dev, "%s\n", __func__);
+
+	return 0;
+}
+
+static int rt1316_io_init(struct device *dev, struct sdw_slave *slave)
+{
+	struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+
+	if (rt1316->hw_init)
+		return 0;
+
+	if (rt1316->first_hw_init) {
+		regcache_cache_only(rt1316->regmap, false);
+		regcache_cache_bypass(rt1316->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);
+
+	/* sw reset */
+	regmap_write(rt1316->regmap, 0xc000, 0x02);
+
+	/* initial settings - blind write */
+	regmap_write(rt1316->regmap, 0xc710, 0x17);
+	regmap_write(rt1316->regmap, 0xc711, 0x80);
+	regmap_write(rt1316->regmap, 0xc712, 0x26);
+	regmap_write(rt1316->regmap, 0xc713, 0x06);
+	regmap_write(rt1316->regmap, 0xc714, 0x80);
+	regmap_write(rt1316->regmap, 0xc715, 0x06);
+	regmap_write(rt1316->regmap, 0xc702, 0x0a);
+	regmap_write(rt1316->regmap, 0xc703, 0x0a);
+	regmap_write(rt1316->regmap, 0xc001, 0x45);
+	regmap_write(rt1316->regmap, 0xc003, 0x00);
+	regmap_write(rt1316->regmap, 0xc004, 0x11);
+	regmap_write(rt1316->regmap, 0xc005, 0x00);
+	regmap_write(rt1316->regmap, 0xc006, 0x00);
+	regmap_write(rt1316->regmap, 0xc106, 0x00);
+	regmap_write(rt1316->regmap, 0xc007, 0x11);
+	regmap_write(rt1316->regmap, 0xc008, 0x11);
+	regmap_write(rt1316->regmap, 0xc009, 0x00);
+
+	regmap_write(rt1316->regmap, 0x2f0a, 0x00);
+	regmap_write(rt1316->regmap, 0xd101, 0xf0);
+	regmap_write(rt1316->regmap, 0xd103, 0x9b);
+	regmap_write(rt1316->regmap, 0x2f36, 0x8e);
+	regmap_write(rt1316->regmap, 0x3206, 0x80);
+	regmap_write(rt1316->regmap, 0x3211, 0x0b);
+	regmap_write(rt1316->regmap, 0x3216, 0x06);
+	regmap_write(rt1316->regmap, 0xc614, 0x20);
+	regmap_write(rt1316->regmap, 0xc615, 0x0a);
+	regmap_write(rt1316->regmap, 0xc616, 0x02);
+	regmap_write(rt1316->regmap, 0xc617, 0x00);
+	regmap_write(rt1316->regmap, 0xc60b, 0x10);
+	regmap_write(rt1316->regmap, 0xc60e, 0x05);
+	regmap_write(rt1316->regmap, 0xc102, 0x00);
+	regmap_write(rt1316->regmap, 0xc090, 0xb0);
+	regmap_write(rt1316->regmap, 0xc00f, 0x01);
+	regmap_write(rt1316->regmap, 0xc09c, 0x7b);
+
+	regmap_write(rt1316->regmap, 0xc602, 0x07);
+	regmap_write(rt1316->regmap, 0xc603, 0x07);
+	regmap_write(rt1316->regmap, 0xc0a3, 0x71);
+	regmap_write(rt1316->regmap, 0xc00b, 0x30);
+	regmap_write(rt1316->regmap, 0xc093, 0x80);
+	regmap_write(rt1316->regmap, 0xc09d, 0x80);
+	regmap_write(rt1316->regmap, 0xc0b0, 0x77);
+	regmap_write(rt1316->regmap, 0xc010, 0xa5);
+	regmap_write(rt1316->regmap, 0xc050, 0x83);
+	regmap_write(rt1316->regmap, 0x2f55, 0x03);
+	regmap_write(rt1316->regmap, 0x3217, 0xb5);
+	regmap_write(rt1316->regmap, 0x3202, 0x02);
+
+	regmap_write(rt1316->regmap,
+		SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x00);
+
+	/* for IV sense */
+	regmap_write(rt1316->regmap, 0x2232, 0x80);
+	regmap_write(rt1316->regmap, 0xc0b0, 0x77);
+	regmap_write(rt1316->regmap, 0xc011, 0x00);
+	regmap_write(rt1316->regmap, 0xc020, 0x00);
+	regmap_write(rt1316->regmap, 0xc023, 0x00);
+	regmap_write(rt1316->regmap, 0x3101, 0x00);
+	regmap_write(rt1316->regmap, 0x3004, 0xa0);
+	regmap_write(rt1316->regmap, 0x3005, 0xb1);
+	regmap_write(rt1316->regmap, 0xc007, 0x11);
+	regmap_write(rt1316->regmap, 0xc008, 0x11);
+	regmap_write(rt1316->regmap, 0xc009, 0x00);
+	regmap_write(rt1316->regmap, 0xc022, 0xd6);
+	regmap_write(rt1316->regmap, 0xc025, 0xd6);
+
+	regmap_write(rt1316->regmap, 0xd001, 0x03);
+	regmap_write(rt1316->regmap, 0xd002, 0xbf);
+	regmap_write(rt1316->regmap, 0xd003, 0x03);
+	regmap_write(rt1316->regmap, 0xd004, 0xbf);
+
+	if (rt1316->first_hw_init) {
+		regcache_cache_bypass(rt1316->regmap, false);
+		regcache_mark_dirty(rt1316->regmap);
+	} else
+		rt1316->first_hw_init = true;
+
+	/* Mark Slave initialization complete */
+	rt1316->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;
+}
+
+static int rt1316_update_status(struct sdw_slave *slave,
+					enum sdw_slave_status status)
+{
+	struct  rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
+
+	/* Update the status */
+	rt1316->status = status;
+
+	if (status == SDW_SLAVE_UNATTACHED)
+		rt1316->hw_init = false;
+
+	if (status == SDW_SLAVE_ATTACHED)
+		regcache_mark_dirty(rt1316->regmap);
+
+	/*
+	 * Perform initialization only if slave status is present and
+	 * hw_init flag is false
+	 */
+	if (rt1316->hw_init || rt1316->status != SDW_SLAVE_ATTACHED)
+		return 0;
+
+	/* perform I/O transfers required for Slave initialization */
+	return rt1316_io_init(&slave->dev, slave);
+}
+
+static int rt1316_classd_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 rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE23,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE27,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE22,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE23,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE27,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE22,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		break;
+
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int rt1316_pde24_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 rt1316_sdw_priv *rt1316 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE24,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt1316->regmap,
+			SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE24,
+				RT1316_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		break;
+	}
+	return 0;
+}
+
+static const char * const rt1316_rx_data_ch_select[] = {
+	"L,R",
+	"L,L",
+	"L,R",
+	"L,L+R",
+	"R,L",
+	"R,R",
+	"R,L+R",
+	"L+R,L",
+	"L+R,R",
+	"L+R,L+R",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum,
+	SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0,
+	rt1316_rx_data_ch_select);
+
+static const char * const rt1316_xu24_bypass_ctl[] = {
+	"Not Bypass",
+	"Bypass",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_xu24_bypass_enum,
+	SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0,
+	rt1316_xu24_bypass_ctl);
+
+static const char * const rt1316_lr_iv_sel[] = {
+	"0",
+	"1",
+	"2",
+	"3",
+	"4",
+	"5",
+	"6",
+	"7",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_l_v_tag_enum,
+	0x3004, 0, rt1316_lr_iv_sel);
+static SOC_ENUM_SINGLE_DECL(rt1316_l_i_tag_enum,
+	0x3004, 4, rt1316_lr_iv_sel);
+static SOC_ENUM_SINGLE_DECL(rt1316_r_v_tag_enum,
+	0x3005, 0, rt1316_lr_iv_sel);
+static SOC_ENUM_SINGLE_DECL(rt1316_r_i_tag_enum,
+	0x3005, 4, rt1316_lr_iv_sel);
+
+static const struct snd_kcontrol_new rt1316_snd_controls[] = {
+
+	/* I2S Data Channel Selection */
+	SOC_ENUM("RX Channel Select", rt1316_rx_data_ch_enum),
+
+	/* XU24 Bypass Control */
+	SOC_ENUM("XU24 Bypass Control", rt1316_xu24_bypass_enum),
+
+	/* Left/Right IV tag */
+	SOC_ENUM("Left V Tag Select", rt1316_l_v_tag_enum),
+	SOC_ENUM("Left I Tag Select", rt1316_l_i_tag_enum),
+	SOC_ENUM("Right V Tag Select", rt1316_r_v_tag_enum),
+	SOC_ENUM("Right I Tag Select", rt1316_r_i_tag_enum),
+
+	/* IV mixer Control */
+	SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1),
+	SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt1316_sto_dac_l =
+	SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+		SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L),
+		0, 1, 1);
+
+static const struct snd_kcontrol_new rt1316_sto_dac_r =
+	SOC_DAPM_SINGLE_AUTODISABLE("Switch",
+		SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R),
+		0, 1, 1);
+
+static const struct snd_soc_dapm_widget rt1316_dapm_widgets[] = {
+	/* Audio Interface */
+	SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+	/* Digital Interface */
+	SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_SWITCH("DAC L", SND_SOC_NOPM, 0, 0, &rt1316_sto_dac_l),
+	SND_SOC_DAPM_SWITCH("DAC R", SND_SOC_NOPM, 0, 0, &rt1316_sto_dac_r),
+
+	/* Output Lines */
+	SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+		rt1316_classd_event,
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_OUTPUT("SPOL"),
+	SND_SOC_DAPM_OUTPUT("SPOR"),
+
+	SND_SOC_DAPM_SUPPLY("PDE 24", SND_SOC_NOPM, 0, 0,
+		rt1316_pde24_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_SIGGEN("I Gen"),
+	SND_SOC_DAPM_SIGGEN("V Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1316_dapm_routes[] = {
+	{ "DAC", NULL, "DP1RX" },
+	{ "DAC L", "Switch", "DAC" },
+	{ "DAC R", "Switch", "DAC" },
+	{ "CLASS D", NULL, "DAC L" },
+	{ "CLASS D", NULL, "DAC R" },
+	{ "SPOL", NULL, "CLASS D" },
+	{ "SPOR", NULL, "CLASS D" },
+
+	{ "I Sense", NULL, "I Gen" },
+	{ "V Sense", NULL, "V Gen" },
+	{ "I Sense", NULL, "PDE 24" },
+	{ "V Sense", NULL, "PDE 24" },
+	{ "DP2TX", NULL, "I Sense" },
+	{ "DP2TX", NULL, "V Sense" },
+};
+
+static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+				int direction)
+{
+	struct sdw_stream_data *stream;
+
+	if (!sdw_stream)
+		return 0;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return -ENOMEM;
+
+	stream->sdw_stream = sdw_stream;
+
+	/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		dai->playback_dma_data = stream;
+	else
+		dai->capture_dma_data = stream;
+
+	return 0;
+}
+
+static void rt1316_sdw_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct sdw_stream_data *stream;
+
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+	kfree(stream);
+}
+
+static int rt1316_sdw_set_tdm_slot(struct snd_soc_dai *dai,
+				   unsigned int tx_mask,
+				   unsigned int rx_mask,
+				   int slots, int slot_width)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt1316_sdw_priv *rt1316 =
+		snd_soc_component_get_drvdata(component);
+
+	if (tx_mask)
+		return -EINVAL;
+
+	if (slots > 2)
+		return -EINVAL;
+
+	rt1316->rx_mask = rx_mask;
+	rt1316->slots = slots;
+	/* slot_width is not used since it's irrelevant for SoundWire */
+
+	return 0;
+}
+
+static int rt1316_sdw_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 rt1316_sdw_priv *rt1316 =
+		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_data *stream;
+	int retval, port, num_channels, ch_mask;
+
+	dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!stream)
+		return -EINVAL;
+
+	if (!rt1316->sdw_slave)
+		return -EINVAL;
+
+	/* SoundWire specific configuration */
+	/* port 1 for playback */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		direction = SDW_DATA_DIR_RX;
+		port = 1;
+	} else {
+		direction = SDW_DATA_DIR_TX;
+		port = 2;
+	}
+
+	if (rt1316->slots) {
+		num_channels = rt1316->slots;
+		ch_mask = rt1316->rx_mask;
+	} else {
+		num_channels = params_channels(params);
+		ch_mask = (1 << num_channels) - 1;
+	}
+
+	stream_config.frame_rate = params_rate(params);
+	stream_config.ch_count = num_channels;
+	stream_config.bps = snd_pcm_format_width(params_format(params));
+	stream_config.direction = direction;
+
+	port_config.ch_mask = ch_mask;
+	port_config.num = port;
+
+	retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config,
+				&port_config, 1, stream->sdw_stream);
+	if (retval) {
+		dev_err(dai->dev, "Unable to configure port\n");
+		return retval;
+	}
+
+	return 0;
+}
+
+static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt1316_sdw_priv *rt1316 =
+		snd_soc_component_get_drvdata(component);
+	struct sdw_stream_data *stream =
+		snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!rt1316->sdw_slave)
+		return -EINVAL;
+
+	sdw_stream_remove_slave(rt1316->sdw_slave, stream->sdw_stream);
+	return 0;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static struct sdw_slave_ops rt1316_slave_ops = {
+	.read_prop = rt1316_read_prop,
+	.update_status = rt1316_update_status,
+};
+
+static const struct snd_soc_component_driver soc_component_sdw_rt1316 = {
+	.controls = rt1316_snd_controls,
+	.num_controls = ARRAY_SIZE(rt1316_snd_controls),
+	.dapm_widgets = rt1316_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(rt1316_dapm_widgets),
+	.dapm_routes = rt1316_dapm_routes,
+	.num_dapm_routes = ARRAY_SIZE(rt1316_dapm_routes),
+};
+
+static const struct snd_soc_dai_ops rt1316_aif_dai_ops = {
+	.hw_params = rt1316_sdw_hw_params,
+	.hw_free	= rt1316_sdw_pcm_hw_free,
+	.set_sdw_stream	= rt1316_set_sdw_stream,
+	.shutdown	= rt1316_sdw_shutdown,
+	.set_tdm_slot	= rt1316_sdw_set_tdm_slot,
+};
+
+#define RT1316_STEREO_RATES SNDRV_PCM_RATE_48000
+#define RT1316_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1316_sdw_dai[] = {
+	{
+		.name = "rt1316-aif",
+		.playback = {
+			.stream_name = "DP1 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT1316_STEREO_RATES,
+			.formats = RT1316_FORMATS,
+		},
+		.capture = {
+			.stream_name = "DP2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT1316_STEREO_RATES,
+			.formats = RT1316_FORMATS,
+		},
+		.ops = &rt1316_aif_dai_ops,
+	},
+};
+
+static int rt1316_sdw_init(struct device *dev, struct regmap *regmap,
+				struct sdw_slave *slave)
+{
+	struct rt1316_sdw_priv *rt1316;
+	int ret;
+
+	rt1316 = devm_kzalloc(dev, sizeof(*rt1316), GFP_KERNEL);
+	if (!rt1316)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, rt1316);
+	rt1316->sdw_slave = slave;
+	rt1316->regmap = regmap;
+
+	/*
+	 * Mark hw_init to false
+	 * HW init will be performed when device reports present
+	 */
+	rt1316->hw_init = false;
+	rt1316->first_hw_init = false;
+
+	ret =  devm_snd_soc_register_component(dev,
+				&soc_component_sdw_rt1316,
+				rt1316_sdw_dai,
+				ARRAY_SIZE(rt1316_sdw_dai));
+
+	dev_dbg(&slave->dev, "%s\n", __func__);
+
+	return ret;
+}
+
+static int rt1316_sdw_probe(struct sdw_slave *slave,
+				const struct sdw_device_id *id)
+{
+	struct regmap *regmap;
+
+	/* Regmap Initialization */
+	regmap = devm_regmap_init_sdw(slave, &rt1316_sdw_regmap);
+	if (!regmap)
+		return -EINVAL;
+
+	return rt1316_sdw_init(&slave->dev, regmap, slave);
+}
+
+static const struct sdw_device_id rt1316_id[] = {
+	SDW_SLAVE_ENTRY_EXT(0x025d, 0x1316, 0x3, 0x1, 0),
+	{},
+};
+MODULE_DEVICE_TABLE(sdw, rt1316_id);
+
+static int __maybe_unused rt1316_dev_suspend(struct device *dev)
+{
+	struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+
+	if (!rt1316->hw_init)
+		return 0;
+
+	regcache_cache_only(rt1316->regmap, true);
+
+	return 0;
+}
+
+#define RT1316_PROBE_TIMEOUT 2000
+
+static int __maybe_unused rt1316_dev_resume(struct device *dev)
+{
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
+	unsigned long time;
+
+	if (!rt1316->hw_init)
+		return 0;
+
+	if (!slave->unattach_request)
+		goto regmap_sync;
+
+	time = wait_for_completion_timeout(&slave->initialization_complete,
+				msecs_to_jiffies(RT1316_PROBE_TIMEOUT));
+	if (!time) {
+		dev_err(&slave->dev, "Initialization not complete, timed out\n");
+		return -ETIMEDOUT;
+	}
+
+regmap_sync:
+	slave->unattach_request = 0;
+	regcache_cache_only(rt1316->regmap, false);
+	regcache_sync(rt1316->regmap);
+
+	return 0;
+}
+
+static const struct dev_pm_ops rt1316_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume)
+	SET_RUNTIME_PM_OPS(rt1316_dev_suspend, rt1316_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1316_sdw_driver = {
+	.driver = {
+		.name = "rt1316-sdca",
+		.owner = THIS_MODULE,
+		.pm = &rt1316_pm,
+	},
+	.probe = rt1316_sdw_probe,
+	.ops = &rt1316_slave_ops,
+	.id_table = rt1316_id,
+};
+module_sdw_driver(rt1316_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1316 driver SDCA SDW");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h
new file mode 100644
index 000000000000..50e51bf2bf88
--- /dev/null
+++ b/sound/soc/codecs/rt1316-sdw.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1316-sdw.h -- RT1316 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1316_SDW_H__
+#define __RT1316_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* RT1316 SDCA function topology */
+#define FUN_SMART_AMP 0x04
+
+/* RT1316 SDCA entity */
+#define RT1316_SDCA_ENT_PDE23 0x31
+#define RT1316_SDCA_ENT_PDE27 0x32
+#define RT1316_SDCA_ENT_PDE22 0x33
+#define RT1316_SDCA_ENT_PDE24 0x34
+#define RT1316_SDCA_ENT_XU24 0x24
+#define RT1316_SDCA_ENT_FU21 0x03
+#define RT1316_SDCA_ENT_UDMPU21 0x02
+
+/* RT1316 SDCA control */
+#define RT1316_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT1316_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1316_SDCA_CTL_BYPASS 0x01
+#define RT1316_SDCA_CTL_FU_MUTE 0x01
+#define RT1316_SDCA_CTL_FU_VOLUME 0x02
+#define RT1316_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+/* RT1316 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* Power State */
+#define PS0 0x00
+#define PS3 0x03
+
+/* Mute Control */
+#define UNMUTE 0x00
+#define MUTE 0x01
+
+static const struct reg_default rt1316_reg_defaults[] = {
+	{ 0x3004, 0x00 },
+	{ 0x3005, 0x00 },
+	{ 0x3206, 0x00 },
+	{ 0xc001, 0x00 },
+	{ 0xc002, 0x00 },
+	{ 0xc003, 0x00 },
+	{ 0xc004, 0x00 },
+	{ 0xc005, 0x00 },
+	{ 0xc006, 0x00 },
+	{ 0xc007, 0x00 },
+	{ 0xc008, 0x00 },
+	{ 0xc009, 0x00 },
+	{ 0xc00a, 0x00 },
+	{ 0xc00b, 0x00 },
+	{ 0xc00c, 0x00 },
+	{ 0xc00d, 0x00 },
+	{ 0xc00e, 0x00 },
+	{ 0xc00f, 0x00 },
+	{ 0xc010, 0xa5 },
+	{ 0xc011, 0x00 },
+	{ 0xc012, 0xff },
+	{ 0xc013, 0xff },
+	{ 0xc014, 0x40 },
+	{ 0xc015, 0x00 },
+	{ 0xc016, 0x00 },
+	{ 0xc017, 0x00 },
+	{ 0xc605, 0x30 },
+	{ 0xc700, 0x0a },
+	{ 0xc701, 0xaa },
+	{ 0xc702, 0x1a },
+	{ 0xc703, 0x0a },
+	{ 0xc710, 0x80 },
+	{ 0xc711, 0x00 },
+	{ 0xc712, 0x3e },
+	{ 0xc713, 0x80 },
+	{ 0xc714, 0x80 },
+	{ 0xc715, 0x06 },
+	{ 0xd101, 0x00 },
+	{ 0xd102, 0x30 },
+	{ 0xd103, 0x00 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_FU21, RT1316_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_XU24, RT1316_SDCA_CTL_BYPASS, 0), 0x01 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE23, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE22, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUN_SMART_AMP, RT1316_SDCA_ENT_PDE24, RT1316_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+struct rt1316_sdw_priv {
+	struct snd_soc_component *component;
+	struct regmap *regmap;
+	struct sdw_slave *sdw_slave;
+	enum sdw_slave_status status;
+	struct sdw_bus_params params;
+	bool hw_init;
+	bool first_hw_init;
+	int rx_mask;
+	int slots;
+};
+
+struct sdw_stream_data {
+	struct sdw_stream_runtime *sdw_stream;
+};
+
+#endif /* __RT1316_SDW_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v2 5/5] ASoC/SoundWire: rt711-sdca: Add RT711 SDCA vendor-specific driver
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
                   ` (3 preceding siblings ...)
  2020-11-30 14:40 ` [PATCH v2 4/5] ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver Bard Liao
@ 2020-11-30 14:40 ` Bard Liao
  2020-12-01  2:42 ` [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Liao, Bard
  2020-12-02 17:19 ` (subset) " Mark Brown
  6 siblings, 0 replies; 16+ messages in thread
From: Bard Liao @ 2020-11-30 14:40 UTC (permalink / raw)
  To: alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

From: Shuming Fan <shumingf@realtek.com>

This is the initial codec driver for rt711 SDCA version.

Signed-off-by: Shuming Fan <shumingf@realtek.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com>
---
 sound/soc/codecs/Kconfig          |    7 +
 sound/soc/codecs/Makefile         |    2 +
 sound/soc/codecs/rt711-sdca-sdw.c |  424 +++++++++
 sound/soc/codecs/rt711-sdca-sdw.h |  101 ++
 sound/soc/codecs/rt711-sdca.c     | 1482 +++++++++++++++++++++++++++++
 sound/soc/codecs/rt711-sdca.h     |  246 +++++
 6 files changed, 2262 insertions(+)
 create mode 100644 sound/soc/codecs/rt711-sdca-sdw.c
 create mode 100644 sound/soc/codecs/rt711-sdca-sdw.h
 create mode 100644 sound/soc/codecs/rt711-sdca.c
 create mode 100644 sound/soc/codecs/rt711-sdca.h

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index ebc124142f90..a9064f279d33 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -176,6 +176,7 @@ config SND_SOC_ALL_CODECS
 	imply SND_SOC_RT5682_SDW
 	imply SND_SOC_RT700_SDW
 	imply SND_SOC_RT711_SDW
+	imply SND_SOC_RT711_SDCA_SDW
 	imply SND_SOC_RT715_SDW
 	imply SND_SOC_RT715_SDCA_SDW
 	imply SND_SOC_RT1308_SDW
@@ -1214,6 +1215,12 @@ config SND_SOC_RT711_SDW
 	select SND_SOC_RT711
 	select REGMAP_SOUNDWIRE
 
+config SND_SOC_RT711_SDCA_SDW
+	tristate "Realtek RT711 SDCA Codec - SDW"
+	depends on SOUNDWIRE
+	select REGMAP_SOUNDWIRE
+	select REGMAP_SOUNDWIRE_MBQ
+
 config SND_SOC_RT715
 	tristate
 
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 601bbb8b46e7..8bdd587bbd3b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -188,6 +188,7 @@ snd-soc-rt5682-sdw-objs := rt5682-sdw.o
 snd-soc-rt5682-i2c-objs := rt5682-i2c.o
 snd-soc-rt700-objs := rt700.o rt700-sdw.o
 snd-soc-rt711-objs := rt711.o rt711-sdw.o
+snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o
 snd-soc-rt715-objs := rt715.o rt715-sdw.o
 snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
 snd-soc-sgtl5000-objs := sgtl5000.o
@@ -500,6 +501,7 @@ obj-$(CONFIG_SND_SOC_RT5682_I2C)	+= snd-soc-rt5682-i2c.o
 obj-$(CONFIG_SND_SOC_RT5682_SDW)	+= snd-soc-rt5682-sdw.o
 obj-$(CONFIG_SND_SOC_RT700)     += snd-soc-rt700.o
 obj-$(CONFIG_SND_SOC_RT711)     += snd-soc-rt711.o
+obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW)     += snd-soc-rt711-sdca.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_SGTL5000)  += snd-soc-sgtl5000.o
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
new file mode 100644
index 000000000000..6aaf9e09c118
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdw-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/module.h>
+
+#include "rt711-sdca.h"
+#include "rt711-sdca-sdw.h"
+
+static bool rt711_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case SDW_DP0_INT:
+	case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INTMASK4:
+	case 0x201a ... 0x2027:
+	case 0x2029 ... 0x202a:
+	case 0x202d ... 0x2034:
+	case 0x2200 ... 0x2204:
+	case 0x2206 ... 0x2212:
+	case 0x2220 ... 0x2223:
+	case 0x2230 ... 0x2239:
+	case 0x2f01 ... 0x2f0f:
+	case 0x2f30 ... 0x2f36:
+	case 0x2f50 ... 0x2f5a:
+	case 0x2f60:
+	case 0x3200 ... 0x3212:
+	case SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0):
+	case SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0):
+	case SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+		SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+	case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt711_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case SDW_DP0_INT:
+	case SDW_SCP_SDCA_INT1 ... SDW_SCP_SDCA_INT4:
+	case 0x201b:
+	case 0x201c:
+	case 0x201d:
+	case 0x201f:
+	case 0x2021:
+	case 0x2023:
+	case 0x2230:
+	case 0x202d ... 0x202f: /* BRA */
+	case 0x2200 ... 0x2212: /* i2c debug */
+	case RT711_RC_CAL_STATUS:
+	case SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0):
+	case SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ...
+		SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+	case RT711_BUF_ADDR_HID1 ... RT711_BUF_ADDR_HID2:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt711_sdca_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2000000 ... 0x20000ff:
+	case 0x5600000 ... 0x56000ff:
+	case 0x5700000 ... 0x57000ff:
+	case 0x5800000 ... 0x58000ff:
+	case 0x5900000 ... 0x59000ff:
+	case 0x5b00000 ... 0x5b000ff:
+	case 0x5f00000 ... 0x5f000ff:
+	case 0x6100000 ... 0x61000ff:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool rt711_sdca_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case 0x2000000:
+	case 0x200001a:
+	case 0x2000046:
+	case 0x2000080:
+	case 0x2000081:
+	case 0x2000083:
+	case 0x5800000:
+	case 0x5800001:
+	case 0x5f00001:
+	case 0x6100008:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config rt711_sdca_regmap = {
+	.reg_bits = 32,
+	.val_bits = 8,
+	.readable_reg = rt711_sdca_readable_register,
+	.volatile_reg = rt711_sdca_volatile_register,
+	.max_register = 0x44ffffff,
+	.reg_defaults = rt711_sdca_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static const struct regmap_config rt711_sdca_mbq_regmap = {
+	.name = "sdw-mbq",
+	.reg_bits = 32,
+	.val_bits = 16,
+	.readable_reg = rt711_sdca_mbq_readable_register,
+	.volatile_reg = rt711_sdca_mbq_volatile_register,
+	.max_register = 0x40800f12,
+	.reg_defaults = rt711_sdca_mbq_defaults,
+	.num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults),
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static int rt711_sdca_update_status(struct sdw_slave *slave,
+				enum sdw_slave_status status)
+{
+	struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+	/* Update the status */
+	rt711->status = status;
+
+	if (status == SDW_SLAVE_UNATTACHED)
+		rt711->hw_init = false;
+
+	if (status == SDW_SLAVE_ATTACHED) {
+		if (rt711->hs_jack) {
+			regcache_cache_only(rt711->regmap, false);
+			regcache_cache_bypass(rt711->regmap, true);
+			regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK1,
+				SDW_SCP_SDCA_INTMASK_SDCA_0);
+			regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK2,
+				SDW_SCP_SDCA_INTMASK_SDCA_8);
+			regcache_cache_bypass(rt711->regmap, false);
+		} else {
+			regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK1, 0);
+			regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK2, 0);
+		}
+	}
+
+	/*
+	 * Perform initialization only if slave status is present and
+	 * hw_init flag is false
+	 */
+	if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED)
+		return 0;
+
+	/* perform I/O transfers required for Slave initialization */
+	return rt711_sdca_io_init(&slave->dev, slave);
+}
+
+static int rt711_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;
+
+	prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+	prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+	prop->is_sdca = true;
+
+	prop->paging_support = true;
+
+	/* first we need to allocate memory for set bits in port lists */
+	prop->source_ports = 0x14; /* BITMAP: 00010100 */
+	prop->sink_ports = 0x8; /* BITMAP:  00001000 */
+
+	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 = 20;
+
+	/* wake-up event */
+	prop->wake_capable = 1;
+
+	return 0;
+}
+
+static int rt711_sdca_interrupt_callback(struct sdw_slave *slave,
+					struct sdw_slave_intr_status *status)
+{
+	struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+	int ret, stat;
+	int count = 0, retry = 3;
+	unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0;
+	unsigned int buf;
+
+	dev_dbg(&slave->dev,
+		"%s control_port_stat=%x, sdca_cascade=%x", __func__,
+		status->control_port, status->sdca_cascade);
+
+	if (cancel_delayed_work_sync(&rt711->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 (rt711->scp_sdca_stat2)
+			scp_sdca_stat2 = rt711->scp_sdca_stat2;
+	}
+
+	ret = regmap_read(rt711->regmap, SDW_SCP_SDCA_INT1, &rt711->scp_sdca_stat1);
+	if (ret < 0)
+		goto io_error;
+	ret = regmap_read(rt711->regmap, SDW_SCP_SDCA_INT2, &rt711->scp_sdca_stat2);
+	if (ret < 0)
+		goto io_error;
+	if (scp_sdca_stat2)
+		rt711->scp_sdca_stat2 |= scp_sdca_stat2;
+
+	do {
+		/* clear flag */
+		ret = regmap_read(rt711->regmap, SDW_SCP_SDCA_INT1, &buf);
+		if (ret < 0)
+			goto io_error;
+		if (buf & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+			ret = regmap_write(rt711->regmap, SDW_SCP_SDCA_INT1, SDW_SCP_SDCA_INTMASK_SDCA_0);
+			if (ret < 0)
+				goto io_error;
+		}
+		ret = regmap_read(rt711->regmap, SDW_SCP_SDCA_INT2, &buf);
+		if (ret < 0)
+			goto io_error;
+		if (buf & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+			ret = regmap_write(rt711->regmap, SDW_SCP_SDCA_INT2, SDW_SCP_SDCA_INTMASK_SDCA_8);
+			if (ret < 0)
+				goto io_error;
+		}
+
+		/* check if flag clear or not */
+		ret = regmap_read(rt711->regmap, SDW_DP0_INT, &buf);
+		if (ret < 0)
+			goto io_error;
+		sdca_cascade = buf & SDW_DP0_SDCA_CASCADE;
+
+		ret = regmap_read(rt711->regmap, SDW_SCP_SDCA_INT1, &buf);
+		if (ret < 0)
+			goto io_error;
+		scp_sdca_stat1 = buf & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+		ret = regmap_read(rt711->regmap, SDW_SCP_SDCA_INT2, &buf);
+		if (ret < 0)
+			goto io_error;
+		scp_sdca_stat2 = buf & 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__,
+			rt711->scp_sdca_stat1, rt711->scp_sdca_stat2);
+
+	if (status->sdca_cascade)
+		mod_delayed_work(system_power_efficient_wq,
+			&rt711->jack_detect_work, msecs_to_jiffies(30));
+
+	return 0;
+
+io_error:
+	pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+	return ret;
+}
+
+static struct sdw_slave_ops rt711_sdca_slave_ops = {
+	.read_prop = rt711_sdca_read_prop,
+	.interrupt_callback = rt711_sdca_interrupt_callback,
+	.update_status = rt711_sdca_update_status,
+};
+
+static int rt711_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, &rt711_sdca_mbq_regmap);
+	if (IS_ERR(mbq_regmap))
+		return PTR_ERR(mbq_regmap);
+
+	regmap = devm_regmap_init_sdw(slave, &rt711_sdca_regmap);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return rt711_sdca_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt711_sdca_sdw_remove(struct sdw_slave *slave)
+{
+	struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev);
+
+	if (rt711 && rt711->hw_init)
+		cancel_delayed_work(&rt711->jack_detect_work);
+
+	return 0;
+}
+
+static const struct sdw_device_id rt711_sdca_id[] = {
+	SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x3, 0x1, 0),
+	{},
+};
+MODULE_DEVICE_TABLE(sdw, rt711_sdca_id);
+
+static int __maybe_unused rt711_sdca_dev_suspend(struct device *dev)
+{
+	struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+
+	if (!rt711->hw_init)
+		return 0;
+
+	cancel_delayed_work_sync(&rt711->jack_detect_work);
+	cancel_delayed_work_sync(&rt711->jack_btn_check_work);
+
+	regcache_cache_only(rt711->regmap, true);
+	regcache_cache_only(rt711->mbq_regmap, true);
+
+	return 0;
+}
+
+#define RT711_PROBE_TIMEOUT 2000
+
+static int __maybe_unused rt711_sdca_dev_resume(struct device *dev)
+{
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+	unsigned long time;
+
+	if (!rt711->hw_init)
+		return 0;
+
+	if (!slave->unattach_request)
+		goto regmap_sync;
+
+	time = wait_for_completion_timeout(&slave->initialization_complete,
+				msecs_to_jiffies(RT711_PROBE_TIMEOUT));
+	if (!time) {
+		dev_err(&slave->dev, "Initialization not complete, timed out\n");
+		return -ETIMEDOUT;
+	}
+
+regmap_sync:
+	slave->unattach_request = 0;
+	regcache_cache_only(rt711->regmap, false);
+	regcache_sync(rt711->regmap);
+	regcache_cache_only(rt711->mbq_regmap, false);
+	regcache_sync(rt711->mbq_regmap);
+	return 0;
+}
+
+static const struct dev_pm_ops rt711_sdca_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume)
+	SET_RUNTIME_PM_OPS(rt711_sdca_dev_suspend, rt711_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt711_sdca_sdw_driver = {
+	.driver = {
+		.name = "rt711-sdca",
+		.owner = THIS_MODULE,
+		.pm = &rt711_sdca_pm,
+	},
+	.probe = rt711_sdca_sdw_probe,
+	.remove = rt711_sdca_sdw_remove,
+	.ops = &rt711_sdca_slave_ops,
+	.id_table = rt711_sdca_id,
+};
+module_sdw_driver(rt711_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT711 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca-sdw.h b/sound/soc/codecs/rt711-sdca-sdw.h
new file mode 100644
index 000000000000..b0c0f1f65f40
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca-sdw.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt711-sdw-sdca.h -- RT711 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDW_SDCA_H__
+#define __RT711_SDW_SDCA_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+static const struct reg_default rt711_sdca_reg_defaults[] = {
+	{ 0x005c, 0x00 },
+	{ 0x005d, 0x00 },
+	{ 0x201a, 0x00 },
+	{ 0x201e, 0x00 },
+	{ 0x201f, 0x00 },
+	{ 0x2020, 0x00 },
+	{ 0x2021, 0x00 },
+	{ 0x2022, 0x00 },
+	{ 0x2023, 0x00 },
+	{ 0x2024, 0x00 },
+	{ 0x2025, 0x01 },
+	{ 0x2026, 0x00 },
+	{ 0x2027, 0x00 },
+	{ 0x2029, 0x00 },
+	{ 0x202a, 0x00 },
+	{ 0x202d, 0x00 },
+	{ 0x202e, 0x00 },
+	{ 0x202f, 0x00 },
+	{ 0x2030, 0x00 },
+	{ 0x2031, 0x00 },
+	{ 0x2032, 0x00 },
+	{ 0x2033, 0x00 },
+	{ 0x2230, 0x00 },
+	{ 0x2231, 0x2f },
+	{ 0x2232, 0x80 },
+	{ 0x2233, 0x00 },
+	{ 0x2234, 0x00 },
+	{ 0x2235, 0x00 },
+	{ 0x2236, 0x00 },
+	{ 0x2237, 0x00 },
+	{ 0x2238, 0x00 },
+	{ 0x2239, 0x00 },
+	{ 0x2f01, 0x00 },
+	{ 0x2f02, 0x09 },
+	{ 0x2f03, 0x00 },
+	{ 0x2f04, 0x00 },
+	{ 0x2f05, 0x0b },
+	{ 0x2f06, 0x01 },
+	{ 0x2f08, 0x00 },
+	{ 0x2f09, 0x00 },
+	{ 0x2f0a, 0x00 },
+	{ 0x2f0b, 0x00 },
+	{ 0x2f0c, 0x00 },
+	{ 0x2f0d, 0x00 },
+	{ 0x2f0e, 0x14 },
+	{ 0x2f0f, 0x00 },
+	{ 0x2f50, 0x03 },
+	{ 0x2f5a, 0x00 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDE28, RT711_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L), 0x01 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R), 0x01 },
+};
+
+static const struct reg_default rt711_sdca_mbq_defaults[] = {
+	{ 0x2000009, 0x1029 },
+	{ 0x2000011, 0x007a },
+	{ 0x200001a, 0x8003 },
+	{ 0x2000045, 0x5289 },
+	{ 0x2000048, 0x8049 },
+	{ 0x200004a, 0xa83b },
+	{ 0x200006b, 0x5064 },
+	{ 0x200006f, 0x058b },
+	{ 0x5800000, 0x0008 },
+	{ 0x5800001, 0x0000 },
+	{ 0x5f00001, 0x000a },
+	{ 0x6100000, 0x6100 },
+	{ 0x6100035, 0x0060 },
+	{ 0x6100036, 0x0029 },
+	{ 0x610003f, 0xff12 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L), 0x00 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R), 0x00 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 },
+	{ SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L), 0x00 },
+	{ SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R), 0x00 },
+};
+
+#endif /* __RT711_SDW_SDCA_H__ */
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
new file mode 100644
index 000000000000..19276180b329
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -0,0 +1,1482 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt711-sdca.c -- rt711 SDCA ALSA SoC audio driver
+//
+// Copyright(c) 2020 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/jack.h>
+
+#include "rt711-sdca.h"
+
+static int rt711_sdca_index_write(struct rt711_sdca_priv *rt711,
+		unsigned int nid, unsigned int reg, unsigned int value)
+{
+	int ret;
+	struct regmap *regmap = rt711->mbq_regmap;
+	unsigned int addr = (nid << 20) | reg;
+
+	ret = regmap_write(regmap, addr, value);
+	if (ret < 0)
+		dev_err(rt711->component->dev,
+			"Failed to set private value: %06x <= %04x ret=%d\n",
+			addr, value, ret);
+
+	return ret;
+}
+
+static int rt711_sdca_index_read(struct rt711_sdca_priv *rt711,
+		unsigned int nid, unsigned int reg, unsigned int *value)
+{
+	int ret;
+	struct regmap *regmap = rt711->mbq_regmap;
+	unsigned int addr = (nid << 20) | reg;
+
+	ret = regmap_read(regmap, addr, value);
+	if (ret < 0)
+		dev_err(rt711->component->dev,
+			"Failed to get private value: %06x => %04x ret=%d\n",
+			addr, *value, ret);
+
+	return ret;
+}
+
+static int rt711_sdca_index_update_bits(struct rt711_sdca_priv *rt711,
+	unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+	unsigned int tmp;
+	int ret;
+
+	ret = rt711_sdca_index_read(rt711, nid, reg, &tmp);
+	if (ret < 0)
+		return ret;
+
+	set_mask_bits(&tmp, mask, val);
+	return rt711_sdca_index_write(rt711, nid, reg, tmp);
+}
+
+static void rt711_sdca_reset(struct rt711_sdca_priv *rt711)
+{
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+		RT711_PARA_VERB_CTL, RT711_HIDDEN_REG_SW_RESET,
+		RT711_HIDDEN_REG_SW_RESET);
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+}
+
+static int rt711_sdca_calibration(struct rt711_sdca_priv *rt711)
+{
+	unsigned int val, loop_rc = 0, loop_dc = 0;
+	struct device *dev;
+	struct regmap *regmap = rt711->regmap;
+	int chk_cnt = 100;
+	int ret = 0;
+
+	mutex_lock(&rt711->calibrate_mutex);
+	dev = regmap_get_device(regmap);
+
+	regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val);
+	/* RC calibration */
+	if (!(val & 0x40))
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL,
+			RT711_MISC_POWER_CTL0, 0x0010, 0x0010);
+
+	for (loop_rc = 0; loop_rc < chk_cnt && !(val & 0x40); loop_rc++) {
+		usleep_range(10000, 11000);
+		ret = regmap_read(rt711->regmap, RT711_RC_CAL_STATUS, &val);
+		if (ret < 0)
+			goto _cali_fail_;
+	}
+	if (loop_rc == chk_cnt)
+		dev_err(dev, "%s, RC calibration time-out!\n", __func__);
+
+	/* HP calibration by manual mode setting */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+		RT711_FSM_CTL, 0x2000, 0x2000);
+
+	/* Calibration manual mode */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+		RT711_FSM_CTL, 0xf, RT711_CALI_CTL);
+
+	/* reset HP calibration */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+		RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST, 0x00);
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+		RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_FORCE_CALI_RST,
+		RT711_DAC_DC_FORCE_CALI_RST);
+
+	/* cal_clk_en_reg */
+	if (rt711->hw_ver == RT711_VER_VD0)
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+			RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_CLK_EN,
+			RT711_DAC_DC_CALI_CLK_EN);
+
+	/* trigger */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_CALI,
+		RT711_DAC_DC_CALI_CTL1, RT711_DAC_DC_CALI_TRIGGER,
+		RT711_DAC_DC_CALI_TRIGGER);
+
+	/* wait for calibration process */
+	rt711_sdca_index_read(rt711, RT711_VENDOR_CALI,
+		RT711_DAC_DC_CALI_CTL1, &val);
+
+	for (loop_dc = 0; loop_dc < chk_cnt &&
+		(val & RT711_DAC_DC_CALI_TRIGGER); loop_dc++) {
+		usleep_range(10000, 11000);
+		ret = rt711_sdca_index_read(rt711, RT711_VENDOR_CALI,
+			RT711_DAC_DC_CALI_CTL1, &val);
+		if (ret < 0)
+			goto _cali_fail_;
+	}
+	if (loop_dc == chk_cnt)
+		dev_err(dev, "%s, calibration time-out!\n", __func__);
+
+	if (loop_dc == chk_cnt || loop_rc == chk_cnt)
+		ret = -ETIMEDOUT;
+
+_cali_fail_:
+	/* enable impedance sense */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+		RT711_FSM_CTL, RT711_FSM_IMP_EN, RT711_FSM_IMP_EN);
+
+	/* release HP-JD and trigger FSM */
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_DIGITAL_MISC_CTRL4, 0x201b);
+
+	mutex_unlock(&rt711->calibrate_mutex);
+	dev_dbg(dev, "%s calibration complete, ret=%d\n", __func__, ret);
+	return ret;
+}
+
+static unsigned int rt711_sdca_button_detect(struct rt711_sdca_priv *rt711)
+{
+	unsigned int btn_type = 0, offset, idx, val, owner;
+	int ret;
+	unsigned char buf[3];
+
+	/* get current UMP message owner */
+	ret = regmap_read(rt711->regmap,
+		SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_CURRENT_OWNER, 0),
+		&owner);
+	if (ret < 0)
+		return 0;
+
+	/* if owner is device then there is no button event from device */
+	if (owner == 1)
+		return 0;
+
+	/* read UMP message offset */
+	ret = regmap_read(rt711->regmap,
+		SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+		&offset);
+	if (ret < 0)
+		goto _end_btn_det_;
+
+	for (idx = 0; idx < sizeof(buf); idx++) {
+		ret = regmap_read(rt711->regmap,
+			RT711_BUF_ADDR_HID1 + offset + idx, &val);
+		if (ret < 0)
+			goto _end_btn_det_;
+		buf[idx] = val & 0xff;
+	}
+
+	if (buf[0] == 0x11) {
+		switch (buf[1] & 0xf0) {
+		case 0x10:
+			btn_type |= SND_JACK_BTN_0;
+			break;
+		case 0x20:
+			btn_type |= SND_JACK_BTN_1;
+			break;
+		case 0x40:
+			btn_type |= SND_JACK_BTN_2;
+			break;
+		case 0x80:
+			btn_type |= SND_JACK_BTN_3;
+			break;
+		}
+		switch (buf[2]) {
+		case 0x01:
+		case 0x10:
+			btn_type |= SND_JACK_BTN_0;
+			break;
+		case 0x02:
+		case 0x20:
+			btn_type |= SND_JACK_BTN_1;
+			break;
+		case 0x04:
+		case 0x40:
+			btn_type |= SND_JACK_BTN_2;
+			break;
+		case 0x08:
+		case 0x80:
+			btn_type |= SND_JACK_BTN_3;
+			break;
+		}
+	}
+
+_end_btn_det_:
+	/* Host is owner, so set back to device */
+	if (owner == 0)
+		/* set owner to device */
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01,
+				RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE, 0), 0x01);
+
+	return btn_type;
+}
+
+static int rt711_sdca_headset_detect(struct rt711_sdca_priv *rt711)
+{
+	unsigned int det_mode;
+	int ret;
+
+	/* get detected_mode */
+	ret = regmap_read(rt711->regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_DETECTED_MODE, 0),
+		&det_mode);
+	if (ret < 0)
+		goto io_error;
+
+	switch (det_mode) {
+	case 0x00:
+		rt711->jack_type = 0;
+		break;
+	case 0x03:
+		rt711->jack_type = SND_JACK_HEADPHONE;
+		break;
+	case 0x05:
+		rt711->jack_type = SND_JACK_HEADSET;
+		break;
+	}
+
+	/* write selected_mode */
+	if (det_mode) {
+		ret = regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_SDCA_CTL_SELECTED_MODE, 0),
+			det_mode);
+		if (ret < 0)
+			goto io_error;
+	}
+
+	dev_dbg(&rt711->slave->dev,
+		"%s, detected_mode=0x%x\n", __func__, det_mode);
+
+	return 0;
+
+io_error:
+	pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+	return ret;
+}
+
+static void rt711_sdca_jack_detect_handler(struct work_struct *work)
+{
+	struct rt711_sdca_priv *rt711 =
+		container_of(work, struct rt711_sdca_priv, jack_detect_work.work);
+	int btn_type = 0, ret;
+
+	if (!rt711->hs_jack)
+		return;
+
+	if (!rt711->component->card->instantiated)
+		return;
+
+	/* SDW_SCP_SDCA_INT_SDCA_0 is used for jack detection */
+	if (rt711->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) {
+		ret = rt711_sdca_headset_detect(rt711);
+		if (ret < 0)
+			return;
+	}
+
+	/* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */
+	if (rt711->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8)
+		btn_type = rt711_sdca_button_detect(rt711);
+
+	if (rt711->jack_type == 0)
+		btn_type = 0;
+
+	dev_dbg(&rt711->slave->dev,
+		"in %s, jack_type=0x%x\n", __func__, rt711->jack_type);
+	dev_dbg(&rt711->slave->dev,
+		"in %s, btn_type=0x%x\n", __func__, btn_type);
+	dev_dbg(&rt711->slave->dev,
+		"in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__,
+		rt711->scp_sdca_stat1, rt711->scp_sdca_stat2);
+
+	snd_soc_jack_report(rt711->hs_jack, rt711->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(rt711->hs_jack, rt711->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,
+			&rt711->jack_btn_check_work, msecs_to_jiffies(200));
+	}
+}
+
+static void rt711_sdca_btn_check_handler(struct work_struct *work)
+{
+	struct rt711_sdca_priv *rt711 =
+		container_of(work, struct rt711_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(rt711->regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49, RT711_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(rt711->regmap,
+			SDW_SDCA_CTL(FUN_HID, RT711_SDCA_ENT_HID01, RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0),
+			&offset);
+		if (ret < 0)
+			goto io_error;
+
+		for (idx = 0; idx < sizeof(buf); idx++) {
+			ret = regmap_read(rt711->regmap,
+				RT711_BUF_ADDR_HID1 + offset + idx, &val);
+			if (ret < 0)
+				goto io_error;
+			buf[idx] = val & 0xff;
+		}
+
+		if (buf[0] == 0x11) {
+			switch (buf[1] & 0xf0) {
+			case 0x10:
+				btn_type |= SND_JACK_BTN_0;
+				break;
+			case 0x20:
+				btn_type |= SND_JACK_BTN_1;
+				break;
+			case 0x40:
+				btn_type |= SND_JACK_BTN_2;
+				break;
+			case 0x80:
+				btn_type |= SND_JACK_BTN_3;
+				break;
+			}
+			switch (buf[2]) {
+			case 0x01:
+			case 0x10:
+				btn_type |= SND_JACK_BTN_0;
+				break;
+			case 0x02:
+			case 0x20:
+				btn_type |= SND_JACK_BTN_1;
+				break;
+			case 0x04:
+			case 0x40:
+				btn_type |= SND_JACK_BTN_2;
+				break;
+			case 0x08:
+			case 0x80:
+				btn_type |= SND_JACK_BTN_3;
+				break;
+			}
+		}
+	} else
+		rt711->jack_type = 0;
+
+	dev_dbg(&rt711->slave->dev, "%s, btn_type=0x%x\n",	__func__, btn_type);
+	snd_soc_jack_report(rt711->hs_jack, rt711->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(rt711->hs_jack, rt711->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,
+			&rt711->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 rt711_sdca_jack_init(struct rt711_sdca_priv *rt711)
+{
+	mutex_lock(&rt711->calibrate_mutex);
+
+	if (rt711->hs_jack) {
+		/* Enable HID1 event & set button RTC mode */
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_PUSH_BTN_INT_CTL6, 0x80f0, 0x8000);
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_PUSH_BTN_INT_CTL2, 0x11dd, 0x11dd);
+		rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_PUSH_BTN_INT_CTL7, 0xffff);
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_PUSH_BTN_INT_CTL9, 0xf000, 0x0000);
+
+		/* GE_mode_change_event_en & Hid1_push_button_event_en */
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0c00);
+
+		switch (rt711->jd_src) {
+		case RT711_JD1:
+			/* default settings was already for JD1 */
+			break;
+		case RT711_JD2:
+			rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+				RT711_JD_CTL1, RT711_JD2_DIGITAL_MODE_SEL,
+				RT711_JD2_DIGITAL_MODE_SEL);
+			rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+				RT711_JD_CTL2, RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2,
+				RT711_JD2_2PORT_200K_DECODE_HP | RT711_HP_JD_SEL_JD2);
+			rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+				RT711_CC_DET1,
+				RT711_HP_JD_FINAL_RESULT_CTL_JD12,
+				RT711_HP_JD_FINAL_RESULT_CTL_JD12);
+			break;
+		default:
+			dev_warn(rt711->component->dev, "Wrong JD source\n");
+			break;
+		}
+
+		/* set SCP_SDCA_IntMask1[0]=1 */
+		regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK1,
+			SDW_SCP_SDCA_INTMASK_SDCA_0);
+		/* set SCP_SDCA_IntMask2[0]=1 */
+		regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK2,
+			SDW_SCP_SDCA_INTMASK_SDCA_8);
+		dev_dbg(&rt711->slave->dev, "in %s enable\n", __func__);
+	} else {
+		/* disable HID 1/2 event */
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_GE_MODE_RELATED_CTL, 0x0c00, 0x0000);
+
+		/* set SCP_SDCA_IntMask1[0]=0 */
+		regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK1, 0);
+		/* set SCP_SDCA_IntMask2[0]=0 */
+		regmap_write(rt711->regmap, SDW_SCP_SDCA_INTMASK2, 0);
+		dev_dbg(&rt711->slave->dev, "in %s disable\n", __func__);
+	}
+
+	mutex_unlock(&rt711->calibrate_mutex);
+}
+
+static int rt711_sdca_set_jack_detect(struct snd_soc_component *component,
+	struct snd_soc_jack *hs_jack, void *data)
+{
+	struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	rt711->hs_jack = hs_jack;
+
+	if (!rt711->hw_init) {
+		dev_dbg(&rt711->slave->dev,
+			"%s hw_init not ready yet\n", __func__);
+		return 0;
+	}
+
+	rt711_sdca_jack_init(rt711);
+	return 0;
+}
+
+/* For SDCA control DAC/ADC Gain */
+static int rt711_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+	unsigned int read_l, read_r, gain_l_val, gain_r_val;
+	unsigned int i, adc_vol_flag = 0;
+
+	if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+		strstr(ucontrol->id.name, "FU0F Capture Volume"))
+		adc_vol_flag = 1;
+
+	/* control value to 2's complement value */
+	/* L Channel */
+	gain_l_val = ucontrol->value.integer.value[0];
+	if (gain_l_val > mc->max)
+		gain_l_val = mc->max;
+	read_l = gain_l_val;
+
+	if (mc->shift == 8) /* boost gain */
+		gain_l_val = (gain_l_val * 10) << mc->shift;
+	else { /* ADC/DAC gain */
+		if (adc_vol_flag && gain_l_val > mc->shift)
+			gain_l_val = (gain_l_val - mc->shift) * 75;
+		else
+			gain_l_val = (mc->shift - gain_l_val) * 75;
+		gain_l_val <<= 8;
+		gain_l_val /= 100;
+		if (!(adc_vol_flag && read_l > mc->shift)) {
+			gain_l_val = ~gain_l_val;
+			gain_l_val += 1;
+		}
+		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;
+	read_r = gain_r_val;
+
+	if (mc->shift == 8) /* boost gain */
+		gain_r_val = (gain_r_val * 10) << mc->shift;
+	else { /* ADC/DAC gain */
+		if (adc_vol_flag && gain_r_val > mc->shift)
+			gain_r_val = (gain_r_val - mc->shift) * 75;
+		else
+			gain_r_val = (mc->shift - gain_r_val) * 75;
+		gain_r_val <<= 8;
+		gain_r_val /= 100;
+		if (!(adc_vol_flag && read_r > mc->shift)) {
+			gain_r_val = ~gain_r_val;
+			gain_r_val += 1;
+		}
+		gain_r_val &= 0xffff;
+	}
+
+	for (i = 0; i < 3; i++) { /* retry 3 times at most */
+		/* Lch*/
+		regmap_write(rt711->mbq_regmap, mc->reg, gain_l_val);
+
+		/* Rch */
+		regmap_write(rt711->mbq_regmap, mc->rreg, gain_r_val);
+
+		regmap_read(rt711->mbq_regmap, mc->reg, &read_l);
+		regmap_read(rt711->mbq_regmap, mc->rreg, &read_r);
+		if (read_r == gain_r_val && read_l == gain_l_val)
+			break;
+	}
+
+	return i == 3 ? -EIO : 0;
+}
+
+static int rt711_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+	struct rt711_sdca_priv *rt711 = 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, neg_flag = 0;
+
+	if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
+		strstr(ucontrol->id.name, "FU0F Capture Volume"))
+		adc_vol_flag = 1;
+
+	regmap_read(rt711->mbq_regmap, mc->reg, &read_l);
+	regmap_read(rt711->mbq_regmap, mc->rreg, &read_r);
+
+	/* 2's complement value to control value */
+	if (mc->shift == 8) /* boost gain */
+		ctl_l = (read_l >> mc->shift) / 10;
+	else { /* ADC/DAC gain */
+		ctl_l = read_l;
+		if (read_l & BIT(15)) {
+			ctl_l = 0xffff & ~(read_l - 1);
+			neg_flag = 1;
+		}
+		ctl_l *= 100;
+		ctl_l >>= 8;
+		if (adc_vol_flag) {
+			if (neg_flag)
+				ctl_l = mc->shift - (ctl_l / 75);
+			else
+				ctl_l = mc->shift + (ctl_l / 75);
+		} else
+			ctl_l = mc->max - (ctl_l / 75);
+	}
+
+	neg_flag = 0;
+	if (read_l != read_r) {
+		if (mc->shift == 8) /* boost gain */
+			ctl_r = (read_r >> mc->shift) / 10;
+		else { /* ADC/DAC gain */
+			ctl_r = read_r;
+			if (read_r & BIT(15)) {
+				ctl_r = 0xffff & ~(read_r - 1);
+				neg_flag = 1;
+			}
+			ctl_r *= 100;
+			ctl_r >>= 8;
+			if (adc_vol_flag) {
+				if (neg_flag)
+					ctl_r = mc->shift - (ctl_r / 75);
+				else
+					ctl_r = mc->shift + (ctl_r / 75);
+			} else
+				ctl_r = mc->max - (ctl_r / 75);
+		}
+	} else
+		ctl_r = ctl_l;
+
+	ucontrol->value.integer.value[0] = ctl_l;
+	ucontrol->value.integer.value[1] = ctl_r;
+
+	return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt711_sdca_snd_controls[] = {
+	SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+		0x57, 0x57, 0,
+		rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, out_vol_tlv),
+	SOC_DOUBLE_R("FU1E Capture Switch",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_L),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_MUTE, CH_R),
+		0, 1, 1),
+	SOC_DOUBLE_R("FU0F Capture Switch",
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_L),
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_MUTE, CH_R),
+		0, 1, 1),
+	SOC_DOUBLE_R_EXT_TLV("FU1E Capture Volume",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+		0x17, 0x3f, 0,
+		rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume",
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_L),
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F, RT711_SDCA_CTL_FU_VOLUME, CH_R),
+		0x17, 0x3f, 0,
+		rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, in_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU44 Gain Volume",
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_L),
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PLATFORM_FU44, RT711_SDCA_CTL_FU_CH_GAIN, CH_R),
+		8, 3, 0,
+		rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv),
+	SOC_DOUBLE_R_EXT_TLV("FU15 Gain Volume",
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_L),
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_PLATFORM_FU15, RT711_SDCA_CTL_FU_CH_GAIN, CH_R),
+		8, 3, 0,
+		rt711_sdca_set_gain_get, rt711_sdca_set_gain_put, mic_vol_tlv),
+};
+
+static int rt711_sdca_mux_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_component *component =
+		snd_soc_dapm_kcontrol_component(kcontrol);
+	struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+	unsigned int val = 0, mask_sft;
+
+	if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+		mask_sft = 10;
+	else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+		mask_sft = 13;
+	else
+		return -EINVAL;
+
+	rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_MUX_CTL1, &val);
+
+	ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+	return 0;
+}
+
+static int rt711_sdca_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 rt711_sdca_priv *rt711 = 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;
+
+	if (item[0] >= e->items)
+		return -EINVAL;
+
+	if (strstr(ucontrol->id.name, "ADC 22 Mux"))
+		mask_sft = 10;
+	else if (strstr(ucontrol->id.name, "ADC 23 Mux"))
+		mask_sft = 13;
+	else
+		return -EINVAL;
+
+	val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+	rt711_sdca_index_read(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_MUX_CTL1, &val2);
+	val2 = (val2 >> mask_sft) & 0x7;
+
+	if (val == val2)
+		change = 0;
+	else
+		change = 1;
+
+	if (change)
+		rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+			RT711_HDA_LEGACY_MUX_CTL1, 0x7 << mask_sft,
+			val << mask_sft);
+
+	snd_soc_dapm_mux_update_power(dapm, kcontrol,
+		item[0], e, NULL);
+
+	return change;
+}
+
+static const char * const adc_mux_text[] = {
+	"MIC2",
+	"LINE1",
+	"LINE2",
+	"DMIC",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+	rt711_adc22_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+	rt711_adc23_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt711_sdca_adc22_mux =
+	SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt711_adc22_enum,
+			rt711_sdca_mux_get, rt711_sdca_mux_put);
+
+static const struct snd_kcontrol_new rt711_sdca_adc23_mux =
+	SOC_DAPM_ENUM_EXT("ADC 23 Mux", rt711_adc23_enum,
+			rt711_sdca_mux_get, rt711_sdca_mux_put);
+
+static int rt711_sdca_fu05_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+				RT711_SDCA_CTL_FU_MUTE, CH_L),
+				UNMUTE);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+				RT711_SDCA_CTL_FU_MUTE, CH_R),
+				UNMUTE);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+				RT711_SDCA_CTL_FU_MUTE, CH_L),
+				MUTE);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU05,
+				RT711_SDCA_CTL_FU_MUTE, CH_R),
+				MUTE);
+		break;
+	}
+	return 0;
+}
+
+static int rt711_sdca_fu0f_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+				RT711_SDCA_CTL_FU_MUTE, CH_L),
+				UNMUTE);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+				RT711_SDCA_CTL_FU_MUTE, CH_R),
+				UNMUTE);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+				RT711_SDCA_CTL_FU_MUTE, CH_L),
+				MUTE);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_USER_FU0F,
+				RT711_SDCA_CTL_FU_MUTE, CH_R),
+				MUTE);
+		break;
+	}
+	return 0;
+}
+
+static int rt711_sdca_fu1e_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+				RT711_SDCA_CTL_FU_MUTE, CH_L),
+				UNMUTE);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+				RT711_SDCA_CTL_FU_MUTE, CH_R),
+				UNMUTE);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+				RT711_SDCA_CTL_FU_MUTE, CH_L),
+				MUTE);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_USER_FU1E,
+				RT711_SDCA_CTL_FU_MUTE, CH_R),
+				MUTE);
+			break;
+	}
+	return 0;
+}
+
+static int rt711_sdca_pde28_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDE28,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDE28,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		break;
+	}
+	return 0;
+}
+
+static int rt711_sdca_pde29_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDE29,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDE29,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		break;
+	}
+	return 0;
+}
+
+static int rt711_sdca_pde2a_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_PDE2A,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_PDE2A,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		break;
+	}
+	return 0;
+}
+
+static int rt711_sdca_line1_power_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+	static unsigned int sel_mode = 0xffff;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_read(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49,
+				RT711_SDCA_CTL_SELECTED_MODE, 0),
+				&sel_mode);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_LINE1,
+				RT711_SDCA_CTL_VENDOR_DEF, 0),
+				0x1);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49,
+				RT711_SDCA_CTL_SELECTED_MODE, 0),
+				0x7);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_LINE1,
+				RT711_SDCA_CTL_VENDOR_DEF, 0),
+				0x0);
+		if (sel_mode != 0xffff)
+			regmap_write(rt711->regmap,
+				SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_GE49,
+				RT711_SDCA_CTL_SELECTED_MODE, 0),
+				sel_mode);
+		break;
+	}
+
+	return 0;
+}
+
+static int rt711_sdca_line2_power_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 rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDELINE2,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS0);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_LINE2,
+				RT711_SDCA_CTL_VENDOR_DEF, 0),
+				0x1);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_LINE2,
+				RT711_SDCA_CTL_VENDOR_DEF, 0),
+				0x0);
+		regmap_write(rt711->regmap,
+			SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_PDELINE2,
+				RT711_SDCA_CTL_REQ_POWER_STATE, 0),
+				PS3);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget rt711_sdca_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("HP"),
+	SND_SOC_DAPM_INPUT("MIC2"),
+	SND_SOC_DAPM_INPUT("DMIC1"),
+	SND_SOC_DAPM_INPUT("DMIC2"),
+	SND_SOC_DAPM_INPUT("LINE1"),
+	SND_SOC_DAPM_INPUT("LINE2"),
+
+	SND_SOC_DAPM_PGA_E("LINE1 Power", SND_SOC_NOPM,
+		0, 0, NULL, 0, rt711_sdca_line1_power_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_PGA_E("LINE2 Power", SND_SOC_NOPM,
+		0, 0, NULL, 0, rt711_sdca_line2_power_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_SUPPLY("PDE 28", SND_SOC_NOPM, 0, 0,
+		rt711_sdca_pde28_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("PDE 29", SND_SOC_NOPM, 0, 0,
+		rt711_sdca_pde29_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("PDE 2A", SND_SOC_NOPM, 0, 0,
+		rt711_sdca_pde2a_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_DAC_E("FU 05", NULL, SND_SOC_NOPM, 0, 0,
+		rt711_sdca_fu05_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_ADC_E("FU 0F", NULL, SND_SOC_NOPM, 0, 0,
+		rt711_sdca_fu0f_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+		rt711_sdca_fu1e_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0,
+		&rt711_sdca_adc22_mux),
+	SND_SOC_DAPM_MUX("ADC 23 Mux", SND_SOC_NOPM, 0, 0,
+		&rt711_sdca_adc23_mux),
+
+	SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DP4TX", "DP4 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt711_sdca_audio_map[] = {
+	{"FU 05", NULL, "DP3RX"},
+	{"DP2TX", NULL, "FU 0F"},
+	{"DP4TX", NULL, "FU 1E"},
+
+	{"LINE1 Power", NULL, "LINE1"},
+	{"LINE2 Power", NULL, "LINE2"},
+	{"HP", NULL, "PDE 28"},
+	{"FU 0F", NULL, "PDE 29"},
+	{"FU 1E", NULL, "PDE 2A"},
+
+	{"FU 0F", NULL, "ADC 22 Mux"},
+	{"FU 1E", NULL, "ADC 23 Mux"},
+	{"ADC 22 Mux", "DMIC", "DMIC1"},
+	{"ADC 22 Mux", "LINE1", "LINE1 Power"},
+	{"ADC 22 Mux", "LINE2", "LINE2 Power"},
+	{"ADC 22 Mux", "MIC2", "MIC2"},
+	{"ADC 23 Mux", "DMIC", "DMIC2"},
+	{"ADC 23 Mux", "LINE1", "LINE1 Power"},
+	{"ADC 23 Mux", "LINE2", "LINE2 Power"},
+	{"ADC 23 Mux", "MIC2", "MIC2"},
+
+	{"HP", NULL, "FU 05"},
+};
+
+static int rt711_sdca_parse_dt(struct rt711_sdca_priv *rt711, struct device *dev)
+{
+	device_property_read_u32(dev, "realtek,jd-src", &rt711->jd_src);
+
+	return 0;
+}
+
+static int rt711_sdca_probe(struct snd_soc_component *component)
+{
+	struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+
+	rt711_sdca_parse_dt(rt711, &rt711->slave->dev);
+	rt711->component = component;
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
+	.probe = rt711_sdca_probe,
+	.controls = rt711_sdca_snd_controls,
+	.num_controls = ARRAY_SIZE(rt711_sdca_snd_controls),
+	.dapm_widgets = rt711_sdca_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(rt711_sdca_dapm_widgets),
+	.dapm_routes = rt711_sdca_audio_map,
+	.num_dapm_routes = ARRAY_SIZE(rt711_sdca_audio_map),
+	.set_jack = rt711_sdca_set_jack_detect,
+};
+
+static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+				int direction)
+{
+	struct sdw_stream_data *stream;
+
+	if (!sdw_stream)
+		return 0;
+
+	stream = kzalloc(sizeof(*stream), GFP_KERNEL);
+	if (!stream)
+		return -ENOMEM;
+
+	stream->sdw_stream = sdw_stream;
+
+	/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
+	if (direction == SNDRV_PCM_STREAM_PLAYBACK)
+		dai->playback_dma_data = stream;
+	else
+		dai->capture_dma_data = stream;
+
+	return 0;
+}
+
+static void rt711_sdca_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct sdw_stream_data *stream;
+
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+	snd_soc_dai_set_dma_data(dai, substream, NULL);
+	kfree(stream);
+}
+
+static int rt711_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 rt711_sdca_priv *rt711 = 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_data *stream;
+	int retval, port, num_channels;
+	unsigned int sampling_rate;
+
+	dev_dbg(dai->dev, "%s %s", __func__, dai->name);
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!stream)
+		return -EINVAL;
+
+	if (!rt711->slave)
+		return -EINVAL;
+
+	/* SoundWire specific configuration */
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		direction = SDW_DATA_DIR_RX;
+		port = 3;
+	} else {
+		direction = SDW_DATA_DIR_TX;
+		if (dai->id == RT711_AIF1)
+			port = 2;
+		else if (dai->id == RT711_AIF2)
+			port = 4;
+		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(rt711->slave, &stream_config,
+					&port_config, 1, stream->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 44100:
+		sampling_rate = RT711_SDCA_RATE_44100HZ;
+		break;
+	case 48000:
+		sampling_rate = RT711_SDCA_RATE_48000HZ;
+		break;
+	case 96000:
+		sampling_rate = RT711_SDCA_RATE_96000HZ;
+		break;
+	case 192000:
+		sampling_rate = RT711_SDCA_RATE_192000HZ;
+		break;
+	default:
+		dev_err(component->dev, "Rate %d is not supported\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	/* set sampling frequency */
+	regmap_write(rt711->regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_CS01, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+		sampling_rate);
+	regmap_write(rt711->regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_CS11, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+		sampling_rate);
+	regmap_write(rt711->regmap,
+		SDW_SDCA_CTL(FUN_MIC_ARRAY, RT711_SDCA_ENT_CS1F, RT711_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+		sampling_rate);
+
+	return 0;
+}
+
+static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
+	struct sdw_stream_data *stream =
+		snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!rt711->slave)
+		return -EINVAL;
+
+	sdw_stream_remove_slave(rt711->slave, stream->sdw_stream);
+	return 0;
+}
+
+#define RT711_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
+			SNDRV_PCM_RATE_192000)
+#define RT711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+			SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_ops rt711_sdca_ops = {
+	.hw_params	= rt711_sdca_pcm_hw_params,
+	.hw_free	= rt711_sdca_pcm_hw_free,
+	.set_sdw_stream	= rt711_sdca_set_sdw_stream,
+	.shutdown	= rt711_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver rt711_sdca_dai[] = {
+	{
+		.name = "rt711-sdca-aif1",
+		.id = RT711_AIF1,
+		.playback = {
+			.stream_name = "DP3 Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT711_STEREO_RATES,
+			.formats = RT711_FORMATS,
+		},
+		.capture = {
+			.stream_name = "DP2 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT711_STEREO_RATES,
+			.formats = RT711_FORMATS,
+		},
+		.ops = &rt711_sdca_ops,
+	},
+	{
+		.name = "rt711-sdca-aif2",
+		.id = RT711_AIF2,
+		.capture = {
+			.stream_name = "DP4 Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = RT711_STEREO_RATES,
+			.formats = RT711_FORMATS,
+		},
+		.ops = &rt711_sdca_ops,
+	}
+};
+
+int rt711_sdca_init(struct device *dev, struct regmap *regmap,
+			struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+	struct rt711_sdca_priv *rt711;
+	int ret;
+
+	rt711 = devm_kzalloc(dev, sizeof(*rt711), GFP_KERNEL);
+	if (!rt711)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, rt711);
+	rt711->slave = slave;
+	rt711->regmap = regmap;
+	rt711->mbq_regmap = mbq_regmap;
+
+	/*
+	 * Mark hw_init to false
+	 * HW init will be performed when device reports present
+	 */
+	rt711->hw_init = false;
+	rt711->first_hw_init = false;
+
+	/* JD source uses JD2 in default */
+	rt711->jd_src = RT711_JD2;
+
+	ret =  devm_snd_soc_register_component(dev,
+			&soc_sdca_dev_rt711,
+			rt711_sdca_dai,
+			ARRAY_SIZE(rt711_sdca_dai));
+
+	dev_dbg(&slave->dev, "%s\n", __func__);
+
+	return ret;
+}
+
+static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711)
+{
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_GPIO_TEST_MODE_CTL2, 0x0e00);
+	rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_GPIO_CTL, 0x0008);
+
+	regmap_write(rt711->regmap, 0x2f5a, 0x01);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_ADC27_VOL_SET, 0x8728);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_COMBO_JACK_AUTO_CTL3, 0xa472);
+
+	regmap_write(rt711->regmap, 0x2f50, 0x02);
+
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_ANALOG_CTL,
+		RT711_MISC_POWER_CTL4, 0x6000, 0x6000);
+
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+		RT711_COMBO_JACK_AUTO_CTL3, 0x000c, 0x000c);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_CONFIG_CTL, 0x0000);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_VAD,
+		RT711_VAD_SRAM_CTL1, 0x0050);
+}
+
+static void rt711_sdca_vd1_io_init(struct rt711_sdca_priv *rt711)
+{
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_UNSOLICITED_CTL, 0x0300, 0x0000);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_COMBO_JACK_AUTO_CTL3, 0xa43e);
+
+	regmap_write(rt711->regmap, 0x2f5a, 0x05);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_JD_CTRL6, 0x0500);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_REG,
+		RT711_DMIC_CTL1, 0x6173);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_HDA_LEGACY_CONFIG_CTL, 0x0000);
+
+	rt711_sdca_index_write(rt711, RT711_VENDOR_VAD,
+		RT711_VAD_SRAM_CTL1, 0x0050);
+}
+
+int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+	struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
+	int ret = 0;
+	unsigned int val;
+
+	if (rt711->hw_init)
+		return 0;
+
+	if (rt711->first_hw_init) {
+		regcache_cache_only(rt711->regmap, false);
+		regcache_cache_bypass(rt711->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);
+
+	rt711_sdca_reset(rt711);
+
+	rt711_sdca_index_read(rt711, RT711_VENDOR_REG, RT711_JD_PRODUCT_NUM, &val);
+	rt711->hw_ver = val & 0xf;
+
+	if (rt711->hw_ver == RT711_VER_VD0)
+		rt711_sdca_vd0_io_init(rt711);
+	else
+		rt711_sdca_vd1_io_init(rt711);
+
+	/* DP4 mux select from 08_filter_Out_pri */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_REG,
+		RT711_FILTER_SRC_SEL, 0x1800, 0x0800);
+
+	/* ge_exclusive_inbox_en disable */
+	rt711_sdca_index_update_bits(rt711, RT711_VENDOR_HDA_CTL,
+		RT711_PUSH_BTN_INT_CTL0, 0x20, 0x00);
+
+	if (!rt711->first_hw_init) {
+		INIT_DELAYED_WORK(&rt711->jack_detect_work,
+			rt711_sdca_jack_detect_handler);
+		INIT_DELAYED_WORK(&rt711->jack_btn_check_work,
+			rt711_sdca_btn_check_handler);
+		mutex_init(&rt711->calibrate_mutex);
+	}
+
+	/* calibration */
+	ret = rt711_sdca_calibration(rt711);
+	if (ret < 0)
+		dev_err(dev, "%s, calibration failed!\n", __func__);
+
+	/* HP output enable */
+	regmap_write(rt711->regmap,
+		SDW_SDCA_CTL(FUN_JACK_CODEC, RT711_SDCA_ENT_OT1, RT711_SDCA_CTL_VENDOR_DEF, 0), 0x4);
+
+	/*
+	 * if set_jack callback occurred early than io_init,
+	 * we set up the jack detection function now
+	 */
+	if (rt711->hs_jack)
+		rt711_sdca_jack_init(rt711);
+
+	if (rt711->first_hw_init) {
+		regcache_cache_bypass(rt711->regmap, false);
+		regcache_mark_dirty(rt711->regmap);
+	} else
+		rt711->first_hw_init = true;
+
+	/* Mark Slave initialization complete */
+	rt711->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 RT711 SDCA SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
new file mode 100644
index 000000000000..c85b0afd7343
--- /dev/null
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -0,0 +1,246 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt711-sdca.h -- RT711 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2020 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT711_SDCA_H__
+#define __RT711_SDCA_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  rt711_sdca_priv {
+	struct regmap *regmap, *mbq_regmap;
+	struct snd_soc_component *component;
+	struct sdw_slave *slave;
+	enum sdw_slave_status status;
+	struct sdw_bus_params params;
+	bool hw_init;
+	bool first_hw_init;
+	struct snd_soc_jack *hs_jack;
+	struct delayed_work jack_detect_work;
+	struct delayed_work jack_btn_check_work;
+	struct mutex calibrate_mutex; /* for headset calibration */
+	int jack_type, jd_src;
+	unsigned int scp_sdca_stat1, scp_sdca_stat2;
+	int hw_ver;
+};
+
+struct sdw_stream_data {
+	struct sdw_stream_runtime *sdw_stream;
+};
+
+/* NID */
+#define RT711_AUDIO_FUNCTION_GROUP			0x01
+#define RT711_DAC_OUT2					0x03
+#define RT711_ADC_IN1					0x09
+#define RT711_ADC_IN2					0x08
+#define RT711_DMIC1					0x12
+#define RT711_DMIC2					0x13
+#define RT711_MIC2					0x19
+#define RT711_LINE1					0x1a
+#define RT711_LINE2					0x1b
+#define RT711_BEEP					0x1d
+#define RT711_VENDOR_REG				0x20
+#define RT711_HP_OUT					0x21
+#define RT711_MIXER_IN1					0x22
+#define RT711_MIXER_IN2					0x23
+#define RT711_INLINE_CMD				0x55
+#define RT711_VENDOR_CALI				0x58
+#define RT711_VENDOR_IMS_DRE				0x5b
+#define RT711_VENDOR_VAD				0x5e
+#define RT711_VENDOR_ANALOG_CTL				0x5f
+#define RT711_VENDOR_HDA_CTL				0x61
+
+/* Index (NID:20h) */
+#define RT711_JD_PRODUCT_NUM			0x00
+#define RT711_DMIC_CTL1					0x06
+#define RT711_JD_CTL1					0x08
+#define RT711_JD_CTL2					0x09
+#define RT711_CC_DET1					0x11
+#define RT711_PARA_VERB_CTL				0x1a
+#define RT711_COMBO_JACK_AUTO_CTL1			0x45
+#define RT711_COMBO_JACK_AUTO_CTL2			0x46
+#define RT711_COMBO_JACK_AUTO_CTL3			0x47
+#define RT711_INLINE_CMD_CTL				0x48
+#define RT711_DIGITAL_MISC_CTRL4			0x4a
+#define RT711_JD_CTRL6			0x6a
+#define RT711_VREFOUT_CTL				0x6b
+#define RT711_GPIO_TEST_MODE_CTL2			0x6d
+#define RT711_FSM_CTL					0x6f
+#define RT711_IRQ_FLAG_TABLE1				0x80
+#define RT711_IRQ_FLAG_TABLE2				0x81
+#define RT711_IRQ_FLAG_TABLE3				0x82
+#define RT711_HP_FSM_CTL				0x83
+#define RT711_TX_RX_MUX_CTL				0x91
+#define RT711_FILTER_SRC_SEL				0xb0
+#define RT711_ADC27_VOL_SET				0xb7
+
+/* Index (NID:58h) */
+#define RT711_DAC_DC_CALI_CTL1				0x00
+#define RT711_DAC_DC_CALI_CTL2				0x01
+
+/* Index (NID:5bh) */
+#define RT711_IMS_DIGITAL_CTL1				0x00
+#define RT711_HP_IMS_RESULT_L				0x20
+#define RT711_HP_IMS_RESULT_R				0x21
+
+/* Index (NID:5eh) */
+#define RT711_VAD_SRAM_CTL1				0x10
+
+/* Index (NID:5fh) */
+#define RT711_MISC_POWER_CTL0				0x01
+#define RT711_MISC_POWER_CTL4				0x05
+
+/* Index (NID:61h) */
+#define RT711_HDA_LEGACY_MUX_CTL1			0x00
+#define RT711_HDA_LEGACY_UNSOLICITED_CTL	0x03
+#define RT711_HDA_LEGACY_CONFIG_CTL			0x06
+#define RT711_HDA_LEGACY_RESET_CTL			0x08
+#define RT711_HDA_LEGACY_GPIO_CTL			0x0a
+#define RT711_ADC08_09_PDE_CTL				0x24
+#define RT711_GE_MODE_RELATED_CTL			0x35
+#define RT711_PUSH_BTN_INT_CTL0				0x36
+#define RT711_PUSH_BTN_INT_CTL1				0x37
+#define RT711_PUSH_BTN_INT_CTL2				0x38
+#define RT711_PUSH_BTN_INT_CTL6				0x3c
+#define RT711_PUSH_BTN_INT_CTL7				0x3d
+#define RT711_PUSH_BTN_INT_CTL9				0x3f
+
+/* DAC DC offset calibration control-1 (0x00)(NID:20h) */
+#define RT711_DAC_DC_CALI_TRIGGER (0x1 << 15)
+#define RT711_DAC_DC_CALI_CLK_EN (0x1 << 14)
+#define RT711_DAC_DC_FORCE_CALI_RST (0x1 << 3)
+
+/* jack detect control 1 (0x08)(NID:20h) */
+#define RT711_JD2_DIGITAL_MODE_SEL (0x1 << 1)
+
+/* jack detect control 2 (0x09)(NID:20h) */
+#define RT711_JD2_2PORT_200K_DECODE_HP (0x1 << 13)
+#define RT711_HP_JD_SEL_JD1 (0x0 << 1)
+#define RT711_HP_JD_SEL_JD2 (0x1 << 1)
+
+/* CC DET1 (0x11)(NID:20h) */
+#define RT711_HP_JD_FINAL_RESULT_CTL_JD12 (0x1 << 10)
+#define RT711_HP_JD_FINAL_RESULT_CTL_CCDET (0x0 << 10)
+
+/* Parameter & Verb control (0x1a)(NID:20h) */
+#define RT711_HIDDEN_REG_SW_RESET (0x1 << 14)
+
+/* combo jack auto switch control 2 (0x46)(NID:20h) */
+#define RT711_COMBOJACK_AUTO_DET_STATUS			(0x1 << 11)
+#define RT711_COMBOJACK_AUTO_DET_TRS			(0x1 << 10)
+#define RT711_COMBOJACK_AUTO_DET_CTIA			(0x1 << 9)
+#define RT711_COMBOJACK_AUTO_DET_OMTP			(0x1 << 8)
+
+/* FSM control (0x6f)(NID:20h) */
+#define RT711_CALI_CTL			(0x0 << 0)
+#define RT711_COMBOJACK_CTL		(0x1 << 0)
+#define RT711_IMS_CTL			(0x2 << 0)
+#define RT711_DEPOP_CTL			(0x3 << 0)
+#define RT711_FSM_IMP_EN		(0x1 << 6)
+
+/* Impedance Sense Digital Control 1 (0x00)(NID:5bh) */
+#define RT711_TRIGGER_IMS		(0x1 << 15)
+#define RT711_IMS_EN			(0x1 << 6)
+
+#define RT711_EAPD_HIGH				0x2
+#define RT711_EAPD_LOW				0x0
+#define RT711_MUTE_SFT				7
+/* set input/output mapping to payload[14][15] separately */
+#define RT711_DIR_IN_SFT			6
+#define RT711_DIR_OUT_SFT			7
+
+/* RC Calibration register */
+#define RT711_RC_CAL_STATUS			0x320c
+
+/* Buffer address for HID */
+#define RT711_BUF_ADDR_HID1			0x44030000
+#define RT711_BUF_ADDR_HID2			0x44030020
+
+/* RT711 SDCA function topology */
+#define FUN_JACK_CODEC 0x01
+#define FUN_MIC_ARRAY 0x02
+#define FUN_HID 0x03
+
+/* RT711 SDCA entity */
+#define RT711_SDCA_ENT_HID01 0x01
+#define RT711_SDCA_ENT_GE49 0x49
+#define RT711_SDCA_ENT_USER_FU05 0x05
+#define RT711_SDCA_ENT_USER_FU0F 0x0f
+#define RT711_SDCA_ENT_USER_FU1E 0x1e
+#define RT711_SDCA_ENT_PLATFORM_FU15 0x15
+#define RT711_SDCA_ENT_PLATFORM_FU44 0x44
+#define RT711_SDCA_ENT_PDE28 0x28
+#define RT711_SDCA_ENT_PDE29 0x29
+#define RT711_SDCA_ENT_PDE2A 0x2a
+#define RT711_SDCA_ENT_CS01 0x01
+#define RT711_SDCA_ENT_CS11 0x11
+#define RT711_SDCA_ENT_CS1F 0x1f
+#define RT711_SDCA_ENT_OT1 0x06
+#define RT711_SDCA_ENT_LINE1 0x09
+#define RT711_SDCA_ENT_LINE2 0x31
+#define RT711_SDCA_ENT_PDELINE2 0x36
+#define RT711_SDCA_ENT_USER_FU9 0x41
+
+/* RT711 SDCA control */
+#define RT711_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define RT711_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT711_SDCA_CTL_FU_MUTE 0x01
+#define RT711_SDCA_CTL_FU_VOLUME 0x02
+#define RT711_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define RT711_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11
+#define RT711_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define RT711_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define RT711_SDCA_CTL_SELECTED_MODE 0x01
+#define RT711_SDCA_CTL_DETECTED_MODE 0x02
+#define RT711_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT711_SDCA_CTL_VENDOR_DEF 0x30
+
+/* RT711 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+
+/* Power State */
+#define PS0 0x00
+#define PS3 0x03
+
+/* Mute Control */
+#define UNMUTE 0x00
+#define MUTE 0x01
+
+/* sample frequency index */
+#define RT711_SDCA_RATE_44100HZ		0x08
+#define RT711_SDCA_RATE_48000HZ		0x09
+#define RT711_SDCA_RATE_96000HZ		0x0b
+#define RT711_SDCA_RATE_192000HZ	0x0d
+
+enum {
+	RT711_AIF1,
+	RT711_AIF2,
+	RT711_AIFS,
+};
+
+enum rt711_sdca_jd_src {
+	RT711_JD_NULL,
+	RT711_JD1,
+	RT711_JD2
+};
+
+enum rt711_sdca_ver {
+	RT711_VER_VD0,
+	RT711_VER_VD1
+};
+
+int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave);
+int rt711_sdca_init(struct device *dev, struct regmap *regmap,
+	       struct regmap *mbq_regmap, struct sdw_slave *slave);
+
+int rt711_sdca_jack_detect(struct rt711_sdca_priv *rt711, bool *hp, bool *mic);
+#endif /* __RT711_SDCA_H__ */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* RE: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
                   ` (4 preceding siblings ...)
  2020-11-30 14:40 ` [PATCH v2 5/5] ASoC/SoundWire: rt711-sdca: Add RT711 " Bard Liao
@ 2020-12-01  2:42 ` Liao, Bard
  2020-12-01  4:11   ` Vinod Koul
  2020-12-02 17:19 ` (subset) " Mark Brown
  6 siblings, 1 reply; 16+ messages in thread
From: Liao, Bard @ 2020-12-01  2:42 UTC (permalink / raw)
  To: Bard Liao, alsa-devel, vkoul
  Cc: vinod.koul, linux-kernel, tiwai, broonie, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, Kale, Sanyog R

> -----Original Message-----
> From: Bard Liao <yung-chuan.liao@linux.intel.com>
> Sent: Monday, November 30, 2020 10:40 PM
> To: alsa-devel@alsa-project.org; vkoul@kernel.org
> Cc: vinod.koul@linaro.org; linux-kernel@vger.kernel.org; tiwai@suse.de;
> broonie@kernel.org; gregkh@linuxfoundation.org; jank@cadence.com;
> srinivas.kandagatla@linaro.org; rander.wang@linux.intel.com;
> ranjani.sridharan@linux.intel.com; hui.wang@canonical.com; pierre-
> louis.bossart@linux.intel.com; Kale, Sanyog R <sanyog.r.kale@intel.com>;
> Liao, Bard <bard.liao@intel.com>
> Subject: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA
> support
> 
> The MIPI SoundWire Device Class standard will define audio functionality
> beyond the scope of the existing SoundWire 1.2 standard, which is limited to
> the bus and interface.
> 
> The description is inspired by the USB Audio Class, with "functions",
> "entities", "control selectors", "audio clusters". The main difference with the
> USB Audio class is that the devices are typically on a motherboard and
> descriptors stored in platform firmware instead of being retrieved from the
> device.
> 
> The current set of devices managed in this patchset are conformant with the
> SDCA 0.6 specification and require dedicated drivers since the descriptors
> and platform firmware specification is not complete at this time. They do
> however rely on the hierarchical addressing required by the SDCA standard.
> Future devices conformant with SDCA 1.0 should rely on a class driver.
> 
> This series adds support for the hierarchical SDCA addressing and extends
> regmap. It then provides 3 codecs for RT711-sdca headset codec, RT1316
> amplifier and RT715-scda microphone codec.
> 
> Note that the release of this code before the formal adoption of the SDCA
> 1.0 specification was formally endorsed by the MIPI Board to make sure
> there is no delay for Linux-based support of this specification.
> 
> v2:
> - rt715-sdca: Use rt715_sdca prefix to avoid compiling issue.
> - rt715-sdca: Merge multiple mute/volume operation into single
> mute/volume
>   operation
> - rt711-sdca: Initial ret = 0 as it could be used uninitialized.
> 
> Jack Yu (1):
>   ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec
>     driver
> 
> Pierre-Louis Bossart (2):
>   soundwire: SDCA: add helper macro to access controls
>   regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ
> 
> Shuming Fan (2):
>   ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver
>   ASoC/SoundWire: rt711-sdca: Add RT711 SDCA vendor-specific driver
> 

Hi Vinod/Mark,

Could we take this series into Vinod's tree with Mark's Acked-by?
It failed to build on Mark's tree.

> 
> --
> 2.17.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-12-01  2:42 ` [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Liao, Bard
@ 2020-12-01  4:11   ` Vinod Koul
  2020-12-01 14:35     ` Pierre-Louis Bossart
  0 siblings, 1 reply; 16+ messages in thread
From: Vinod Koul @ 2020-12-01  4:11 UTC (permalink / raw)
  To: Liao, Bard
  Cc: Bard Liao, alsa-devel, linux-kernel, tiwai, broonie, gregkh,
	jank, srinivas.kandagatla, rander.wang, ranjani.sridharan,
	hui.wang, pierre-louis.bossart, Kale, Sanyog R

On 01-12-20, 02:42, Liao, Bard wrote:

> Hi Vinod/Mark,
> 
> Could we take this series into Vinod's tree with Mark's Acked-by?
> It failed to build on Mark's tree.

I see Mark has already applied 1-3 ..

Thanks
-- 
~Vinod

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 2/5] regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ
  2020-11-30 14:40 ` [PATCH v2 2/5] regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ Bard Liao
@ 2020-12-01 11:54   ` Mark Brown
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Brown @ 2020-12-01 11:54 UTC (permalink / raw)
  To: Bard Liao
  Cc: alsa-devel, vkoul, vinod.koul, linux-kernel, tiwai, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

[-- Attachment #1: Type: text/plain, Size: 709 bytes --]

On Mon, Nov 30, 2020 at 10:40:17PM +0800, Bard Liao wrote:
> From: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
> 
> The SoundWire 1.1 specification only allowed for reads and writes of
> bytes. The SoundWire 1.2 specification adds a new capability to
> transfer "Multi-Byte Quantities" (MBQ) across the bus. The transfers
> still happens one-byte-at-a-time, but the update is atomic.

Please do not submit new versions of already applied patches, please
submit incremental updates to the existing code.  Modifying existing
commits creates problems for other users building on top of those
commits so it's best practice to only change pubished git commits if
absolutely essential.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-12-01  4:11   ` Vinod Koul
@ 2020-12-01 14:35     ` Pierre-Louis Bossart
  2020-12-01 14:53       ` Mark Brown
  0 siblings, 1 reply; 16+ messages in thread
From: Pierre-Louis Bossart @ 2020-12-01 14:35 UTC (permalink / raw)
  To: Vinod Koul, Liao, Bard
  Cc: alsa-devel, tiwai, gregkh, linux-kernel, ranjani.sridharan,
	hui.wang, broonie, srinivas.kandagatla, jank, Kale, Sanyog R,
	Bard Liao, rander.wang



On 11/30/20 10:11 PM, Vinod Koul wrote:
> On 01-12-20, 02:42, Liao, Bard wrote:
> 
>> Hi Vinod/Mark,
>>
>> Could we take this series into Vinod's tree with Mark's Acked-by?
>> It failed to build on Mark's tree.
> 
> I see Mark has already applied 1-3 ..

Sorry, I thought Mark had reversed the entire series.

Vinod, would you mind providing a tag for Mark then? The following 
commit is needed to compile:

b7cab9be7c161 ('soundwire: SDCA: detect sdca_cascade interrupt')

Thank you Sir!

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-12-01 14:35     ` Pierre-Louis Bossart
@ 2020-12-01 14:53       ` Mark Brown
  2020-12-02  7:14         ` Vinod Koul
  0 siblings, 1 reply; 16+ messages in thread
From: Mark Brown @ 2020-12-01 14:53 UTC (permalink / raw)
  To: Pierre-Louis Bossart
  Cc: Vinod Koul, Liao, Bard, alsa-devel, tiwai, gregkh, linux-kernel,
	ranjani.sridharan, hui.wang, srinivas.kandagatla, jank, Kale,
	Sanyog R, Bard Liao, rander.wang

[-- Attachment #1: Type: text/plain, Size: 505 bytes --]

On Tue, Dec 01, 2020 at 08:35:42AM -0600, Pierre-Louis Bossart wrote:
> On 11/30/20 10:11 PM, Vinod Koul wrote:

> > I see Mark has already applied 1-3 ..

> Sorry, I thought Mark had reversed the entire series.

Yeah, I just backed out the one change for the driver.

> Vinod, would you mind providing a tag for Mark then? The following commit is
> needed to compile:

> b7cab9be7c161 ('soundwire: SDCA: detect sdca_cascade interrupt')

That'd work, looks like there's only one fix patch it's based off.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-12-01 14:53       ` Mark Brown
@ 2020-12-02  7:14         ` Vinod Koul
  2020-12-02 16:08           ` Mark Brown
  0 siblings, 1 reply; 16+ messages in thread
From: Vinod Koul @ 2020-12-02  7:14 UTC (permalink / raw)
  To: Mark Brown
  Cc: Pierre-Louis Bossart, Liao, Bard, alsa-devel, tiwai, gregkh,
	linux-kernel, ranjani.sridharan, hui.wang, srinivas.kandagatla,
	jank, Kale, Sanyog R, Bard Liao, rander.wang

[-- Attachment #1: Type: text/plain, Size: 1763 bytes --]

Hi Mark, 

On 01-12-20, 14:53, Mark Brown wrote:
> On Tue, Dec 01, 2020 at 08:35:42AM -0600, Pierre-Louis Bossart wrote:
> > On 11/30/20 10:11 PM, Vinod Koul wrote:
> 
> > > I see Mark has already applied 1-3 ..
> 
> > Sorry, I thought Mark had reversed the entire series.
> 
> Yeah, I just backed out the one change for the driver.
> 
> > Vinod, would you mind providing a tag for Mark then? The following commit is
> > needed to compile:
> 
> > b7cab9be7c161 ('soundwire: SDCA: detect sdca_cascade interrupt')
> 
> That'd work, looks like there's only one fix patch it's based off.

I have created the tag, please pull:

The following changes since commit 3650b228f83adda7e5ee532e2b90429c03f7b9ec:

  Linux 5.10-rc1 (2020-10-25 15:14:11 -0700)

are available in the Git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire.git tags/soundwire-for-asoc-5.11

for you to fetch changes up to b7cab9be7c16128a0de21ed7ae67211838813437:

  soundwire: SDCA: detect sdca_cascade interrupt (2020-11-24 14:09:31 +0530)

----------------------------------------------------------------
soundwire-for-asoc-5.11

Tag for asoc to resolve build dependency with commit b7cab9be7c16
("soundwire: SDCA: detect sdca_cascade interrupt")

----------------------------------------------------------------
Pierre-Louis Bossart (1):
      soundwire: SDCA: detect sdca_cascade interrupt

Srinivas Kandagatla (1):
      soundwire: Fix DEBUG_LOCKS_WARN_ON for uninitialized attribute

 drivers/soundwire/bus.c             | 28 +++++++++++++++++++++++++++-
 drivers/soundwire/sysfs_slave_dpn.c |  1 +
 include/linux/soundwire/sdw.h       |  4 ++++
 3 files changed, 32 insertions(+), 1 deletion(-)

-- 
~Vinod

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver
  2020-11-30 14:40 ` [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver Bard Liao
@ 2020-12-02 16:07   ` Mark Brown
  2020-12-03  2:29     ` Liao, Bard
  0 siblings, 1 reply; 16+ messages in thread
From: Mark Brown @ 2020-12-02 16:07 UTC (permalink / raw)
  To: Bard Liao
  Cc: alsa-devel, vkoul, vinod.koul, linux-kernel, tiwai, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, sanyog.r.kale, bard.liao

[-- Attachment #1: Type: text/plain, Size: 222 bytes --]

On Mon, Nov 30, 2020 at 10:40:18PM +0800, Bard Liao wrote:
> From: Jack Yu <jack.yu@realtek.com>
> 
> First version of rt715 sdw sdca codec driver.

This doesn't apply against the ASoC tree, please check and resend.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-12-02  7:14         ` Vinod Koul
@ 2020-12-02 16:08           ` Mark Brown
  0 siblings, 0 replies; 16+ messages in thread
From: Mark Brown @ 2020-12-02 16:08 UTC (permalink / raw)
  To: Vinod Koul
  Cc: Pierre-Louis Bossart, Liao, Bard, alsa-devel, tiwai, gregkh,
	linux-kernel, ranjani.sridharan, hui.wang, srinivas.kandagatla,
	jank, Kale, Sanyog R, Bard Liao, rander.wang

[-- Attachment #1: Type: text/plain, Size: 448 bytes --]

On Wed, Dec 02, 2020 at 12:44:05PM +0530, Vinod Koul wrote:

>   Linux 5.10-rc1 (2020-10-25 15:14:11 -0700)
> 
> are available in the Git repository at:
> 
>   git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/soundwire.git tags/soundwire-for-asoc-5.11
> 
> for you to fetch changes up to b7cab9be7c16128a0de21ed7ae67211838813437:
> 
>   soundwire: SDCA: detect sdca_cascade interrupt (2020-11-24 14:09:31 +0530)

Pulled in, thanks.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: (subset) [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support
  2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
                   ` (5 preceding siblings ...)
  2020-12-01  2:42 ` [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Liao, Bard
@ 2020-12-02 17:19 ` Mark Brown
  6 siblings, 0 replies; 16+ messages in thread
From: Mark Brown @ 2020-12-02 17:19 UTC (permalink / raw)
  To: Bard Liao, alsa-devel, vkoul
  Cc: tiwai, bard.liao, ranjani.sridharan, sanyog.r.kale, hui.wang,
	pierre-louis.bossart, vinod.koul, jank, srinivas.kandagatla,
	gregkh, linux-kernel, rander.wang

On Mon, 30 Nov 2020 22:40:15 +0800, Bard Liao wrote:
> The MIPI SoundWire Device Class standard will define audio functionality
> beyond the scope of the existing SoundWire 1.2 standard, which is limited
> to the bus and interface.
> 
> The description is inspired by the USB Audio Class, with "functions",
> "entities", "control selectors", "audio clusters". The main difference
> with the USB Audio class is that the devices are typically on a motherboard
> and descriptors stored in platform firmware instead of being retrieved
> from the device.
> 
> [...]

Applied to

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

Thanks!

[3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver
      commit: 6f4a038b99677f4db737841b81b9d45ed4b54966
[4/5] ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver
      (no commit info)
[5/5] ASoC/SoundWire: rt711-sdca: Add RT711 SDCA vendor-specific driver
      (no commit info)

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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* RE: [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver
  2020-12-02 16:07   ` Mark Brown
@ 2020-12-03  2:29     ` Liao, Bard
  0 siblings, 0 replies; 16+ messages in thread
From: Liao, Bard @ 2020-12-03  2:29 UTC (permalink / raw)
  To: Mark Brown, Bard Liao
  Cc: alsa-devel, vkoul, vinod.koul, linux-kernel, tiwai, gregkh, jank,
	srinivas.kandagatla, rander.wang, ranjani.sridharan, hui.wang,
	pierre-louis.bossart, Kale, Sanyog R



> -----Original Message-----
> From: Mark Brown <broonie@kernel.org>
> Sent: Thursday, December 3, 2020 12:08 AM
> To: Bard Liao <yung-chuan.liao@linux.intel.com>
> Cc: alsa-devel@alsa-project.org; vkoul@kernel.org; vinod.koul@linaro.org;
> linux-kernel@vger.kernel.org; tiwai@suse.de; gregkh@linuxfoundation.org;
> jank@cadence.com; srinivas.kandagatla@linaro.org;
> rander.wang@linux.intel.com; ranjani.sridharan@linux.intel.com;
> hui.wang@canonical.com; pierre-louis.bossart@linux.intel.com; Kale, Sanyog
> R <sanyog.r.kale@intel.com>; Liao, Bard <bard.liao@intel.com>
> Subject: Re: [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of
> rt715 sdw sdca codec driver
> 
> On Mon, Nov 30, 2020 at 10:40:18PM +0800, Bard Liao wrote:
> > From: Jack Yu <jack.yu@realtek.com>
> >
> > First version of rt715 sdw sdca codec driver.
> 
> This doesn't apply against the ASoC tree, please check and resend.


Looks like the previous version is already in ASoC tree. I will resend the
remaining patches on top of it.


^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2020-12-03  2:30 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-30 14:40 [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Bard Liao
2020-11-30 14:40 ` [PATCH v2 1/5] soundwire: SDCA: add helper macro to access controls Bard Liao
2020-11-30 14:40 ` [PATCH v2 2/5] regmap/SoundWire: sdw: add support for SoundWire 1.2 MBQ Bard Liao
2020-12-01 11:54   ` Mark Brown
2020-11-30 14:40 ` [PATCH v2 3/5] ASoC/SoundWire: rt715-sdca: First version of rt715 sdw sdca codec driver Bard Liao
2020-12-02 16:07   ` Mark Brown
2020-12-03  2:29     ` Liao, Bard
2020-11-30 14:40 ` [PATCH v2 4/5] ASoC/SoundWire: rt1316: Add RT1316 SDCA vendor-specific driver Bard Liao
2020-11-30 14:40 ` [PATCH v2 5/5] ASoC/SoundWire: rt711-sdca: Add RT711 " Bard Liao
2020-12-01  2:42 ` [PATCH v2 0/5] regmap/SoundWire/ASoC: Add SoundWire SDCA support Liao, Bard
2020-12-01  4:11   ` Vinod Koul
2020-12-01 14:35     ` Pierre-Louis Bossart
2020-12-01 14:53       ` Mark Brown
2020-12-02  7:14         ` Vinod Koul
2020-12-02 16:08           ` Mark Brown
2020-12-02 17:19 ` (subset) " Mark Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).