diff options
| author | Peter Ujfalusi <peter.ujfalusi@ti.com> | 2015-06-04 09:04:25 -0400 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2015-06-05 13:53:34 -0400 |
| commit | 3f747a810e19b3ab88c6b303490c66f59e78b80b (patch) | |
| tree | 7f3b0264b26e67dddf59459d688aabf3ec7df708 | |
| parent | 1b68c7dca2ca7426c758debdbf9dd5f7c308c1c8 (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.c | 59 | ||||
| -rw-r--r-- | sound/soc/codecs/tas2552.h | 3 |
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) |
| 197 | static 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 | |||
| 194 | static int tas2552_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) | 216 | static 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 | ||
| 301 | static 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 | |||
| 281 | static int tas2552_mute(struct snd_soc_dai *dai, int mute) | 330 | static 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 | ||
| 331 | static struct snd_soc_dai_ops tas2552_speaker_dai_ops = { | 380 | static 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) |
