This patch will reduce the number of underruns by shifting out 32 bit values instead of 16 bit. It also adds mono support.
Signed-off-by: Troy Kisky troy.kisky@boundarydevices.com --- sound/soc/davinci/davinci-i2s.c | 124 +++++++++++++++++++++++++++------------ sound/soc/davinci/davinci-pcm.c | 31 +++++++--- sound/soc/davinci/davinci-pcm.h | 3 +- 3 files changed, 110 insertions(+), 48 deletions(-)
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 6fa1b6a..a2ad53e 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -356,26 +356,29 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, struct davinci_mcbsp_dev *dev = rtd->dai->cpu_dai->private_data; struct snd_interval *i = NULL; int mcbsp_word_length; + int bits_per_sample; + int bits_per_frame; unsigned int rcr, xcr, srgr; + int channels; + int format; + int element_cnt = 1; u32 spcr;
- /* general line settings */ - spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); - if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { - spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - } else { - spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE; - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr); - } - i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS); - srgr = DAVINCI_MCBSP_SRGR_FSGM; - srgr |= DAVINCI_MCBSP_SRGR_FWID(snd_interval_value(i) - 1); + bits_per_sample = snd_interval_value(i); + /* always 2 samples/frame, mono will convert to stereo */ + bits_per_frame = bits_per_sample << 1; + srgr = DAVINCI_MCBSP_SRGR_FSGM | + DAVINCI_MCBSP_SRGR_FPER(bits_per_frame - 1) | + DAVINCI_MCBSP_SRGR_FWID(bits_per_sample - 1);
- i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_FRAME_BITS); - srgr |= DAVINCI_MCBSP_SRGR_FPER(snd_interval_value(i) - 1); - davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); + /* general line settings */ + spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); + spcr |= DAVINCI_MCBSP_SPCR_FREE; + spcr |= (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? + DAVINCI_MCBSP_SPCR_XINTM(3) : + DAVINCI_MCBSP_SPCR_RINTM(3); + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
rcr = DAVINCI_MCBSP_RCR_RFIG; xcr = DAVINCI_MCBSP_XCR_XFIG; @@ -386,33 +389,80 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1); xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); } + channels = params_channels(params); + format = params_format(params); /* Determine xfer data type */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S8: - dma_params->data_type = 1; - mcbsp_word_length = DAVINCI_MCBSP_WORD_8; - break; - case SNDRV_PCM_FORMAT_S16_LE: - dma_params->data_type = 2; - mcbsp_word_length = DAVINCI_MCBSP_WORD_16; - break; - case SNDRV_PCM_FORMAT_S32_LE: - dma_params->data_type = 4; - mcbsp_word_length = DAVINCI_MCBSP_WORD_32; - break; - default: - printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); - return -EINVAL; + if (channels == 2) { + /* Combining both channels into 1 element can allow x10 the + * amount of time between servicing the dma channel, increase + * effiency, and reduce the chance of overrun/underrun. But, + * it will result in the left & right channels being swapped. + * So, you may want to let the codec know to swap them back. + * + * It may be x10 instead of x2 because the clock from the codec + * may run at mclk speed (ie. tlvaic23b), independent of the + * sample rate. So, having an entire frame at once means it can + * be serviced at the sample rate instead of the mclk speed. + * + * In the now very unlikely case that an underrun still + * occurs, both the left and right samples will be repeated + * so that no pops are heard, and the left and right channels + * won't end up being swapped because of the underrun. + */ + dma_params->convert_mono_stereo = 0; + switch (format) { + case SNDRV_PCM_FORMAT_S8: + dma_params->data_type = 2; /* 2 byte frame */ + mcbsp_word_length = DAVINCI_MCBSP_WORD_16; + break; + case SNDRV_PCM_FORMAT_S16_LE: + dma_params->data_type = 4; /* 4 byte frame */ + mcbsp_word_length = DAVINCI_MCBSP_WORD_32; + break; + case SNDRV_PCM_FORMAT_S32_LE: + element_cnt = 2; + dma_params->data_type = 4; /* 4 byte element */ + mcbsp_word_length = DAVINCI_MCBSP_WORD_32; + break; + default: + printk(KERN_WARNING + "davinci-i2s: unsupported PCM format"); + return -EINVAL; + } + } else { + dma_params->convert_mono_stereo = 1; + /* 1 element in ram becomes 2 for stereo */ + element_cnt = 2; + switch (format) { + case SNDRV_PCM_FORMAT_S8: + /* 1 byte frame in ram */ + dma_params->data_type = 1; + mcbsp_word_length = DAVINCI_MCBSP_WORD_8; + break; + case SNDRV_PCM_FORMAT_S16_LE: + /* 2 byte frame in ram */ + dma_params->data_type = 2; + mcbsp_word_length = DAVINCI_MCBSP_WORD_16; + break; + case SNDRV_PCM_FORMAT_S32_LE: + /* 4 byte element */ + dma_params->data_type = 4; + mcbsp_word_length = DAVINCI_MCBSP_WORD_32; + break; + default: + printk(KERN_WARNING + "davinci-i2s: unsupported PCM format"); + return -EINVAL; + } } - - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); + rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); + xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1);
rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); xcr |= DAVINCI_MCBSP_XCR_XWDLEN1(mcbsp_word_length) | DAVINCI_MCBSP_XCR_XWDLEN2(mcbsp_word_length); - + davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_XCR_REG, xcr); else @@ -542,12 +592,12 @@ struct snd_soc_dai davinci_i2s_dai = { .probe = davinci_i2s_probe, .remove = davinci_i2s_remove, .playback = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = DAVINCI_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, .capture = { - .channels_min = 2, + .channels_min = 1, .channels_max = 2, .rates = DAVINCI_I2S_RATES, .formats = SNDRV_PCM_FMTBIT_S16_LE,}, diff --git a/sound/soc/davinci/davinci-pcm.c b/sound/soc/davinci/davinci-pcm.c index a059965..2002fdc 100644 --- a/sound/soc/davinci/davinci-pcm.c +++ b/sound/soc/davinci/davinci-pcm.c @@ -65,9 +65,9 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) unsigned int dma_offset; dma_addr_t dma_pos; dma_addr_t src, dst; - unsigned short src_bidx, dst_bidx; - unsigned int data_type; unsigned int count; + unsigned int data_type = prtd->params->data_type; + unsigned int convert_mono_stereo = prtd->params->convert_mono_stereo;
period_size = snd_pcm_lib_period_bytes(substream); dma_offset = prtd->period * period_size; @@ -76,26 +76,37 @@ static void davinci_pcm_enqueue_dma(struct snd_pcm_substream *substream) pr_debug("davinci_pcm: audio_set_dma_params_play channel = %d " "dma_ptr = %x period_size=%x\n", lch, dma_pos, period_size);
- data_type = prtd->params->data_type; count = period_size / data_type;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { src = dma_pos; dst = prtd->params->dma_addr; - src_bidx = data_type; - dst_bidx = 0; + if (convert_mono_stereo) + edma_set_src_index(lch, 0, data_type); + else + edma_set_src_index(lch, data_type, 0); + edma_set_dest_index(lch, 0, 0); } else { src = prtd->params->dma_addr; dst = dma_pos; - src_bidx = 0; - dst_bidx = data_type; + edma_set_src_index(lch, 0, 0); + if (convert_mono_stereo) + edma_set_dest_index(lch, 0, data_type); + else + edma_set_dest_index(lch, data_type, 0); }
edma_set_src(lch, src, INCR, W8BIT); edma_set_dest(lch, dst, INCR, W8BIT); - edma_set_src_index(lch, src_bidx, 0); - edma_set_dest_index(lch, dst_bidx, 0); - edma_set_transfer_params(lch, data_type, count, 1, 0, ASYNC); + if (convert_mono_stereo) { + /* + * Each byte is sent twice, so + * A_CNT * B_CNT * C_CNT = 2 * period_size + */ + edma_set_transfer_params(lch, data_type, 2, count, 2, ASYNC); + } else { + edma_set_transfer_params(lch, data_type, count, 1, 0, ASYNC); + }
prtd->period++; if (unlikely(prtd->period >= runtime->periods)) diff --git a/sound/soc/davinci/davinci-pcm.h b/sound/soc/davinci/davinci-pcm.h index 62cb4eb..fc70161 100644 --- a/sound/soc/davinci/davinci-pcm.h +++ b/sound/soc/davinci/davinci-pcm.h @@ -16,7 +16,8 @@ struct davinci_pcm_dma_params { char *name; /* stream identifier */ int channel; /* sync dma channel ID */ dma_addr_t dma_addr; /* device physical address for DMA */ - unsigned int data_type; /* xfer data type */ + unsigned char data_type; /* xfer data type */ + unsigned char convert_mono_stereo; };
struct evm_snd_platform_data {