[alsa-devel] [PATCH V1 08/11] ASoc: DaVinci: i2s, Improve underrun, support mono
Troy Kisky
troy.kisky at boundarydevices.com
Sun Jul 5 04:29:58 CEST 2009
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 at 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 {
--
1.5.6.3
More information about the Alsa-devel
mailing list