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.c82
1 files changed, 82 insertions, 0 deletions
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 3356c91f55b0..52cc950c9fd1 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -33,6 +33,8 @@
#define PCM3168A_FMT_RIGHT_J_16 0x3
#define PCM3168A_FMT_DSP_A 0x4
#define PCM3168A_FMT_DSP_B 0x5
+#define PCM3168A_FMT_I2S_TDM 0x6
+#define PCM3168A_FMT_LEFT_J_TDM 0x7
#define PCM3168A_FMT_DSP_MASK 0x4
#define PCM3168A_NUM_SUPPLIES 6
@@ -401,9 +403,11 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
bool tx, master_mode;
u32 val, mask, shift, reg;
unsigned int rate, fmt, ratio, max_ratio;
+ unsigned int chan;
int i, min_frame_size;
rate = params_rate(params);
+ chan = params_channels(params);
ratio = pcm3168a->sysclk / rate;
@@ -456,6 +460,21 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
+ /* for TDM */
+ if (chan > 2) {
+ switch (fmt) {
+ case PCM3168A_FMT_I2S:
+ fmt = PCM3168A_FMT_I2S_TDM;
+ break;
+ case PCM3168A_FMT_LEFT_J:
+ fmt = PCM3168A_FMT_LEFT_J_TDM;
+ break;
+ default:
+ dev_err(component->dev, "TDM is supported under I2S/Left_J only\n");
+ return -EINVAL;
+ }
+ }
+
if (master_mode)
val = ((i + 1) << shift);
else
@@ -476,7 +495,69 @@ 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;
+
+ 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:
+ sample_min = 24;
+ if (tx)
+ channel_max = 8;
+ else
+ channel_max = 6;
+ break;
+ case PCM3168A_FMT_I2S:
+ sample_min = 24;
+ if (tx)
+ channel_max = 8;
+ else
+ channel_max = 6;
+ 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,
.set_sysclk = pcm3168a_set_dai_sysclk,
.hw_params = pcm3168a_hw_params,
@@ -484,6 +565,7 @@ static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = {
};
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