[5/5] ASoC: pcm179x: Add support for pcm1975

Message ID 20261002090308.941453-6-roman@amarulasolutions.com
State Superseded
Headers
Series ASoC: pcm179x: Add support for pcm1795 and pcm1796 |

Commit Message

Roman Smrž Oct. 2, 2026, 9:03 a.m. UTC
From: Michael Trimarchi <michael@amarulasolutions.com>

The PCM1795 is register compatible with the PCM1792A, but it encodes two
of the register 18 FMT[2:0] values differently: code 001 selects 32-bit
standard data instead of 20-bit, and code 100 selects 32-bit I2S instead
of 16-bit I2S. It has no 20-bit format at all. The PCM1796 shares the
PCM1792A encoding.

The model is read with device_get_match_data(), so each bus driver
carries the model in its own of_device_id table. The tables cannot move
to the common file: MODULE_DEVICE_TABLE() aliases a table that has to be
defined in the same translation unit.

Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
Signed-off-by: Roman Smrž <roman@amarulasolutions.com>
---
 sound/soc/codecs/pcm179x-i2c.c |  3 +-
 sound/soc/codecs/pcm179x-spi.c |  3 +-
 sound/soc/codecs/pcm179x.c     | 62 +++++++++++++++++++++++++++++++---
 sound/soc/codecs/pcm179x.h     | 12 +++++--
 4 files changed, 71 insertions(+), 9 deletions(-)
  

Comments

Dario Binacchi Oct. 2, 2026, 9:44 a.m. UTC | #1
Hi Roman,


