]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
ASoC: codecs: Add uda1342 codec driver
authorBinbin Zhou <zhoubinbin@loongson.cn>
Wed, 9 Oct 2024 07:52:26 +0000 (15:52 +0800)
committerMark Brown <broonie@kernel.org>
Wed, 9 Oct 2024 13:40:51 +0000 (14:40 +0100)
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>
Link: https://patch.msgid.link/927e46b48ca84865a216ce08e7c53df59c2a8c0b.1728459624.git.zhoubinbin@loongson.cn
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/uda1342.c [new file with mode: 0644]
sound/soc/codecs/uda1342.h [new file with mode: 0644]

index 6480f1bd43f42c4e70c92138a53b8908e3946e55..6a6125e94d2dc094b10c6f2fb53dd447e831f23d 100644 (file)
@@ -283,6 +283,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
@@ -2131,6 +2132,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
index 029fa42ce5c0959194b7489863823fd0bc6800b7..ac7d8b71b32b19a41366966065de450b947c9178 100644 (file)
@@ -325,6 +325,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
@@ -735,6 +736,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 (file)
index 0000000..3d49a78
--- /dev/null
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// uda1342.c  --  UDA1342 ALSA SoC Codec driver
+// Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
+//
+// Copyright 2007 Dension Audio Systems Ltd.
+// Copyright 2024 Loongson Technology Co.,Ltd.
+//
+// Modifications by Christian Pellegrin <chripell@evolware.org>
+// Further cleanup and restructuring by:
+//         Binbin Zhou <zhoubinbin@loongson.cn>
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <sound/soc.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 *provider_substream;
+       struct snd_pcm_substream *consumer_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 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;
+
+       /* Master mute */
+       mask = BIT(5);
+       if (mute)
+               val = mask;
+
+       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 *provider_runtime;
+
+       if (uda1342->provider_substream) {
+               provider_runtime = uda1342->provider_substream->runtime;
+
+               snd_pcm_hw_constraint_single(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_RATE, provider_runtime->rate);
+               snd_pcm_hw_constraint_single(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                                            provider_runtime->sample_bits);
+
+               uda1342->consumer_substream = substream;
+       } else {
+               uda1342->provider_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->provider_substream == substream)
+               uda1342->provider_substream = uda1342->consumer_substream;
+
+       uda1342->consumer_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->consumer_substream)
+               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 regmap_update_bits(uda1342->regmap, 0x0,
+                                 STATUS0_DAIFMT_MASK | STATUS0_SYSCLK_MASK, hw_params);
+}
+
+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);
+
+       /* codec supports only full consumer mode */
+       if ((fmt & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_BC_FC) {
+               dev_err(&uda1342->i2c->dev, "unsupported consumer 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 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 const struct snd_soc_component_driver soc_component_dev_uda1342 = {
+       .controls               = uda1342_snd_controls,
+       .num_controls           = ARRAY_SIZE(uda1342_snd_controls),
+       .dapm_widgets           = uda1342_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(uda1342_dapm_widgets),
+       .dapm_routes            = uda1342_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(uda1342_dapm_routes),
+       .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_MAPLE,
+};
+
+static int uda1342_i2c_probe(struct i2c_client *i2c)
+{
+       struct uda1342_priv *uda1342;
+
+       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 DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops,
+                                uda1342_suspend, uda1342_resume, NULL);
+
+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_ops),
+        },
+       .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_AUTHOR("Binbin Zhou <zhoubinbin@loongson.cn>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda1342.h b/sound/soc/codecs/uda1342.h
new file mode 100644 (file)
index 0000000..ff6aea0
--- /dev/null
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Audio support for NXP UDA1342
+ *
+ * Copyright (c) 2005 Giorgio Padrin <giorgio@mandarinlogiq.org>
+ * Copyright (c) 2024 Binbin Zhou <zhoubinbin@loongson.cn>
+ */
+
+#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
+
+/* 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 */
+
+#define STATUS0_DAIFMT_MASK (~(7 << 1))
+#define STATUS0_SYSCLK_MASK (~(3 << 4))
+
+#endif /* _UDA1342_H */