summaryrefslogtreecommitdiffstats
path: root/sound/soc/davinci/davinci-i2s.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/davinci/davinci-i2s.c')
-rw-r--r--sound/soc/davinci/davinci-i2s.c104
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;