On Fri, Oct 2, 2026 at 11:03 AM Roman Smrž <roman@amarulasolutions.com> wrote:
>
> From: Michael Trimarchi <michael@amarulasolutions.com>
>
> The PCM1795 is register compatible with the PCM1792A, but it encodes two
> of the register 18 FMT[2:0] values differently: code 001 selects 32-bit
> standard data instead of 20-bit, and code 100 selects 32-bit I2S instead
> of 16-bit I2S. It has no 20-bit format at all. The PCM1796 shares the
> PCM1792A encoding.
>
> The model is read with device_get_match_data(), so each bus driver
> carries the model in its own of_device_id table. The tables cannot move
> to the common file: MODULE_DEVICE_TABLE() aliases a table that has to be
> defined in the same translation unit.
>
> Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
> Signed-off-by: Roman Smrž <roman@amarulasolutions.com>
> ---
>  sound/soc/codecs/pcm179x-i2c.c |  3 +-
>  sound/soc/codecs/pcm179x-spi.c |  3 +-
>  sound/soc/codecs/pcm179x.c     | 62 +++++++++++++++++++++++++++++++---
>  sound/soc/codecs/pcm179x.h     | 12 +++++--
>  4 files changed, 71 insertions(+), 9 deletions(-)
>
> diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
> index e81e20fadadf..751f4b74647a 100644
> --- a/sound/soc/codecs/pcm179x-i2c.c
> +++ b/sound/soc/codecs/pcm179x-i2c.c
> @@ -32,7 +32,8 @@ static int pcm179x_i2c_probe(struct i2c_client *client)
>  #ifdef CONFIG_OF
>  static const struct of_device_id pcm179x_of_match[] = {
>         { .compatible = "ti,pcm1792a", },
> -       { .compatible = "ti,pcm1796", },
> +       { .compatible = "ti,pcm1795", .data = (void *)PCM1795, },
> +       { .compatible = "ti,pcm1796", .data = (void *)PCM1796, },
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, pcm179x_of_match);
> diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
> index e5e4b942acfc..2b487a4d7c82 100644
> --- a/sound/soc/codecs/pcm179x-spi.c
> +++ b/sound/soc/codecs/pcm179x-spi.c
> @@ -31,7 +31,8 @@ static int pcm179x_spi_probe(struct spi_device *spi)
>
>  static const struct of_device_id pcm179x_of_match[] __maybe_unused = {
>         { .compatible = "ti,pcm1792a", },
> -       { .compatible = "ti,pcm1796" },
> +       { .compatible = "ti,pcm1795", .data = (void *)PCM1795, },
> +       { .compatible = "ti,pcm1796", .data = (void *)PCM1796, },
>         { }
>  };
>  MODULE_DEVICE_TABLE(of, pcm179x_of_match);
> diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
> index c059e410cbbe..3ee781ff54f8 100644
> --- a/sound/soc/codecs/pcm179x.c
> +++ b/sound/soc/codecs/pcm179x.c
> @@ -19,6 +19,7 @@
>  #include <sound/soc.h>
>  #include <sound/tlv.h>
>  #include <linux/of.h>
> +#include <linux/property.h>
>
>  #include "pcm179x.h"
>
> @@ -63,6 +64,7 @@ struct pcm179x_private {
>         struct regmap *regmap;
>         unsigned int format;
>         unsigned int rate;
> +       enum pcm179x_type codec_model;
>  };
>
>  struct pcm179x_fmt_table {
> @@ -71,7 +73,17 @@ struct pcm179x_fmt_table {
>         unsigned int value;     /* FMT[2:0] */
>  };
>
> -static const struct pcm179x_fmt_table pcm1792a_fmt[] = {
> +/*
> + * Register 18 FMT[2:0] is not encoded the same way by every model: value 001
> + * and value 100 are 20-bit and 16-bit I2S on the PCM1792A/PCM1796 but 32-bit
> + * and 32-bit I2S on the PCM1795, and neither model has a 32-bit
> + * left-justified mode.
> + *
> + * The PCM1792A/PCM1796 have no 32-bit format at all, so a 32-bit container
> + * keeps the 24-bit value and is clocked out as 24-bit data, as this driver has
> + * always done. Only the PCM1795 has real 32-bit formats.
> + */
> +static const struct pcm179x_fmt_table pcm179x_fmt[] = {
>         { SND_SOC_DAIFMT_RIGHT_J, 16, 0 },
>         { SND_SOC_DAIFMT_RIGHT_J, 24, 2 },
>         { SND_SOC_DAIFMT_RIGHT_J, 32, 2 },
> @@ -82,16 +94,53 @@ static const struct pcm179x_fmt_table pcm1792a_fmt[] = {
>         { SND_SOC_DAIFMT_I2S,     32, 5 },
>  };
>
> +static const struct pcm179x_fmt_table pcm1795_fmt[] = {
> +       { SND_SOC_DAIFMT_RIGHT_J, 16, 0 },
> +       { SND_SOC_DAIFMT_RIGHT_J, 24, 2 },
> +       { SND_SOC_DAIFMT_RIGHT_J, 32, 1 },
> +       { SND_SOC_DAIFMT_LEFT_J,  24, 3 },
> +       { SND_SOC_DAIFMT_I2S,     24, 5 },
> +       { SND_SOC_DAIFMT_I2S,     32, 4 },
> +};
> +
> +struct pcm179x_model {
> +       u64 formats;
> +       const struct pcm179x_fmt_table *fmt;
> +       unsigned int num_fmt;
> +};
> +
> +static const struct pcm179x_model pcm179x_models[] = {
> +       [PCM1792A] = { PCM179X_FORMATS, pcm179x_fmt, ARRAY_SIZE(pcm179x_fmt) },
> +       [PCM1795] = { PCM1795_FORMATS, pcm1795_fmt, ARRAY_SIZE(pcm1795_fmt) },
> +       [PCM1796] = { PCM179X_FORMATS, pcm179x_fmt, ARRAY_SIZE(pcm179x_fmt) },
> +};
> +
>  static int pcm179x_fmt_value(struct pcm179x_private *priv, unsigned int fmt,
>                              unsigned int width)
>  {
> -       for (unsigned int i = 0; i < ARRAY_SIZE(pcm1792a_fmt); i++)
> -               if (pcm1792a_fmt[i].fmt == fmt && pcm1792a_fmt[i].width == width)
> -                       return pcm1792a_fmt[i].value;
> +       const struct pcm179x_model *model = &pcm179x_models[priv->codec_model];
> +       const struct pcm179x_fmt_table *fmt_tbl = model->fmt;
> +
> +       for (unsigned int i = 0; i < model->num_fmt; i++)
> +               if (fmt_tbl[i].fmt == fmt && fmt_tbl[i].width == width)
> +                       return fmt_tbl[i].value;
>
>         return -EINVAL;
>  }
>
> +static int pcm179x_startup(struct snd_pcm_substream *substream,
> +                          struct snd_soc_dai *dai)
> +{
> +       struct snd_soc_component *component = dai->component;
> +       struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
> +       u64 formats = pcm179x_models[priv->codec_model].formats;
> +
> +       snd_pcm_hw_constraint_mask64(substream->runtime,
> +                                    SNDRV_PCM_HW_PARAM_FORMAT, formats);
> +
> +       return 0;
> +}
> +
>  static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
>                               unsigned int format)
>  {
> @@ -149,6 +198,7 @@ static const u64 pcm179x_selectable_formats =
>         SND_SOC_POSSIBLE_DAIFMT_RIGHT_J;
>
>  static const struct snd_soc_dai_ops pcm179x_dai_ops = {
> +       .startup        = pcm179x_startup,
>         .set_fmt        = pcm179x_set_dai_fmt,
>         .hw_params      = pcm179x_hw_params,
>         .mute_stream    = pcm179x_mute,
> @@ -190,7 +240,7 @@ static struct snd_soc_dai_driver pcm179x_dai = {
>                 .rates = SNDRV_PCM_RATE_CONTINUOUS,
>                 .rate_min = 10000,
>                 .rate_max = 200000,
> -               .formats = PCM1792A_FORMATS, },
> +               .formats = PCM179X_FORMATS, },
>         .ops = &pcm179x_dai_ops,
>  };
>
> @@ -226,6 +276,8 @@ int pcm179x_common_init(struct device *dev, struct regmap *regmap)
>         if (!pcm179x)
>                 return -ENOMEM;
>
> +       pcm179x->codec_model =
> +               (enum pcm179x_type)(uintptr_t)device_get_match_data(dev);
>         pcm179x->regmap = regmap;
>         dev_set_drvdata(dev, pcm179x);
>
> diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h
> index 0039ca8ee742..33971fa93546 100644
> --- a/sound/soc/codecs/pcm179x.h
> +++ b/sound/soc/codecs/pcm179x.h
> @@ -8,8 +8,16 @@
>  #ifndef __PCM179X_H__
>  #define __PCM179X_H__
>
> -#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
> -                         SNDRV_PCM_FMTBIT_S16_LE)
> +enum pcm179x_type {
> +       PCM1792A,
> +       PCM1795,
> +       PCM1796,
> +};
> +
> +#define PCM179X_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
> +                        SNDRV_PCM_FMTBIT_S16_LE)
> +
> +#define PCM1795_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE)
>
>  extern const struct regmap_config pcm179x_regmap_config;
>
> --
> 2.55.0
>
>

