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.ki...@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


_______________________________________________
Davinci-linux-open-source mailing list
Davinci-linux-open-source@linux.davincidsp.com
http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source

Reply via email to