@@ -32,6 +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,pcm1795", .data = (void *)PCM1795, },
+ { .compatible = "ti,pcm1796", .data = (void *)PCM1796, },
{ }
};
MODULE_DEVICE_TABLE(of, pcm179x_of_match);
@@ -31,6 +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,pcm1795", .data = (void *)PCM1795, },
+ { .compatible = "ti,pcm1796", .data = (void *)PCM1796, },
{ }
};
MODULE_DEVICE_TABLE(of, pcm179x_of_match);
@@ -19,6 +19,7 @@
#include <sound/soc.h>
#include <sound/tlv.h>
#include <linux/of.h>
+#include <linux/property.h>
#include "pcm179x.h"
@@ -63,8 +64,83 @@ struct pcm179x_private {
struct regmap *regmap;
unsigned int format;
unsigned int rate;
+ enum pcm179x_type codec_model;
};
+struct pcm179x_fmt {
+ unsigned int fmt; /* SND_SOC_DAIFMT_* */
+ unsigned int width; /* sample width in bits */
+ unsigned int code; /* FMT[2:0] */
+};
+
+/*
+ * Register 18 FMT[2:0] is not encoded the same way by every model: code 001
+ * and code 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 code 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 pcm1792a_fmt[] = {
+ { SND_SOC_DAIFMT_RIGHT_J, 16, 0 },
+ { SND_SOC_DAIFMT_RIGHT_J, 24, 2 },
+ { SND_SOC_DAIFMT_RIGHT_J, 32, 2 },
+ { SND_SOC_DAIFMT_LEFT_J, 24, 3 },
+ { SND_SOC_DAIFMT_LEFT_J, 32, 3 },
+ { SND_SOC_DAIFMT_I2S, 16, 4 },
+ { SND_SOC_DAIFMT_I2S, 24, 5 },
+ { SND_SOC_DAIFMT_I2S, 32, 5 },
+};
+
+static const struct pcm179x_fmt 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 *fmt;
+ unsigned int num_fmt;
+};
+
+static const struct pcm179x_model pcm179x_models[] = {
+ [PCM1792A] = { PCM1792A_FORMATS, pcm1792a_fmt, ARRAY_SIZE(pcm1792a_fmt) },
+ [PCM1795] = { PCM1795_FORMATS, pcm1795_fmt, ARRAY_SIZE(pcm1795_fmt) },
+ [PCM1796] = { PCM1792A_FORMATS, pcm1792a_fmt, ARRAY_SIZE(pcm1792a_fmt) },
+};
+
+static int pcm179x_fmt_value(struct pcm179x_private *priv, unsigned int fmt,
+ unsigned int width)
+{
+ const struct pcm179x_model *model = &pcm179x_models[priv->codec_model];
+ const struct pcm179x_fmt *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].code;
+
+ 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)
{
@@ -96,40 +172,15 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct pcm179x_private *priv = snd_soc_component_get_drvdata(component);
- int val = 0, ret;
+ int val, ret;
priv->rate = params_rate(params);
- switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_RIGHT_J:
- switch (params_width(params)) {
- case 24:
- case 32:
- val = 2;
- break;
- case 16:
- val = 0;
- break;
- default:
- return -EINVAL;
- }
- break;
- case SND_SOC_DAIFMT_I2S:
- switch (params_width(params)) {
- case 24:
- case 32:
- val = 5;
- break;
- case 16:
- val = 4;
- break;
- default:
- return -EINVAL;
- }
- break;
- default:
+ val = pcm179x_fmt_value(priv, priv->format & SND_SOC_DAIFMT_FORMAT_MASK,
+ params_width(params));
+ if (val < 0) {
dev_err(component->dev, "Invalid DAI format\n");
- return -EINVAL;
+ return val;
}
val = val << PCM179X_FMT_SHIFT | PCM179X_ATLD_ENABLE;
@@ -143,6 +194,7 @@ static int pcm179x_hw_params(struct snd_pcm_substream *substream,
}
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,
@@ -182,7 +234,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,
};
@@ -218,6 +270,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);
@@ -8,9 +8,20 @@
#ifndef __PCM179X_H__
#define __PCM179X_H__
+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 PCM1792A_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;
int pcm179x_common_init(struct device *dev, struct regmap *regmap);
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. Describe the encoding with a per-codec table mapping the DAI format and the sample width to the FMT[2:0] value, and select it with the codec model resolved in pcm179x_common_init(). The PCM1792A and the PCM1796 have no 32-bit format, so a 32-bit container keeps the 24-bit code and is clocked out as 24-bit data, as this driver has always done; only the PCM1795 has real 32-bit formats. The formats offered to userspace follow the same split: the DAI advertises the union and pcm179x_startup() narrows it to the model's set, so 16-bit is never offered on a PCM1795 and 32-bit never on a PCM1792A. Left-justified was accepted by set_fmt() but never programmed; it is now mapped to FMT[2:0] = 011, the only left-justified format both models implement. 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> --- sound/soc/codecs/pcm179x-i2c.c | 2 + sound/soc/codecs/pcm179x-spi.c | 2 + sound/soc/codecs/pcm179x.c | 116 ++++++++++++++++++++++++++++++----------- sound/soc/codecs/pcm179x.h | 11 ++++ 4 files changed, 100 insertions(+), 31 deletions(-) To unsubscribe from this group and stop receiving emails from it, send an email to linux-amarula+unsubscribe@amarulasolutions.com.