diff mbox series

[v1,04/10] ASoC: codecs: Add uda1342 codec driver

Message ID 3fd0c3a04f5f3bd293168732db457f6854db706e.1725518229.git.zhoubinbin@loongson.cn (mailing list archive)
State Superseded
Headers show
Series ASoC: Some issues about loongson i2s | expand

Commit Message

Binbin Zhou Sept. 5, 2024, 7:02 a.m. UTC
The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
mic inputs), stereo audio DAC, with basic audio processing.

Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>
---
 sound/soc/codecs/Kconfig   |   8 +
 sound/soc/codecs/Makefile  |   2 +
 sound/soc/codecs/uda1342.c | 397 +++++++++++++++++++++++++++++++++++++
 sound/soc/codecs/uda1342.h |  77 +++++++
 4 files changed, 484 insertions(+)
 create mode 100644 sound/soc/codecs/uda1342.c
 create mode 100644 sound/soc/codecs/uda1342.h

Comments

Mark Brown Sept. 5, 2024, 2:28 p.m. UTC | #1
On Thu, Sep 05, 2024 at 03:02:53PM +0800, Binbin Zhou wrote:

> The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
> mic inputs), stereo audio DAC, with basic audio processing.

This looks basically fine overall, there's some issues below but they're
mostly fairly small and stylistic rather than anything major.

> --- /dev/null
> +++ b/sound/soc/codecs/uda1342.c
> @@ -0,0 +1,397 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * uda1342.c  --  UDA1342 ALSA SoC Codec driver

Please keep the entire comment a C++ one so things look more
intentional.

