[alsa-devel] [PATCH V1 08/11] ASoc: DaVinci: i2s, Improve underrun, support mono

Steve Chen schen at mvista.com
Mon Jul 6 13:09:16 CEST 2009


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



More information about the Alsa-devel mailing list