msm: 8660: audio: HDMI: Fix the HDMI DMA stop issue
LPA_IF dma channel is disabled without checking the per count value.
Because of this dma channel is not functional after few iterations
of continuous playback. Fix this issue by checking the dma per count
value to stop before disabling the LPA_IF dma channel.
Change-Id: Ie9e9a9337ca2c4b76cebd99cd3eb98137be85b89
Signed-off-by: Deepa Madiregama <dmadireg@codeaurora.org>
diff --git a/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h b/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h
index 5c9bfb5..1970d0b 100644
--- a/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h
+++ b/arch/arm/mach-msm/include/mach/audio_dma_msm8k.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -202,6 +202,11 @@
#define LPAIF_DMA_SET_BUFF_CNT(x) DMA_CTRL_ADDR(x, 0x20)
#define LPAIF_DMA_SET_PER_CNT(x) DMA_CTRL_ADDR(x, 0x24)
+#define LPAIF_DMA_PER_CNT_PER_CNT_MASK 0x000FFFFF
+#define LPAIF_DMA_PER_CNT_PER_CNT_SHIFT 0
+#define LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK 0x00F00000
+#define LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT 20
+
/* channel assignments */
#define DMA_CH_0 0
diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
index 97dad67..3965a75 100644
--- a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
+++ b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
@@ -14,5 +14,6 @@
#define __MSM_HDMI_AUDIO_H
int hdmi_audio_enable(bool on , u32 fifo_water_mark);
+int hdmi_audio_packet_enable(bool on);
#endif /* __MSM_HDMI_AUDIO_H*/
diff --git a/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c b/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c
index 11d5d05..16258eb 100644
--- a/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c
+++ b/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c
@@ -74,8 +74,6 @@
pending = (intrsrc
& (UNDER_CH(dma_ch) | PER_CH(dma_ch) | ERR_CH(dma_ch)));
- pr_debug("pending = 0x%08x\n", pending);
-
if (pending & UNDER_CH(dma_ch))
pr_err("under run\n");
if (pending & ERR_CH(dma_ch))
@@ -104,10 +102,9 @@
dai_start_hdmi(lpa_if->dma_ch);
- mb();
-
hdmi_audio_enable(1, HDMI_AUDIO_FIFO_WATER_MARK);
- mb();
+
+ hdmi_audio_packet_enable(1);
return 0;
}
@@ -121,7 +118,7 @@
dma_params.period_size = lpa_if->dma_period_sz;
dma_params.channels = 2;
- lpa_if->dma_ch = 0;
+ lpa_if->dma_ch = 4;
dai_set_params(lpa_if->dma_ch, &dma_params);
register_dma_irq_handler(lpa_if->dma_ch, lpa_if_irq, (void *)lpa_if);
@@ -232,6 +229,7 @@
lpa_if_ptr->dma_buf = 0;
core_req_bus_bandwith(AUDIO_IF_BUS_ID, 100000, 0);
+ mb();
return 0;
}
@@ -244,9 +242,6 @@
const char __user *start = buf;
int xfer, rc;
- pr_debug("count %u cpu_buf %d dma_buf %d\n",
- (unsigned int)count, lpa_if->cpu_buf, lpa_if->dma_buf);
-
mutex_lock(&lpa_if->lock);
if (dma_buf_index < 2) {
@@ -259,6 +254,7 @@
goto end;
}
+ mb();
pr_debug("prefill: count %u audio_buf[%u].size %u\n",
count, dma_buf_index, ab->size);
@@ -315,15 +311,20 @@
static int lpa_if_release(struct inode *inode, struct file *file)
{
struct lpa_if *lpa_if = file->private_data;
- hdmi_audio_enable(0, HDMI_AUDIO_FIFO_WATER_MARK);
- smp_mb();
+ hdmi_audio_packet_enable(0);
+
+ wait_for_dma_cnt_stop(lpa_if->dma_ch);
+
+ hdmi_audio_enable(0, HDMI_AUDIO_FIFO_WATER_MARK);
if (lpa_if->config) {
unregister_dma_irq_handler(lpa_if->dma_ch);
dai_stop_hdmi(lpa_if->dma_ch);
lpa_if->config = 0;
}
+ core_req_bus_bandwith(AUDIO_IF_BUS_ID, 0, 0);
+
return 0;
}
diff --git a/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c b/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c
index 4eeb654..9b8346d 100644
--- a/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c
+++ b/arch/arm/mach-msm/qdsp6v2/snddev_hdmi.c
@@ -102,7 +102,7 @@
snddev_hdmi_active = 0;
if (snddev_hdmi_data->on_apps) {
- pr_debug("%s open done\n", dev_info->name);
+ pr_debug("%s Closed\n", dev_info->name);
mutex_unlock(&snddev_hdmi_lock);
return 0;
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 9805b6c..4b34969 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -3088,10 +3088,36 @@
HDMI_OUTP(HDMI_AUDIO_CFG, hdmi_audio_config);
+ mb();
+ pr_info("%s :HDMI_AUDIO_CFG 0x%08x\n", __func__,
+ HDMI_INP(HDMI_AUDIO_CFG));
+
return 0;
}
EXPORT_SYMBOL(hdmi_audio_enable);
+#define HDMI_AUDIO_PKT_CTRL 0x0020
+#define HDMI_AUDIO_SAMPLE_SEND_ENABLE 1
+
+int hdmi_audio_packet_enable(bool on)
+{
+ u32 hdmi_audio_pkt_ctrl;
+ hdmi_audio_pkt_ctrl = HDMI_INP(HDMI_AUDIO_PKT_CTRL);
+
+ if (on)
+ hdmi_audio_pkt_ctrl |= HDMI_AUDIO_SAMPLE_SEND_ENABLE;
+ else
+ hdmi_audio_pkt_ctrl &= ~(HDMI_AUDIO_SAMPLE_SEND_ENABLE);
+
+ HDMI_OUTP(HDMI_AUDIO_PKT_CTRL, hdmi_audio_pkt_ctrl);
+
+ mb();
+ pr_info("%s : HDMI_AUDIO_PKT_CTRL 0x%08x\n", __func__,
+ HDMI_INP(HDMI_AUDIO_PKT_CTRL));
+ return 0;
+}
+EXPORT_SYMBOL(hdmi_audio_packet_enable);
+
static void hdmi_msm_audio_info_setup(boolean enabled, int num_of_channels,
int level_shift, boolean down_mix)
{
@@ -3294,7 +3320,8 @@
external_common_state->video_resolution,
MSM_HDMI_SAMPLE_RATE_48KHZ, channels);
hdmi_msm_audio_info_setup(TRUE, channels, 0, FALSE);
- hdmi_msm_audio_ctrl_setup(TRUE, 1);
+
+ hdmi_msm_audio_ctrl_setup(FALSE, 1);
/* Turn on Audio FIFO and SAM DROP ISR */
HDMI_OUTP(0x02CC, HDMI_INP(0x02CC) | BIT(1) | BIT(3));
diff --git a/include/sound/dai.h b/include/sound/dai.h
index 27ff980..4d3fb96 100644
--- a/include/sound/dai.h
+++ b/include/sound/dai.h
@@ -43,6 +43,7 @@
void unregister_dma_irq_handler(int dma_ch);
void dai_set_master_mode(uint32_t dma_ch, int mode);
int dai_start_hdmi(uint32_t dma_ch);
+int wait_for_dma_cnt_stop(uint32_t dma_ch);
void dai_stop_hdmi(uint32_t dma_ch);
#endif
diff --git a/sound/soc/msm/lpass-dma.c b/sound/soc/msm/lpass-dma.c
index 84b6f1f..66c1836 100644
--- a/sound/soc/msm/lpass-dma.c
+++ b/sound/soc/msm/lpass-dma.c
@@ -93,6 +93,7 @@
spin_lock_irqsave(&dai_lock, flag);
intrsrc = readl(dai_info.base + LPAIF_IRQ_STAT(0));
writel(intrsrc, dai_info.base + LPAIF_IRQ_CLEAR(0));
+ mb();
while (intrsrc) {
dma_ch = dai_find_dma_channel(intrsrc);
@@ -149,6 +150,7 @@
dai_info.base + LPAIF_DMA_BUFF_LEN(dma_ch));
writel(((dai[dma_ch]->period_len >> 2) - 1),
dai_info.base + LPAIF_DMA_PER_LEN(dma_ch));
+ mb();
}
static void dai_enable_codec(uint32_t dma_ch, int codec)
@@ -237,6 +239,7 @@
dai[dma_ch]->channels = params->channels;
dai[dma_ch]->buffer_len = params->buffer_size;
dai[dma_ch]->period_len = params->period_size;
+ mb();
dai_config_dma(dma_ch);
return dma_ch;
}
@@ -278,6 +281,7 @@
val = readl(dai_info.base + LPAIF_IRQ_EN(0));
val = val | (7 << (dma_ch * 3));
writel(val, dai_info.base + LPAIF_IRQ_EN(0));
+ mb();
val = (HDMI_BURST_INCR4 | HDMI_WPSCNT | HDMI_AUDIO_INTF |
@@ -287,10 +291,58 @@
}
spin_unlock_irqrestore(&dai_lock, flag);
+ mb();
dai_print_state(dma_ch);
return 0;
}
+int wait_for_dma_cnt_stop(uint32_t dma_ch)
+{
+ uint32_t dma_per_cnt_reg_val, dma_per_cnt, prev_dma_per_cnt;
+ uint32_t i;
+
+ pr_info("%s dma_ch %u\n", __func__, dma_ch);
+
+ dma_per_cnt_reg_val = readl_relaxed(dai_info.base +
+ LPAIF_DMA_PER_CNT(dma_ch));
+
+ dma_per_cnt =
+ ((LPAIF_DMA_PER_CNT_PER_CNT_MASK & dma_per_cnt_reg_val) >>
+ LPAIF_DMA_PER_CNT_PER_CNT_SHIFT) -
+ ((LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK & dma_per_cnt_reg_val) >>
+ LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT);
+
+ prev_dma_per_cnt = dma_per_cnt;
+
+ i = 1;
+ pr_info("%s: i = %u dma_per_cnt_reg_val 0x%08x , dma_per_cnt %u\n",
+ __func__, i, dma_per_cnt_reg_val, dma_per_cnt);
+
+ while (i <= 50) {
+ msleep(50);
+
+ dma_per_cnt_reg_val = readl_relaxed(dai_info.base +
+ LPAIF_DMA_PER_CNT(dma_ch));
+
+ dma_per_cnt =
+ ((LPAIF_DMA_PER_CNT_PER_CNT_MASK & dma_per_cnt_reg_val) >>
+ LPAIF_DMA_PER_CNT_PER_CNT_SHIFT) -
+ ((LPAIF_DMA_PER_CNT_FIFO_WORDCNT_MASK & dma_per_cnt_reg_val) >>
+ LPAIF_DMA_PER_CNT_FIFO_WORDCNT_SHIFT);
+
+ i++;
+
+ pr_info("%s: i = %u dma_per_cnt_reg_val 0x%08x , dma_per_cnt %u\n",
+ __func__, i, dma_per_cnt_reg_val, dma_per_cnt);
+
+ if (prev_dma_per_cnt == dma_per_cnt)
+ break;
+
+ prev_dma_per_cnt = dma_per_cnt;
+ }
+ return 0;
+}
+
void dai_stop_hdmi(uint32_t dma_ch)
{
unsigned long flag = 0x0;
@@ -307,14 +359,18 @@
intrVal = 0x0;
writel(intrVal, dai_info.base + LPAIF_DMA_CTL(dma_ch));
+ mb();
+
intrVal = readl(dai_info.base + LPAIF_IRQ_EN(0));
int_mask = ((int_mask) << (dma_ch * 3));
int_mask = ~int_mask;
- intrVal = intrVal && int_mask;
+ intrVal = intrVal & int_mask;
writel(intrVal, dai_info.base + LPAIF_IRQ_EN(0));
+ mb();
+
spin_unlock_irqrestore(&dai_lock, flag);
}