diff options
author | Takashi Iwai <tiwai@suse.de> | 2005-08-16 10:54:12 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-08-30 02:46:29 -0400 |
commit | 1204de32d0df87892e56062042e25c775ca0e08c (patch) | |
tree | 4a5ea83086b0b35cbb63b19a902b293302334bc4 /sound | |
parent | 1cfe43d21bc5ff751e95b6a62410e05956c09e38 (diff) |
[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>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/pci/nm256/nm256.c | 93 |
1 files changed, 70 insertions, 23 deletions
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c index 7eb20b8f89f6..2bbeb10ff7c4 100644 --- a/sound/pci/nm256/nm256.c +++ b/sound/pci/nm256/nm256.c | |||
@@ -189,6 +189,7 @@ struct snd_nm256_stream { | |||
189 | nm256_t *chip; | 189 | nm256_t *chip; |
190 | snd_pcm_substream_t *substream; | 190 | snd_pcm_substream_t *substream; |
191 | int running; | 191 | int running; |
192 | int suspended; | ||
192 | 193 | ||
193 | u32 buf; /* offset from chip->buffer */ | 194 | u32 buf; /* offset from chip->buffer */ |
194 | int bufsize; /* buffer size in bytes */ | 195 | int bufsize; /* buffer size in bytes */ |
@@ -231,8 +232,10 @@ struct snd_nm256 { | |||
231 | int mixer_status_mask; /* bit mask to test the mixer status */ | 232 | int mixer_status_mask; /* bit mask to test the mixer status */ |
232 | 233 | ||
233 | int irq; | 234 | int irq; |
235 | int irq_acks; | ||
234 | irqreturn_t (*interrupt)(int, void *, struct pt_regs *); | 236 | irqreturn_t (*interrupt)(int, void *, struct pt_regs *); |
235 | int badintrcount; /* counter to check bogus interrupts */ | 237 | int badintrcount; /* counter to check bogus interrupts */ |
238 | struct semaphore irq_mutex; | ||
236 | 239 | ||
237 | nm256_stream_t streams[2]; | 240 | nm256_stream_t streams[2]; |
238 | 241 | ||
@@ -464,6 +467,37 @@ snd_nm256_set_format(nm256_t *chip, nm256_stream_t *s, snd_pcm_substream_t *subs | |||
464 | } | 467 | } |
465 | } | 468 | } |
466 | 469 | ||
470 | /* acquire interrupt */ | ||
471 | static int snd_nm256_acquire_irq(nm256_t *chip) | ||
472 | { | ||
473 | down(&chip->irq_mutex); | ||
474 | if (chip->irq < 0) { | ||
475 | if (request_irq(chip->pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, | ||
476 | chip->card->driver, (void*)chip)) { | ||
477 | snd_printk("unable to grab IRQ %d\n", chip->pci->irq); | ||
478 | up(&chip->irq_mutex); | ||
479 | return -EBUSY; | ||
480 | } | ||
481 | chip->irq = chip->pci->irq; | ||
482 | } | ||
483 | chip->irq_acks++; | ||
484 | up(&chip->irq_mutex); | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | /* release interrupt */ | ||
489 | static void snd_nm256_release_irq(nm256_t *chip) | ||
490 | { | ||
491 | down(&chip->irq_mutex); | ||
492 | if (chip->irq_acks > 0) | ||
493 | chip->irq_acks--; | ||
494 | if (chip->irq_acks == 0 && chip->irq >= 0) { | ||
495 | free_irq(chip->irq, (void*)chip); | ||
496 | chip->irq = -1; | ||
497 | } | ||
498 | up(&chip->irq_mutex); | ||
499 | } | ||
500 | |||
467 | /* | 501 | /* |
468 | * start / stop | 502 | * start / stop |
469 | */ | 503 | */ |
@@ -538,15 +572,19 @@ snd_nm256_playback_trigger(snd_pcm_substream_t *substream, int cmd) | |||
538 | 572 | ||
539 | spin_lock(&chip->reg_lock); | 573 | spin_lock(&chip->reg_lock); |
540 | switch (cmd) { | 574 | switch (cmd) { |
541 | case SNDRV_PCM_TRIGGER_START: | ||
542 | case SNDRV_PCM_TRIGGER_RESUME: | 575 | case SNDRV_PCM_TRIGGER_RESUME: |
576 | s->suspended = 0; | ||
577 | /* fallthru */ | ||
578 | case SNDRV_PCM_TRIGGER_START: | ||
543 | if (! s->running) { | 579 | if (! s->running) { |
544 | snd_nm256_playback_start(chip, s, substream); | 580 | snd_nm256_playback_start(chip, s, substream); |
545 | s->running = 1; | 581 | s->running = 1; |
546 | } | 582 | } |
547 | break; | 583 | break; |
548 | case SNDRV_PCM_TRIGGER_STOP: | ||
549 | case SNDRV_PCM_TRIGGER_SUSPEND: | 584 | case SNDRV_PCM_TRIGGER_SUSPEND: |
585 | s->suspended = 1; | ||
586 | /* fallthru */ | ||
587 | case SNDRV_PCM_TRIGGER_STOP: | ||
550 | if (s->running) { | 588 | if (s->running) { |
551 | snd_nm256_playback_stop(chip); | 589 | snd_nm256_playback_stop(chip); |
552 | s->running = 0; | 590 | s->running = 0; |
@@ -818,6 +856,8 @@ snd_nm256_playback_open(snd_pcm_substream_t *substream) | |||
818 | { | 856 | { |
819 | nm256_t *chip = snd_pcm_substream_chip(substream); | 857 | nm256_t *chip = snd_pcm_substream_chip(substream); |
820 | 858 | ||
859 | if (snd_nm256_acquire_irq(chip) < 0) | ||
860 | return -EBUSY; | ||
821 | snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], | 861 | snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_PLAYBACK], |
822 | substream, &snd_nm256_playback); | 862 | substream, &snd_nm256_playback); |
823 | return 0; | 863 | return 0; |
@@ -828,6 +868,8 @@ snd_nm256_capture_open(snd_pcm_substream_t *substream) | |||
828 | { | 868 | { |
829 | nm256_t *chip = snd_pcm_substream_chip(substream); | 869 | nm256_t *chip = snd_pcm_substream_chip(substream); |
830 | 870 | ||
871 | if (snd_nm256_acquire_irq(chip) < 0) | ||
872 | return -EBUSY; | ||
831 | snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], | 873 | snd_nm256_setup_stream(chip, &chip->streams[SNDRV_PCM_STREAM_CAPTURE], |
832 | substream, &snd_nm256_capture); | 874 | substream, &snd_nm256_capture); |
833 | return 0; | 875 | return 0; |
@@ -839,6 +881,9 @@ snd_nm256_capture_open(snd_pcm_substream_t *substream) | |||
839 | static int | 881 | static int |
840 | snd_nm256_playback_close(snd_pcm_substream_t *substream) | 882 | snd_nm256_playback_close(snd_pcm_substream_t *substream) |
841 | { | 883 | { |
884 | nm256_t *chip = snd_pcm_substream_chip(substream); | ||
885 | |||
886 | snd_nm256_release_irq(chip); | ||
842 | return 0; | 887 | return 0; |
843 | } | 888 | } |
844 | 889 | ||
@@ -846,6 +891,9 @@ snd_nm256_playback_close(snd_pcm_substream_t *substream) | |||
846 | static int | 891 | static int |
847 | snd_nm256_capture_close(snd_pcm_substream_t *substream) | 892 | snd_nm256_capture_close(snd_pcm_substream_t *substream) |
848 | { | 893 | { |
894 | nm256_t *chip = snd_pcm_substream_chip(substream); | ||
895 | |||
896 | snd_nm256_release_irq(chip); | ||
849 | return 0; | 897 | return 0; |
850 | } | 898 | } |
851 | 899 | ||
@@ -915,18 +963,16 @@ snd_nm256_pcm(nm256_t *chip, int device) | |||
915 | static void | 963 | static void |
916 | snd_nm256_init_chip(nm256_t *chip) | 964 | snd_nm256_init_chip(nm256_t *chip) |
917 | { | 965 | { |
918 | spin_lock_irq(&chip->reg_lock); | ||
919 | /* Reset everything. */ | 966 | /* Reset everything. */ |
920 | snd_nm256_writeb(chip, 0x0, 0x11); | 967 | snd_nm256_writeb(chip, 0x0, 0x11); |
921 | snd_nm256_writew(chip, 0x214, 0); | 968 | snd_nm256_writew(chip, 0x214, 0); |
922 | /* stop sounds.. */ | 969 | /* stop sounds.. */ |
923 | //snd_nm256_playback_stop(chip); | 970 | //snd_nm256_playback_stop(chip); |
924 | //snd_nm256_capture_stop(chip); | 971 | //snd_nm256_capture_stop(chip); |
925 | spin_unlock_irq(&chip->reg_lock); | ||
926 | } | 972 | } |
927 | 973 | ||
928 | 974 | ||
929 | static inline void | 975 | static irqreturn_t |
930 | snd_nm256_intr_check(nm256_t *chip) | 976 | snd_nm256_intr_check(nm256_t *chip) |
931 | { | 977 | { |
932 | if (chip->badintrcount++ > 1000) { | 978 | if (chip->badintrcount++ > 1000) { |
@@ -947,7 +993,9 @@ snd_nm256_intr_check(nm256_t *chip) | |||
947 | if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) | 993 | if (chip->streams[SNDRV_PCM_STREAM_CAPTURE].running) |
948 | snd_nm256_capture_stop(chip); | 994 | snd_nm256_capture_stop(chip); |
949 | chip->badintrcount = 0; | 995 | chip->badintrcount = 0; |
996 | return IRQ_HANDLED; | ||
950 | } | 997 | } |
998 | return IRQ_NONE; | ||
951 | } | 999 | } |
952 | 1000 | ||
953 | /* | 1001 | /* |
@@ -969,10 +1017,8 @@ snd_nm256_interrupt(int irq, void *dev_id, struct pt_regs *dummy) | |||
969 | status = snd_nm256_readw(chip, NM_INT_REG); | 1017 | status = snd_nm256_readw(chip, NM_INT_REG); |
970 | 1018 | ||
971 | /* Not ours. */ | 1019 | /* Not ours. */ |
972 | if (status == 0) { | 1020 | if (status == 0) |
973 | snd_nm256_intr_check(chip); | 1021 | return snd_nm256_intr_check(chip); |
974 | return IRQ_NONE; | ||
975 | } | ||
976 | 1022 | ||
977 | chip->badintrcount = 0; | 1023 | chip->badintrcount = 0; |
978 | 1024 | ||
@@ -1036,10 +1082,8 @@ snd_nm256_interrupt_zx(int irq, void *dev_id, struct pt_regs *dummy) | |||
1036 | status = snd_nm256_readl(chip, NM_INT_REG); | 1082 | status = snd_nm256_readl(chip, NM_INT_REG); |
1037 | 1083 | ||
1038 | /* Not ours. */ | 1084 | /* Not ours. */ |
1039 | if (status == 0) { | 1085 | if (status == 0) |
1040 | snd_nm256_intr_check(chip); | 1086 | return snd_nm256_intr_check(chip); |
1041 | return IRQ_NONE; | ||
1042 | } | ||
1043 | 1087 | ||
1044 | chip->badintrcount = 0; | 1088 | chip->badintrcount = 0; |
1045 | 1089 | ||
@@ -1192,7 +1236,7 @@ snd_nm256_mixer(nm256_t *chip) | |||
1192 | AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, | 1236 | AC97_PC_BEEP, AC97_PHONE, AC97_MIC, AC97_LINE, AC97_CD, |
1193 | AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, | 1237 | AC97_VIDEO, AC97_AUX, AC97_PCM, AC97_REC_SEL, |
1194 | AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, | 1238 | AC97_REC_GAIN, AC97_GENERAL_PURPOSE, AC97_3D_CONTROL, |
1195 | AC97_EXTENDED_ID, | 1239 | /*AC97_EXTENDED_ID,*/ |
1196 | AC97_VENDOR_ID1, AC97_VENDOR_ID2, | 1240 | AC97_VENDOR_ID1, AC97_VENDOR_ID2, |
1197 | -1 | 1241 | -1 |
1198 | }; | 1242 | }; |
@@ -1206,6 +1250,7 @@ snd_nm256_mixer(nm256_t *chip) | |||
1206 | for (i = 0; mixer_regs[i] >= 0; i++) | 1250 | for (i = 0; mixer_regs[i] >= 0; i++) |
1207 | set_bit(mixer_regs[i], ac97.reg_accessed); | 1251 | set_bit(mixer_regs[i], ac97.reg_accessed); |
1208 | ac97.private_data = chip; | 1252 | ac97.private_data = chip; |
1253 | pbus->no_vra = 1; | ||
1209 | err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); | 1254 | err = snd_ac97_mixer(pbus, &ac97, &chip->ac97); |
1210 | if (err < 0) | 1255 | if (err < 0) |
1211 | return err; | 1256 | return err; |
@@ -1281,6 +1326,7 @@ static int nm256_suspend(snd_card_t *card, pm_message_t state) | |||
1281 | static int nm256_resume(snd_card_t *card) | 1326 | static int nm256_resume(snd_card_t *card) |
1282 | { | 1327 | { |
1283 | nm256_t *chip = card->pm_private_data; | 1328 | nm256_t *chip = card->pm_private_data; |
1329 | int i; | ||
1284 | 1330 | ||
1285 | /* Perform a full reset on the hardware */ | 1331 | /* Perform a full reset on the hardware */ |
1286 | pci_enable_device(chip->pci); | 1332 | pci_enable_device(chip->pci); |
@@ -1289,6 +1335,15 @@ static int nm256_resume(snd_card_t *card) | |||
1289 | /* restore ac97 */ | 1335 | /* restore ac97 */ |
1290 | snd_ac97_resume(chip->ac97); | 1336 | snd_ac97_resume(chip->ac97); |
1291 | 1337 | ||
1338 | for (i = 0; i < 2; i++) { | ||
1339 | nm256_stream_t *s = &chip->streams[i]; | ||
1340 | if (s->substream && s->suspended) { | ||
1341 | spin_lock_irq(&chip->reg_lock); | ||
1342 | snd_nm256_set_format(chip, s, s->substream); | ||
1343 | spin_unlock_irq(&chip->reg_lock); | ||
1344 | } | ||
1345 | } | ||
1346 | |||
1292 | return 0; | 1347 | return 0; |
1293 | } | 1348 | } |
1294 | #endif /* CONFIG_PM */ | 1349 | #endif /* CONFIG_PM */ |
@@ -1360,6 +1415,7 @@ snd_nm256_create(snd_card_t *card, struct pci_dev *pci, | |||
1360 | chip->use_cache = usecache; | 1415 | chip->use_cache = usecache; |
1361 | spin_lock_init(&chip->reg_lock); | 1416 | spin_lock_init(&chip->reg_lock); |
1362 | chip->irq = -1; | 1417 | chip->irq = -1; |
1418 | init_MUTEX(&chip->irq_mutex); | ||
1363 | 1419 | ||
1364 | chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; | 1420 | chip->streams[SNDRV_PCM_STREAM_PLAYBACK].bufsize = play_bufsize; |
1365 | chip->streams[SNDRV_PCM_STREAM_CAPTURE].bufsize = capt_bufsize; | 1421 | 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 | chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; | 1526 | chip->coeff_buf[SNDRV_PCM_STREAM_CAPTURE] = addr; |
1471 | } | 1527 | } |
1472 | 1528 | ||
1473 | /* acquire interrupt */ | ||
1474 | if (request_irq(pci->irq, chip->interrupt, SA_INTERRUPT|SA_SHIRQ, | ||
1475 | card->driver, (void*)chip)) { | ||
1476 | err = -EBUSY; | ||
1477 | snd_printk("unable to grab IRQ %d\n", pci->irq); | ||
1478 | goto __error; | ||
1479 | } | ||
1480 | chip->irq = pci->irq; | ||
1481 | |||
1482 | /* Fixed setting. */ | 1529 | /* Fixed setting. */ |
1483 | chip->mixer_base = NM_MIXER_OFFSET; | 1530 | chip->mixer_base = NM_MIXER_OFFSET; |
1484 | 1531 | ||