[ARM] tegra: spdif/i2s audio: fixes
authorChris Fries <C.Fries@motorola.com>
Sun, 5 Dec 2010 03:49:46 +0000 (21:49 -0600)
committerIliyan Malchev <malchev@google.com>
Tue, 7 Dec 2010 02:06:00 +0000 (18:06 -0800)
-- release can take a long time

Releasing the out file handles can take a long time, because
we wait for a stop completion that may never arrive.

-- fix possible dma list corruption

If things have gone wrong and a "wait_till_stopped()" times
out, prevent list corrption in the DMA by dequeing any
queued requests.

Signed-off-by: Iliyan Malchev <malchev@google.com>
arch/arm/mach-tegra/tegra_i2s_audio.c
arch/arm/mach-tegra/tegra_spdif_audio.c

index c36fe3b5671fb71be2723e3ef89999e464ed0ae1..56857beae63a4788c62a78c8c57859af301ded31 100644 (file)
@@ -690,12 +690,17 @@ static void request_stop_nosync(struct audio_stream *as)
        pr_debug("%s\n", __func__);
        if (!as->stop) {
                as->stop = true;
-               wait_till_stopped(as);
+               if (pending_buffer_requests(as))
+                       wait_till_stopped(as);
                for (i = 0; i < as->num_bufs; i++) {
                        init_completion(&as->comp[i]);
                        complete(&as->comp[i]);
                }
        }
+       if (!tegra_dma_is_empty(as->dma_chan))
+               pr_err("%s: DMA not empty!\n", __func__);
+       /* Stop the DMA then dequeue anything that's in progress. */
+       tegra_dma_cancel(as->dma_chan);
        as->active = false; /* applies to recording only */
        pr_debug("%s: done\n", __func__);
 }
index 3fb90905bc87abe6876a116fcb9e4107355b5310..64a85c28efaa34f8893490b0cff17a2f8a59b5c5 100644 (file)
@@ -455,12 +455,17 @@ static void request_stop_nosync(struct audio_stream *as)
        pr_debug("%s\n", __func__);
        if (!as->stop) {
                as->stop = true;
-               wait_till_stopped(as);
+               if (pending_buffer_requests(as))
+                       wait_till_stopped(as);
                for (i = 0; i < as->num_bufs; i++) {
                        init_completion(&as->comp[i]);
                        complete(&as->comp[i]);
                }
        }
+       if (!tegra_dma_is_empty(as->dma_chan))
+               pr_err("%s: DMA not empty!\n", __func__);
+       /* Stop the DMA then dequeue anything that's in progress. */
+       tegra_dma_cancel(as->dma_chan);
        as->active = false; /* applies to recording only */
        pr_debug("%s: done\n", __func__);
 }
@@ -538,13 +543,9 @@ static void dma_tx_complete_callback(struct tegra_dma_req *req)
 
        complete(&aos->comp[req_num]);
 
-       if (stop_playback_if_necessary(aos)) {
-               pr_debug("%s: done (stopped)\n", __func__);
-               if (!completion_done(&aos->stop_completion)) {
-                       pr_debug("%s: signalling stop completion\n", __func__);
-                       complete(&aos->stop_completion);
-               }
-               return;
+       if (!pending_buffer_requests(aos)) {
+               pr_debug("%s: Playback underflow", __func__);
+               complete(&aos->stop_completion);
        }
 }
 
@@ -724,6 +725,8 @@ static long tegra_spdif_out_ioctl(struct file *file,
                        request_stop_nosync(aos);
                        pr_debug("%s: flushed\n", __func__);
                }
+               if (stop_playback_if_necessary(aos))
+                       pr_debug("%s: done (stopped)\n", __func__);
                aos->stop = false;
                break;
        case TEGRA_AUDIO_OUT_SET_NUM_BUFS: {
@@ -747,6 +750,7 @@ static long tegra_spdif_out_ioctl(struct file *file,
                if (rc < 0)
                        break;
                aos->num_bufs = num;
+               sound_ops->setup(ads);
        }
                break;
        case TEGRA_AUDIO_OUT_GET_NUM_BUFS:
@@ -801,6 +805,8 @@ static int tegra_spdif_out_release(struct inode *inode, struct file *file)
        mutex_lock(&ads->out.lock);
        ads->out.opened = 0;
        request_stop_nosync(&ads->out);
+       if (stop_playback_if_necessary(&ads->out))
+               pr_debug("%s: done (stopped)\n", __func__);
        allow_suspend(&ads->out);
        mutex_unlock(&ads->out.lock);
        pr_debug("%s: done\n", __func__);