summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2017-02-11 02:21:56 -0500
committerTakashi Iwai <tiwai@suse.de>2017-02-13 08:48:43 -0500
commite2acecf2c88370f9d7252e7a05cd7b6d43aed720 (patch)
tree649d2dfe02f2d5d59b1723158a4b934b1a7689e8
parent3fe2cf7eb21ada0a9683b26c1ae309e7f5e90131 (diff)
ALSA: x86: Handle reset at prepare callback
Currently the driver handles some reset procedure at the trigger STOP and the underrun functions, where both are executed in the interrupt context. Especially the underrun function has a sync-loop to clear the UNDERRUN status bit, and this is supposed to be one of plausible causes of GPU hangup. Since the job to be done in the interrupt handler should be minimum, we move the reset function out of trigger and underrun, and push it into the prepare (and hw_free) callbacks instead. Here a new flag, need_reset, is introduced to indicate the requirement of the reset procedure. This is for avoiding the multiple resets when PCM prepare is called sequentially. Also in the UNDERRUN bit-clear sync loop, take a longer pause to be in the safer side. Taking a longer delay is no longer a problem now because we're running in the normal context. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/x86/intel_hdmi_audio.c39
-rw-r--r--sound/x86/intel_hdmi_audio.h1
2 files changed, 26 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 @@
29#include <linux/interrupt.h> 29#include <linux/interrupt.h>
30#include <linux/pm_runtime.h> 30#include <linux/pm_runtime.h>
31#include <linux/dma-mapping.h> 31#include <linux/dma-mapping.h>
32#include <linux/delay.h>
32#include <asm/cacheflush.h> 33#include <asm/cacheflush.h>
33#include <sound/core.h> 34#include <sound/core.h>
34#include <sound/asoundef.h> 35#include <sound/asoundef.h>
@@ -976,8 +977,6 @@ static void had_process_buffer_done(struct snd_intelhad *intelhaddata)
976 had_substream_put(intelhaddata); 977 had_substream_put(intelhaddata);
977} 978}
978 979
979#define MAX_CNT 0xFF
980
981/* 980/*
982 * The interrupt status 'sticky' bits might not be cleared by 981 * The interrupt status 'sticky' bits might not be cleared by
983 * setting '1' to that bit once... 982 * setting '1' to that bit once...
@@ -987,31 +986,37 @@ static void wait_clear_underrun_bit(struct snd_intelhad *intelhaddata)
987 int i; 986 int i;
988 u32 val; 987 u32 val;
989 988
990 for (i = 0; i < MAX_CNT; i++) { 989 for (i = 0; i < 100; i++) {
991 /* clear bit30, 31 AUD_HDMI_STATUS */ 990 /* clear bit30, 31 AUD_HDMI_STATUS */
992 had_read_register(intelhaddata, AUD_HDMI_STATUS, &val); 991 had_read_register(intelhaddata, AUD_HDMI_STATUS, &val);
993 if (!(val & AUD_HDMI_STATUS_MASK_UNDERRUN)) 992 if (!(val & AUD_HDMI_STATUS_MASK_UNDERRUN))
994 return; 993 return;
994 udelay(100);
995 cond_resched();
995 had_write_register(intelhaddata, AUD_HDMI_STATUS, val); 996 had_write_register(intelhaddata, AUD_HDMI_STATUS, val);
996 } 997 }
997 dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n"); 998 dev_err(intelhaddata->dev, "Unable to clear UNDERRUN bits\n");
998} 999}
999 1000
1000/* called from irq handler */ 1001/* Perform some reset procedure but only when need_reset is set;
1001static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata) 1002 * this is called from prepare or hw_free callbacks once after trigger STOP
1003 * or underrun has been processed in order to settle down the h/w state.
1004 */
1005static void had_do_reset(struct snd_intelhad *intelhaddata)
1002{ 1006{
1003 struct snd_pcm_substream *substream; 1007 if (!intelhaddata->need_reset)
1008 return;
1004 1009
1005 /* Handle Underrun interrupt within Audio Unit */
1006 had_write_register(intelhaddata, AUD_CONFIG, 0);
1007 intelhaddata->aud_config.regval = 0;
1008 /* Reset buffer pointers */ 1010 /* Reset buffer pointers */
1009 had_reset_audio(intelhaddata); 1011 had_reset_audio(intelhaddata);
1010
1011 wait_clear_underrun_bit(intelhaddata); 1012 wait_clear_underrun_bit(intelhaddata);
1013 intelhaddata->need_reset = false;
1014}
1012 1015
1013 if (!intelhaddata->connected) 1016/* called from irq handler */
1014 return; /* disconnected? - bail out */ 1017static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
1018{
1019 struct snd_pcm_substream *substream;
1015 1020
1016 /* Report UNDERRUN error to above layers */ 1021 /* Report UNDERRUN error to above layers */
1017 substream = had_substream_get(intelhaddata); 1022 substream = had_substream_get(intelhaddata);
@@ -1019,6 +1024,7 @@ static void had_process_buffer_underrun(struct snd_intelhad *intelhaddata)
1019 snd_pcm_stop_xrun(substream); 1024 snd_pcm_stop_xrun(substream);
1020 had_substream_put(intelhaddata); 1025 had_substream_put(intelhaddata);
1021 } 1026 }
1027 intelhaddata->need_reset = true;
1022} 1028}
1023 1029
1024/* 1030/*
@@ -1134,9 +1140,13 @@ static int had_pcm_hw_params(struct snd_pcm_substream *substream,
1134 */ 1140 */
1135static int had_pcm_hw_free(struct snd_pcm_substream *substream) 1141static int had_pcm_hw_free(struct snd_pcm_substream *substream)
1136{ 1142{
1143 struct snd_intelhad *intelhaddata;
1137 unsigned long addr; 1144 unsigned long addr;
1138 u32 pages; 1145 u32 pages;
1139 1146
1147 intelhaddata = snd_pcm_substream_chip(substream);
1148 had_do_reset(intelhaddata);
1149
1140 /* mark back the pages as cached/writeback region before the free */ 1150 /* mark back the pages as cached/writeback region before the free */
1141 if (substream->runtime->dma_area != NULL) { 1151 if (substream->runtime->dma_area != NULL) {
1142 addr = (unsigned long) substream->runtime->dma_area; 1152 addr = (unsigned long) substream->runtime->dma_area;
@@ -1188,8 +1198,7 @@ static int had_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
1188 spin_unlock(&intelhaddata->had_spinlock); 1198 spin_unlock(&intelhaddata->had_spinlock);
1189 /* Disable Audio */ 1199 /* Disable Audio */
1190 had_enable_audio(intelhaddata, false); 1200 had_enable_audio(intelhaddata, false);
1191 /* Reset buffer pointers */ 1201 intelhaddata->need_reset = true;
1192 had_reset_audio(intelhaddata);
1193 break; 1202 break;
1194 1203
1195 default: 1204 default:
@@ -1227,6 +1236,8 @@ static int had_pcm_prepare(struct snd_pcm_substream *substream)
1227 dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate); 1236 dev_dbg(intelhaddata->dev, "rate=%d\n", runtime->rate);
1228 dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels); 1237 dev_dbg(intelhaddata->dev, "channels=%d\n", runtime->channels);
1229 1238
1239 had_do_reset(intelhaddata);
1240
1230 /* Get N value in KHz */ 1241 /* Get N value in KHz */
1231 disp_samp_freq = intelhaddata->tmds_clock_speed; 1242 disp_samp_freq = intelhaddata->tmds_clock_speed;
1232 1243
diff --git a/sound/x86/intel_hdmi_audio.h b/sound/x86/intel_hdmi_audio.h
index a96728a4e7bc..8b9e184fef44 100644
--- a/sound/x86/intel_hdmi_audio.h
+++ b/sound/x86/intel_hdmi_audio.h
@@ -130,6 +130,7 @@ struct snd_intelhad {
130 union aud_cfg aud_config; /* AUD_CONFIG reg value cache */ 130 union aud_cfg aud_config; /* AUD_CONFIG reg value cache */
131 struct work_struct hdmi_audio_wq; 131 struct work_struct hdmi_audio_wq;
132 struct mutex mutex; /* for protecting chmap and eld */ 132 struct mutex mutex; /* for protecting chmap and eld */
133 bool need_reset;
133}; 134};
134 135
135#endif /* _INTEL_HDMI_AUDIO_ */ 136#endif /* _INTEL_HDMI_AUDIO_ */