> +static int uda1342_mute(struct snd_soc_dai *dai, int mute, int direction)
> +{
> +	struct snd_soc_component *component = dai->component;
> +	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
> +	unsigned int mask;
> +	unsigned int val = 0;
> +
> +	dev_info(&uda1342->i2c->dev, "mute: %d\n", mute);

This is far too noisy to be logged and will DoS the logs, please just
remove it.

> +
> +	/* Master mute */
> +	mask = BIT(5);
> +	val = mute ? mask : 0;

Please use normal conditional statements to improve legibility.

> +static void uda1342_shutdown(struct snd_pcm_substream *substream,
> +			     struct snd_soc_dai *dai)
> +{
> +	struct snd_soc_component *component = dai->component;
> +	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
> +
> +	if (uda1342->master_substream == substream)
> +		uda1342->master_substream = uda1342->slave_substream;

Please avoid using master/slave terminology, we've tended to go for
provider/consumer in ASoC.

> +static int uda1342_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 uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
> +	struct device *dev = &uda1342->i2c->dev;
> +	unsigned int hw_params = 0;
> +
> +	if (substream == uda1342->slave_substream) {
> +		dev_info(dev, "ignoring hw_params for slave substream\n");
> +		return 0;
> +	}

This is also too noisy, it should be _dbg() at most.

> +	/* codec supports only full slave mode */
> +	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
> +		dev_err(dev, "unsupported slave mode.\n");
> +		return -EINVAL;
> +	}

Use the more modern _CBC_CFC.

> +	/*
> +	 * We can't setup DAI format here as it depends on the word bit num,
> +	 * so let's just store the value for later
> +	 */
> +	uda1342->dai_fmt = fmt;

No need to even store it if only one value is supported.

> +static int uda1342_set_bias_level(struct snd_soc_component *component,
> +				  enum snd_soc_bias_level level)
> +{
> +	switch (level) {
> +	case SND_SOC_BIAS_ON:
> +		break;
> +	case SND_SOC_BIAS_PREPARE:
> +		break;
> +	case SND_SOC_BIAS_STANDBY:
> +		break;
> +	case SND_SOC_BIAS_OFF:
> +		break;
> +	}
> +
> +	return 0;
> +}

This does nothing so it can just be removed.

> +static const struct soc_enum uda1342_mixer_enum[] = {
> +	SOC_ENUM_SINGLE(0x10, 3, 0x04, uda1342_deemph),
> +	SOC_ENUM_SINGLE(0x10, 0, 0x04, uda1342_mixmode),
> +};

This doesn't seem to be referenced anywhere so can be removed, or should
be added to the controls or DAPM?

> +static int uda1342_soc_probe(struct snd_soc_component *component)
> +{
> +	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
> +
> +	snd_soc_add_component_controls(component, uda1342_snd_controls,
> +				       ARRAY_SIZE(uda1342_snd_controls));
> +	snd_soc_dapm_new_controls(dapm, uda1342_dapm_widgets,
> +				  ARRAY_SIZE(uda1342_dapm_widgets));
> +	snd_soc_dapm_add_routes(dapm, uda1342_dapm_routes,
> +				ARRAY_SIZE(uda1342_dapm_routes));

You can point to these arrays from the component struct and have the
core register them for you.

> +static const struct regmap_config uda1342_regmap = {
> +	.reg_bits = 8,
> +	.val_bits = 16,
> +	.max_register = 0x21,
> +	.reg_defaults = uda1342_reg_defaults,
> +	.num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults),
> +	.cache_type = REGCACHE_RBTREE,

In general _MAPLE is preferred for new things unless you have a specific
reason to use _RBTREE, it uses a more modern data structure and should
generally do better.
kernel test robot Sept. 6, 2024, 2:10 a.m. UTC | #2
Hi Binbin,

kernel test robot noticed the following build warnings:

[auto build test WARNING on 097a44db5747403b19d05a9664e8ec6adba27e3b]

url:    https://github.com/intel-lab-lkp/linux/commits/Binbin-Zhou/ASoC-dt-bindings-Add-Everest-ES8323-Codec/20240905-150958
base:   097a44db5747403b19d05a9664e8ec6adba27e3b
patch link:    https://lore.kernel.org/r/3fd0c3a04f5f3bd293168732db457f6854db706e.1725518229.git.zhoubinbin%40loongson.cn
patch subject: [PATCH v1 04/10] ASoC: codecs: Add uda1342 codec driver
config: parisc-randconfig-r071-20240906 (https://download.01.org/0day-ci/archive/20240906/202409060936.0UeDj3S7-lkp@intel.com/config)
compiler: hppa-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240906/202409060936.0UeDj3S7-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202409060936.0UeDj3S7-lkp@intel.com/

All warnings (new ones prefixed by >>):

   sound/soc/codecs/uda1342.c: In function 'uda1342_i2c_probe':
>> sound/soc/codecs/uda1342.c:331:13: warning: unused variable 'ret' [-Wunused-variable]
     331 |         int ret;
         |             ^~~
   sound/soc/codecs/uda1342.c: At top level:
>> sound/soc/codecs/uda1342.c:358:12: warning: 'uda1342_resume' defined but not used [-Wunused-function]
     358 | static int uda1342_resume(struct device *dev)
         |            ^~~~~~~~~~~~~~
>> sound/soc/codecs/uda1342.c:349:12: warning: 'uda1342_suspend' defined but not used [-Wunused-function]
     349 | static int uda1342_suspend(struct device *dev)
         |            ^~~~~~~~~~~~~~~
>> sound/soc/codecs/uda1342.c:232:30: warning: 'uda1342_mixer_enum' defined but not used [-Wunused-const-variable=]
     232 | static const struct soc_enum uda1342_mixer_enum[] = {
         |                              ^~~~~~~~~~~~~~~~~~


vim +/ret +331 sound/soc/codecs/uda1342.c

   231	
 > 232	static const struct soc_enum uda1342_mixer_enum[] = {
   233		SOC_ENUM_SINGLE(0x10, 3, 0x04, uda1342_deemph),
   234		SOC_ENUM_SINGLE(0x10, 0, 0x04, uda1342_mixmode),
   235	};
   236	
   237	static const struct snd_kcontrol_new uda1342_snd_controls[] = {
   238		SOC_SINGLE("Master Playback Volume", 0x11, 0, 0x3F, 1),
   239		SOC_SINGLE("Analog1 Volume", 0x12, 0, 0x1F, 1),
   240	};
   241	
   242	/* Common DAPM widgets */
   243	static const struct snd_soc_dapm_widget uda1342_dapm_widgets[] = {
   244		SND_SOC_DAPM_INPUT("VINL1"),
   245		SND_SOC_DAPM_INPUT("VINR1"),
   246		SND_SOC_DAPM_INPUT("VINL2"),
   247		SND_SOC_DAPM_INPUT("VINR2"),
   248	
   249		SND_SOC_DAPM_DAC("DAC", "Playback", 0, 1, 0),
   250		SND_SOC_DAPM_ADC("ADC", "Capture", 0, 9, 0),
   251	
   252		SND_SOC_DAPM_OUTPUT("VOUTL"),
   253		SND_SOC_DAPM_OUTPUT("VOUTR"),
   254	};
   255	
   256	static const struct snd_soc_dapm_route uda1342_dapm_routes[] = {
   257		{ "ADC", NULL, "VINL1" },
   258		{ "ADC", NULL, "VINR1" },
   259		{ "ADC", NULL, "VINL2" },
   260		{ "ADC", NULL, "VINR2" },
   261		{ "VOUTL", NULL, "DAC" },
   262		{ "VOUTR", NULL, "DAC" },
   263	};
   264	
   265	static const struct snd_soc_dai_ops uda1342_dai_ops = {
   266		.startup	= uda1342_startup,
   267		.shutdown	= uda1342_shutdown,
   268		.hw_params	= uda1342_hw_params,
   269		.mute_stream	= uda1342_mute,
   270		.set_sysclk	= uda1342_set_dai_sysclk,
   271		.set_fmt	= uda1342_set_dai_fmt,
   272	};
   273	
   274	static struct snd_soc_dai_driver uda1342_dai = {
   275		.name = "uda1342-hifi",
   276		/* playback capabilities */
   277		.playback = {
   278			.stream_name = "Playback",
   279			.channels_min = 1,
   280			.channels_max = 2,
   281			.rates = SNDRV_PCM_RATE_8000_48000,
   282			.formats = UDA134X_FORMATS,
   283		},
   284		/* capture capabilities */
   285		.capture = {
   286			.stream_name = "Capture",
   287			.channels_min = 1,
   288			.channels_max = 2,
   289			.rates = SNDRV_PCM_RATE_8000_48000,
   290			.formats = UDA134X_FORMATS,
   291		},
   292		/* pcm operations */
   293		.ops = &uda1342_dai_ops,
   294	};
   295	
   296	static int uda1342_soc_probe(struct snd_soc_component *component)
   297	{
   298		struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
   299	
   300		snd_soc_add_component_controls(component, uda1342_snd_controls,
   301					       ARRAY_SIZE(uda1342_snd_controls));
   302		snd_soc_dapm_new_controls(dapm, uda1342_dapm_widgets,
   303					  ARRAY_SIZE(uda1342_dapm_widgets));
   304		snd_soc_dapm_add_routes(dapm, uda1342_dapm_routes,
   305					ARRAY_SIZE(uda1342_dapm_routes));
   306	
   307		return 0;
   308	}
   309	
   310	static const struct snd_soc_component_driver soc_component_dev_uda1342 = {
   311		.probe			= uda1342_soc_probe,
   312		.set_bias_level		= uda1342_set_bias_level,
   313		.suspend_bias_off	= 1,
   314		.idle_bias_on		= 1,
   315		.use_pmdown_time	= 1,
   316		.endianness		= 1,
   317	};
   318	
   319	static const struct regmap_config uda1342_regmap = {
   320		.reg_bits = 8,
   321		.val_bits = 16,
   322		.max_register = 0x21,
   323		.reg_defaults = uda1342_reg_defaults,
   324		.num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults),
   325		.cache_type = REGCACHE_RBTREE,
   326	};
   327	
   328	static int uda1342_i2c_probe(struct i2c_client *i2c)
   329	{
   330		struct uda1342_priv *uda1342;
 > 331		int ret;
   332	
   333		uda1342 = devm_kzalloc(&i2c->dev, sizeof(*uda1342), GFP_KERNEL);
   334		if (!uda1342)
   335			return -ENOMEM;
   336	
   337		uda1342->regmap = devm_regmap_init_i2c(i2c, &uda1342_regmap);
   338		if (IS_ERR(uda1342->regmap))
   339			return PTR_ERR(uda1342->regmap);
   340	
   341		i2c_set_clientdata(i2c, uda1342);
   342		uda1342->i2c = i2c;
   343	
   344		return devm_snd_soc_register_component(&i2c->dev,
   345						       &soc_component_dev_uda1342,
   346						       &uda1342_dai, 1);
   347	}
   348	
 > 349	static int uda1342_suspend(struct device *dev)
   350	{
   351		struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
   352	
   353		regcache_cache_only(uda1342->regmap, true);
   354	
   355		return 0;
   356	}
   357	
 > 358	static int uda1342_resume(struct device *dev)
   359	{
   360		struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
   361	
   362		regcache_mark_dirty(uda1342->regmap);
   363		regcache_sync(uda1342->regmap);
   364	
   365		return 0;
   366	}
   367
Binbin Zhou Sept. 10, 2024, 7:36 a.m. UTC | #3
Hi Mark:

Thanks for your detailed review.

On Thu, Sep 5, 2024 at 8:28 PM Mark Brown <broonie@kernel.org> wrote:
>
> On Thu, Sep 05, 2024 at 03:02:53PM +0800, Binbin Zhou wrote:
>
> > The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA
> > mic inputs), stereo audio DAC, with basic audio processing.
>
> This looks basically fine overall, there's some issues below but they're
> mostly fairly small and stylistic rather than anything major.
>
> > --- /dev/null
> > +++ b/sound/soc/codecs/uda1342.c
> > @@ -0,0 +1,397 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/*
> > + * uda1342.c  --  UDA1342 ALSA SoC Codec driver
>
> Please keep the entire comment a C++ one so things look more
> intentional.
>
> > +static int uda1342_mute(struct snd_soc_dai *dai, int mute, int direction)
> > +{
> > +     struct snd_soc_component *component = dai->component;
> > +     struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
> > +     unsigned int mask;
> > +     unsigned int val = 0;
> > +
> > +     dev_info(&uda1342->i2c->dev, "mute: %d\n", mute);
>
> This is far too noisy to be logged and will DoS the logs, please just
> remove it.

OK, I will drop it.
>
> > +
> > +     /* Master mute */
> > +     mask = BIT(5);
> > +     val = mute ? mask : 0;
>
> Please use normal conditional statements to improve legibility.

OK, the code as following:

if (mute)
                val = mask;
>
> > +static void uda1342_shutdown(struct snd_pcm_substream *substream,
> > +                          struct snd_soc_dai *dai)
> > +{
> > +     struct snd_soc_component *component = dai->component;
> > +     struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
> > +
> > +     if (uda1342->master_substream == substream)
> > +             uda1342->master_substream = uda1342->slave_substream;
>
> Please avoid using master/slave terminology, we've tended to go for
> provider/consumer in ASoC.

OK, I see, I will  rename it.
>
> > +static int uda1342_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 uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
> > +     struct device *dev = &uda1342->i2c->dev;
> > +     unsigned int hw_params = 0;
> > +
> > +     if (substream == uda1342->slave_substream) {
> > +             dev_info(dev, "ignoring hw_params for slave substream\n");
> > +             return 0;
> > +     }
>
> This is also too noisy, it should be _dbg() at most.

OK, I will drop it.
>
> > +     /* codec supports only full slave mode */
> > +     if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
> > +             dev_err(dev, "unsupported slave mode.\n");
> > +             return -EINVAL;
> > +     }
>
> Use the more modern _CBC_CFC.

OK, it should be replaced by SND_SOC_DAIFMT_BC_FC.
>
> > +     /*
> > +      * We can't setup DAI format here as it depends on the word bit num,
> > +      * so let's just store the value for later
> > +      */
> > +     uda1342->dai_fmt = fmt;
>
> No need to even store it if only one value is supported.

Emm, I will put the fmt setting here from the hw_param function.
Also, the dai_fmt could be dropped.
>
> > +static int uda1342_set_bias_level(struct snd_soc_component *component,
> > +                               enum snd_soc_bias_level level)
> > +{
> > +     switch (level) {
> > +     case SND_SOC_BIAS_ON:
> > +             break;
> > +     case SND_SOC_BIAS_PREPARE:
> > +             break;
> > +     case SND_SOC_BIAS_STANDBY:
> > +             break;
> > +     case SND_SOC_BIAS_OFF:
> > +             break;
> > +     }
> > +
> > +     return 0;
> > +}
>
> This does nothing so it can just be removed.

I will drop it.
>
> > +static const struct soc_enum uda1342_mixer_enum[] = {
> > +     SOC_ENUM_SINGLE(0x10, 3, 0x04, uda1342_deemph),
> > +     SOC_ENUM_SINGLE(0x10, 0, 0x04, uda1342_mixmode),
> > +};
>
> This doesn't seem to be referenced anywhere so can be removed, or should
> be added to the controls or DAPM?

I will drop it.
>
> > +static int uda1342_soc_probe(struct snd_soc_component *component)
> > +{
> > +     struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
> > +
> > +     snd_soc_add_component_controls(component, uda1342_snd_controls,
> > +                                    ARRAY_SIZE(uda1342_snd_controls));
> > +     snd_soc_dapm_new_controls(dapm, uda1342_dapm_widgets,
> > +                               ARRAY_SIZE(uda1342_dapm_widgets));
> > +     snd_soc_dapm_add_routes(dapm, uda1342_dapm_routes,
> > +                             ARRAY_SIZE(uda1342_dapm_routes));
>
> You can point to these arrays from the component struct and have the
> core register them for you.

OK, I will do it.

Thanks.
Binbin
>
> > +static const struct regmap_config uda1342_regmap = {
> > +     .reg_bits = 8,
> > +     .val_bits = 16,
> > +     .max_register = 0x21,
> > +     .reg_defaults = uda1342_reg_defaults,
> > +     .num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults),
> > +     .cache_type = REGCACHE_RBTREE,
>
> In general _MAPLE is preferred for new things unless you have a specific
> reason to use _RBTREE, it uses a more modern data structure and should
> generally do better.
diff mbox series

Patch

diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 85270aa43512..347ac5e8eca7 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -281,6 +281,7 @@  config SND_SOC_ALL_CODECS
 	imply SND_SOC_TWL4030
 	imply SND_SOC_TWL6040
 	imply SND_SOC_UDA1334
+	imply SND_SOC_UDA1342
 	imply SND_SOC_UDA1380
 	imply SND_SOC_WCD9335
 	imply SND_SOC_WCD934X
@@ -2118,6 +2119,13 @@  config SND_SOC_UDA1334
 	  and has basic features such as de-emphasis (at 44.1 kHz sampling
 	  rate) and mute.
 
+config SND_SOC_UDA1342
+	tristate "NXP UDA1342 CODEC"
+	depends on I2C
+	help
+	  The UDA1342 is an NXP audio codec, support 2x Stereo audio ADC (4x PGA mic inputs),
+	  stereo audio DAC, with basic audio processing.
+
 config SND_SOC_UDA1380
 	tristate
 	depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 00ff3fdf0133..f5b1c8499d6c 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -319,6 +319,7 @@  snd-soc-ts3a227e-y := ts3a227e.o
 snd-soc-twl4030-y := twl4030.o
 snd-soc-twl6040-y := twl6040.o
 snd-soc-uda1334-y := uda1334.o
+snd-soc-uda1342-y := uda1342.o
 snd-soc-uda1380-y := uda1380.o
 snd-soc-wcd-classh-y := wcd-clsh-v2.o
 snd-soc-wcd-mbhc-y := wcd-mbhc-v2.o
@@ -723,6 +724,7 @@  obj-$(CONFIG_SND_SOC_TS3A227E)	+= snd-soc-ts3a227e.o
 obj-$(CONFIG_SND_SOC_TWL4030)	+= snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)	+= snd-soc-twl6040.o
 obj-$(CONFIG_SND_SOC_UDA1334)	+= snd-soc-uda1334.o
+obj-$(CONFIG_SND_SOC_UDA1342)	+= snd-soc-uda1342.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
 obj-$(CONFIG_SND_SOC_WCD_CLASSH)	+= snd-soc-wcd-classh.o
 obj-$(CONFIG_SND_SOC_WCD_MBHC)	+= snd-soc-wcd-mbhc.o
diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c
new file mode 100644
index 000000000000..e26aa4148434
--- /dev/null
+++ b/sound/soc/codecs/uda1342.c
@@ -0,0 +1,397 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * uda1342.c  --  UDA1342 ALSA SoC Codec driver
+ *
+ * Modifications by Christian Pellegrin <chripell@evolware.org>
+ *
+ * Copyright 2007 Dension Audio Systems Ltd.
+ * Author: Zoltan Devai
+ *
+ * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "uda1342.h"
+
+#define UDA134X_FORMATS	(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
+			 SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
+
+struct uda1342_priv {
+	int sysclk;
+	int dai_fmt;
+
+	struct snd_pcm_substream *master_substream;
+	struct snd_pcm_substream *slave_substream;
+
+	struct regmap *regmap;
+	struct i2c_client *i2c;
+};
+
+static const struct reg_default uda1342_reg_defaults[] = {
+	{ 0x00, 0x1042 },
+	{ 0x01, 0x0000 },
+	{ 0x10, 0x0088 },
+	{ 0x11, 0x0000 },
+	{ 0x12, 0x0000 },
+	{ 0x20, 0x0080 },
+	{ 0x21, 0x0080 },
+};
+
+static inline void uda1342_reset(struct uda1342_priv *uda1342)
+{
+	unsigned int mask = BIT(15);
+
+	regmap_write(uda1342->regmap, 0x00, mask);
+}
+
+static int uda1342_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+	struct snd_soc_component *component = dai->component;
+	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+	unsigned int mask;
+	unsigned int val = 0;
+
+	dev_info(&uda1342->i2c->dev, "mute: %d\n", mute);
+
+	/* Master mute */
+	mask = BIT(5);
+	val = mute ? mask : 0;
+
+	return regmap_update_bits(uda1342->regmap, 0x10, mask, val);
+}
+
+static int uda1342_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+	struct snd_pcm_runtime *master_runtime;
+
+	if (uda1342->master_substream) {
+		master_runtime = uda1342->master_substream->runtime;
+
+		snd_pcm_hw_constraint_single(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_RATE, master_runtime->rate);
+		snd_pcm_hw_constraint_single(substream->runtime,
+					     SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+					     master_runtime->sample_bits);
+
+		uda1342->slave_substream = substream;
+	} else {
+		uda1342->master_substream = substream;
+	}
+
+	return 0;
+}
+
+static void uda1342_shutdown(struct snd_pcm_substream *substream,
+			     struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+
+	if (uda1342->master_substream == substream)
+		uda1342->master_substream = uda1342->slave_substream;
+
+	uda1342->slave_substream = NULL;
+}
+
+static int uda1342_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 uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+	struct device *dev = &uda1342->i2c->dev;
+	unsigned int hw_params = 0;
+
+	if (substream == uda1342->slave_substream) {
+		dev_info(dev, "ignoring hw_params for slave substream\n");
+		return 0;
+	}
+
+	/* set SYSCLK / fs ratio */
+	switch (uda1342->sysclk / params_rate(params)) {
+	case 512:
+		break;
+	case 384:
+		hw_params |= BIT(4);
+		break;
+	case 256:
+		hw_params |= BIT(5);
+		break;
+	default:
+		dev_err(dev, "unsupported frequency\n");
+		return -EINVAL;
+	}
+
+	/* set DAI format and word length */
+	switch (uda1342->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+	case SND_SOC_DAIFMT_I2S:
+		break;
+	case SND_SOC_DAIFMT_RIGHT_J:
+		switch (params_width(params)) {
+		case 16:
+			hw_params |= BIT(1);
+			break;
+		case 18:
+			hw_params |= BIT(2);
+			break;
+		case 20:
+			hw_params |= BIT(2) | BIT(1);
+			break;
+		default:
+			dev_err(dev, "unsupported format (right)\n");
+			return -EINVAL;
+		}
+		break;
+	case SND_SOC_DAIFMT_LEFT_J:
+		hw_params |= BIT(3);
+		break;
+	default:
+		dev_err(dev, "unsupported format\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int uda1342_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+				  int clk_id, unsigned int freq, int dir)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+	struct device *dev = &uda1342->i2c->dev;
+
+	/*
+	 * Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
+	 * because the codec is slave. Of course limitations of the clock
+	 * master (the IIS controller) apply.
+	 * We'll error out on set_hw_params if it's not OK
+	 */
+	if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
+		uda1342->sysclk = freq;
+		return 0;
+	}
+
+	dev_err(dev, "unsupported sysclk\n");
+
+	return -EINVAL;
+}
+
+static int uda1342_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+	struct snd_soc_component *component = codec_dai->component;
+	struct uda1342_priv *uda1342 = snd_soc_component_get_drvdata(component);
+	struct device *dev = &uda1342->i2c->dev;
+
+	/* codec supports only full slave mode */
+	if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+		dev_err(dev, "unsupported slave mode.\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * We can't setup DAI format here as it depends on the word bit num,
+	 * so let's just store the value for later
+	 */
+	uda1342->dai_fmt = fmt;
+
+	return 0;
+}
+
+static int uda1342_set_bias_level(struct snd_soc_component *component,
+				  enum snd_soc_bias_level level)
+{
+	switch (level) {
+	case SND_SOC_BIAS_ON:
+		break;
+	case SND_SOC_BIAS_PREPARE:
+		break;
+	case SND_SOC_BIAS_STANDBY:
+		break;
+	case SND_SOC_BIAS_OFF:
+		break;
+	}
+
+	return 0;
+}
+
+static const char *const uda1342_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
+static const char *const uda1342_mixmode[] = {"Differential", "Analog1", "Analog2", "Both"};
+
+static const struct soc_enum uda1342_mixer_enum[] = {
+	SOC_ENUM_SINGLE(0x10, 3, 0x04, uda1342_deemph),
+	SOC_ENUM_SINGLE(0x10, 0, 0x04, uda1342_mixmode),
+};
+
+static const struct snd_kcontrol_new uda1342_snd_controls[] = {
+	SOC_SINGLE("Master Playback Volume", 0x11, 0, 0x3F, 1),
+	SOC_SINGLE("Analog1 Volume", 0x12, 0, 0x1F, 1),
+};
+
+/* Common DAPM widgets */
+static const struct snd_soc_dapm_widget uda1342_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("VINL1"),
+	SND_SOC_DAPM_INPUT("VINR1"),
+	SND_SOC_DAPM_INPUT("VINL2"),
+	SND_SOC_DAPM_INPUT("VINR2"),
+
+	SND_SOC_DAPM_DAC("DAC", "Playback", 0, 1, 0),
+	SND_SOC_DAPM_ADC("ADC", "Capture", 0, 9, 0),
+
+	SND_SOC_DAPM_OUTPUT("VOUTL"),
+	SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda1342_dapm_routes[] = {
+	{ "ADC", NULL, "VINL1" },
+	{ "ADC", NULL, "VINR1" },
+	{ "ADC", NULL, "VINL2" },
+	{ "ADC", NULL, "VINR2" },
+	{ "VOUTL", NULL, "DAC" },
+	{ "VOUTR", NULL, "DAC" },
+};
+
+static const struct snd_soc_dai_ops uda1342_dai_ops = {
+	.startup	= uda1342_startup,
+	.shutdown	= uda1342_shutdown,
+	.hw_params	= uda1342_hw_params,
+	.mute_stream	= uda1342_mute,
+	.set_sysclk	= uda1342_set_dai_sysclk,
+	.set_fmt	= uda1342_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver uda1342_dai = {
+	.name = "uda1342-hifi",
+	/* playback capabilities */
+	.playback = {
+		.stream_name = "Playback",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = UDA134X_FORMATS,
+	},
+	/* capture capabilities */
+	.capture = {
+		.stream_name = "Capture",
+		.channels_min = 1,
+		.channels_max = 2,
+		.rates = SNDRV_PCM_RATE_8000_48000,
+		.formats = UDA134X_FORMATS,
+	},
+	/* pcm operations */
+	.ops = &uda1342_dai_ops,
+};
+
+static int uda1342_soc_probe(struct snd_soc_component *component)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+
+	snd_soc_add_component_controls(component, uda1342_snd_controls,
+				       ARRAY_SIZE(uda1342_snd_controls));
+	snd_soc_dapm_new_controls(dapm, uda1342_dapm_widgets,
+				  ARRAY_SIZE(uda1342_dapm_widgets));
+	snd_soc_dapm_add_routes(dapm, uda1342_dapm_routes,
+				ARRAY_SIZE(uda1342_dapm_routes));
+
+	return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_uda1342 = {
+	.probe			= uda1342_soc_probe,
+	.set_bias_level		= uda1342_set_bias_level,
+	.suspend_bias_off	= 1,
+	.idle_bias_on		= 1,
+	.use_pmdown_time	= 1,
+	.endianness		= 1,
+};
+
+static const struct regmap_config uda1342_regmap = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = 0x21,
+	.reg_defaults = uda1342_reg_defaults,
+	.num_reg_defaults = ARRAY_SIZE(uda1342_reg_defaults),
+	.cache_type = REGCACHE_RBTREE,
+};
+
+static int uda1342_i2c_probe(struct i2c_client *i2c)
+{
+	struct uda1342_priv *uda1342;
+	int ret;
+
+	uda1342 = devm_kzalloc(&i2c->dev, sizeof(*uda1342), GFP_KERNEL);
+	if (!uda1342)
+		return -ENOMEM;
+
+	uda1342->regmap = devm_regmap_init_i2c(i2c, &uda1342_regmap);
+	if (IS_ERR(uda1342->regmap))
+		return PTR_ERR(uda1342->regmap);
+
+	i2c_set_clientdata(i2c, uda1342);
+	uda1342->i2c = i2c;
+
+	return devm_snd_soc_register_component(&i2c->dev,
+					       &soc_component_dev_uda1342,
+					       &uda1342_dai, 1);
+}
+
+static int uda1342_suspend(struct device *dev)
+{
+	struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
+
+	regcache_cache_only(uda1342->regmap, true);
+
+	return 0;
+}
+
+static int uda1342_resume(struct device *dev)
+{
+	struct uda1342_priv *uda1342 = dev_get_drvdata(dev);
+
+	regcache_mark_dirty(uda1342->regmap);
+	regcache_sync(uda1342->regmap);
+
+	return 0;
+}
+
+static const struct dev_pm_ops uda1342_pm = {
+	SET_SYSTEM_SLEEP_PM_OPS(uda1342_suspend, uda1342_resume)
+};
+
+static const struct i2c_device_id uda1342_i2c_id[] = {
+	 { "uda1342", 0 },
+	 { }
+};
+MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id);
+
+static const struct of_device_id uda1342_of_match[] = {
+	 { .compatible = "nxp,uda1342" },
+	 { }
+};
+MODULE_DEVICE_TABLE(of, uda1342_of_match);
+
+static struct i2c_driver uda1342_i2c_driver = {
+	.driver = {
+		.name =  "uda1342",
+		.of_match_table = uda1342_of_match,
+		.pm = pm_sleep_ptr(&uda1342_pm),
+	 },
+	.probe = uda1342_i2c_probe,
+	.id_table = uda1342_i2c_id,
+};
+module_i2c_driver(uda1342_i2c_driver);
+
+MODULE_DESCRIPTION("UDA1342 ALSA soc codec driver");
+MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1342.h b/sound/soc/codecs/uda1342.h
new file mode 100644
index 000000000000..1f4632643ddd
--- /dev/null
+++ b/sound/soc/codecs/uda1342.h
@@ -0,0 +1,77 @@ 
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Audio support for NXP UDA1342
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright (c) 2024 Loongson Technology Corporation Limited.
+ */
+
+#ifndef _UDA1342_H
+#define _UDA1342_H
+
+#define UDA1342_CLK	0x00
+#define UDA1342_IFACE	0x01
+#define UDA1342_PM	0x02
+#define UDA1342_AMIX	0x03
+#define UDA1342_HP	0x04
+#define UDA1342_MVOL	0x11
+#define UDA1342_MIXVOL	0x12
+#define UDA1342_MODE	0x12
+#define UDA1342_DEEMP	0x13
+#define UDA1342_MIXER	0x14
+#define UDA1342_INTSTAT	0x18
+#define UDA1342_DEC	0x20
+#define UDA1342_PGA	0x21
+#define UDA1342_ADC	0x22
+#define UDA1342_AGC	0x23
+#define UDA1342_DECSTAT	0x28
+#define UDA1342_RESET	0x7f
+
+#define UDA1342_CACHEREGNUM 0x24
+
+/* Register flags */
+#define R00_EN_ADC	0x0800
+#define R00_EN_DEC	0x0400
+#define R00_EN_DAC	0x0200
+#define R00_EN_INT	0x0100
+#define R00_DAC_CLK	0x0010
+#define R01_SFORI_I2S   0x0000
+#define R01_SFORI_LSB16 0x0100
+#define R01_SFORI_LSB18 0x0200
+#define R01_SFORI_LSB20 0x0300
+#define R01_SFORI_MSB   0x0500
+#define R01_SFORI_MASK  0x0700
+#define R01_SFORO_I2S   0x0000
+#define R01_SFORO_LSB16 0x0001
+#define R01_SFORO_LSB18 0x0002
+#define R01_SFORO_LSB20 0x0003
+#define R01_SFORO_LSB24 0x0004
+#define R01_SFORO_MSB   0x0005
+#define R01_SFORO_MASK  0x0007
+#define R01_SEL_SOURCE  0x0040
+#define R01_SIM		0x0010
+#define R02_PON_PLL	0x8000
+#define R02_PON_HP	0x2000
+#define R02_PON_DAC	0x0400
+#define R02_PON_BIAS	0x0100
+#define R02_EN_AVC	0x0080
+#define R02_PON_AVC	0x0040
+#define R02_PON_LNA	0x0010
+#define R02_PON_PGAL	0x0008
+#define R02_PON_ADCL	0x0004
+#define R02_PON_PGAR	0x0002
+#define R02_PON_ADCR	0x0001
+#define R13_MTM		0x4000
+#define R14_SILENCE	0x0080
+#define R14_SDET_ON	0x0040
+#define R21_MT_ADC	0x8000
+#define R22_SEL_LNA	0x0008
+#define R22_SEL_MIC	0x0004
+#define R22_SKIP_DCFIL	0x0002
+#define R23_AGC_EN	0x0001
+
+#define UDA1342_DAI_DUPLEX	0 /* playback and capture on single DAI */
+#define UDA1342_DAI_PLAYBACK	1 /* playback DAI */
+#define UDA1342_DAI_CAPTURE	2 /* capture DAI */
+
+#endif /* _UDA1342_H */