diff mbox series

ASoC: codecs: Added MAX98373 Soundwire Driver

Message ID 20200617163015.16809-1-pierre-louis.bossart@linux.intel.com (mailing list archive)
State New, archived
Headers show
Series ASoC: codecs: Added MAX98373 Soundwire Driver | expand

Commit Message

Pierre-Louis Bossart June 17, 2020, 4:30 p.m. UTC
From: Ryan Lee <ryans.lee@maximintegrated.com>

Add SoundWire support for this amplifier, tested on Tigerlake platform.

Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@linux.intel.com>
Signed-off-by: Ryan Lee <ryans.lee@maximintegrated.com>
Signed-off-by: Naveen Manohar <naveen.m@intel.com>
Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
---
 sound/soc/codecs/Kconfig        |  14 +
 sound/soc/codecs/Makefile       |   2 +
 sound/soc/codecs/max98373-sdw.c | 950 ++++++++++++++++++++++++++++++++
 sound/soc/codecs/max98373-sdw.h |  73 +++
 sound/soc/codecs/max98373.c     |  15 +
 sound/soc/codecs/max98373.h     |   8 +
 6 files changed, 1062 insertions(+)
 create mode 100644 sound/soc/codecs/max98373-sdw.c
 create mode 100644 sound/soc/codecs/max98373-sdw.h


base-commit: f61b9273c347980f570d1f9cecb867a7835c613d

Comments

Mark Brown June 18, 2020, 11:09 a.m. UTC | #1
On Wed, Jun 17, 2020 at 11:30:15AM -0500, Pierre-Louis Bossart wrote:

