Commit ea265c0a authored by Nicolas Pitre's avatar Nicolas Pitre Committed by Jaroslav Kysela

[ALSA] make the pxa2xx-ac97 module more robust against PXA27x bugs

Modules: ARM PXA2XX driver

The SDONE and CDONE interrupt on the PXA27x might become unusable in
some conditions.  Let's use an hybrid approach (interrupt with timeout)
to have the best possible behavior in all conditions.

Also let's not care about CAR_CAIP anymore.  This is useless.
Signed-off-by: default avatarNicolas Pitre <nico@cam.org>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent aa1e77e6
...@@ -37,39 +37,47 @@ static DECLARE_MUTEX(car_mutex); ...@@ -37,39 +37,47 @@ static DECLARE_MUTEX(car_mutex);
static DECLARE_WAIT_QUEUE_HEAD(gsr_wq); static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
static volatile long gsr_bits; static volatile long gsr_bits;
/*
* Beware PXA27x bugs:
*
* o Slot 12 read from modem space will hang controller.
* o CDONE, SDONE interrupt fails after any slot 12 IO.
*
* We therefore have an hybrid approach for waiting on SDONE (interrupt or
* 1 jiffy timeout if interrupt never comes).
*/
static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg) static unsigned short pxa2xx_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
{ {
unsigned short val = -1; unsigned short val = -1;
volatile u32 *reg_addr; volatile u32 *reg_addr;
down(&car_mutex); down(&car_mutex);
if (CAR & CAR_CAIP) {
printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__);
goto out;
}
/* set up primary or secondary codec space */ /* set up primary or secondary codec space */
reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1); reg_addr += (reg >> 1);
/* start read access across the ac97 link */ /* start read access across the ac97 link */
GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0; gsr_bits = 0;
val = *reg_addr; val = *reg_addr;
if (reg == AC97_GPIO_STATUS) if (reg == AC97_GPIO_STATUS)
goto out; goto out;
wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1); if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
if (!gsr_bits & GSR_SDONE) { !((GSR | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n", printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
__FUNCTION__, reg, gsr_bits); __FUNCTION__, reg, GSR | gsr_bits);
val = -1; val = -1;
goto out; goto out;
} }
/* valid data now */ /* valid data now */
GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0; gsr_bits = 0;
val = *reg_addr; val = *reg_addr;
/* but we've just started another cycle... */ /* but we've just started another cycle... */
wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1); wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1);
out: up(&car_mutex); out: up(&car_mutex);
return val; return val;
...@@ -81,22 +89,19 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne ...@@ -81,22 +89,19 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne
down(&car_mutex); down(&car_mutex);
if (CAR & CAR_CAIP) {
printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__);
goto out;
}
/* set up primary or secondary codec space */ /* set up primary or secondary codec space */
reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE; reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1); reg_addr += (reg >> 1);
GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0; gsr_bits = 0;
*reg_addr = val; *reg_addr = val;
wait_event_timeout(gsr_wq, gsr_bits & GSR_CDONE, 1); if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
if (!gsr_bits & GSR_SDONE) !((GSR | gsr_bits) & GSR_CDONE))
printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n", printk(KERN_ERR "%s: write error (ac97_reg=%d GSR=%#lx)\n",
__FUNCTION__, reg, gsr_bits); __FUNCTION__, reg, GSR | gsr_bits);
out: up(&car_mutex); up(&car_mutex);
} }
static void pxa2xx_ac97_reset(struct snd_ac97 *ac97) static void pxa2xx_ac97_reset(struct snd_ac97 *ac97)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment