Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: ti: davinci-i2s: Add TDM support

TDM is not supported by the McBSP driver. The McBSP datasheet does not
name explicitly TDM as a supported format but it is possible to configure
the McBSP to do TDM if all slots are used by McBSP.

Add TDM support. It uses single-phase frame. Slot width is used to
compute the McBSP's word length.

Implement the set_tdm_slot() hook of snd_soc_dai_ops struct. It only
supports TDM if all slots are used by McBSP.

The snd_soc_dai_driver's channels_max is updated from 2 to 128.

This was tested with BP_FC format on a platform designed off of
DAVINCI/OMAP_L138. A check is done in davinci_i2s_set_dai_fmt() to
prevent TDM to be used with BC_FC and BC_FP formats.

Signed-off-by: Bastien Curutchet <bastien.curutchet@bootlin.com>
Acked-by: Peter Ujfalusi <peter.ujfalusi@gmail.com>
Link: https://msgid.link/r/20240402071213.11671-8-bastien.curutchet@bootlin.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Bastien Curutchet and committed by
Mark Brown
37e313cd 7dd7a6d2

+87 -5
+87 -5
sound/soc/ti/davinci-i2s.c
··· 160 160 unsigned int fmt; 161 161 int clk_div; 162 162 bool i2s_accurate_sck; 163 + 164 + int tdm_slots; 165 + int slot_width; 163 166 }; 164 167 165 168 static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, ··· 216 213 toggle_clock(dev, playback); 217 214 } 218 215 216 + static int davinci_i2s_tdm_word_length(int tdm_slot_width) 217 + { 218 + switch (tdm_slot_width) { 219 + case 8: 220 + return DAVINCI_MCBSP_WORD_8; 221 + case 12: 222 + return DAVINCI_MCBSP_WORD_12; 223 + case 16: 224 + return DAVINCI_MCBSP_WORD_16; 225 + case 20: 226 + return DAVINCI_MCBSP_WORD_20; 227 + case 24: 228 + return DAVINCI_MCBSP_WORD_24; 229 + case 32: 230 + return DAVINCI_MCBSP_WORD_32; 231 + default: 232 + return -EINVAL; 233 + } 234 + } 235 + 236 + static int davinci_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai, 237 + unsigned int tx_mask, 238 + unsigned int rx_mask, 239 + int slots, int slot_width) 240 + { 241 + struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai); 242 + 243 + dev_dbg(dev->dev, "slots %d, slot_width %d\n", slots, slot_width); 244 + 245 + if (slots > 128 || !slots) { 246 + dev_err(dev->dev, "Invalid number of slots\n"); 247 + return -EINVAL; 248 + } 249 + 250 + if (rx_mask != (1 << slots) - 1) { 251 + dev_err(dev->dev, "Invalid RX mask (0x%08x) : all slots must be used by McBSP\n", 252 + rx_mask); 253 + return -EINVAL; 254 + } 255 + 256 + if (tx_mask != (1 << slots) - 1) { 257 + dev_err(dev->dev, "Invalid TX mask (0x%08x) : all slots must be used by McBSP\n", 258 + tx_mask); 259 + return -EINVAL; 260 + } 261 + 262 + if (davinci_i2s_tdm_word_length(slot_width) < 0) { 263 + dev_err(dev->dev, "%s: Unsupported slot_width %d\n", __func__, slot_width); 264 + return -EINVAL; 265 + } 266 + 267 + dev->tdm_slots = slots; 268 + dev->slot_width = slot_width; 269 + 270 + return 0; 271 + } 272 + 219 273 #define DEFAULT_BITPERSAMPLE 16 220 274 221 275 static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, ··· 298 238 DAVINCI_MCBSP_PCR_CLKRM; 299 239 break; 300 240 case SND_SOC_DAIFMT_BC_FP: 241 + if (dev->tdm_slots || dev->slot_width) { 242 + dev_err(dev->dev, "TDM is not supported for BC_FP format\n"); 243 + return -EINVAL; 244 + } 245 + 301 246 /* 302 247 * McBSP CLKR pin is the input for the Sample Rate Generator. 303 248 * McBSP FSR and FSX are driven by the Sample Rate Generator. ··· 311 246 pcr |= DAVINCI_MCBSP_PCR_SCLKME; 312 247 break; 313 248 case SND_SOC_DAIFMT_BC_FC: 249 + if (dev->tdm_slots || dev->slot_width) { 250 + dev_err(dev->dev, "TDM is not supported for BC_FC format\n"); 251 + return -EINVAL; 252 + } 253 + 314 254 /* codec is master */ 315 255 pcr = 0; 316 256 break; ··· 453 383 454 384 master = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK; 455 385 fmt = params_format(params); 456 - mcbsp_word_length = asp_word_length[fmt]; 386 + if (dev->slot_width) 387 + mcbsp_word_length = davinci_i2s_tdm_word_length(dev->slot_width); 388 + else 389 + mcbsp_word_length = asp_word_length[fmt]; 390 + 391 + if (mcbsp_word_length < 0) 392 + return mcbsp_word_length; 457 393 458 394 switch (master) { 459 395 case SND_SOC_DAIFMT_BP_FP: ··· 559 483 switch (master) { 560 484 case SND_SOC_DAIFMT_BP_FP: 561 485 case SND_SOC_DAIFMT_BP_FC: 562 - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0); 563 - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0); 486 + if (dev->tdm_slots > 0) { 487 + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(dev->tdm_slots - 1); 488 + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(dev->tdm_slots - 1); 489 + } else { 490 + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0); 491 + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0); 492 + } 564 493 break; 565 494 case SND_SOC_DAIFMT_BC_FC: 566 495 case SND_SOC_DAIFMT_BC_FP: ··· 690 609 .hw_params = davinci_i2s_hw_params, 691 610 .set_fmt = davinci_i2s_set_dai_fmt, 692 611 .set_clkdiv = davinci_i2s_dai_set_clkdiv, 612 + .set_tdm_slot = davinci_i2s_set_tdm_slot, 693 613 694 614 }; 695 615 696 616 static struct snd_soc_dai_driver davinci_i2s_dai = { 697 617 .playback = { 698 618 .channels_min = 2, 699 - .channels_max = 2, 619 + .channels_max = 128, 700 620 .rates = DAVINCI_I2S_RATES, 701 621 .formats = DAVINCI_I2S_FORMATS, 702 622 }, 703 623 .capture = { 704 624 .channels_min = 2, 705 - .channels_max = 2, 625 + .channels_max = 128, 706 626 .rates = DAVINCI_I2S_RATES, 707 627 .formats = DAVINCI_I2S_FORMATS, 708 628 },