> +static void max98373_read_config(struct sdw_slave *slave)
> +{
> +	int value;
> +	struct device *dev = &slave->dev;
> +	struct max98373_priv *max98373 = dev_get_drvdata(dev);
> +
> +	if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
> +		max98373->v_slot = value & 0xF;
> +	else
> +		max98373->v_slot = 0;

The DT bindings need updating to add SoundWire support.

> +	/* L/R mix configuration */
> +	regmap_write(max98373->regmap,
> +		     MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
> +		     0x80);
> +	regmap_write(max98373->regmap,
> +		     MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
> +		     0x1);
> +	/* Set initial volume (0dB) */
> +	regmap_write(max98373->regmap,
> +		     MAX98373_R203D_AMP_DIG_VOL_CTRL,
> +		     0x00);
> +	regmap_write(max98373->regmap,
> +		     MAX98373_R203E_AMP_PATH_GAIN,
> +		     0x00);

I'd expect these to be chip defaults, especially the volumes.

> +	/* Speaker enable */
> +	regmap_update_bits(max98373->regmap,
> +			   MAX98373_R2043_AMP_EN,
> +			   MAX98373_SPK_EN_MASK, 1);

I'd expect this to be managed via DAPM.

> +/* SPDX-License-Identifier: GPL-2.0-only

AIUI this needs the trailing */ on the same line for the license
compliance people.
Pierre-Louis Bossart June 18, 2020, 12:42 p.m. UTC | #2
Thanks for the review Mark,

>> +static void max98373_read_config(struct sdw_slave *slave)
>> +{
>> +	int value;
>> +	struct device *dev = &slave->dev;
>> +	struct max98373_priv *max98373 = dev_get_drvdata(dev);
>> +
>> +	if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
>> +		max98373->v_slot = value & 0xF;
>> +	else
>> +		max98373->v_slot = 0;
> 
> The DT bindings need updating to add SoundWire support.

Interesting. The properties are the same in I2C and SoundWire mode, so 
would we need a completely different file that just specifies the 
SoundWire DeviceID, e.g.

properties:
   compatible:
     const: sdw10217201000

What's the process for such dual-mode devices?

> 
>> +	/* L/R mix configuration */
>> +	regmap_write(max98373->regmap,
>> +		     MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
>> +		     0x80);
>> +	regmap_write(max98373->regmap,
>> +		     MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
>> +		     0x1);
>> +	/* Set initial volume (0dB) */
>> +	regmap_write(max98373->regmap,
>> +		     MAX98373_R203D_AMP_DIG_VOL_CTRL,
>> +		     0x00);
>> +	regmap_write(max98373->regmap,
>> +		     MAX98373_R203E_AMP_PATH_GAIN,
>> +		     0x00);
> 
> I'd expect these to be chip defaults, especially the volumes.

The same sequence is already used in the I2C probe. if this needs to 
change, it's got to be applied for both cases.
> 
>> +	/* Speaker enable */
>> +	regmap_update_bits(max98373->regmap,
>> +			   MAX98373_R2043_AMP_EN,
>> +			   MAX98373_SPK_EN_MASK, 1);
> 
> I'd expect this to be managed via DAPM.

It's also copied as is from the existing I2C parts.

We should probably cut the common parts out, as done for rt5682. Ryan, 
can you look into this.

> 
>> +/* SPDX-License-Identifier: GPL-2.0-only
> 
> AIUI this needs the trailing */ on the same line for the license
> compliance people.

Indeed, that's a miss.
Mark Brown June 18, 2020, 3:10 p.m. UTC | #3
On Thu, Jun 18, 2020 at 07:42:25AM -0500, Pierre-Louis Bossart wrote:

> > The DT bindings need updating to add SoundWire support.

> Interesting. The properties are the same in I2C and SoundWire mode, so would
> we need a completely different file that just specifies the SoundWire
> DeviceID, e.g.

> properties:
>   compatible:
>     const: sdw10217201000

> What's the process for such dual-mode devices?

I'd hope it could be added to the existing bindings document like for
other dual bus devices.

> > > +	regmap_write(max98373->regmap,
> > > +		     MAX98373_R203D_AMP_DIG_VOL_CTRL,
> > > +		     0x00);
> > > +	regmap_write(max98373->regmap,
> > > +		     MAX98373_R203E_AMP_PATH_GAIN,
> > > +		     0x00);

> > I'd expect these to be chip defaults, especially the volumes.

> The same sequence is already used in the I2C probe. if this needs to change,
> it's got to be applied for both cases.

Yes, it should.

> We should probably cut the common parts out, as done for rt5682. Ryan, can
> you look into this.

Indeed.
Ryan Lee June 18, 2020, 10:07 p.m. UTC | #4
>-----Original Message-----
>From: Mark Brown <broonie@kernel.org>
>Sent: Thursday, June 18, 2020 8:11 AM
>To: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
>Cc: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>; alsa-
>devel@alsa-project.org; Ryan Lee <RyanS.Lee@maximintegrated.com>;
>tiwai@suse.de; gregkh@linuxfoundation.org; vkoul@kernel.org; Naveen
>Manohar <naveen.m@intel.com>; Bard liao <yung-
>chuan.liao@linux.intel.com>; Rander Wang <rander.wang@linux.intel.com>
>Subject: Re: [PATCH] ASoC: codecs: Added MAX98373 Soundwire Driver
>
>On Thu, Jun 18, 2020 at 07:42:25AM -0500, Pierre-Louis Bossart wrote:
>
>> > The DT bindings need updating to add SoundWire support.
>
>> Interesting. The properties are the same in I2C and SoundWire mode, so
>> would we need a completely different file that just specifies the
>> SoundWire DeviceID, e.g.
>
>> properties:
>>   compatible:
>>     const: sdw10217201000
>
>> What's the process for such dual-mode devices?
>
>I'd hope it could be added to the existing bindings document like for other
>dual bus devices.
>
>> > > +	regmap_write(max98373->regmap,
>> > > +		     MAX98373_R203D_AMP_DIG_VOL_CTRL,
>> > > +		     0x00);
>> > > +	regmap_write(max98373->regmap,
>> > > +		     MAX98373_R203E_AMP_PATH_GAIN,
>> > > +		     0x00);
>
>> > I'd expect these to be chip defaults, especially the volumes.
>
>> The same sequence is already used in the I2C probe. if this needs to
>> change, it's got to be applied for both cases.
>
>Yes, it should.

Agreed. Shall remove volume configuration.

>
>> We should probably cut the common parts out, as done for rt5682. Ryan,
>> can you look into this.
>
>Indeed.

OK. Shall re-use common parts and remove duplication.
diff mbox series

Patch

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 986a6308818b..24073730fb8c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -116,6 +116,7 @@  config SND_SOC_ALL_CODECS
 	imply SND_SOC_MAX98926
 	imply SND_SOC_MAX98927
 	imply SND_SOC_MAX98373
+	imply SND_SOC_MAX98373_SDW
 	imply SND_SOC_MAX98390
 	imply SND_SOC_MAX9850
 	imply SND_SOC_MAX9860
@@ -871,6 +872,19 @@  config SND_SOC_MAX98373
 	tristate "Maxim Integrated MAX98373 Speaker Amplifier"
 	depends on I2C
 
+config SND_SOC_MAX98373_SDW
+	tristate "Maxim Integrated MAX98373 Speaker Amplifier - SDW"
+	depends on SOUNDWIRE
+	select SND_SOC_MAX98373
+	select REGMAP_SOUNDWIRE
+	help
+	  Enable support for Maxim Integrated MAX98373 Soundwire
+	  amplifier. MAX98373 supports either the MIPI SoundWire
+	  compatible interface for audio and control data, or
+	  the PCM interface for audio data and a standard I2C
+	  interface for control data. Select this if MAX98373 is
+	  connected via soundwire.
+
 config SND_SOC_MAX98390
 	tristate "Maxim Integrated MAX98390 Speaker Amplifier"
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 47ae3cebb61e..426da2db874b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -115,6 +115,7 @@  snd-soc-max98925-objs := max98925.o
 snd-soc-max98926-objs := max98926.o
 snd-soc-max98927-objs := max98927.o
 snd-soc-max98373-objs := max98373.o
+snd-soc-max98373-sdw-objs := max98373-sdw.o
 snd-soc-max98390-objs := max98390.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-max9860-objs := max9860.o
@@ -418,6 +419,7 @@  obj-$(CONFIG_SND_SOC_MAX98925)	+= snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX98926)	+= snd-soc-max98926.o
 obj-$(CONFIG_SND_SOC_MAX98927)	+= snd-soc-max98927.o
 obj-$(CONFIG_SND_SOC_MAX98373)	+= snd-soc-max98373.o
+obj-$(CONFIG_SND_SOC_MAX98373_SDW)   += snd-soc-max98373-sdw.o
 obj-$(CONFIG_SND_SOC_MAX98390)	+= snd-soc-max98390.o
 obj-$(CONFIG_SND_SOC_MAX9850)	+= snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MAX9860)	+= snd-soc-max9860.o
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
new file mode 100644
index 000000000000..074ca4c500bc
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -0,0 +1,950 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020, Maxim Integrated
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include "max98373-sdw.h"
+
+struct sdw_stream_data {
+	struct sdw_stream_runtime *sdw_stream;
+};
+
+static struct reg_default max98373_reg[] = {
+	{MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
+	{MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
+	{MAX98373_R0042_SCP_INIT_STAT_2, 0x00},
+	{MAX98373_R0044_SCP_CTRL, 0x00},
+	{MAX98373_R0045_SCP_SYSTEM_CTRL, 0x00},
+	{MAX98373_R0046_SCP_DEV_NUMBER, 0x00},
+	{MAX98373_R0050_SCP_DEV_ID_0, 0x21},
+	{MAX98373_R0051_SCP_DEV_ID_1, 0x01},
+	{MAX98373_R0052_SCP_DEV_ID_2, 0x9F},
+	{MAX98373_R0053_SCP_DEV_ID_3, 0x87},
+	{MAX98373_R0054_SCP_DEV_ID_4, 0x08},
+	{MAX98373_R0055_SCP_DEV_ID_5, 0x00},
+	{MAX98373_R0060_SCP_FRAME_CTLR, 0x00},
+	{MAX98373_R0070_SCP_FRAME_CTLR, 0x00},
+	{MAX98373_R0100_DP1_INIT_STAT, 0x00},
+	{MAX98373_R0101_DP1_INIT_MASK, 0x00},
+	{MAX98373_R0102_DP1_PORT_CTRL, 0x00},
+	{MAX98373_R0103_DP1_BLOCK_CTRL_1, 0x00},
+	{MAX98373_R0104_DP1_PREPARE_STATUS, 0x00},
+	{MAX98373_R0105_DP1_PREPARE_CTRL, 0x00},
+	{MAX98373_R0120_DP1_CHANNEL_EN, 0x00},
+	{MAX98373_R0122_DP1_SAMPLE_CTRL1, 0x00},
+	{MAX98373_R0123_DP1_SAMPLE_CTRL2, 0x00},
+	{MAX98373_R0124_DP1_OFFSET_CTRL1, 0x00},
+	{MAX98373_R0125_DP1_OFFSET_CTRL2, 0x00},
+	{MAX98373_R0126_DP1_HCTRL, 0x00},
+	{MAX98373_R0127_DP1_BLOCK_CTRL3, 0x00},
+	{MAX98373_R0130_DP1_CHANNEL_EN, 0x00},
+	{MAX98373_R0132_DP1_SAMPLE_CTRL1, 0x00},
+	{MAX98373_R0133_DP1_SAMPLE_CTRL2, 0x00},
+	{MAX98373_R0134_DP1_OFFSET_CTRL1, 0x00},
+	{MAX98373_R0135_DP1_OFFSET_CTRL2, 0x00},
+	{MAX98373_R0136_DP1_HCTRL, 0x0136},
+	{MAX98373_R0137_DP1_BLOCK_CTRL3, 0x00},
+	{MAX98373_R0300_DP3_INIT_STAT, 0x00},
+	{MAX98373_R0301_DP3_INIT_MASK, 0x00},
+	{MAX98373_R0302_DP3_PORT_CTRL, 0x00},
+	{MAX98373_R0303_DP3_BLOCK_CTRL_1, 0x00},
+	{MAX98373_R0304_DP3_PREPARE_STATUS, 0x00},
+	{MAX98373_R0305_DP3_PREPARE_CTRL, 0x00},
+	{MAX98373_R0320_DP3_CHANNEL_EN, 0x00},
+	{MAX98373_R0322_DP3_SAMPLE_CTRL1, 0x00},
+	{MAX98373_R0323_DP3_SAMPLE_CTRL2, 0x00},
+	{MAX98373_R0324_DP3_OFFSET_CTRL1, 0x00},
+	{MAX98373_R0325_DP3_OFFSET_CTRL2, 0x00},
+	{MAX98373_R0326_DP3_HCTRL, 0x00},
+	{MAX98373_R0327_DP3_BLOCK_CTRL3, 0x00},
+	{MAX98373_R0330_DP3_CHANNEL_EN, 0x00},
+	{MAX98373_R0332_DP3_SAMPLE_CTRL1, 0x00},
+	{MAX98373_R0333_DP3_SAMPLE_CTRL2, 0x00},
+	{MAX98373_R0334_DP3_OFFSET_CTRL1, 0x00},
+	{MAX98373_R0335_DP3_OFFSET_CTRL2, 0x00},
+	{MAX98373_R0336_DP3_HCTRL, 0x00},
+	{MAX98373_R0337_DP3_BLOCK_CTRL3, 0x00},
+	{MAX98373_R2000_SW_RESET, 0x00},
+	{MAX98373_R2001_INT_RAW1, 0x00},
+	{MAX98373_R2002_INT_RAW2, 0x00},
+	{MAX98373_R2003_INT_RAW3, 0x00},
+	{MAX98373_R2004_INT_STATE1, 0x00},
+	{MAX98373_R2005_INT_STATE2, 0x00},
+	{MAX98373_R2006_INT_STATE3, 0x00},
+	{MAX98373_R2007_INT_FLAG1, 0x00},
+	{MAX98373_R2008_INT_FLAG2, 0x00},
+	{MAX98373_R2009_INT_FLAG3, 0x00},
+	{MAX98373_R200A_INT_EN1, 0x00},
+	{MAX98373_R200B_INT_EN2, 0x00},
+	{MAX98373_R200C_INT_EN3, 0x00},
+	{MAX98373_R200D_INT_FLAG_CLR1, 0x00},
+	{MAX98373_R200E_INT_FLAG_CLR2, 0x00},
+	{MAX98373_R200F_INT_FLAG_CLR3, 0x00},
+	{MAX98373_R2010_IRQ_CTRL, 0x00},
+	{MAX98373_R2014_THERM_WARN_THRESH, 0x10},
+	{MAX98373_R2015_THERM_SHDN_THRESH, 0x27},
+	{MAX98373_R2016_THERM_HYSTERESIS, 0x01},
+	{MAX98373_R2017_THERM_FOLDBACK_SET, 0xC0},
+	{MAX98373_R2018_THERM_FOLDBACK_EN, 0x00},
+	{MAX98373_R201E_PIN_DRIVE_STRENGTH, 0x55},
+	{MAX98373_R2020_PCM_TX_HIZ_EN_1, 0xFE},
+	{MAX98373_R2021_PCM_TX_HIZ_EN_2, 0xFF},
+	{MAX98373_R2022_PCM_TX_SRC_1, 0x00},
+	{MAX98373_R2023_PCM_TX_SRC_2, 0x00},
+	{MAX98373_R2024_PCM_DATA_FMT_CFG, 0xC0},
+	{MAX98373_R2025_AUDIO_IF_MODE, 0x00},
+	{MAX98373_R2026_PCM_CLOCK_RATIO, 0x04},
+	{MAX98373_R2027_PCM_SR_SETUP_1, 0x08},
+	{MAX98373_R2028_PCM_SR_SETUP_2, 0x88},
+	{MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1, 0x00},
+	{MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2, 0x00},
+	{MAX98373_R202B_PCM_RX_EN, 0x00},
+	{MAX98373_R202C_PCM_TX_EN, 0x00},
+	{MAX98373_R202E_ICC_RX_CH_EN_1, 0x00},
+	{MAX98373_R202F_ICC_RX_CH_EN_2, 0x00},
+	{MAX98373_R2030_ICC_TX_HIZ_EN_1, 0xFF},
+	{MAX98373_R2031_ICC_TX_HIZ_EN_2, 0xFF},
+	{MAX98373_R2032_ICC_LINK_EN_CFG, 0x30},
+	{MAX98373_R2034_ICC_TX_CNTL, 0x00},
+	{MAX98373_R2035_ICC_TX_EN, 0x00},
+	{MAX98373_R2036_SOUNDWIRE_CTRL, 0x05},
+	{MAX98373_R203D_AMP_DIG_VOL_CTRL, 0x00},
+	{MAX98373_R203E_AMP_PATH_GAIN, 0x08},
+	{MAX98373_R203F_AMP_DSP_CFG, 0x02},
+	{MAX98373_R2040_TONE_GEN_CFG, 0x00},
+	{MAX98373_R2041_AMP_CFG, 0x03},
+	{MAX98373_R2042_AMP_EDGE_RATE_CFG, 0x00},
+	{MAX98373_R2043_AMP_EN, 0x00},
+	{MAX98373_R2046_IV_SENSE_ADC_DSP_CFG, 0x04},
+	{MAX98373_R2047_IV_SENSE_ADC_EN, 0x00},
+	{MAX98373_R2051_MEAS_ADC_SAMPLING_RATE, 0x00},
+	{MAX98373_R2052_MEAS_ADC_PVDD_FLT_CFG, 0x00},
+	{MAX98373_R2053_MEAS_ADC_THERM_FLT_CFG, 0x00},
+	{MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK, 0x00},
+	{MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK, 0x00},
+	{MAX98373_R2056_MEAS_ADC_PVDD_CH_EN, 0x00},
+	{MAX98373_R2090_BDE_LVL_HOLD, 0x00},
+	{MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0x00},
+	{MAX98373_R2092_BDE_CLIPPER_MODE, 0x00},
+	{MAX98373_R2097_BDE_L1_THRESH, 0x00},
+	{MAX98373_R2098_BDE_L2_THRESH, 0x00},
+	{MAX98373_R2099_BDE_L3_THRESH, 0x00},
+	{MAX98373_R209A_BDE_L4_THRESH, 0x00},
+	{MAX98373_R209B_BDE_THRESH_HYST, 0x00},
+	{MAX98373_R20A8_BDE_L1_CFG_1, 0x00},
+	{MAX98373_R20A9_BDE_L1_CFG_2, 0x00},
+	{MAX98373_R20AA_BDE_L1_CFG_3, 0x00},
+	{MAX98373_R20AB_BDE_L2_CFG_1, 0x00},
+	{MAX98373_R20AC_BDE_L2_CFG_2, 0x00},
+	{MAX98373_R20AD_BDE_L2_CFG_3, 0x00},
+	{MAX98373_R20AE_BDE_L3_CFG_1, 0x00},
+	{MAX98373_R20AF_BDE_L3_CFG_2, 0x00},
+	{MAX98373_R20B0_BDE_L3_CFG_3, 0x00},
+	{MAX98373_R20B1_BDE_L4_CFG_1, 0x00},
+	{MAX98373_R20B2_BDE_L4_CFG_2, 0x00},
+	{MAX98373_R20B3_BDE_L4_CFG_3, 0x00},
+	{MAX98373_R20B4_BDE_INFINITE_HOLD_RELEASE, 0x00},
+	{MAX98373_R20B5_BDE_EN, 0x00},
+	{MAX98373_R20B6_BDE_CUR_STATE_READBACK, 0x00},
+	{MAX98373_R20D1_DHT_CFG, 0x01},
+	{MAX98373_R20D2_DHT_ATTACK_CFG, 0x02},
+	{MAX98373_R20D3_DHT_RELEASE_CFG, 0x03},
+	{MAX98373_R20D4_DHT_EN, 0x00},
+	{MAX98373_R20E0_LIMITER_THRESH_CFG, 0x00},
+	{MAX98373_R20E1_LIMITER_ATK_REL_RATES, 0x00},
+	{MAX98373_R20E2_LIMITER_EN, 0x00},
+	{MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG, 0x00},
+	{MAX98373_R20FF_GLOBAL_SHDN, 0x00},
+	{MAX98373_R21FF_REV_ID, 0x42},
+};
+
+static bool max98373_readable_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX98373_R21FF_REV_ID:
+	case MAX98373_R2010_IRQ_CTRL:
+	/* SoundWire Control Port Registers */
+	case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR:
+	/* Soundwire Data Port 1 Registers */
+	case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3:
+	/* Soundwire Data Port 3 Registers */
+	case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3:
+	case MAX98373_R2000_SW_RESET ... MAX98373_R200C_INT_EN3:
+	case MAX98373_R2014_THERM_WARN_THRESH
+		... MAX98373_R2018_THERM_FOLDBACK_EN:
+	case MAX98373_R201E_PIN_DRIVE_STRENGTH
+		... MAX98373_R2036_SOUNDWIRE_CTRL:
+	case MAX98373_R203D_AMP_DIG_VOL_CTRL ... MAX98373_R2043_AMP_EN:
+	case MAX98373_R2046_IV_SENSE_ADC_DSP_CFG
+		... MAX98373_R2047_IV_SENSE_ADC_EN:
+	case MAX98373_R2051_MEAS_ADC_SAMPLING_RATE
+		... MAX98373_R2056_MEAS_ADC_PVDD_CH_EN:
+	case MAX98373_R2090_BDE_LVL_HOLD ... MAX98373_R2092_BDE_CLIPPER_MODE:
+	case MAX98373_R2097_BDE_L1_THRESH
+		... MAX98373_R209B_BDE_THRESH_HYST:
+	case MAX98373_R20A8_BDE_L1_CFG_1 ... MAX98373_R20B3_BDE_L4_CFG_3:
+	case MAX98373_R20B5_BDE_EN ... MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+	case MAX98373_R20D1_DHT_CFG ... MAX98373_R20D4_DHT_EN:
+	case MAX98373_R20E0_LIMITER_THRESH_CFG ... MAX98373_R20E2_LIMITER_EN:
+	case MAX98373_R20FE_DEVICE_AUTO_RESTART_CFG
+		... MAX98373_R20FF_GLOBAL_SHDN:
+		return true;
+	default:
+		return false;
+	}
+};
+
+static bool max98373_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MAX98373_R203E_AMP_PATH_GAIN:
+	case MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK:
+	case MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK:
+	case MAX98373_R20B6_BDE_CUR_STATE_READBACK:
+	case MAX98373_R21FF_REV_ID:
+	/* SoundWire Control Port Registers */
+	case MAX98373_R0040_SCP_INIT_STAT_1 ... MAX98373_R0070_SCP_FRAME_CTLR:
+	/* Soundwire Data Port 1 Registers */
+	case MAX98373_R0100_DP1_INIT_STAT ... MAX98373_R0137_DP1_BLOCK_CTRL3:
+	/* Soundwire Data Port 3 Registers */
+	case MAX98373_R0300_DP3_INIT_STAT ... MAX98373_R0337_DP3_BLOCK_CTRL3:
+	case MAX98373_R2000_SW_RESET ... MAX98373_R2009_INT_FLAG3:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static const struct regmap_config max98373_sdw_regmap = {
+	.reg_bits = 32,
+	.val_bits = 8,
+	.max_register = MAX98373_R21FF_REV_ID,
+	.reg_defaults  = max98373_reg,
+	.num_reg_defaults = ARRAY_SIZE(max98373_reg),
+	.readable_reg = max98373_readable_register,
+	.volatile_reg = max98373_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+	.use_single_read = true,
+	.use_single_write = true,
+};
+
+static void max98373_read_config(struct sdw_slave *slave)
+{
+	int value;
+	struct device *dev = &slave->dev;
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+	if (!device_property_read_u32(dev, "maxim,vmon-slot-no", &value))
+		max98373->v_slot = value & 0xF;
+	else
+		max98373->v_slot = 0;
+
+	if (!device_property_read_u32(dev, "maxim,imon-slot-no", &value))
+		max98373->i_slot = value & 0xF;
+	else
+		max98373->i_slot = 1;
+
+	if (!device_property_read_u32(dev, "maxim,spkfb-slot-no", &value))
+		max98373->spkfb_slot = value & 0xF;
+	else
+		max98373->spkfb_slot = 2;
+
+	/* update interleave mode info */
+	if (device_property_read_bool(dev, "maxim,interleave_mode"))
+		max98373->interleave_mode = true;
+	else
+		max98373->interleave_mode = false;
+}
+
+/*     MAX98373 reset and initialization. */
+static int max98373_reset(struct device *dev)
+{
+	int ret, reg, count;
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+	/* Software Reset */
+	ret = regmap_update_bits(max98373->regmap,
+				 MAX98373_R2000_SW_RESET,
+				 MAX98373_SOFT_RESET,
+				 MAX98373_SOFT_RESET);
+	if (ret) {
+		dev_err(dev, "Reset command failed. (ret:%d)\n", ret);
+		return ret;
+	}
+
+	for (count = 0; count < 3; count++) {
+		usleep_range(10000, 11000);
+		/* Software Reset Verification */
+		ret = regmap_read(max98373->regmap,
+				  MAX98373_R21FF_REV_ID, &reg);
+		if (!ret) {
+			dev_dbg(dev, "Reset completed (retry:%d)\n", count);
+			return 0;
+		}
+	}
+
+	dev_err(dev, "Reset failed. (ret:%d)\n", ret);
+	return ret;
+}
+
+/* Power management functions and structure */
+static int max98373_suspend(struct device *dev)
+{
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+
+	regcache_cache_only(max98373->regmap, true);
+	regcache_mark_dirty(max98373->regmap);
+	return 0;
+}
+
+static int max98373_resume(struct device *dev)
+{
+	struct sdw_slave *slave = dev_to_sdw_dev(dev);
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+	unsigned long time;
+
+	time = wait_for_completion_timeout(&slave->initialization_complete,
+					   msecs_to_jiffies(2000));
+	if (!time) {
+		dev_err(dev, "Initialization not complete, timed out\n");
+		return -ETIMEDOUT;
+	}
+
+	regcache_cache_only(max98373->regmap, false);
+	regcache_sync(max98373->regmap);
+
+	return 0;
+}
+
+static const struct dev_pm_ops max98373_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(max98373_suspend, max98373_resume)
+	SET_RUNTIME_PM_OPS(max98373_suspend, max98373_resume, NULL)
+};
+
+static int max98373_read_prop(struct sdw_slave *slave)
+{
+	struct sdw_slave_prop *prop = &slave->prop;
+	int nval, i, num_of_ports;
+	u32 bit;
+	unsigned long addr;
+	struct sdw_dpn_prop *dpn;
+
+	/* BITMAP: 00001000  Dataport 3 is active */
+	prop->source_ports = BIT(3);
+	/* BITMAP: 00000010  Dataport 1 is active */
+	prop->sink_ports = BIT(1);
+	prop->paging_support = true;
+	prop->clk_stop_timeout = 20;
+
+	nval = hweight32(prop->source_ports);
+	num_of_ports = nval;
+	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);
+	num_of_ports += nval;
+	prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+					   sizeof(*prop->sink_dpn_prop),
+					   GFP_KERNEL);
+	if (!prop->sink_dpn_prop)
+		return -ENOMEM;
+
+	i = 0;
+	dpn = prop->sink_dpn_prop;
+	addr = prop->sink_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++;
+	}
+
+	/* Allocate port_ready based on num_of_ports */
+	slave->port_ready = devm_kcalloc(&slave->dev, num_of_ports,
+					 sizeof(*slave->port_ready),
+					 GFP_KERNEL);
+	if (!slave->port_ready)
+		return -ENOMEM;
+
+	/* Initialize completion */
+	for (i = 0; i < num_of_ports; i++)
+		init_completion(&slave->port_ready[i]);
+
+	/* set the timeout values */
+	prop->clk_stop_timeout = 20;
+
+	return 0;
+}
+
+static int max98373_io_init(struct sdw_slave *slave)
+{
+	struct device *dev = &slave->dev;
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+	int ret;
+
+	if (max98373->pm_init_once) {
+		regcache_cache_only(max98373->regmap, false);
+		regcache_cache_bypass(max98373->regmap, true);
+	}
+
+	/*
+	 * PM runtime is only enabled when a Slave reports as Attached
+	 */
+	if (!max98373->pm_init_once) {
+		/* set autosuspend parameters */
+		pm_runtime_set_autosuspend_delay(dev, 3000);
+		pm_runtime_use_autosuspend(dev);
+
+		/* update count of parent 'active' children */
+		pm_runtime_set_active(dev);
+
+		/* make sure the device does not suspend immediately */
+		pm_runtime_mark_last_busy(dev);
+
+		pm_runtime_enable(dev);
+	}
+
+	pm_runtime_get_noresume(dev);
+
+	/* Software Reset */
+	ret = max98373_reset(dev);
+	if (ret)
+		return ret;
+
+	/* Set soundwire mode */
+	regmap_write(max98373->regmap, MAX98373_R2025_AUDIO_IF_MODE, 3);
+	/* Enable ADC */
+	regmap_write(max98373->regmap, MAX98373_R2047_IV_SENSE_ADC_EN, 3);
+	/* Set default Soundwire clock */
+	regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, 5);
+	/* Set default sampling rate for speaker and IVDAC */
+	regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88);
+	/* IV default slot configuration */
+	regmap_write(max98373->regmap,
+		     MAX98373_R2020_PCM_TX_HIZ_EN_1,
+		     0xFF);
+	regmap_write(max98373->regmap,
+		     MAX98373_R2021_PCM_TX_HIZ_EN_2,
+		     0xFF);
+	/* L/R mix configuration */
+	regmap_write(max98373->regmap,
+		     MAX98373_R2029_PCM_TO_SPK_MONO_MIX_1,
+		     0x80);
+	regmap_write(max98373->regmap,
+		     MAX98373_R202A_PCM_TO_SPK_MONO_MIX_2,
+		     0x1);
+	/* Set initial volume (0dB) */
+	regmap_write(max98373->regmap,
+		     MAX98373_R203D_AMP_DIG_VOL_CTRL,
+		     0x00);
+	regmap_write(max98373->regmap,
+		     MAX98373_R203E_AMP_PATH_GAIN,
+		     0x00);
+	/* Enable DC blocker */
+	regmap_write(max98373->regmap,
+		     MAX98373_R203F_AMP_DSP_CFG,
+		     0x3);
+	/* Enable IMON VMON DC blocker */
+	regmap_write(max98373->regmap,
+		     MAX98373_R2046_IV_SENSE_ADC_DSP_CFG,
+		     0x7);
+	/* voltage, current slot configuration */
+	regmap_write(max98373->regmap,
+		     MAX98373_R2022_PCM_TX_SRC_1,
+		     (max98373->i_slot << MAX98373_PCM_TX_CH_SRC_A_I_SHIFT |
+		     max98373->v_slot) & 0xFF);
+	if (max98373->v_slot < 8)
+		regmap_update_bits(max98373->regmap,
+				   MAX98373_R2020_PCM_TX_HIZ_EN_1,
+				   1 << max98373->v_slot, 0);
+	else
+		regmap_update_bits(max98373->regmap,
+				   MAX98373_R2021_PCM_TX_HIZ_EN_2,
+				   1 << (max98373->v_slot - 8), 0);
+
+	if (max98373->i_slot < 8)
+		regmap_update_bits(max98373->regmap,
+				   MAX98373_R2020_PCM_TX_HIZ_EN_1,
+				   1 << max98373->i_slot, 0);
+	else
+		regmap_update_bits(max98373->regmap,
+				   MAX98373_R2021_PCM_TX_HIZ_EN_2,
+				   1 << (max98373->i_slot - 8), 0);
+
+	/* speaker feedback slot configuration */
+	regmap_write(max98373->regmap,
+		     MAX98373_R2023_PCM_TX_SRC_2,
+		     max98373->spkfb_slot & 0xFF);
+
+	/* Set interleave mode */
+	if (max98373->interleave_mode)
+		regmap_update_bits(max98373->regmap,
+				   MAX98373_R2024_PCM_DATA_FMT_CFG,
+				   MAX98373_PCM_TX_CH_INTERLEAVE_MASK,
+				   MAX98373_PCM_TX_CH_INTERLEAVE_MASK);
+
+	/* Speaker enable */
+	regmap_update_bits(max98373->regmap,
+			   MAX98373_R2043_AMP_EN,
+			   MAX98373_SPK_EN_MASK, 1);
+
+	regmap_write(max98373->regmap, MAX98373_R20B5_BDE_EN, 1);
+	regmap_write(max98373->regmap, MAX98373_R20E2_LIMITER_EN, 1);
+
+	if (max98373->pm_init_once) {
+		regcache_cache_bypass(max98373->regmap, false);
+		regcache_mark_dirty(max98373->regmap);
+	}
+
+	max98373->pm_init_once = true;
+	max98373->hw_init = true;
+
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
+
+	return 0;
+}
+
+static int max98373_clock_calculate(struct sdw_slave *slave,
+				    unsigned int clk_freq)
+{
+	int x, y;
+	static const int max98373_clk_family[] = {
+		7680000, 8400000, 9600000, 11289600,
+		12000000, 12288000, 13000000
+	};
+
+	for (x = 0; x < 4; x++)
+		for (y = 0; y < ARRAY_SIZE(max98373_clk_family); y++)
+			if (clk_freq == (max98373_clk_family[y] >> x))
+				return (x << 3) + y;
+
+	/* Set default clock (12.288 Mhz) if the value is not in the list */
+	dev_err(&slave->dev, "Requested clock not found. (clk_freq = %d)\n",
+		clk_freq);
+	return 0x5;
+}
+
+static int max98373_clock_config(struct sdw_slave *slave,
+				 struct sdw_bus_params *params)
+{
+	struct device *dev = &slave->dev;
+	struct max98373_priv *max98373 = dev_get_drvdata(dev);
+	unsigned int clk_freq, value;
+
+	clk_freq = (params->curr_dr_freq >> 1);
+
+	/*
+	 *	Select the proper value for the register based on the
+	 *	requested clock. If the value is not in the list,
+	 *	use reasonable default - 12.288 Mhz
+	 */
+	value = max98373_clock_calculate(slave, clk_freq);
+
+	/* SWCLK */
+	regmap_write(max98373->regmap, MAX98373_R2036_SOUNDWIRE_CTRL, value);
+
+	/* The default Sampling Rate value for IV is 48KHz*/
+	regmap_write(max98373->regmap, MAX98373_R2028_PCM_SR_SETUP_2, 0x88);
+
+	return 0;
+}
+
+#define MAX98373_RATES SNDRV_PCM_RATE_8000_96000
+#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98373_sdw_dai_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 max98373_priv *max98373 =
+		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 ret, chan_sz, sampling_rate;
+
+	stream = snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!stream)
+		return -EINVAL;
+
+	if (!max98373->slave)
+		return -EINVAL;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		direction = SDW_DATA_DIR_RX;
+		port_config.num = 1;
+	} else {
+		direction = SDW_DATA_DIR_TX;
+		port_config.num = 3;
+	}
+
+	stream_config.frame_rate = params_rate(params);
+	stream_config.bps = snd_pcm_format_width(params_format(params));
+	stream_config.direction = direction;
+
+	if (max98373->slot && direction == SDW_DATA_DIR_RX) {
+		stream_config.ch_count = max98373->slot;
+		port_config.ch_mask = max98373->rx_mask;
+	} else {
+		/* only IV are supported by capture */
+		if (direction == SDW_DATA_DIR_TX)
+			stream_config.ch_count = 2;
+		else
+			stream_config.ch_count = params_channels(params);
+
+		port_config.ch_mask = GENMASK(stream_config.ch_count - 1, 0);
+	}
+
+	ret = sdw_stream_add_slave(max98373->slave, &stream_config,
+				   &port_config, 1, stream->sdw_stream);
+	if (ret) {
+		dev_err(dai->dev, "Unable to configure port\n");
+		return ret;
+	}
+
+	if (params_channels(params) > 16) {
+		dev_err(component->dev, "Unsupported channels %d\n",
+			params_channels(params));
+		return -EINVAL;
+	}
+
+	/* Channel size configuration */
+	switch (snd_pcm_format_width(params_format(params))) {
+	case 16:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_16;
+		break;
+	case 24:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_24;
+		break;
+	case 32:
+		chan_sz = MAX98373_PCM_MODE_CFG_CHANSZ_32;
+		break;
+	default:
+		dev_err(component->dev, "Channel size unsupported %d\n",
+			params_format(params));
+		return -EINVAL;
+	}
+
+	max98373->ch_size = snd_pcm_format_width(params_format(params));
+
+	regmap_update_bits(max98373->regmap,
+			   MAX98373_R2024_PCM_DATA_FMT_CFG,
+			   MAX98373_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+	dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+	/* Sampling rate configuration */
+	switch (params_rate(params)) {
+	case 8000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_8000;
+		break;
+	case 11025:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_11025;
+		break;
+	case 12000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_12000;
+		break;
+	case 16000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_16000;
+		break;
+	case 22050:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_22050;
+		break;
+	case 24000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_24000;
+		break;
+	case 32000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_32000;
+		break;
+	case 44100:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_44100;
+		break;
+	case 48000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_48000;
+		break;
+	case 88200:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_88200;
+		break;
+	case 96000:
+		sampling_rate = MAX98373_PCM_SR_SET1_SR_96000;
+		break;
+	default:
+		dev_err(component->dev, "Rate %d is not supported\n",
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	/* set correct sampling frequency */
+	regmap_update_bits(max98373->regmap,
+			   MAX98373_R2028_PCM_SR_SETUP_2,
+			   MAX98373_PCM_SR_SET2_SR_MASK,
+			   sampling_rate << MAX98373_PCM_SR_SET2_SR_SHIFT);
+
+	/* set sampling rate of IV */
+	regmap_update_bits(max98373->regmap,
+			   MAX98373_R2028_PCM_SR_SETUP_2,
+			   MAX98373_PCM_SR_SET2_IVADC_SR_MASK,
+			   sampling_rate);
+
+	return 0;
+}
+
+static int max98373_pcm_hw_free(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct max98373_priv *max98373 =
+		snd_soc_component_get_drvdata(component);
+	struct sdw_stream_data *stream =
+		snd_soc_dai_get_dma_data(dai, substream);
+
+	if (!max98373->slave)
+		return -EINVAL;
+
+	sdw_stream_remove_slave(max98373->slave, stream->sdw_stream);
+	return 0;
+}
+
+static int max98373_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 max98373_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 max98373_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 max98373_priv *max98373 =
+		snd_soc_component_get_drvdata(component);
+
+	/* tx_mask is unused since it's irrelevant for I/V feedback */
+	if (tx_mask)
+		return -EINVAL;
+
+	if (!rx_mask && !slots && !slot_width)
+		max98373->tdm_mode = false;
+	else
+		max98373->tdm_mode = true;
+
+	max98373->rx_mask = rx_mask;
+	max98373->slot = slots;
+
+	return 0;
+}
+
+static const struct snd_soc_dai_ops max98373_dai_sdw_ops = {
+	.hw_params = max98373_sdw_dai_hw_params,
+	.hw_free = max98373_pcm_hw_free,
+	.set_sdw_stream = max98373_set_sdw_stream,
+	.shutdown = max98373_shutdown,
+	.set_tdm_slot = max98373_sdw_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver max98373_sdw_dai[] = {
+	{
+		.name = "max98373-aif1",
+		.playback = {
+			.stream_name = "HiFi Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MAX98373_RATES,
+			.formats = MAX98373_FORMATS,
+		},
+		.capture = {
+			.stream_name = "HiFi Capture",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = MAX98373_RATES,
+			.formats = MAX98373_FORMATS,
+		},
+		.ops = &max98373_dai_sdw_ops,
+	}
+};
+
+static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+	struct max98373_priv *max98373;
+	int ret;
+	struct device *dev = &slave->dev;
+
+	/*  Allocate and assign private driver data structure  */
+	max98373 = devm_kzalloc(dev, sizeof(*max98373), GFP_KERNEL);
+	if (!max98373)
+		return -ENOMEM;
+
+	dev_set_drvdata(dev, max98373);
+	max98373->regmap = regmap;
+	max98373->slave = slave;
+
+	/* Read voltage and slot configuration */
+	max98373_read_config(slave);
+
+	max98373->hw_init = false;
+	max98373->pm_init_once = false;
+
+	/* codec registration  */
+	ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw,
+					      max98373_sdw_dai,
+					      ARRAY_SIZE(max98373_sdw_dai));
+	if (ret < 0)
+		dev_err(dev, "Failed to register codec: %d\n", ret);
+
+	return ret;
+}
+
+static int max98373_update_status(struct sdw_slave *slave,
+				  enum sdw_slave_status status)
+{
+	struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev);
+
+	if (status == SDW_SLAVE_UNATTACHED)
+		max98373->hw_init = false;
+
+	/*
+	 * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+	 */
+	if (max98373->hw_init || status != SDW_SLAVE_ATTACHED)
+		return 0;
+
+	/* perform I/O transfers required for Slave initialization */
+	return max98373_io_init(slave);
+}
+
+static int max98373_bus_config(struct sdw_slave *slave,
+			       struct sdw_bus_params *params)
+{
+	int ret;
+
+	ret = max98373_clock_config(slave, params);
+	if (ret < 0)
+		dev_err(&slave->dev, "Invalid clk config");
+
+	return ret;
+}
+
+/*
+ * slave_ops: callbacks for get_clock_stop_mode, clock_stop and
+ * port_prep are not defined for now
+ */
+static struct sdw_slave_ops max98373_slave_ops = {
+	.read_prop = max98373_read_prop,
+	.update_status = max98373_update_status,
+	.bus_config = max98373_bus_config,
+};
+
+static int max98373_sdw_probe(struct sdw_slave *slave,
+			      const struct sdw_device_id *id)
+{
+	struct regmap *regmap;
+
+	/* Regmap Initialization */
+	regmap = devm_regmap_init_sdw(slave, &max98373_sdw_regmap);
+	if (!regmap)
+		return -EINVAL;
+
+	return max98373_init(slave, regmap);
+}
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98373_of_match[] = {
+	{ .compatible = "maxim,max98373", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, max98373_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98373_acpi_match[] = {
+	{ "MX98373", 0 },
+	{},
+};
+MODULE_DEVICE_TABLE(acpi, max98373_acpi_match);
+#endif
+
+static const struct sdw_device_id max98373_id[] = {
+	SDW_SLAVE_ENTRY(0x019F, 0x8373, 0),
+	{},
+};
+MODULE_DEVICE_TABLE(sdw, max98373_id);
+
+static struct sdw_driver max98373_sdw_driver = {
+	.driver = {
+		.name = "max98373",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(max98373_of_match),
+		.acpi_match_table = ACPI_PTR(max98373_acpi_match),
+		.pm = &max98373_pm,
+	},
+	.probe = max98373_sdw_probe,
+	.remove = NULL,
+	.ops = &max98373_slave_ops,
+	.id_table = max98373_id,
+};
+
+module_sdw_driver(max98373_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98373 driver SDW");
+MODULE_AUTHOR("Oleg Sherbakov <oleg.sherbakov@maximintegrated.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/max98373-sdw.h b/sound/soc/codecs/max98373-sdw.h
new file mode 100644
index 000000000000..9c40e671240c
--- /dev/null
+++ b/sound/soc/codecs/max98373-sdw.h
@@ -0,0 +1,73 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only
+ *  Copyright (c) 2020 Maxim Integrated
+ */
+
+#ifndef _MAX98373_SDW_H
+#define _MAX98373_SDW_H
+
+#include "max98373.h"
+
+/* SoundWire Slave Control Port (SCP)  */
+#define MAX98373_R0040_SCP_INIT_STAT_1		0x0040
+#define MAX98373_R0041_SCP_INIT_MASK_1		0x0041
+#define MAX98373_R0042_SCP_INIT_STAT_2		0x0042
+#define MAX98373_R0044_SCP_CTRL			0x0044
+#define MAX98373_R0045_SCP_SYSTEM_CTRL		0x0045
+#define MAX98373_R0046_SCP_DEV_NUMBER		0x0046
+#define MAX98373_R0050_SCP_DEV_ID_0		0x0050
+#define MAX98373_R0051_SCP_DEV_ID_1		0x0051
+#define MAX98373_R0052_SCP_DEV_ID_2		0x0052
+#define MAX98373_R0053_SCP_DEV_ID_3		0x0053
+#define MAX98373_R0054_SCP_DEV_ID_4		0x0054
+#define MAX98373_R0055_SCP_DEV_ID_5		0x0055
+#define MAX98373_R0060_SCP_FRAME_CTLR		0x0060
+#define MAX98373_R0070_SCP_FRAME_CTLR		0x0070
+
+/* SoundWire Device Data Port (DP)  */
+/* Data Port 1 Registers */
+#define MAX98373_R0100_DP1_INIT_STAT		0x0100
+#define MAX98373_R0101_DP1_INIT_MASK		0x0101
+#define MAX98373_R0102_DP1_PORT_CTRL		0x0102
+#define MAX98373_R0103_DP1_BLOCK_CTRL_1		0x0103
+#define MAX98373_R0104_DP1_PREPARE_STATUS	0x0104
+#define MAX98373_R0105_DP1_PREPARE_CTRL		0x0105
+/* Data Port 1 Bank 0 Registers */
+#define MAX98373_R0120_DP1_CHANNEL_EN		0x0120
+#define MAX98373_R0122_DP1_SAMPLE_CTRL1		0x0122
+#define MAX98373_R0123_DP1_SAMPLE_CTRL2		0x0123
+#define MAX98373_R0124_DP1_OFFSET_CTRL1		0x0124
+#define MAX98373_R0125_DP1_OFFSET_CTRL2		0x0125
+#define MAX98373_R0126_DP1_HCTRL		0x0126
+#define MAX98373_R0127_DP1_BLOCK_CTRL3		0x0127
+/* Data Port 1 Bank 1 Registers */
+#define MAX98373_R0130_DP1_CHANNEL_EN		0x0130
+#define MAX98373_R0132_DP1_SAMPLE_CTRL1		0x0132
+#define MAX98373_R0133_DP1_SAMPLE_CTRL2		0x0133
+#define MAX98373_R0134_DP1_OFFSET_CTRL1		0x0134
+#define MAX98373_R0135_DP1_OFFSET_CTRL2		0x0135
+#define MAX98373_R0136_DP1_HCTRL		0x0136
+#define MAX98373_R0137_DP1_BLOCK_CTRL3		0x0137
+/* Data Port 3 Registers */
+#define MAX98373_R0300_DP3_INIT_STAT		0x0300
+#define MAX98373_R0301_DP3_INIT_MASK		0x0301
+#define MAX98373_R0302_DP3_PORT_CTRL		0x0302
+#define MAX98373_R0303_DP3_BLOCK_CTRL_1		0x0303
+#define MAX98373_R0304_DP3_PREPARE_STATUS	0x0304
+#define MAX98373_R0305_DP3_PREPARE_CTRL		0x0305
+/* Data Port 3 Bank 0 Registers */
+#define MAX98373_R0320_DP3_CHANNEL_EN		0x0320
+#define MAX98373_R0322_DP3_SAMPLE_CTRL1		0x0322
+#define MAX98373_R0323_DP3_SAMPLE_CTRL2		0x0323
+#define MAX98373_R0324_DP3_OFFSET_CTRL1		0x0324
+#define MAX98373_R0325_DP3_OFFSET_CTRL2		0x0325
+#define MAX98373_R0326_DP3_HCTRL		0x0326
+#define MAX98373_R0327_DP3_BLOCK_CTRL3		0x0327
+/* Data Port 3 Bank 1 Registers */
+#define MAX98373_R0330_DP3_CHANNEL_EN		0x0330
+#define MAX98373_R0332_DP3_SAMPLE_CTRL1		0x0332
+#define MAX98373_R0333_DP3_SAMPLE_CTRL2		0x0333
+#define MAX98373_R0334_DP3_OFFSET_CTRL1		0x0334
+#define MAX98373_R0335_DP3_OFFSET_CTRL2		0x0335
+#define MAX98373_R0336_DP3_HCTRL		0x0336
+#define MAX98373_R0337_DP3_BLOCK_CTRL3		0x0337
+#endif
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
index 96718e3a1ad0..f03835e1f3e4 100644
--- a/sound/soc/codecs/max98373.c
+++ b/sound/soc/codecs/max98373.c
@@ -875,6 +875,21 @@  static const struct snd_soc_component_driver soc_codec_dev_max98373 = {
 	.non_legacy_dai_naming	= 1,
 };
 
+const struct snd_soc_component_driver soc_codec_dev_max98373_sdw = {
+	.probe			= NULL,
+	.controls		= max98373_snd_controls,
+	.num_controls		= ARRAY_SIZE(max98373_snd_controls),
+	.dapm_widgets		= max98373_dapm_widgets,
+	.num_dapm_widgets	= ARRAY_SIZE(max98373_dapm_widgets),
+	.dapm_routes		= max98373_audio_map,
+	.num_dapm_routes	= ARRAY_SIZE(max98373_audio_map),
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+	.non_legacy_dai_naming	= 1,
+};
+EXPORT_SYMBOL_GPL(soc_codec_dev_max98373_sdw);
+
 static const struct regmap_config max98373_regmap = {
 	.reg_bits = 16,
 	.val_bits = 8,
diff --git a/sound/soc/codecs/max98373.h b/sound/soc/codecs/max98373.h
index 63dae8be7105..507b3e967951 100644
--- a/sound/soc/codecs/max98373.h
+++ b/sound/soc/codecs/max98373.h
@@ -212,5 +212,13 @@  struct max98373_priv {
 	bool interleave_mode;
 	unsigned int ch_size;
 	bool tdm_mode;
+	/* variables to support soundwire */
+	struct sdw_slave *slave;
+	bool hw_init;
+	bool pm_init_once;
+	int slot;
+	unsigned int rx_mask;
 };
+
+extern const struct snd_soc_component_driver soc_codec_dev_max98373_sdw;
 #endif