summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorChen Liangjun <b36089@freescale.com>2012-09-04 16:51:41 +0800
committerChen Liangjun <b36089@freescale.com>2012-09-05 09:24:26 +0800
commitaa7eb2a5853b932e138546283bb7bae283ccec90 (patch)
tree494ccd807a1f105aec7957000ae8abb2fc562ace /sound
parent61413ebc30c0b1367040f8da52e2ec7ce84ebb95 (diff)
ENGR00222900 HDMI AUDIO: fix kernel panic when doing suspend-resume test
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>
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/imx/imx-hdmi-dma.c42
1 files changed, 40 insertions, 2 deletions
diff --git a/sound/soc/imx/imx-hdmi-dma.c b/sound/soc/imx/imx-hdmi-dma.c
index 70c8cb13d3de..45811429985f 100644
--- a/sound/soc/imx/imx-hdmi-dma.c
+++ b/sound/soc/imx/imx-hdmi-dma.c
@@ -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();