The patch
ASoC: davinci-mcasp: Add set_tdm_slots() support
has been applied to the asoc tree at
git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
From dd55ff8346a972cca1ad056c8258ee96d090633e Mon Sep 17 00:00:00 2001
From: Jyri Sarha jsarha@ti.com Date: Wed, 9 Sep 2015 21:27:44 +0300 Subject: [PATCH] ASoC: davinci-mcasp: Add set_tdm_slots() support
Implements set_tdm_slot() callback for mcasp. Channel constraints are updated according to the configured tdm mask and slots each time set_tdm_slot() is called. The special case when slot width is set to zero is allowed and it means that slot width is the same as the sample width.
Signed-off-by: Jyri Sarha jsarha@ti.com Signed-off-by: Mark Brown broonie@kernel.org --- sound/soc/davinci/davinci-mcasp.c | 255 ++++++++++++++++++++++++++------------ 1 file changed, 174 insertions(+), 81 deletions(-)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index add6bb9..fa47a39 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -80,6 +80,8 @@ struct davinci_mcasp {
/* McASP specific data */ int tdm_slots; + u32 tdm_mask[2]; + int slot_width; u8 op_mode; u8 num_serializer; u8 *serial_dir; @@ -596,6 +598,84 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id, return 0; }
+/* All serializers must have equal number of channels */ +static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream, + int serializers) +{ + struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream]; + unsigned int *list = (unsigned int *) cl->list; + int slots = mcasp->tdm_slots; + int i, count = 0; + + if (mcasp->tdm_mask[stream]) + slots = hweight32(mcasp->tdm_mask[stream]); + + for (i = 2; i <= slots; i++) + list[count++] = i; + + for (i = 2; i <= serializers; i++) + list[count++] = i*slots; + + cl->count = count; + + return 0; +} + +static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp) +{ + int rx_serializers = 0, tx_serializers = 0, ret, i; + + for (i = 0; i < mcasp->num_serializer; i++) + if (mcasp->serial_dir[i] == TX_MODE) + tx_serializers++; + else if (mcasp->serial_dir[i] == RX_MODE) + rx_serializers++; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK, + tx_serializers); + if (ret) + return ret; + + ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE, + rx_serializers); + + return ret; +} + + +static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai); + + dev_dbg(mcasp->dev, + "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n", + __func__, tx_mask, rx_mask, slots, slot_width); + + if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) { + dev_err(mcasp->dev, + "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n", + tx_mask, rx_mask, slots); + return -EINVAL; + } + + if (slot_width && + (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) { + dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n", + __func__, slot_width); + return -EINVAL; + } + + mcasp->tdm_slots = slots; + mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask; + mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask; + mcasp->slot_width = slot_width; + + return davinci_mcasp_set_ch_constraints(mcasp); +} + static int davinci_config_channel_size(struct davinci_mcasp *mcasp, int word_length) { @@ -632,6 +712,9 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, */ rx_rotate = (slot_length - word_length) / 4; word_length = slot_length; + } else if (mcasp->slot_width) { + rx_rotate = (mcasp->slot_width - word_length) / 4; + word_length = mcasp->slot_width; }
/* mapping of the XSSZ bit-field as described in the datasheet */ @@ -777,33 +860,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
/* * If more than one serializer is needed, then use them with - * their specified tdm_slots count. Otherwise, one serializer - * can cope with the transaction using as many slots as channels - * in the stream, requires channels symmetry + * all the specified tdm_slots. Otherwise, one serializer can + * cope with the transaction using just as many slots as there + * are channels in the stream. */ - active_serializers = (channels + total_slots - 1) / total_slots; - if (active_serializers == 1) - active_slots = channels; - else - active_slots = total_slots; - - for (i = 0; i < active_slots; i++) - mask |= (1 << i); + if (mcasp->tdm_mask[stream]) { + active_slots = hweight32(mcasp->tdm_mask[stream]); + active_serializers = (channels + active_slots - 1) / + active_slots; + if (active_serializers == 1) { + active_slots = channels; + for (i = 0; i < total_slots; i++) { + if ((1 << i) & mcasp->tdm_mask[stream]) { + mask |= (1 << i); + if (--active_slots <= 0) + break; + } + } + } + } else { + active_serializers = (channels + total_slots - 1) / total_slots; + if (active_serializers == 1) + active_slots = channels; + else + active_slots = total_slots;
+ for (i = 0; i < active_slots; i++) + mask |= (1 << i); + } mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
if (!mcasp->dat_port) busel = TXSEL;
- mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, - FSXMOD(total_slots), FSXMOD(0x1FF)); - - mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); - mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); - mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, - FSRMOD(total_slots), FSRMOD(0x1FF)); + if (stream == SNDRV_PCM_STREAM_PLAYBACK) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, + FSXMOD(total_slots), FSXMOD(0x1FF)); + } else if (stream == SNDRV_PCM_STREAM_CAPTURE) { + mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask); + mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD); + mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, + FSRMOD(total_slots), FSRMOD(0x1FF)); + }
return 0; } @@ -923,6 +1023,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, int sbits = params_width(params); int ppm, div;
+ if (mcasp->slot_width) + sbits = mcasp->slot_width; + div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots, &ppm); if (ppm) @@ -1028,6 +1131,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_interval range; int i;
+ if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + snd_interval_any(&range); range.empty = 1;
@@ -1070,10 +1176,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) { if (snd_mask_test(fmt, i)) { - uint bclk_freq = snd_pcm_format_width(i)*slots*rate; + uint sbits = snd_pcm_format_width(i); int ppm;
- davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm); + if (rd->mcasp->slot_width) + sbits = rd->mcasp->slot_width; + + davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate, + &ppm); if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) { snd_mask_set(&nfmt, i); count++; @@ -1095,6 +1205,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, &mcasp->ruledata[substream->stream]; u32 max_channels = 0; int i, dir; + int tdm_slots = mcasp->tdm_slots; + + if (mcasp->tdm_mask[substream->stream]) + tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
mcasp->substreams[substream->stream] = substream;
@@ -1115,7 +1229,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, max_channels++; } ruledata->serializers = max_channels; - max_channels *= mcasp->tdm_slots; + max_channels *= tdm_slots; /* * If the already active stream has less channels than the calculated * limnit based on the seirializers * tdm_slots, we need to use that as @@ -1125,15 +1239,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream, */ if (mcasp->channels && mcasp->channels < max_channels) max_channels = mcasp->channels; + /* + * But we can always allow channels upto the amount of + * the available tdm_slots. + */ + if (max_channels < tdm_slots) + max_channels = tdm_slots;
snd_pcm_hw_constraint_minmax(substream->runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2, max_channels);
- if (mcasp->chconstr[substream->stream].count) - snd_pcm_hw_constraint_list(substream->runtime, - 0, SNDRV_PCM_HW_PARAM_CHANNELS, - &mcasp->chconstr[substream->stream]); + snd_pcm_hw_constraint_list(substream->runtime, + 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &mcasp->chconstr[substream->stream]); + + if (mcasp->slot_width) + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + 8, mcasp->slot_width);
/* * If we rely on implicit BCLK divider setting we should @@ -1185,6 +1309,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { .set_fmt = davinci_mcasp_set_dai_fmt, .set_clkdiv = davinci_mcasp_set_clkdiv, .set_sysclk = davinci_mcasp_set_sysclk, + .set_tdm_slot = davinci_mcasp_set_tdm_slot, };
static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai) @@ -1514,59 +1639,6 @@ nodata: return pdata; }
-/* All serializers must have equal number of channels */ -static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, - struct snd_pcm_hw_constraint_list *cl, - int serializers) -{ - unsigned int *list; - int i, count = 0; - - if (serializers <= 1) - return 0; - - list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) * - (mcasp->tdm_slots + serializers - 2), - GFP_KERNEL); - if (!list) - return -ENOMEM; - - for (i = 2; i <= mcasp->tdm_slots; i++) - list[count++] = i; - - for (i = 2; i <= serializers; i++) - list[count++] = i*mcasp->tdm_slots; - - cl->count = count; - cl->list = list; - - return 0; -} - - -static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp) -{ - int rx_serializers = 0, tx_serializers = 0, ret, i; - - for (i = 0; i < mcasp->num_serializer; i++) - if (mcasp->serial_dir[i] == TX_MODE) - tx_serializers++; - else if (mcasp->serial_dir[i] == RX_MODE) - rx_serializers++; - - ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ - SNDRV_PCM_STREAM_PLAYBACK], - tx_serializers); - if (ret) - return ret; - - ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[ - SNDRV_PCM_STREAM_CAPTURE], - rx_serializers); - - return ret; -} - enum { PCM_EDMA, PCM_SDMA, @@ -1783,7 +1855,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev) mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE; }
- ret = davinci_mcasp_init_ch_constraints(mcasp); + /* Allocate memory for long enough list for all possible + * scenarios. Maximum number tdm slots is 32 and there cannot + * be more serializers than given in the configuration. The + * serializer directions could be taken into account, but it + * would make code much more complex and save only couple of + * bytes. + */ + mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list = + devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (32 + mcasp->num_serializer - 2), + GFP_KERNEL); + + mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list = + devm_kzalloc(mcasp->dev, sizeof(unsigned int) * + (32 + mcasp->num_serializer - 2), + GFP_KERNEL); + + if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list || + !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list) + return -ENOMEM; + + ret = davinci_mcasp_set_ch_constraints(mcasp); if (ret) goto err;