aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Ujfalusi <peter.ujfalusi@ti.com>2015-06-04 09:04:25 -0400
committerMark Brown <broonie@kernel.org>2015-06-05 13:53:34 -0400
commit3f747a810e19b3ab88c6b303490c66f59e78b80b (patch)
tree7f3b0264b26e67dddf59459d688aabf3ec7df708
parent1b68c7dca2ca7426c758debdbf9dd5f7c308c1c8 (diff)
ASoC: tas2552: Add TDM support
TDM support is achieved using DSP transfer mode and setting a programmable offset which specifies where data begins with respect to the frame sync. Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com> Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r--sound/soc/codecs/tas2552.c59
-rw-r--r--sound/soc/codecs/tas2552.h3
2 files changed, 58 insertions, 4 deletions
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index 7615d1bc5f5d..432aa54fe707 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -78,6 +78,9 @@ struct tas2552_data {
78 unsigned char regs[TAS2552_VBAT_DATA]; 78 unsigned char regs[TAS2552_VBAT_DATA];
79 unsigned int pll_clkin; 79 unsigned int pll_clkin;
80 unsigned int pdm_clk; 80 unsigned int pdm_clk;
81
82 unsigned int dai_fmt;
83 unsigned int tdm_delay;
81}; 84};
82 85
83/* Input mux controls */ 86/* Input mux controls */
@@ -191,10 +194,29 @@ static int tas2552_hw_params(struct snd_pcm_substream *substream,
191#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \ 194#define TAS2552_DAI_FMT_MASK (TAS2552_BCLKDIR | \
192 TAS2552_WCLKDIR | \ 195 TAS2552_WCLKDIR | \
193 TAS2552_DATAFORMAT_MASK) 196 TAS2552_DATAFORMAT_MASK)
197static int tas2552_prepare(struct snd_pcm_substream *substream,
198 struct snd_soc_dai *dai)
199{
200 struct snd_soc_codec *codec = dai->codec;
201 struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
202 int delay = 0;
203
204 /* TDM slot selection only valid in DSP_A/_B mode */
205 if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_A)
206 delay += (tas2552->tdm_delay + 1);
207 else if (tas2552->dai_fmt == SND_SOC_DAIFMT_DSP_B)
208 delay += tas2552->tdm_delay;
209
210 /* Configure data delay */
211 snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
212
213 return 0;
214}
215
194static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) 216static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
195{ 217{
196 struct snd_soc_codec *codec = dai->codec; 218 struct snd_soc_codec *codec = dai->codec;
197 u8 delay = 0; 219 struct tas2552_data *tas2552 = dev_get_drvdata(codec->dev);
198 u8 serial_format; 220 u8 serial_format;
199 221
200 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 222 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
@@ -220,7 +242,6 @@ static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
220 case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF): 242 case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
221 break; 243 break;
222 case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF): 244 case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
223 delay = 1;
224 case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF): 245 case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
225 serial_format |= TAS2552_DATAFORMAT_DSP; 246 serial_format |= TAS2552_DATAFORMAT_DSP;
226 break; 247 break;
@@ -234,11 +255,10 @@ static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
234 dev_vdbg(codec->dev, "DAI Format is not found\n"); 255 dev_vdbg(codec->dev, "DAI Format is not found\n");
235 return -EINVAL; 256 return -EINVAL;
236 } 257 }
258 tas2552->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
237 259
238 snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK, 260 snd_soc_update_bits(codec, TAS2552_SER_CTRL_1, TAS2552_DAI_FMT_MASK,
239 serial_format); 261 serial_format);
240 snd_soc_write(codec, TAS2552_SER_CTRL_2, delay);
241
242 return 0; 262 return 0;
243} 263}
244 264
@@ -278,6 +298,35 @@ static int tas2552_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
278 return 0; 298 return 0;
279} 299}
280 300
301static int tas2552_set_dai_tdm_slot(struct snd_soc_dai *dai,
302 unsigned int tx_mask, unsigned int rx_mask,
303 int slots, int slot_width)
304{
305 struct snd_soc_codec *codec = dai->codec;
306 struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
307 unsigned int lsb;
308
309 if (unlikely(!tx_mask)) {
310 dev_err(codec->dev, "tx masks need to be non 0\n");
311 return -EINVAL;
312 }
313
314 /* TDM based on DSP mode requires slots to be adjacent */
315 lsb = __ffs(tx_mask);
316 if ((lsb + 1) != __fls(tx_mask)) {
317 dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
318 return -EINVAL;
319 }
320
321 tas2552->tdm_delay = lsb * slot_width;
322
323 /* DOUT in high-impedance on inactive bit clocks */
324 snd_soc_update_bits(codec, TAS2552_DOUT,
325 TAS2552_SDOUT_TRISTATE, TAS2552_SDOUT_TRISTATE);
326
327 return 0;
328}
329
281static int tas2552_mute(struct snd_soc_dai *dai, int mute) 330static int tas2552_mute(struct snd_soc_dai *dai, int mute)
282{ 331{
283 u8 cfg1_reg = 0; 332 u8 cfg1_reg = 0;
@@ -330,8 +379,10 @@ static const struct dev_pm_ops tas2552_pm = {
330 379
331static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { 380static struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
332 .hw_params = tas2552_hw_params, 381 .hw_params = tas2552_hw_params,
382 .prepare = tas2552_prepare,
333 .set_sysclk = tas2552_set_dai_sysclk, 383 .set_sysclk = tas2552_set_dai_sysclk,
334 .set_fmt = tas2552_set_dai_fmt, 384 .set_fmt = tas2552_set_dai_fmt,
385 .set_tdm_slot = tas2552_set_dai_tdm_slot,
335 .digital_mute = tas2552_mute, 386 .digital_mute = tas2552_mute,
336}; 387};
337 388
diff --git a/sound/soc/codecs/tas2552.h b/sound/soc/codecs/tas2552.h
index 0a12b511e951..5bdc7eaaddea 100644
--- a/sound/soc/codecs/tas2552.h
+++ b/sound/soc/codecs/tas2552.h
@@ -62,6 +62,9 @@
62#define TAS2552_LIM_EN (1 << 2) 62#define TAS2552_LIM_EN (1 << 2)
63#define TAS2552_IVSENSE_EN (1 << 1) 63#define TAS2552_IVSENSE_EN (1 << 1)
64 64
65/* DOUT Register Masks */
66#define TAS2552_SDOUT_TRISTATE (1 << 2)
67
65/* Serial Interface Control Register Masks */ 68/* Serial Interface Control Register Masks */
66#define TAS2552_DATAFORMAT_I2S (0x0 << 2) 69#define TAS2552_DATAFORMAT_I2S (0x0 << 2)
67#define TAS2552_DATAFORMAT_DSP (0x1 << 2) 70#define TAS2552_DATAFORMAT_DSP (0x1 << 2)