diff options
Diffstat (limited to 'sound/soc/davinci/davinci-i2s.c')
-rw-r--r-- | sound/soc/davinci/davinci-i2s.c | 104 |
1 files changed, 65 insertions, 39 deletions
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 12a6c549ee6e..6362ca05506e 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -97,22 +97,52 @@ enum { DAVINCI_MCBSP_WORD_32, }; -static struct davinci_pcm_dma_params davinci_i2s_pcm_out = { - .name = "I2S PCM Stereo out", +static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = 1, + [SNDRV_PCM_FORMAT_S16_LE] = 2, + [SNDRV_PCM_FORMAT_S32_LE] = 4, }; -static struct davinci_pcm_dma_params davinci_i2s_pcm_in = { - .name = "I2S PCM Stereo in", +static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8, + [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16, + [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32, +}; + +static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { + [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE, + [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE, }; struct davinci_mcbsp_dev { + struct davinci_pcm_dma_params dma_params[2]; void __iomem *base; #define MOD_DSP_A 0 #define MOD_DSP_B 1 int mode; u32 pcr; struct clk *clk; - struct davinci_pcm_dma_params *dma_params[2]; + /* + * Combining both channels into 1 element will at least double 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. + * + * If relabeling the left and right channels is not possible, + * you may want to let the codec know to swap them back. + * + * It may allow x10 the amount of time to service dma requests, + * if the codec is master and is using an unnecessarily fast bit clock + * (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 bit clock rate. + * + * In the now 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. + */ + unsigned enable_channel_combine:1; }; static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, @@ -215,14 +245,6 @@ static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback) toggle_clock(dev, playback); } -static int davinci_i2s_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct davinci_mcbsp_dev *dev = cpu_dai->private_data; - cpu_dai->dma_data = dev->dma_params[substream->stream]; - return 0; -} - #define DEFAULT_BITPERSAMPLE 16 static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai, @@ -353,12 +375,15 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { - struct davinci_pcm_dma_params *dma_params = dai->dma_data; struct davinci_mcbsp_dev *dev = dai->private_data; + struct davinci_pcm_dma_params *dma_params = + &dev->dma_params[substream->stream]; struct snd_interval *i = NULL; int mcbsp_word_length; unsigned int rcr, xcr, srgr; u32 spcr; + snd_pcm_format_t fmt; + unsigned element_cnt = 1; /* general line settings */ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); @@ -388,27 +413,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); } /* 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: + fmt = params_format(params); + if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) { printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); return -EINVAL; } - dma_params->acnt = dma_params->data_type; - rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); - xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); + if (params_channels(params) == 2) { + element_cnt = 2; + if (double_fmt[fmt] && dev->enable_channel_combine) { + element_cnt = 1; + fmt = double_fmt[fmt]; + } + } + dma_params->acnt = dma_params->data_type = data_type[fmt]; + dma_params->fifo_level = 0; + mcbsp_word_length = asp_word_length[fmt]; + 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); @@ -472,7 +494,6 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream, #define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000 static struct snd_soc_dai_ops davinci_i2s_dai_ops = { - .startup = davinci_i2s_startup, .shutdown = davinci_i2s_shutdown, .prepare = davinci_i2s_prepare, .trigger = davinci_i2s_trigger, @@ -524,7 +545,13 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENOMEM; goto err_release_region; } - + if (pdata) { + dev->enable_channel_combine = pdata->enable_channel_combine; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size = + pdata->sram_size_playback; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = + pdata->sram_size_capture; + } dev->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(dev->clk)) { ret = -ENODEV; @@ -534,12 +561,10 @@ static int davinci_i2s_probe(struct platform_device *pdev) dev->base = (void __iomem *)IO_ADDRESS(mem->start); - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK] = &davinci_i2s_pcm_out; - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->dma_addr = + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].dma_addr = (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DXR_REG); - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE] = &davinci_i2s_pcm_in; - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->dma_addr = + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].dma_addr = (dma_addr_t)(io_v2p(dev->base) + DAVINCI_MCBSP_DRR_REG); /* first TX, then RX */ @@ -549,7 +574,7 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENXIO; goto err_free_mem; } - dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK]->channel = res->start; + dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].channel = res->start; res = platform_get_resource(pdev, IORESOURCE_DMA, 1); if (!res) { @@ -557,9 +582,10 @@ static int davinci_i2s_probe(struct platform_device *pdev) ret = -ENXIO; goto err_free_mem; } - dev->dma_params[SNDRV_PCM_STREAM_CAPTURE]->channel = res->start; + dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; davinci_i2s_dai.private_data = dev; + davinci_i2s_dai.dma_data = dev->dma_params; ret = snd_soc_register_dai(&davinci_i2s_dai); if (ret != 0) goto err_free_mem; |