Merge branch 'topic/hda-ctl-reset' into topic/hda

This commit is contained in:
Takashi Iwai 2009-06-02 12:15:48 +02:00
commit 3c4dbda003
4 changed files with 69 additions and 33 deletions

View File

@ -165,28 +165,29 @@ static int codec_exec_verb(struct hda_codec *codec, unsigned int cmd,
unsigned int *res) unsigned int *res)
{ {
struct hda_bus *bus = codec->bus; struct hda_bus *bus = codec->bus;
int err, repeated = 0; int err;
if (res) if (res)
*res = -1; *res = -1;
again:
snd_hda_power_up(codec); snd_hda_power_up(codec);
mutex_lock(&bus->cmd_mutex); mutex_lock(&bus->cmd_mutex);
again:
err = bus->ops.command(bus, cmd); err = bus->ops.command(bus, cmd);
if (!err) { if (!err && res)
if (res) { *res = bus->ops.get_response(bus);
*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;
}
}
}
}
mutex_unlock(&bus->cmd_mutex); mutex_unlock(&bus->cmd_mutex);
snd_hda_power_down(codec); 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; 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); 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 * snd_hda_codec_write - send a single command without waiting for response
* @codec: the HDA codec * @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 verb, unsigned int parm)
{ {
unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm); unsigned int cmd = make_codec_cmd(codec, nid, direct, verb, parm);
#ifdef SND_HDA_SUPPORT_SYNC_WRITE
unsigned int res; unsigned int res;
return codec_exec_verb(codec, cmd, &res); return codec_exec_verb(codec, cmd,
#else codec->bus->sync_write ? &res : NULL);
return codec_exec_verb(codec, cmd, NULL);
#endif
} }
EXPORT_SYMBOL_HDA(snd_hda_codec_write); EXPORT_SYMBOL_HDA(snd_hda_codec_write);
@ -3894,11 +3887,10 @@ EXPORT_SYMBOL_HDA(auto_pin_cfg_labels);
/** /**
* snd_hda_suspend - suspend the codecs * snd_hda_suspend - suspend the codecs
* @bus: the HDA bus * @bus: the HDA bus
* @state: suspsend state
* *
* Returns 0 if successful. * 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; struct hda_codec *codec;

View File

@ -574,6 +574,8 @@ struct hda_bus_ops {
/* attach a PCM stream */ /* attach a PCM stream */
int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec, int (*attach_pcm)(struct hda_bus *bus, struct hda_codec *codec,
struct hda_pcm *pcm); struct hda_pcm *pcm);
/* reset bus for retry verb */
void (*bus_reset)(struct hda_bus *bus);
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
/* notify power-up/down from codec to controller */ /* notify power-up/down from codec to controller */
void (*pm_notify)(struct hda_bus *bus); void (*pm_notify)(struct hda_bus *bus);
@ -622,8 +624,13 @@ struct hda_bus {
/* misc op flags */ /* misc op flags */
unsigned int needs_damn_long_delay :1; 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 shutdown :1; /* being unloaded */
unsigned int rirb_error:1; /* error in codec communication */ 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 * power management
*/ */
#ifdef CONFIG_PM #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); int snd_hda_resume(struct hda_bus *bus);
#endif #endif

View File

@ -661,14 +661,23 @@ static unsigned int azx_rirb_get_response(struct hda_bus *bus)
return -1; return -1;
} }
snd_printk(KERN_ERR SFX "azx_get_response timeout (ERROR): " /* a fatal communication error; need either to reset or to fallback
"last cmd=0x%08x\n", chip->last_cmd); * to the single_cmd mode
/* re-initialize CORB/RIRB */ */
spin_lock_irq(&chip->reg_lock);
bus->rirb_error = 1; 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_free_cmd_io(chip);
azx_init_cmd_io(chip); azx_init_cmd_io(chip);
spin_unlock_irq(&chip->reg_lock);
return -1; return -1;
} }
@ -709,6 +718,7 @@ static int azx_single_send_cmd(struct hda_bus *bus, u32 val)
struct azx *chip = bus->private_data; struct azx *chip = bus->private_data;
int timeout = 50; int timeout = 50;
bus->rirb_error = 0;
while (timeout--) { while (timeout--) {
/* check ICB busy bit */ /* check ICB busy bit */
if (!((azx_readw(chip, IRS) & ICH6_IRS_BUSY))) { 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); struct hda_pcm *cpcm);
static void azx_stop_chip(struct azx *chip); 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 * 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.command = azx_send_cmd;
bus_temp.ops.get_response = azx_get_response; bus_temp.ops.get_response = azx_get_response;
bus_temp.ops.attach_pcm = azx_attach_pcm_stream; bus_temp.ops.attach_pcm = azx_attach_pcm_stream;
bus_temp.ops.bus_reset = azx_bus_reset;
#ifdef CONFIG_SND_HDA_POWER_SAVE #ifdef CONFIG_SND_HDA_POWER_SAVE
bus_temp.power_save = &power_save; bus_temp.power_save = &power_save;
bus_temp.ops.pm_notify = azx_power_notify; 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++) for (i = 0; i < AZX_MAX_PCMS; i++)
snd_pcm_suspend_all(chip->pcm[i]); snd_pcm_suspend_all(chip->pcm[i]);
if (chip->initialized) if (chip->initialized)
snd_hda_suspend(chip->bus, state); snd_hda_suspend(chip->bus);
azx_stop_chip(chip); azx_stop_chip(chip);
if (chip->irq >= 0) { if (chip->irq >= 0) {
free_irq(chip->irq, chip); free_irq(chip->irq, chip);

View File

@ -5375,6 +5375,15 @@ again:
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP) if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init); 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_ctl = stac92hd71bxx_loopback;
spec->aloopback_mask = 0x50; spec->aloopback_mask = 0x50;
spec->aloopback_shift = 0; spec->aloopback_shift = 0;