diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /sound/soc/davinci/davinci-i2s.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'sound/soc/davinci/davinci-i2s.c')
-rw-r--r-- | sound/soc/davinci/davinci-i2s.c | 87 |
1 files changed, 64 insertions, 23 deletions
diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index 4ae707048021..adadcd3aa1b1 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c | |||
@@ -12,6 +12,7 @@ | |||
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/device.h> | 14 | #include <linux/device.h> |
15 | #include <linux/slab.h> | ||
15 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
16 | #include <linux/io.h> | 17 | #include <linux/io.h> |
17 | #include <linux/clk.h> | 18 | #include <linux/clk.h> |
@@ -97,12 +98,24 @@ enum { | |||
97 | DAVINCI_MCBSP_WORD_32, | 98 | DAVINCI_MCBSP_WORD_32, |
98 | }; | 99 | }; |
99 | 100 | ||
101 | static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = { | ||
102 | [SNDRV_PCM_FORMAT_S8] = 1, | ||
103 | [SNDRV_PCM_FORMAT_S16_LE] = 2, | ||
104 | [SNDRV_PCM_FORMAT_S32_LE] = 4, | ||
105 | }; | ||
106 | |||
107 | static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = { | ||
108 | [SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8, | ||
109 | [SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16, | ||
110 | [SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32, | ||
111 | }; | ||
112 | |||
113 | static const unsigned char double_fmt[SNDRV_PCM_FORMAT_S32_LE + 1] = { | ||
114 | [SNDRV_PCM_FORMAT_S8] = SNDRV_PCM_FORMAT_S16_LE, | ||
115 | [SNDRV_PCM_FORMAT_S16_LE] = SNDRV_PCM_FORMAT_S32_LE, | ||
116 | }; | ||
117 | |||
100 | struct davinci_mcbsp_dev { | 118 | struct davinci_mcbsp_dev { |
101 | /* | ||
102 | * dma_params must be first because rtd->dai->cpu_dai->private_data | ||
103 | * is cast to a pointer of an array of struct davinci_pcm_dma_params in | ||
104 | * davinci_pcm_open. | ||
105 | */ | ||
106 | struct davinci_pcm_dma_params dma_params[2]; | 119 | struct davinci_pcm_dma_params dma_params[2]; |
107 | void __iomem *base; | 120 | void __iomem *base; |
108 | #define MOD_DSP_A 0 | 121 | #define MOD_DSP_A 0 |
@@ -110,6 +123,27 @@ struct davinci_mcbsp_dev { | |||
110 | int mode; | 123 | int mode; |
111 | u32 pcr; | 124 | u32 pcr; |
112 | struct clk *clk; | 125 | struct clk *clk; |
126 | /* | ||
127 | * Combining both channels into 1 element will at least double the | ||
128 | * amount of time between servicing the dma channel, increase | ||
129 | * effiency, and reduce the chance of overrun/underrun. But, | ||
130 | * it will result in the left & right channels being swapped. | ||
131 | * | ||
132 | * If relabeling the left and right channels is not possible, | ||
133 | * you may want to let the codec know to swap them back. | ||
134 | * | ||
135 | * It may allow x10 the amount of time to service dma requests, | ||
136 | * if the codec is master and is using an unnecessarily fast bit clock | ||
137 | * (ie. tlvaic23b), independent of the sample rate. So, having an | ||
138 | * entire frame at once means it can be serviced at the sample rate | ||
139 | * instead of the bit clock rate. | ||
140 | * | ||
141 | * In the now unlikely case that an underrun still | ||
142 | * occurs, both the left and right samples will be repeated | ||
143 | * so that no pops are heard, and the left and right channels | ||
144 | * won't end up being swapped because of the underrun. | ||
145 | */ | ||
146 | unsigned enable_channel_combine:1; | ||
113 | }; | 147 | }; |
114 | 148 | ||
115 | static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, | 149 | static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev, |
@@ -349,6 +383,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
349 | int mcbsp_word_length; | 383 | int mcbsp_word_length; |
350 | unsigned int rcr, xcr, srgr; | 384 | unsigned int rcr, xcr, srgr; |
351 | u32 spcr; | 385 | u32 spcr; |
386 | snd_pcm_format_t fmt; | ||
387 | unsigned element_cnt = 1; | ||
352 | 388 | ||
353 | /* general line settings */ | 389 | /* general line settings */ |
354 | spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); | 390 | spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG); |
@@ -378,27 +414,24 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream, | |||
378 | xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); | 414 | xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1); |
379 | } | 415 | } |
380 | /* Determine xfer data type */ | 416 | /* Determine xfer data type */ |
381 | switch (params_format(params)) { | 417 | fmt = params_format(params); |
382 | case SNDRV_PCM_FORMAT_S8: | 418 | if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) { |
383 | dma_params->data_type = 1; | ||
384 | mcbsp_word_length = DAVINCI_MCBSP_WORD_8; | ||
385 | break; | ||
386 | case SNDRV_PCM_FORMAT_S16_LE: | ||
387 | dma_params->data_type = 2; | ||
388 | mcbsp_word_length = DAVINCI_MCBSP_WORD_16; | ||
389 | break; | ||
390 | case SNDRV_PCM_FORMAT_S32_LE: | ||
391 | dma_params->data_type = 4; | ||
392 | mcbsp_word_length = DAVINCI_MCBSP_WORD_32; | ||
393 | break; | ||
394 | default: | ||
395 | printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); | 419 | printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n"); |
396 | return -EINVAL; | 420 | return -EINVAL; |
397 | } | 421 | } |
398 | 422 | ||
399 | dma_params->acnt = dma_params->data_type; | 423 | if (params_channels(params) == 2) { |
400 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(1); | 424 | element_cnt = 2; |
401 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(1); | 425 | if (double_fmt[fmt] && dev->enable_channel_combine) { |
426 | element_cnt = 1; | ||
427 | fmt = double_fmt[fmt]; | ||
428 | } | ||
429 | } | ||
430 | dma_params->acnt = dma_params->data_type = data_type[fmt]; | ||
431 | dma_params->fifo_level = 0; | ||
432 | mcbsp_word_length = asp_word_length[fmt]; | ||
433 | rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(element_cnt - 1); | ||
434 | xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(element_cnt - 1); | ||
402 | 435 | ||
403 | rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | | 436 | rcr |= DAVINCI_MCBSP_RCR_RWDLEN1(mcbsp_word_length) | |
404 | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); | 437 | DAVINCI_MCBSP_RCR_RWDLEN2(mcbsp_word_length); |
@@ -513,7 +546,13 @@ static int davinci_i2s_probe(struct platform_device *pdev) | |||
513 | ret = -ENOMEM; | 546 | ret = -ENOMEM; |
514 | goto err_release_region; | 547 | goto err_release_region; |
515 | } | 548 | } |
516 | 549 | if (pdata) { | |
550 | dev->enable_channel_combine = pdata->enable_channel_combine; | ||
551 | dev->dma_params[SNDRV_PCM_STREAM_PLAYBACK].sram_size = | ||
552 | pdata->sram_size_playback; | ||
553 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].sram_size = | ||
554 | pdata->sram_size_capture; | ||
555 | } | ||
517 | dev->clk = clk_get(&pdev->dev, NULL); | 556 | dev->clk = clk_get(&pdev->dev, NULL); |
518 | if (IS_ERR(dev->clk)) { | 557 | if (IS_ERR(dev->clk)) { |
519 | ret = -ENODEV; | 558 | ret = -ENODEV; |
@@ -547,6 +586,8 @@ static int davinci_i2s_probe(struct platform_device *pdev) | |||
547 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; | 586 | dev->dma_params[SNDRV_PCM_STREAM_CAPTURE].channel = res->start; |
548 | 587 | ||
549 | davinci_i2s_dai.private_data = dev; | 588 | davinci_i2s_dai.private_data = dev; |
589 | davinci_i2s_dai.capture.dma_data = dev->dma_params; | ||
590 | davinci_i2s_dai.playback.dma_data = dev->dma_params; | ||
550 | ret = snd_soc_register_dai(&davinci_i2s_dai); | 591 | ret = snd_soc_register_dai(&davinci_i2s_dai); |
551 | if (ret != 0) | 592 | if (ret != 0) |
552 | goto err_free_mem; | 593 | goto err_free_mem; |