diff options
Diffstat (limited to 'sound/soc/davinci/davinci-mcasp.c')
-rw-r--r-- | sound/soc/davinci/davinci-mcasp.c | 305 |
1 files changed, 202 insertions, 103 deletions
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c index 7d45d98a861f..4495a40a9468 100644 --- a/sound/soc/davinci/davinci-mcasp.c +++ b/sound/soc/davinci/davinci-mcasp.c @@ -80,12 +80,13 @@ 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; u8 version; u8 bclk_div; - u16 bclk_lrclk_ratio; int streams; u32 irq_request[2]; int dma_request[2]; @@ -556,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id, mcasp->bclk_div = div; break; - case 2: /* BCLK/LRCLK ratio */ - mcasp->bclk_lrclk_ratio = div; + case 2: /* + * BCLK/LRCLK ratio descries how many bit-clock cycles + * fit into one frame. The clock ratio is given for a + * full period of data (for I2S format both left and + * right channels), so it has to be divided by number + * of tdm-slots (for I2S - divided by 2). + * Instead of storing this ratio, we calculate a new + * tdm_slot width by dividing the the ratio by the + * number of configured tdm slots. + */ + mcasp->slot_width = div / mcasp->tdm_slots; + if (div % mcasp->tdm_slots) + dev_warn(mcasp->dev, + "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots", + __func__, div, mcasp->tdm_slots); break; default: @@ -596,12 +610,92 @@ 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) + int sample_width) { u32 fmt; - u32 tx_rotate = (word_length / 4) & 0x7; - u32 mask = (1ULL << word_length) - 1; + u32 tx_rotate = (sample_width / 4) & 0x7; + u32 mask = (1ULL << sample_width) - 1; + u32 slot_width = sample_width; + /* * For captured data we should not rotate, inversion and masking is * enoguh to get the data to the right position: @@ -614,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp, u32 rx_rotate = 0; /* - * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv() - * callback, take it into account here. That allows us to for example - * send 32 bits per channel to the codec, while only 16 of them carry - * audio payload. - * The clock ratio is given for a full period of data (for I2S format - * both left and right channels), so it has to be divided by number of - * tdm-slots (for I2S - divided by 2). + * Setting the tdm slot width either with set_clkdiv() or + * set_tdm_slot() allows us to for example send 32 bits per + * channel to the codec, while only 16 of them carry audio + * payload. */ - if (mcasp->bclk_lrclk_ratio) { - u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots; - + if (mcasp->slot_width) { /* - * When we have more bclk then it is needed for the data, we - * need to use the rotation to move the received samples to have - * correct alignment. + * When we have more bclk then it is needed for the + * data, we need to use the rotation to move the + * received samples to have correct alignment. */ - rx_rotate = (slot_length - word_length) / 4; - word_length = slot_length; + slot_width = mcasp->slot_width; + rx_rotate = (slot_width - sample_width) / 4; } /* mapping of the XSSZ bit-field as described in the datasheet */ - fmt = (word_length >> 1) - 1; + fmt = (slot_width >> 1) - 1; if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) { mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt), @@ -776,33 +865,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; } @@ -922,6 +1028,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) @@ -1027,6 +1136,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; @@ -1069,10 +1181,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++; @@ -1094,6 +1210,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; @@ -1114,7 +1234,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 @@ -1124,15 +1244,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 @@ -1184,6 +1314,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 +1645,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 +1861,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; |