ASoC: add start treshold to prevent underflows in some cases
[pandora-kernel.git] / sound / soc / omap / omap-pcm.c
index 690bfea..b411eb4 100644 (file)
@@ -97,7 +97,7 @@ static int omap_pcm_hw_params(struct snd_pcm_substream *substream,
        prtd->dma_data = dma_data;
        err = omap_request_dma(dma_data->dma_req, dma_data->name,
                               omap_pcm_dma_irq, substream, &prtd->dma_ch);
-       if (!cpu_is_omap1510()) {
+       if (!err & !cpu_is_omap1510()) {
                /*
                 * Link channel with itself so DMA doesn't need any
                 * reprogramming while looping the buffer
@@ -147,12 +147,14 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
                dma_params.src_or_dst_synch     = OMAP_DMA_DST_SYNC;
                dma_params.src_start            = runtime->dma_addr;
                dma_params.dst_start            = dma_data->port_addr;
+               dma_params.dst_port             = OMAP_DMA_PORT_MPUI;
        } else {
                dma_params.src_amode            = OMAP_DMA_AMODE_CONSTANT;
                dma_params.dst_amode            = OMAP_DMA_AMODE_POST_INC;
                dma_params.src_or_dst_synch     = OMAP_DMA_SRC_SYNC;
                dma_params.src_start            = dma_data->port_addr;
                dma_params.dst_start            = runtime->dma_addr;
+               dma_params.src_port             = OMAP_DMA_PORT_MPUI;
        }
        /*
         * Set DMA transfer frame size equal to ALSA period size and frame
@@ -166,6 +168,31 @@ static int omap_pcm_prepare(struct snd_pcm_substream *substream)
 
        omap_enable_dma_irq(prtd->dma_ch, OMAP_DMA_FRAME_IRQ);
 
+       /*
+        * To handle realtime streaming sources properly, we need to be sure
+        * we have at least 2 periods of size larger than FIFO in the buffer
+        * to start playing without underflowing. This is because DMA is always
+        * ahead of playback by amount close to FIFO size and needs to have
+        * next period ready when previous one finishes transfering to FIFO.
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+           runtime->periods > 1) {
+               struct snd_soc_pcm_runtime *rtd = substream->private_data;
+               int *mcbsp_bus_id = rtd->dai->cpu_dai->private_data;
+               int fifo_samples = (*mcbsp_bus_id == 1) ? 1024 : 256;
+               int period_samples, period_frames;
+               
+               period_samples = bytes_to_samples(runtime,
+                               snd_pcm_lib_period_bytes(substream));
+               if (period_samples < fifo_samples + runtime->channels)
+                       period_samples = fifo_samples + runtime->channels;
+               period_frames = bytes_to_frames(runtime,
+                               samples_to_bytes(runtime, period_samples)) * 2;
+
+               if (runtime->start_threshold < period_frames)
+                       runtime->start_threshold = period_frames;
+       }
+
        return 0;
 }
 
@@ -231,7 +258,7 @@ static int omap_pcm_open(struct snd_pcm_substream *substream)
        if (ret < 0)
                goto out;
 
-       prtd = kzalloc(sizeof(prtd), GFP_KERNEL);
+       prtd = kzalloc(sizeof(*prtd), GFP_KERNEL);
        if (prtd == NULL) {
                ret = -ENOMEM;
                goto out;