summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Tull <r80115@freescale.com>2010-04-12 17:57:03 -0500
committerAlan Tull <r80115@freescale.com>2010-04-13 09:35:55 -0500
commit976a44d4c493091490d685ebda49659f6e55e5b9 (patch)
tree776dee04b1fbc6c70568255bcb649728322cc5c1
parentc57c13403aa0f3b64f2295c1f8ebe5adcb96d654 (diff)
ENGR00122462 mxs: support mmapped buffer for 24/32 bit spdif
Due to an apparent hw bug we have to shift data in the audio buffer by one byte to play out S/PDIF. ENGR00122025 and ENGR00122231 are a workaround for playback with non-mmapped buffers. This patch is the workaround for mmapped buffers. Signed-off-by: Alan Tull <r80115@freescale.com>
-rw-r--r--sound/soc/mxs/mxs-pcm.c42
1 files changed, 42 insertions, 0 deletions
diff --git a/sound/soc/mxs/mxs-pcm.c b/sound/soc/mxs/mxs-pcm.c
index 965bbbadafea..a9f0358687f4 100644
--- a/sound/soc/mxs/mxs-pcm.c
+++ b/sound/soc/mxs/mxs-pcm.c
@@ -62,6 +62,7 @@ struct mxs_runtime_data {
u32 dma_ch;
u32 dma_period;
u32 dma_totsize;
+ unsigned long appl_ptr_bytes;
int format;
struct mxs_pcm_dma_params *params;
@@ -71,8 +72,11 @@ struct mxs_runtime_data {
static irqreturn_t mxs_pcm_dma_irq(int irq, void *dev_id)
{
struct snd_pcm_substream *substream = dev_id;
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct mxs_runtime_data *prtd = substream->runtime->private_data;
struct mxs_dma_info dma_info;
+ void *pdma;
+ unsigned long prev_appl_offset, appl_count, cont, appl_ptr_bytes;
mxs_dma_get_info(prtd->dma_ch, &dma_info);
@@ -81,6 +85,32 @@ static irqreturn_t mxs_pcm_dma_irq(int irq, void *dev_id)
__func__, prtd->params->dma_ch, prtd->params->name);
mxs_dma_ack_irq(prtd->dma_ch);
} else {
+ if ((prtd->params->dma_ch == MXS_DMA_CHANNEL_AHB_APBX_SPDIF) &&
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) &&
+ ((prtd->format == SNDRV_PCM_FORMAT_S24_LE)
+ || (prtd->format == SNDRV_PCM_FORMAT_S20_3LE))) {
+
+ appl_ptr_bytes =
+ frames_to_bytes(runtime,
+ runtime->control->appl_ptr);
+
+ appl_count = appl_ptr_bytes - prtd->appl_ptr_bytes;
+ prev_appl_offset =
+ prtd->appl_ptr_bytes % prtd->dma_totsize;
+ cont = prtd->dma_totsize - prev_appl_offset;
+
+ if (appl_count > cont) {
+ pdma = runtime->dma_area + prev_appl_offset;
+ memmove(pdma + 1, pdma, cont - 1);
+ pdma = runtime->dma_area;
+ memmove(pdma + 1, pdma, appl_count - cont - 1);
+ } else {
+ pdma = runtime->dma_area + prev_appl_offset;
+ memmove(pdma + 1, pdma, appl_count - 1);
+ }
+ prtd->appl_ptr_bytes = appl_ptr_bytes;
+ }
mxs_dma_ack_irq(prtd->dma_ch);
snd_pcm_period_elapsed(substream);
}
@@ -168,11 +198,23 @@ static void mxs_pcm_stop(struct snd_pcm_substream *substream)
static int mxs_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
+ struct snd_pcm_runtime *runtime = substream->runtime;
struct mxs_runtime_data *prtd = substream->runtime->private_data;
int ret = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ if ((prtd->params->dma_ch == MXS_DMA_CHANNEL_AHB_APBX_SPDIF) &&
+ (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+ (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) &&
+ ((prtd->format == SNDRV_PCM_FORMAT_S24_LE)
+ || (prtd->format == SNDRV_PCM_FORMAT_S20_3LE))) {
+ prtd->appl_ptr_bytes =
+ frames_to_bytes(runtime,
+ runtime->control->appl_ptr);
+ memmove(runtime->dma_area + 1, runtime->dma_area,
+ prtd->appl_ptr_bytes - 1);
+ }
mxs_dma_enable(prtd->dma_ch);
break;