summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-11-06 17:11:10 +0100
committerTakashi Iwai <tiwai@suse.de>2008-11-06 17:29:48 +0100
commit6ce4a3bc1b93e8ca50b142b00dd73bfdb5c4a172 (patch)
treed6adcd686919c9107d638966aee06bb63ee8d5fd /sound
parent33fa35ed0d7e8996cc68cc2ffc21f12b38fa03c1 (diff)
ALSA: hda - Make codec-probing more robust
When an error occurs during the codec probing, typically accessing to an non-existing codec slot, the controller chip gets often screwed up and can no longer communicate with the codecs. This patch adds a preparation phase just to probe codec addresses before actually creating codec instances. If any error occurs during this probing phase, the driver resets the controller to recover. This will (hopefully) fix the famous "single_cmd" errors. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/hda_intel.c55
1 files changed, 55 insertions, 0 deletions
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index bf8e6f94aeb..f3c447cf67f 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -392,6 +392,7 @@ struct azx {
unsigned int msi :1;
unsigned int irq_pending_warned :1;
unsigned int via_dmapos_patch :1; /* enable DMA-position fix for VIA */
+ unsigned int probing :1; /* codec probing phase */
/* for debugging */
unsigned int last_cmd; /* last issued command (to sync) */
@@ -624,6 +625,14 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
goto again;
}
+ if (chip->probing) {
+ /* If this critical timeout happens during the codec probing
+ * phase, this is likely an access to a non-existing codec
+ * slot. Better to return an error and reset the system.
+ */
+ return -1;
+ }
+
snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
"switching to single_cmd mode: last cmd=0x%08x\n",
chip->last_cmd);
@@ -1175,8 +1184,28 @@ static int azx_setup_controller(struct azx *chip, struct azx_dev *azx_dev)
return 0;
}
+/*
+ * Probe the given codec address
+ */
+static int probe_codec(struct azx *chip, int addr)
+{
+ unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
+ (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
+ unsigned int res;
+
+ chip->probing = 1;
+ azx_send_cmd(chip->bus, cmd);
+ res = azx_get_response(chip->bus);
+ chip->probing = 0;
+ if (res == -1)
+ return -EIO;
+ snd_printdd("hda_intel: codec #%d probed OK\n", addr);
+ return 0;
+}
+
static int azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *cpcm);
+static void azx_stop_chip(struct azx *chip);
/*
* Codec initialization
@@ -1216,6 +1245,32 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
max_slots = azx_max_codecs[chip->driver_type];
if (!max_slots)
max_slots = AZX_MAX_CODECS;
+
+ /* First try to probe all given codec slots */
+ for (c = 0; c < max_slots; c++) {
+ if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
+ if (probe_codec(chip, c) < 0) {
+ /* Some BIOSen give you wrong codec addresses
+ * that don't exist
+ */
+ snd_printk(KERN_WARNING
+ "hda_intel: Codec #%d probe error; "
+ "disabling it...\n", c);
+ chip->codec_mask &= ~(1 << c);
+ /* More badly, accessing to a non-existing
+ * codec often screws up the controller chip,
+ * and distrubs the further communications.
+ * Thus if an error occurs during probing,
+ * better to reset the controller chip to
+ * get back to the sanity state.
+ */
+ azx_stop_chip(chip);
+ azx_init_chip(chip);
+ }
+ }
+ }
+
+ /* Then create codec instances */
for (c = 0; c < max_slots; c++) {
if ((chip->codec_mask & (1 << c)) & codec_probe_mask) {
struct hda_codec *codec;