summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/pcm3168a.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/pcm3168a.c')
-rw-r--r--sound/soc/codecs/pcm3168a.c261
1 files changed, 127 insertions, 134 deletions
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index f1104d7d6426..9711fab296eb 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -9,7 +9,9 @@
#include <linux/clk.h>
#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
+#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -21,8 +23,7 @@
#define PCM3168A_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE | \
- SNDRV_PCM_FMTBIT_S24_LE | \
- SNDRV_PCM_FMTBIT_S32_LE)
+ SNDRV_PCM_FMTBIT_S24_LE)
#define PCM3168A_FMT_I2S 0x0
#define PCM3168A_FMT_LEFT_J 0x1
@@ -44,18 +45,27 @@ static const char *const pcm3168a_supply_names[PCM3168A_NUM_SUPPLIES] = {
"VCCDA2"
};
+#define PCM3168A_DAI_DAC 0
+#define PCM3168A_DAI_ADC 1
+
+/* ADC/DAC side parameters */
+struct pcm3168a_io_params {
+ bool master_mode;
+ unsigned int fmt;
+ int tdm_slots;
+ u32 tdm_mask;
+ int slot_width;
+};
+
struct pcm3168a_priv {
struct regulator_bulk_data supplies[PCM3168A_NUM_SUPPLIES];
struct regmap *regmap;
struct clk *scki;
- bool adc_master_mode;
- bool dac_master_mode;
+ struct gpio_desc *gpio_rst;
unsigned long sysclk;
- unsigned int adc_fmt;
- unsigned int dac_fmt;
- int tdm_slots;
- u32 tdm_mask[2];
- int slot_width;
+
+ struct pcm3168a_io_params io_params[2];
+ struct snd_soc_dai_driver dai_drv[2];
};
static const char *const pcm3168a_roll_off[] = { "Sharp", "Slow" };
@@ -263,7 +273,7 @@ static unsigned int pcm3168a_scki_ratios[] = {
#define PCM3168A_NUM_SCKI_RATIOS_DAC ARRAY_SIZE(pcm3168a_scki_ratios)
#define PCM3168A_NUM_SCKI_RATIOS_ADC (ARRAY_SIZE(pcm3168a_scki_ratios) - 2)
-#define PCM1368A_MAX_SYSCLK 36864000
+#define PCM3168A_MAX_SYSCLK 36864000
static int pcm3168a_reset(struct pcm3168a_priv *pcm3168a)
{
@@ -296,7 +306,7 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(dai->component);
int ret;
- if (freq > PCM1368A_MAX_SYSCLK)
+ if (freq > PCM3168A_MAX_SYSCLK)
return -EINVAL;
ret = clk_set_rate(pcm3168a->scki, freq);
@@ -308,8 +318,35 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
-static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
- unsigned int format, bool dac)
+static void pcm3168a_update_fixup_pcm_stream(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ u64 formats = SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE;
+ unsigned int channel_max = dai->id == PCM3168A_DAI_DAC ? 8 : 6;
+
+ if (pcm3168a->io_params[dai->id].fmt == PCM3168A_FMT_RIGHT_J) {
+ /* S16_LE is only supported in RIGHT_J mode */
+ formats |= SNDRV_PCM_FMTBIT_S16_LE;
+
+ /*
+ * If multi DIN/DOUT is not selected, RIGHT_J can only support
+ * two channels (no TDM support)
+ */
+ if (pcm3168a->io_params[dai->id].tdm_slots != 2)
+ channel_max = 2;
+ }
+
+ if (dai->id == PCM3168A_DAI_DAC) {
+ dai->driver->playback.channels_max = channel_max;
+ dai->driver->playback.formats = formats;
+ } else {
+ dai->driver->capture.channels_max = channel_max;
+ dai->driver->capture.formats = formats;
+ }
+}
+
+static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai, unsigned int format)
{
struct snd_soc_component *component = dai->component;
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
@@ -356,35 +393,24 @@ static int pcm3168a_set_dai_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- if (dac) {
+ if (dai->id == PCM3168A_DAI_DAC) {
reg = PCM3168A_DAC_PWR_MST_FMT;
mask = PCM3168A_DAC_FMT_MASK;
shift = PCM3168A_DAC_FMT_SHIFT;
- pcm3168a->dac_master_mode = master_mode;
- pcm3168a->dac_fmt = fmt;
} else {
reg = PCM3168A_ADC_MST_FMT;
mask = PCM3168A_ADC_FMTAD_MASK;
shift = PCM3168A_ADC_FMTAD_SHIFT;
- pcm3168a->adc_master_mode = master_mode;
- pcm3168a->adc_fmt = fmt;
}
- regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
+ pcm3168a->io_params[dai->id].master_mode = master_mode;
+ pcm3168a->io_params[dai->id].fmt = fmt;
- return 0;
-}
+ regmap_update_bits(pcm3168a->regmap, reg, mask, fmt << shift);
-static int pcm3168a_set_dai_fmt_dac(struct snd_soc_dai *dai,
- unsigned int format)
-{
- return pcm3168a_set_dai_fmt(dai, format, true);
-}
+ pcm3168a_update_fixup_pcm_stream(dai);
-static int pcm3168a_set_dai_fmt_adc(struct snd_soc_dai *dai,
- unsigned int format)
-{
- return pcm3168a_set_dai_fmt(dai, format, false);
+ return 0;
}
static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -393,6 +419,7 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
{
struct snd_soc_component *component = dai->component;
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
dev_err(component->dev,
@@ -408,22 +435,15 @@ static int pcm3168a_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return -EINVAL;
}
- if (pcm3168a->tdm_slots && pcm3168a->tdm_slots != slots) {
- dev_err(component->dev, "Not matching slots %d vs %d\n",
- pcm3168a->tdm_slots, slots);
- return -EINVAL;
- }
-
- if (pcm3168a->slot_width && pcm3168a->slot_width != slot_width) {
- dev_err(component->dev, "Not matching slot_width %d vs %d\n",
- pcm3168a->slot_width, slot_width);
- return -EINVAL;
- }
+ io_params->tdm_slots = slots;
+ io_params->slot_width = slot_width;
+ /* Ignore the not relevant mask for the DAI/direction */
+ if (dai->id == PCM3168A_DAI_DAC)
+ io_params->tdm_mask = tx_mask;
+ else
+ io_params->tdm_mask = rx_mask;
- pcm3168a->tdm_slots = slots;
- pcm3168a->slot_width = slot_width;
- pcm3168a->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask;
- pcm3168a->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = rx_mask;
+ pcm3168a_update_fixup_pcm_stream(dai);
return 0;
}
@@ -434,7 +454,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
- bool tx, master_mode;
+ struct pcm3168a_io_params *io_params = &pcm3168a->io_params[dai->id];
+ bool master_mode;
u32 val, mask, shift, reg;
unsigned int rate, fmt, ratio, max_ratio;
unsigned int tdm_slots;
@@ -444,23 +465,21 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
ratio = pcm3168a->sysclk / rate;
- tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- if (tx) {
+ if (dai->id == PCM3168A_DAI_DAC) {
max_ratio = PCM3168A_NUM_SCKI_RATIOS_DAC;
reg = PCM3168A_DAC_PWR_MST_FMT;
mask = PCM3168A_DAC_MSDA_MASK;
shift = PCM3168A_DAC_MSDA_SHIFT;
- master_mode = pcm3168a->dac_master_mode;
- fmt = pcm3168a->dac_fmt;
} else {
max_ratio = PCM3168A_NUM_SCKI_RATIOS_ADC;
reg = PCM3168A_ADC_MST_FMT;
mask = PCM3168A_ADC_MSAD_MASK;
shift = PCM3168A_ADC_MSAD_SHIFT;
- master_mode = pcm3168a->adc_master_mode;
- fmt = pcm3168a->adc_fmt;
}
+ master_mode = io_params->master_mode;
+ fmt = io_params->fmt;
+
for (i = 0; i < max_ratio; i++) {
if (pcm3168a_scki_ratios[i] == ratio)
break;
@@ -471,8 +490,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (pcm3168a->slot_width)
- slot_width = pcm3168a->slot_width;
+ if (io_params->slot_width)
+ slot_width = io_params->slot_width;
else
slot_width = params_width(params);
@@ -497,8 +516,8 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- if (pcm3168a->tdm_slots)
- tdm_slots = pcm3168a->tdm_slots;
+ if (io_params->tdm_slots)
+ tdm_slots = io_params->tdm_slots;
else
tdm_slots = params_channels(params);
@@ -534,7 +553,7 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(pcm3168a->regmap, reg, mask, val);
- if (tx) {
+ if (dai->id == PCM3168A_DAI_DAC) {
mask = PCM3168A_DAC_FMT_MASK;
shift = PCM3168A_DAC_FMT_SHIFT;
} else {
@@ -547,84 +566,18 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static int pcm3168a_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component);
- bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
- unsigned int fmt;
- unsigned int sample_min;
- unsigned int channel_max;
- unsigned int channel_maxs[] = {
- 6, /* rx */
- 8 /* tx */
- };
-
- if (tx)
- fmt = pcm3168a->dac_fmt;
- else
- fmt = pcm3168a->adc_fmt;
-
- /*
- * Available Data Bits
- *
- * RIGHT_J : 24 / 16
- * LEFT_J : 24
- * I2S : 24
- *
- * TDM available
- *
- * I2S
- * LEFT_J
- */
- switch (fmt) {
- case PCM3168A_FMT_RIGHT_J:
- sample_min = 16;
- channel_max = 2;
- break;
- case PCM3168A_FMT_LEFT_J:
- case PCM3168A_FMT_I2S:
- case PCM3168A_FMT_DSP_A:
- case PCM3168A_FMT_DSP_B:
- sample_min = 24;
- channel_max = channel_maxs[tx];
- break;
- default:
- sample_min = 24;
- channel_max = 2;
- }
-
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- sample_min, 32);
-
- snd_pcm_hw_constraint_minmax(substream->runtime,
- SNDRV_PCM_HW_PARAM_CHANNELS,
- 2, channel_max);
-
- return 0;
-}
-static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
- .startup = pcm3168a_startup,
- .set_fmt = pcm3168a_set_dai_fmt_dac,
+static const struct snd_soc_dai_ops pcm3168a_dai_ops = {
+ .set_fmt = pcm3168a_set_dai_fmt,
.set_sysclk = pcm3168a_set_dai_sysclk,
.hw_params = pcm3168a_hw_params,
.digital_mute = pcm3168a_digital_mute,
.set_tdm_slot = pcm3168a_set_tdm_slot,
};
-static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = {
- .startup = pcm3168a_startup,
- .set_fmt = pcm3168a_set_dai_fmt_adc,
- .set_sysclk = pcm3168a_set_dai_sysclk,
- .hw_params = pcm3168a_hw_params,
- .set_tdm_slot = pcm3168a_set_tdm_slot,
-};
-
static struct snd_soc_dai_driver pcm3168a_dais[] = {
{
.name = "pcm3168a-dac",
+ .id = PCM3168A_DAI_DAC,
.playback = {
.stream_name = "Playback",
.channels_min = 1,
@@ -632,10 +585,11 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = PCM3168A_FORMATS
},
- .ops = &pcm3168a_dac_dai_ops
+ .ops = &pcm3168a_dai_ops
},
{
.name = "pcm3168a-adc",
+ .id = PCM3168A_DAI_ADC,
.capture = {
.stream_name = "Capture",
.channels_min = 1,
@@ -643,7 +597,7 @@ static struct snd_soc_dai_driver pcm3168a_dais[] = {
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = PCM3168A_FORMATS
},
- .ops = &pcm3168a_adc_dai_ops
+ .ops = &pcm3168a_dai_ops
},
};
@@ -692,6 +646,7 @@ static bool pcm3168a_readable_register(struct device *dev, unsigned int reg)
static bool pcm3168a_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
+ case PCM3168A_RST_SMODE:
case PCM3168A_DAC_ZERO:
case PCM3168A_ADC_OV:
return true;
@@ -751,6 +706,25 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, pcm3168a);
+ /*
+ * Request the reset (connected to RST pin) gpio line as non exclusive
+ * as the same reset line might be connected to multiple pcm3168a codec
+ *
+ * The RST is low active, we want the GPIO line to be high initially, so
+ * request the initial level to LOW which in practice means DEASSERTED:
+ * The deasserted level of GPIO_ACTIVE_LOW is HIGH.
+ */
+ pcm3168a->gpio_rst = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW |
+ GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+ if (IS_ERR(pcm3168a->gpio_rst)) {
+ ret = PTR_ERR(pcm3168a->gpio_rst);
+ if (ret != -EPROBE_DEFER )
+ dev_err(dev, "failed to acquire RST gpio: %d\n", ret);
+
+ return ret;
+ }
+
pcm3168a->scki = devm_clk_get(dev, "scki");
if (IS_ERR(pcm3168a->scki)) {
ret = PTR_ERR(pcm3168a->scki);
@@ -792,18 +766,28 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
goto err_regulator;
}
- ret = pcm3168a_reset(pcm3168a);
- if (ret) {
- dev_err(dev, "Failed to reset device: %d\n", ret);
- goto err_regulator;
+ if (pcm3168a->gpio_rst) {
+ /*
+ * The device is taken out from reset via GPIO line, wait for
+ * 3846 SCKI clock cycles for the internal reset de-assertion
+ */
+ msleep(DIV_ROUND_UP(3846 * 1000, pcm3168a->sysclk));
+ } else {
+ ret = pcm3168a_reset(pcm3168a);
+ if (ret) {
+ dev_err(dev, "Failed to reset device: %d\n", ret);
+ goto err_regulator;
+ }
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
- ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, pcm3168a_dais,
- ARRAY_SIZE(pcm3168a_dais));
+ memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
+ ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
+ pcm3168a->dai_drv,
+ ARRAY_SIZE(pcm3168a->dai_drv));
if (ret) {
dev_err(dev, "failed to register component: %d\n", ret);
goto err_regulator;
@@ -832,6 +816,15 @@ static void pcm3168a_disable(struct device *dev)
void pcm3168a_remove(struct device *dev)
{
+ struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
+
+ /*
+ * The RST is low active, we want the GPIO line to be low when the
+ * driver is removed, so set level to 1 which in practice means
+ * ASSERTED:
+ * The asserted level of GPIO_ACTIVE_LOW is LOW.
+ */
+ gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
pm_runtime_disable(dev);
#ifndef CONFIG_PM
pcm3168a_disable(dev);
OpenPOWER on IntegriCloud