Reviewed-by: Dario Binacchi <dario.binacchi@amarulasolutions.com>

Reagrds,
Dario
--
Dario Binacchi
Senior Embedded Software Engineer
M. +39 328 0625246
dario.binacchi@amarulasolutions.com
―――――――――――――――
Amarula Solutions SRL
 Via Felice Cavallotti 25D, 41012 Carpi, MO, IT
info@amarulasolutions.com
www.amarulasolutions.com

To unsubscribe from this group and stop receiving emails from it, send an email to linux-amarula+unsubscribe@amarulasolutions.com.
  

Patch

diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
index e81e20fadadf..751f4b74647a 100644
--- a/sound/soc/codecs/pcm179x-i2c.c
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -32,7 +32,8 @@  static int pcm179x_i2c_probe(struct i2c_client *client)
 #ifdef CONFIG_OF
 static const struct of_device_id pcm179x_of_match[] = {
 	{ .compatible = "ti,pcm1792a", },
-	{ .compatible = "ti,pcm1796", },
+	{ .compatible = "ti,pcm1795", .data = (void *)PCM1795, },
+	{ .compatible = "ti,pcm1796", .data = (void *)PCM1796, },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, pcm179x_of_match);
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
index e5e4b942acfc..2b487a4d7c82 100644
--- a/sound/soc/codecs/pcm179x-spi.c
+++ b/sound/soc/codecs/pcm179x-spi.c
@@ -31,7 +31,8 @@  static int pcm179x_spi_probe(struct spi_device *spi)
 
 static const struct of_device_id pcm179x_of_match[] __maybe_unused = {
 	{ .compatible = "ti,pcm1792a", },
-	{ .compatible = "ti,pcm1796" },
+	{ .compatible = "ti,pcm1795", .data = (void *)PCM1795, },
+	{ .compatible = "ti,pcm1796", .data = (void *)PCM1796, },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, pcm179x_of_match);
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index c059e410cbbe..3ee781ff54f8 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -19,6 +19,7 @@ 
 #include <sound/soc.h>
 #include <sound/tlv.h>
 #include <linux/of.h>
