Skip to content

Commit

Permalink
[ALSA] make the pxa2xx-ac97 module more robust against PXA27x bugs
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nicolas Pitre authored and Jaroslav Kysela committed Jan 3, 2006
1 parent aa1e77e commit ea265c0
Showing 1 changed file with 22 additions and 17 deletions.
39 changes: 22 additions & 17 deletions sound/arm/pxa2xx-ac97.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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)
Expand Down

0 comments on commit ea265c0

Please sign in to comment.