aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-06-02 12:15:48 +0200
committerTakashi Iwai <tiwai@suse.de>2009-06-02 12:15:48 +0200
commit3c4dbda003b5941b351bec8d1954e8eaa6e7bc09 (patch)
treee082b5f735b3c43ad9e19e39e3ce8119f1a7d69c /sound
parent3b315d70b094e8b439358756a9084438fd7a71c2 (diff)
parentb20f3b834673be9ead83a3c6f07fa3881d1a990f (diff)
Merge branch 'topic/hda-ctl-reset' into topic/hda
Diffstat (limited to 'sound')
-rw-r--r--sound/pci/hda/hda_codec.c44
-rw-r--r--sound/pci/hda/hda_codec.h9
-rw-r--r--sound/pci/hda/hda_intel.c40
-rw-r--r--sound/pci/hda/patch_sigmatel.c9
4 files changed, 69 insertions, 33 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index d1d5fb9d7af..562403a2348 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -165,28 +165,29 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
unsigned int *res)
{
struct hda_bus *bus = codec->bus;
- int err, repeated = 0;
+ int err;
if (res)
*res = -1;
+ again:
snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex);
- again:
err = bus->ops.command(bus, cmd);
- if (!err) {
- if (res) {
- *res = bus->ops.get_response(bus);
- if (*res == -1 && bus->rirb_error) {
- if (repeated++ < 1) {
- snd_printd(KERN_WARNING "hda_codec: "
- "Trying verb 0x%08x again\n", cmd);
- goto again;
- }
- }
- }
- }
+ if (!err && res)
+ *res = bus->ops.get_response(bus);
mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec);
+ if (res && *res == -1 && bus->rirb_error) {
+ if (bus->response_reset) {
+ snd_printd("hda_codec: resetting BUS due to "
+ "fatal communication error\n");
+ bus->ops.bus_reset(bus);
+ }
+ goto again;
+ }
+ /* clear reset-flag when the communication gets recovered */
+ if (!err)
+ bus->response_reset = 0;
return err;
}
@@ -213,11 +214,6 @@ unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
}
EXPORT_SYMBOL_HDA(snd_hda_codec_read);
-/* Define the below to send and receive verbs synchronously.
- * If you often get any codec communication errors, this is worth to try.
- */
-/* #define SND_HDA_SUPPORT_SYNC_WRITE */
-
/**
* snd_hda_codec_write - send a single command without waiting for response
* @codec: the HDA codec
@@ -234,12 +230,9 @@ int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int direct,
unsigned int verb, unsigned int parm)
{
unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
-#ifdef SND_HDA_SUPPORT_SYNC_WRITE
unsigned int res;
- return codec_exec_verb(codec, cmd, &res);
-#else
- return codec_exec_verb(codec, cmd, NULL);
-#endif
+ return codec_exec_verb(codec, cmd,
+ codec->bus->sync_write ? &res : NULL);
}
EXPORT_SYMBOL_HDA(snd_hda_codec_write);
@@ -3894,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
/**
* snd_hda_suspend - suspend the codecs
* @bus: the HDA bus
- * @state: suspsend state
*
* Returns 0 if successful.
*/
-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state)
+int snd_hda_suspend(struct hda_bus *bus)
{
struct hda_codec *codec;
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index c5bd40f77bb..cad79efaabc 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -574,6 +574,8 @@ struct hda_bus_ops {
/* attach a PCM stream */
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *pcm);
+ /* reset bus for retry verb */
+ void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_bus *bus);
@@ -622,8 +624,13 @@ struct hda_bus {
/* misc op flags */
unsigned int needs_damn_long_delay :1;
+ unsigned int allow_bus_reset:1; /* allow bus reset at fatal error */
+ unsigned int sync_write:1; /* sync after verb write */
+ /* status for codec/controller */
unsigned int shutdown :1; /* being unloaded */
unsigned int rirb_error:1; /* error in codec communication */
+ unsigned int response_reset:1; /* controller was reset */
+ unsigned int in_reset:1; /* during reset operation */
};
/*
@@ -907,7 +914,7 @@ void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
* power management
*/
#ifdef CONFIG_PM
-int snd_hda_suspend(struct hda_bus *bus, pm_message_t state);
+int snd_hda_suspend(struct hda_bus *bus);
int snd_hda_resume(struct hda_bus *bus);
#endif
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index b063d0e3d32..9f44645a1d0 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -661,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
return -1;
}
- snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): "
- "last cmd=0x%08x\n", chip->last_cmd);
- /* re-initialize CORB/RIRB */
- spin_lock_irq(&chip->reg_lock);
+ /* a fatal communication error; need either to reset or to fallback
+ * to the single_cmd mode
+ */
bus->rirb_error = 1;
+ if (bus->allow_bus_reset && !bus->response_reset && !bus->in_reset) {
+ bus->response_reset = 1;
+ return -1; /* give a chance to retry */
+ }
+
+ snd_printk(KERN_ERR "hda_intel: azx_get_response timeout, "
+ "switching to single_cmd mode: last cmd=0x%08x\n",
+ chip->last_cmd);
+ chip->single_cmd = 1;
+ bus->response_reset = 0;
+ /* re-initialize CORB/RIRB */
azx_free_cmd_io(chip);
azx_init_cmd_io(chip);
- spin_unlock_irq(&chip->reg_lock);
return -1;
}
@@ -709,6 +718,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
struct azx *chip = bus->private_data;
int timeout = 50;
+ bus->rirb_error = 0;
while (timeout--) {
/* check ICB busy bit */
if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) {
@@ -1247,6 +1257,23 @@ 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);
+static void azx_bus_reset(struct hda_bus *bus)
+{
+ struct azx *chip = bus->private_data;
+ int i;
+
+ bus->in_reset = 1;
+ azx_stop_chip(chip);
+ azx_init_chip(chip);
+ if (chip->initialized) {
+ for (i = 0; i < AZX_MAX_PCMS; i++)
+ snd_pcm_suspend_all(chip->pcm[i]);
+ snd_hda_suspend(chip->bus);
+ snd_hda_resume(chip->bus);
+ }
+ bus->in_reset = 0;
+}
+
/*
* Codec initialization
*/
@@ -1270,6 +1297,7 @@ static int __devinit azx_codec_create(struct azx *chip, const char *model,
bus_temp.ops.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
+ bus_temp.ops.bus_reset = azx_bus_reset;
#ifdef CONFIG_SND_HDA_POWER_SAVE
bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify;
@@ -1997,7 +2025,7 @@ static int azx_suspend(struct pci_dev *pci, pm_message_t state)
for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]);
if (chip->initialized)
- snd_hda_suspend(chip->bus, state);
+ snd_hda_suspend(chip->bus);
azx_stop_chip(chip);
if (chip->irq >= 0) {
free_irq(chip->irq, chip);
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 48f4a36c481..42f944bb641 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -5375,6 +5375,15 @@ again:
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
+ /* Some HP machines seem to have unstable codec communications
+ * especially with ATI fglrx driver. For recovering from the
+ * CORB/RIRB stall, allow the BUS reset and keep always sync
+ */
+ if (spec->board_config == STAC_HP_DV5) {
+ codec->bus->sync_write = 1;
+ codec->bus->allow_bus_reset = 1;
+ }
+
spec->aloopback_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0;