On Sat, 2009-07-04 at 19:29 -0700, Troy Kisky wrote:
> This patch will reduce the number of underruns by
> shifting out 32 bit values instead of 16 bit. It also
> adds mono support.

Doesn't ALSA already automatically handle mono-stero conversions?  I
don't think we need to provide the same functionality in the driver.

> 
> 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 {


_______________________________________________
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