Commit 1204de32 authored by Takashi Iwai's avatar Takashi Iwai Committed by Jaroslav Kysela

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