summaryrefslogtreecommitdiff
path: root/sound/x86/intel_hdmi_audio.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/x86/intel_hdmi_audio.c')
-rw-r--r--sound/x86/intel_hdmi_audio.c39
1 files changed, 25 insertions, 14 deletions
diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c
index c0a080e5d1f4..015d57cd9725 100644
--- a/sound/x86/intel_hdmi_audio.c
+++ b/sound/x86/intel_hdmi_audio.c
@@ -29,6 +29,7 @@
#include <linux/interrupt.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
+#include <linux/delay.h>
#include <asm/cacheflush.h>
#include <sound/core.h>
#include <sound/asoundef.h>
@@ -976,8 +977,6 @@ static void had_process_buffer_done(struct snd_intelhad *intelhaddata)
had_substream_put(intelhaddata);
}
-#define MAX_CNT 0xFF
-
/*
* The interrupt status 'sticky' bits might not be cleared by
* setting '1' to that bit once...
@@ -987,31 +986,37 @@ static void wait_clear_underrun_bit(struct snd_intelhad *intelhaddata)
int i;
u32 val;
- for (i = 0; i < MAX_CNT; i++) {
+ for (i = 0; i < 100; i++) {
/* clear bit30, 31 AUD_HDMI_STATUS */
had_read_register(intelhaddata, AUD_HDMI_STATUS, &val);
if (!(val & AUD_HDMI_STATUS_MASK_UNDERRUN))
return;
+ udelay(100);
+ cond_resched();
had_write_register(intelhaddata, AUD_HDMI_STATUS, val);
}
dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n");
}
-/* called from irq handler */
-static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
+/* Perform some reset procedure but only when need_reset is set;
+ * this is called from prepare or hw_free callbacks once after trigger STOP
+ * or underrun has been processed in order to settle down the h/w state.
+ */
+static void had_do_reset(struct snd_intelhad *intelhaddata)
{
- struct snd_pcm_substream *substream;
+ if (!intelhaddata->need_reset)
+ return;
- /* Handle Underrun interrupt within Audio Unit */
- had_write_register(intelhaddata, AUD_CONFIG, 0);
- intelhaddata->aud_config.regval = 0;
/* Reset buffer pointers */
had_reset_audio(intelhaddata);
-
wait_clear_underrun_bit(intelhaddata);
+ intelhaddata->need_reset = false;
+}
- if (!intelhaddata->connected)
- return; /* disconnected? - bail out */
+/* called from irq handler */
+static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
+{
+ struct snd_pcm_substream *substream;
/* Report UNDERRUN error to above layers */
substream = had_substream_get(intelhaddata);
@@ -1019,6 +1024,7 @@ static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
snd_pcm_stop_xrun(substream);
had_substream_put(intelhaddata);
}
+ intelhaddata->need_reset = true;
}
/*
@@ -1134,9 +1140,13 @@ static int had_pcm_hw_params(struct snd_pcm_substream *substream,
*/
static int had_pcm_hw_free(struct snd_pcm_substream *substream)
{
+ struct snd_intelhad *intelhaddata;
unsigned long addr;
u32 pages;
+ intelhaddata = snd_pcm_substream_chip(substream);
+ had_do_reset(intelhaddata);
+
/* mark back the pages as cached/writeback region before the free */
if (substream->runtime->dma_area != NULL) {
addr = (unsigned long) substream->runtime->dma_area;
@@ -1188,8 +1198,7 @@ static int had_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
spin_unlock(&intelhaddata->had_spinlock);
/* Disable Audio */
had_enable_audio(intelhaddata, false);
- /* Reset buffer pointers */
- had_reset_audio(intelhaddata);
+ intelhaddata->need_reset = true;
break;
default:
@@ -1227,6 +1236,8 @@ static int had_pcm_prepare(struct snd_pcm_substream *substream)
dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate);
dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels);
+ had_do_reset(intelhaddata);
+
/* Get N value in KHz */
disp_samp_freq = intelhaddata->tmds_clock_speed;