[ALSA] nm256 - Fix PM and irq handling

NM256 driver
- Fixed the PCM resume - restoring the rate setting
- Fixed the handling of buggy irqs
- Dynamically acquire/release irq handler to make the driver more robust
  to unknown irq storms (as OSS driver does).

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2005-08-16 16:54:12 +02:00 committed by Jaroslav Kysela
parent 1cfe43d21b
commit 1204de32d0

View File

@ -189,6 +189,7 @@ struct snd_nm256_stream {
nm256_t *chip;
snd_pcm_substream_t *substream;
int running;
int suspended;
u32 buf; /* offset from chip->buffer */
int bufsize; /* buffer size in bytes */
@ -231,8 +232,10 @@ struct snd_nm256 {
int mixer_status_mask; /* bit mask to test the mixer status */
int irq;
int irq_acks;
irqreturn_t (*interrupt)(int, void *, struct pt_regs *);
int badintrcount; /* counter to check bogus interrupts */
struct semaphore irq_mutex;
nm256_stream_t streams[2];
@ -464,6 +467,37 @@ snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *subs
}
}
/* acquire interrupt */
static int snd_nm256_acquire_irq(nm256_t *chip)
{
down(&chip->irq_mutex);
if (chip->irq < 0) {
if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ,
chip->card->driver, (void*)chip)) {
snd_printk("unable to grab IRQ %d\n", chip->pci->irq);
up(&chip->irq_mutex);
return -EBUSY;
}
chip->irq = chip->pci->irq;
}
chip->irq_acks++;
up(&chip->irq_mutex);
return 0;
}
/* release interrupt */
static void snd_nm256_release_irq(nm256_t *chip)
{
down(&chip->irq_mutex);
if (chip->irq_acks > 0)
chip->irq_acks--;
if (chip->irq_acks == 0 && chip->irq >= 0) {
free_irq(chip->irq, (void*)chip);
chip->irq = -1;
}
up(&chip->irq_mutex);
}
/*
* start / stop
*/
@ -538,15 +572,19 @@ snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd)
spin_lock(&chip->reg_lock);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
s->suspended = 0;
/* fallthru */
case SNDRV_PCM_TRIGGER_START:
if (! s->running) {
snd_nm256_playback_start(chip, s, substream);
s->running = 1;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
s->suspended = 1;
/* fallthru */
case SNDRV_PCM_TRIGGER_STOP:
if (s->running) {
snd_nm256_playback_stop(chip);
s->running = 0;
@ -818,6 +856,8 @@ snd_nm256_playback_open(snd_pcm_substream_t *substream)
{
nm256_t *chip = snd_pcm_substream_chip(substream);
if (snd_nm256_acquire_irq(chip) < 0)
return -EBUSY;
snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK],
substream, &snd_nm256_playback);
return 0;
@ -828,6 +868,8 @@ snd_nm256_capture_open(snd_pcm_substream_t *substream)
{
nm256_t *chip = snd_pcm_substream_chip(substream);
if (snd_nm256_acquire_irq(chip) < 0)
return -EBUSY;
snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE],
substream, &snd_nm256_capture);
return 0;
@ -839,6 +881,9 @@ snd_nm256_capture_open(snd_pcm_substream_t *substream)
static int
snd_nm256_playback_close(snd_pcm_substream_t *substream)
{
nm256_t *chip = snd_pcm_substream_chip(substream);
snd_nm256_release_irq(chip);
return 0;
}
@ -846,6 +891,9 @@ snd_nm256_playback_close(snd_pcm_substream_t *substream)
static int
snd_nm256_capture_close(snd_pcm_substream_t *substream)
{
nm256_t *chip = snd_pcm_substream_chip(substream);
snd_nm256_release_irq(chip);
return 0;
}
@ -915,18 +963,16 @@ snd_nm256_pcm(nm256_t *chip, int device)
static void
snd_nm256_init_chip(nm256_t *chip)
{
spin_lock_irq(&chip->reg_lock);
/* Reset everything. */
snd_nm256_writeb(chip, 0x0, 0x11);
snd_nm256_writew(chip, 0x214, 0);
/* stop sounds.. */
//snd_nm256_playback_stop(chip);
//snd_nm256_capture_stop(chip);
spin_unlock_irq(&chip->reg_lock);
}
static inline void
static irqreturn_t
snd_nm256_intr_check(nm256_t *chip)
{
if (chip->badintrcount++ > 1000) {
@ -947,7 +993,9 @@ snd_nm256_intr_check(nm256_t *chip)
if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running)
snd_nm256_capture_stop(chip);
chip->badintrcount = 0;
return IRQ_HANDLED;
}
return IRQ_NONE;
}
/*
@ -969,10 +1017,8 @@ snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy)
status = snd_nm256_readw(chip, NM_INT_REG);
/* Not ours. */
if (status == 0) {
snd_nm256_intr_check(chip);
return IRQ_NONE;
}
if (status == 0)
return snd_nm256_intr_check(chip);
chip->badintrcount = 0;
@ -1036,10 +1082,8 @@ snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy)
status = snd_nm256_readl(chip, NM_INT_REG);
/* Not ours. */
if (status == 0) {
snd_nm256_intr_check(chip);
return IRQ_NONE;
}
if (status == 0)
return snd_nm256_intr_check(chip);
chip->badintrcount = 0;
@ -1192,7 +1236,7 @@ snd_nm256_mixer(nm256_t *chip)
AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD,
AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL,
AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL,
AC97_EXTENDED_ID,
/*AC97_EXTENDED_ID,*/
AC97_VENDOR_ID1, AC97_VENDOR_ID2,
-1
};
@ -1206,6 +1250,7 @@ snd_nm256_mixer(nm256_t *chip)
for (i = 0; mixer_regs[i] >= 0; i++)
set_bit(mixer_regs[i], ac97.reg_accessed);
ac97.private_data = chip;
pbus->no_vra = 1;
err = snd_ac97_mixer(pbus, &ac97, &chip->ac97);
if (err < 0)
return err;
@ -1281,6 +1326,7 @@ static int nm256_suspend(snd_card_t *card, pm_message_t state)
static int nm256_resume(snd_card_t *card)
{
nm256_t *chip = card->pm_private_data;
int i;
/* Perform a full reset on the hardware */
pci_enable_device(chip->pci);
@ -1289,6 +1335,15 @@ static int nm256_resume(snd_card_t *card)
/* restore ac97 */
snd_ac97_resume(chip->ac97);
for (i = 0; i < 2; i++) {
nm256_stream_t *s = &chip->streams[i];
if (s->substream && s->suspended) {
spin_lock_irq(&chip->reg_lock);
snd_nm256_set_format(chip, s, s->substream);
spin_unlock_irq(&chip->reg_lock);
}
}
return 0;
}
#endif /* CONFIG_PM */
@ -1360,6 +1415,7 @@ snd_nm256_create(snd_card_t *card, struct pci_dev *pci,
chip->use_cache = usecache;
spin_lock_init(&chip->reg_lock);
chip->irq = -1;
init_MUTEX(&chip->irq_mutex);
chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize;
chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize;
@ -1470,15 +1526,6 @@ snd_nm256_create(snd_card_t *card, struct pci_dev *pci,
chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr;
}
/* acquire interrupt */
if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ,
card->driver, (void*)chip)) {
err = -EBUSY;
snd_printk("unable to grab IRQ %d\n", pci->irq);
goto __error;
}
chip->irq = pci->irq;
/* Fixed setting. */
chip->mixer_base = NM_MIXER_OFFSET;