summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2008-11-06 16:11:10 (GMT)
committerTakashi Iwai <tiwai@suse.de>2008-11-06 16:29:48 (GMT)
commit6ce4a3bc1b93e8ca50b142b00dd73bfdb5c4a172 (patch)
treed6adcd686919c9107d638966aee06bb63ee8d5fd
parent33fa35ed0d7e8996cc68cc2ffc21f12b38fa03c1 (diff)
downloadlinux-6ce4a3bc1b93e8ca50b142b00dd73bfdb5c4a172.tar.xz
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>
-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 bf8e6f9..f3c447c 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;