]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00222900 HDMI AUDIO: fix kernel panic when doing suspend-resume test
authorChen Liangjun <b36089@freescale.com>
Tue, 4 Sep 2012 08:51:41 +0000 (16:51 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:35:22 +0000 (08:35 +0200)
In MX6 series, HDMI audio driver is responsible for add IEC header to
audio samples. Driver would maintain variables to cover this work.

The old driver would cause memory access exceeding issue:
1. Resume from an playback. In this case, variable maintained by ALSA is
updated while variable maintained by HDMI driver is not updated. The
mmap copy operation would run into error state due to misalignment.
2. underrun!!! The same error would happens as the items above.

In this patch, add variable check while adding IED header.

Signed-off-by: Chen Liangjun <b36089@freescale.com>
sound/soc/imx/imx-hdmi-dma.c

index 70c8cb13d3deb4d7e43aa683a228f10f89abcf27..45811429985fda2b920c6c8a4bba294559d5c34e 100644 (file)
@@ -597,9 +597,15 @@ static void hdmi_sdma_isr(void *data)
                if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
                        appl_bytes = frames_to_bytes(runtime,
                                                runtime->control->appl_ptr);
+                       if (rtd->appl_bytes > appl_bytes)
+                               rtd->appl_bytes = 0;
                        offset = rtd->appl_bytes % rtd->buffer_bytes;
                        space_to_end = rtd->buffer_bytes - offset;
                        count = appl_bytes - rtd->appl_bytes;
+                       if (count > rtd->buffer_bytes) {
+                               pr_info("HDMI is slow,ring buffer size[%ld], count[%ld]!\n",
+                                               rtd->buffer_bytes, count);
+                       }
                        rtd->appl_bytes = appl_bytes;
 
                        if (count <= space_to_end) {
@@ -645,9 +651,15 @@ static irqreturn_t hdmi_dma_isr(int irq, void *dev_id)
                if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
                        appl_bytes = frames_to_bytes(runtime,
                                                runtime->control->appl_ptr);
+                       if (rtd->appl_bytes > appl_bytes)
+                               rtd->appl_bytes = 0;
                        offset = rtd->appl_bytes % rtd->buffer_bytes;
                        space_to_end = rtd->buffer_bytes - offset;
                        count = appl_bytes - rtd->appl_bytes;
+                       if (count > rtd->buffer_bytes) {
+                               pr_info("HDMI is slow,ring buffer size[%ld],count[%ld]!\n",
+                                               rtd->buffer_bytes, count);
+                       }
                        rtd->appl_bytes = appl_bytes;
 
                        if (count <= space_to_end) {
@@ -1066,6 +1078,8 @@ static int hdmi_dma_hw_params(struct snd_pcm_substream *substream,
        /* Init par for mmap optimizate */
        init_table(rtd->channels);
 
+       rtd->appl_bytes = 0;
+
        return 0;
 }
 
@@ -1073,6 +1087,7 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct imx_hdmi_dma_runtime_data *rtd = runtime->private_data;
+       unsigned long offset,  count, space_to_end, appl_bytes;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -1080,11 +1095,34 @@ static int hdmi_dma_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                rtd->frame_idx = 0;
                if (runtime->access == SNDRV_PCM_ACCESS_MMAP_INTERLEAVED) {
-                       rtd->appl_bytes = frames_to_bytes(runtime,
+                       appl_bytes = frames_to_bytes(runtime,
                                                runtime->control->appl_ptr);
+                       /* If resume, the rtd->appl_bytes may stil
+                        * keep the old value but the control->
+                        * appl_ptr is clear. Reset it if this
+                        * misalignment happens*/
+                       if (rtd->appl_bytes > appl_bytes)
+                               rtd->appl_bytes = 0;
+                       offset = rtd->appl_bytes % rtd->buffer_bytes;
+                       space_to_end = rtd->buffer_bytes - offset;
+                       count = appl_bytes - rtd->appl_bytes;
 
-                       hdmi_dma_mmap_copy(substream, 0, rtd->appl_bytes);
+                       if (count > rtd->buffer_bytes) {
+                               pr_err("Error Count,ring buffer size[%ld], count[%ld]!\n",
+                                               rtd->buffer_bytes, count);
+                               return -EINVAL;
+                       }
 
+                       rtd->appl_bytes = appl_bytes;
+
+                       if (count <= space_to_end) {
+                               hdmi_dma_mmap_copy(substream, offset, count);
+                       } else {
+                               hdmi_dma_mmap_copy(substream,
+                                               offset, space_to_end);
+                               hdmi_dma_mmap_copy(substream,
+                                               0, count - space_to_end);
+                       }
                }
                dumpregs();