aboutsummaryrefslogtreecommitdiff
path: root/sound/arm
diff options
context:
space:
mode:
authorNicolas Pitre <nico@cam.org>2005-12-12 15:41:47 +0100
committerJaroslav Kysela <perex@suse.cz>2006-01-03 12:30:51 +0100
commitea265c0a433fda15fb69b9fd733e0ea4215c216e (patch)
tree56873a5de0850fd6b03d76bda9502e6d3107e6e9 /sound/arm
parentaa1e77e691025149908f7641e77de93ffd7f1188 (diff)
[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: Nicolas Pitre <nico@cam.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/arm')
-rw-r--r--sound/arm/pxa2xx-ac97.c39
1 files changed, 22 insertions, 17 deletions
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index dda64beb202..3acbc6023d1 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -37,39 +37,47 @@ static DECLARE_MUTEX(car_mutex);
static DECLARE_WAIT_QUEUE_HEAD(gsr_wq);
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)
{
unsigned short val = -1;
volatile u32 *reg_addr;
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 */
reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1);
/* start read access across the ac97 link */
+ GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0;
val = *reg_addr;
if (reg == AC97_GPIO_STATUS)
goto out;
- wait_event_timeout(gsr_wq, gsr_bits & GSR_SDONE, 1);
- if (!gsr_bits & GSR_SDONE) {
+ if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_SDONE, 1) <= 0 &&
+ !((GSR | gsr_bits) & GSR_SDONE)) {
printk(KERN_ERR "%s: read error (ac97_reg=%d GSR=%#lx)\n",
- __FUNCTION__, reg, gsr_bits);
+ __FUNCTION__, reg, GSR | gsr_bits);
val = -1;
goto out;
}
/* valid data now */
+ GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0;
val = *reg_addr;
/* 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);
return val;
@@ -81,22 +89,19 @@ static void pxa2xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigne
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 */
reg_addr = (ac97->num & 1) ? &SAC_REG_BASE : &PAC_REG_BASE;
reg_addr += (reg >> 1);
+
+ GSR = GSR_CDONE | GSR_SDONE;
gsr_bits = 0;
*reg_addr = val;
- wait_event_timeout(gsr_wq, gsr_bits & GSR_CDONE, 1);
- if (!gsr_bits & GSR_SDONE)
+ if (wait_event_timeout(gsr_wq, (GSR | gsr_bits) & GSR_CDONE, 1) <= 0 &&
+ !((GSR | gsr_bits) & GSR_CDONE))
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)