+#include <linux/property.h>
 
 #include "pcm179x.h"
 
@@ -63,6 +64,7 @@  struct pcm179x_private {
 	struct regmap *regmap;
 	unsigned int format;
 	unsigned int rate;
+	enum pcm179x_type codec_model;
 };
 
 struct pcm179x_fmt_table {
@@ -71,7 +73,17 @@  struct pcm179x_fmt_table {
 	unsigned int value;	/* FMT[2:0] */
 };
 
-static const struct pcm179x_fmt_table pcm1792a_fmt[] = {
+/*
+ * Register 18 FMT[2:0] is not encoded the same way by every model: value 001
+ * and value 100 are 20-bit and 16-bit I2S on the PCM1792A/PCM1796 but 32-bit
+ * and 32-bit I2S on the PCM1795, and neither model has a 32-bit
+ * left-justified mode.
+ *
+ * The PCM1792A/PCM1796 have no 32-bit format at all, so a 32-bit container
+ * keeps the 24-bit value and is clocked out as 24-bit data, as this driver has
+ * always done. Only the PCM1795 has real 32-bit formats.
+ */
+static const struct pcm179x_fmt_table pcm179x_fmt[] = {
 	{ SND_SOC_DAIFMT_RIGHT_J, 16, 0 },
 	{ SND_SOC_DAIFMT_RIGHT_J, 24, 2 },
 	{ SND_SOC_DAIFMT_RIGHT_J, 32, 2 },
@@ -82,16 +94,53 @@  static const struct pcm179x_fmt_table pcm1792a_fmt[] = {
 	{ SND_SOC_DAIFMT_I2S,     32, 5 },
 };
 
+static const struct pcm179x_fmt_table pcm1795_fmt[] = {
+	{ SND_SOC_DAIFMT_RIGHT_J, 16, 0 },
+	{ SND_SOC_DAIFMT_RIGHT_J, 24, 2 },
+	{ SND_SOC_DAIFMT_RIGHT_J, 32, 1 },
+	{ SND_SOC_DAIFMT_LEFT_J,  24, 3 },
+	{ SND_SOC_DAIFMT_I2S,     24, 5 },
+	{ SND_SOC_DAIFMT_I2S,     32, 4 },
+};
+
+struct pcm179x_model {
+	u64 formats;
+	const struct pcm179x_fmt_table *fmt;
+	unsigned int num_fmt;
+};
+
+static const struct pcm179x_model pcm179x_models[] = {
+	[PCM1792A] = { PCM179X_FORMATS, pcm179x_fmt, ARRAY_SIZE(pcm179x_fmt) },
+	[PCM1795] = { PCM1795_FORMATS, pcm1795_fmt, ARRAY_SIZE(pcm1795_fmt) },
+	[PCM1796] = { PCM179X_FORMATS, pcm179x_fmt, ARRAY_SIZE(pcm179x_fmt) },
+};
+
 static int pcm179x_fmt_value(struct pcm179x_private *priv, unsigned int fmt,
 			     unsigned int width)
 {
-	for (unsigned int i = 0; i < ARRAY_SIZE(pcm1792a_fmt); i++)
-		if (pcm1792a_fmt[i].fmt == fmt && pcm1792a_fmt[i].width == width)
-			return pcm1792a_fmt[i].value;
+	const struct pcm179x_model *model = &pcm179x_models[priv->codec_model];
+	const struct pcm179x_fmt_table *fmt_tbl = model->fmt;
+
+	for (unsigned int i = 0; i < model->num_fmt; i++)
+		if (fmt_tbl[i].fmt == fmt && fmt_tbl[i].width == width)
+			return fmt_tbl[i].value;
 
 	return -EINVAL;
 }
 
+static int pcm179x_startup(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	struct snd_soc_component *component = dai->component;
+	struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
+	u64 formats = pcm179x_models[priv->codec_model].formats;
+
+	snd_pcm_hw_constraint_mask64(substream->runtime,
+				     SNDRV_PCM_HW_PARAM_FORMAT, formats);
+
+	return 0;
+}
+
 static int pcm179x_set_dai_fmt(struct snd_soc_dai *codec_dai,
                              unsigned int format)
 {
@@ -149,6 +198,7 @@  static const u64 pcm179x_selectable_formats =
 	SND_SOC_POSSIBLE_DAIFMT_RIGHT_J;
 
 static const struct snd_soc_dai_ops pcm179x_dai_ops = {
+	.startup	= pcm179x_startup,
 	.set_fmt	= pcm179x_set_dai_fmt,
 	.hw_params	= pcm179x_hw_params,
 	.mute_stream	= pcm179x_mute,
@@ -190,7 +240,7 @@  static struct snd_soc_dai_driver pcm179x_dai = {
 		.rates = SNDRV_PCM_RATE_CONTINUOUS,
 		.rate_min = 10000,
 		.rate_max = 200000,
-		.formats = PCM1792A_FORMATS, },
+		.formats = PCM179X_FORMATS, },
 	.ops = &pcm179x_dai_ops,
 };
 
@@ -226,6 +276,8 @@  int pcm179x_common_init(struct device *dev, struct regmap *regmap)
 	if (!pcm179x)
 		return -ENOMEM;
 
+	pcm179x->codec_model =
+		(enum pcm179x_type)(uintptr_t)device_get_match_data(dev);
 	pcm179x->regmap = regmap;
 	dev_set_drvdata(dev, pcm179x);
 
diff --git a/sound/soc/codecs/pcm179x.h b/sound/soc/codecs/pcm179x.h
index 0039ca8ee742..33971fa93546 100644
--- a/sound/soc/codecs/pcm179x.h
+++ b/sound/soc/codecs/pcm179x.h
@@ -8,8 +8,16 @@ 
 #ifndef __PCM179X_H__
 #define __PCM179X_H__
 
-#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
-			  SNDRV_PCM_FMTBIT_S16_LE)
+enum pcm179x_type {
+	PCM1792A,
+	PCM1795,
+	PCM1796,
+};
+
+#define PCM179X_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+			 SNDRV_PCM_FMTBIT_S16_LE)
+
+#define PCM1795_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE)
 
 extern const struct regmap_config pcm179x_regmap_config;