diff options
author | Andreas Mohr <andi@lisas.de> | 2008-06-23 05:50:47 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@perex.cz> | 2008-06-26 03:01:47 -0400 |
commit | 627d3e7abca30d6e86787c98dd7cbac0233bc5a9 (patch) | |
tree | 9837968886cda7893c827aaa9edfe3053d95139f | |
parent | 981bcead3f2279a1ec6fb5f2c57aff79ed61a700 (diff) |
ALSA: PCI168 snd-azt3328: some more fixups
- fix problem with codec register 0x6a being write-only
by adding a software shadow register
(caused annoying noise after module loading due to _toggling_
between gameport and audio bits instead of configuring them properly)
- rename several "Wave" mixer controls to "PCM", since this is
what Wine and several other apps are looking for (IOW, _requiring_)
and this is what AC97 specs use as naming, too,
thus I'd guess it's what these controls are
- cleanup, small optimizations
Signed-off-by: Andreas Mohr <andi@lisas.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
-rw-r--r-- | sound/pci/azt3328.c | 109 | ||||
-rw-r--r-- | sound/pci/azt3328.h | 16 |
2 files changed, 76 insertions, 49 deletions
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index b832333c3023..22f18f3cfbc9 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c | |||
@@ -289,6 +289,12 @@ struct snd_azf3328 { | |||
289 | struct pci_dev *pci; | 289 | struct pci_dev *pci; |
290 | int irq; | 290 | int irq; |
291 | 291 | ||
292 | /* register 0x6a is write-only, thus need to remember setting. | ||
293 | * If we need to add more registers here, then we might try to fold this | ||
294 | * into some transparent combined shadow register handling with | ||
295 | * CONFIG_PM register storage below, but that's slightly difficult. */ | ||
296 | u16 shadow_reg_codec_6AH; | ||
297 | |||
292 | #ifdef CONFIG_PM | 298 | #ifdef CONFIG_PM |
293 | /* register value containers for power management | 299 | /* register value containers for power management |
294 | * Note: not always full I/O range preserved (just like Win driver!) */ | 300 | * Note: not always full I/O range preserved (just like Win driver!) */ |
@@ -324,21 +330,6 @@ snd_azf3328_io_reg_setb(unsigned reg, u8 mask, int do_set) | |||
324 | return 0; | 330 | return 0; |
325 | } | 331 | } |
326 | 332 | ||
327 | static int | ||
328 | snd_azf3328_io_reg_setw(unsigned reg, u16 mask, int do_set) | ||
329 | { | ||
330 | u16 prev = inw(reg), new; | ||
331 | |||
332 | new = (do_set) ? (prev|mask) : (prev & ~mask); | ||
333 | /* we need to always write the new value no matter whether it differs | ||
334 | * or not, since some register bits don't indicate their setting */ | ||
335 | outw(new, reg); | ||
336 | if (new != prev) | ||
337 | return 1; | ||
338 | |||
339 | return 0; | ||
340 | } | ||
341 | |||
342 | static inline void | 333 | static inline void |
343 | snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value) | 334 | snd_azf3328_codec_outb(const struct snd_azf3328 *chip, unsigned reg, u8 value) |
344 | { | 335 | { |
@@ -662,7 +653,7 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, | |||
662 | "pre 3D", "post 3D" | 653 | "pre 3D", "post 3D" |
663 | }; | 654 | }; |
664 | struct azf3328_mixer_reg reg; | 655 | struct azf3328_mixer_reg reg; |
665 | const char *p = NULL; | 656 | const char * const *p = NULL; |
666 | 657 | ||
667 | snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); | 658 | snd_azf3328_mixer_reg_decode(®, kcontrol->private_value); |
668 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; | 659 | uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; |
@@ -673,20 +664,20 @@ snd_azf3328_info_mixer_enum(struct snd_kcontrol *kcontrol, | |||
673 | if (reg.reg == IDX_MIXER_ADVCTL2) { | 664 | if (reg.reg == IDX_MIXER_ADVCTL2) { |
674 | switch(reg.lchan_shift) { | 665 | switch(reg.lchan_shift) { |
675 | case 8: /* modem out sel */ | 666 | case 8: /* modem out sel */ |
676 | p = texts1[uinfo->value.enumerated.item]; | 667 | p = texts1; |
677 | break; | 668 | break; |
678 | case 9: /* mono sel source */ | 669 | case 9: /* mono sel source */ |
679 | p = texts2[uinfo->value.enumerated.item]; | 670 | p = texts2; |
680 | break; | 671 | break; |
681 | case 15: /* PCM Out Path */ | 672 | case 15: /* PCM Out Path */ |
682 | p = texts4[uinfo->value.enumerated.item]; | 673 | p = texts4; |
683 | break; | 674 | break; |
684 | } | 675 | } |
685 | } else | 676 | } else |
686 | if (reg.reg == IDX_MIXER_REC_SELECT) | 677 | if (reg.reg == IDX_MIXER_REC_SELECT) |
687 | p = texts3[uinfo->value.enumerated.item]; | 678 | p = texts3; |
688 | 679 | ||
689 | strcpy(uinfo->value.enumerated.name, p); | 680 | strcpy(uinfo->value.enumerated.name, p[uinfo->value.enumerated.item]); |
690 | return 0; | 681 | return 0; |
691 | } | 682 | } |
692 | 683 | ||
@@ -745,9 +736,11 @@ snd_azf3328_put_mixer_enum(struct snd_kcontrol *kcontrol, | |||
745 | static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { | 736 | static struct snd_kcontrol_new snd_azf3328_mixer_controls[] __devinitdata = { |
746 | AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1), | 737 | AZF3328_MIXER_SWITCH("Master Playback Switch", IDX_MIXER_PLAY_MASTER, 15, 1), |
747 | AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1), | 738 | AZF3328_MIXER_VOL_STEREO("Master Playback Volume", IDX_MIXER_PLAY_MASTER, 0x1f, 1), |
748 | AZF3328_MIXER_SWITCH("Wave Playback Switch", IDX_MIXER_WAVEOUT, 15, 1), | 739 | AZF3328_MIXER_SWITCH("PCM Playback Switch", IDX_MIXER_WAVEOUT, 15, 1), |
749 | AZF3328_MIXER_VOL_STEREO("Wave Playback Volume", IDX_MIXER_WAVEOUT, 0x1f, 1), | 740 | AZF3328_MIXER_VOL_STEREO("PCM Playback Volume", |
750 | AZF3328_MIXER_SWITCH("Wave 3D Bypass Playback Switch", IDX_MIXER_ADVCTL2, 7, 1), | 741 | IDX_MIXER_WAVEOUT, 0x1f, 1), |
742 | AZF3328_MIXER_SWITCH("PCM 3D Bypass Playback Switch", | ||
743 | IDX_MIXER_ADVCTL2, 7, 1), | ||
751 | AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1), | 744 | AZF3328_MIXER_SWITCH("FM Playback Switch", IDX_MIXER_FMSYNTH, 15, 1), |
752 | AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1), | 745 | AZF3328_MIXER_VOL_STEREO("FM Playback Volume", IDX_MIXER_FMSYNTH, 0x1f, 1), |
753 | AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1), | 746 | AZF3328_MIXER_SWITCH("CD Playback Switch", IDX_MIXER_CDAUDIO, 15, 1), |
@@ -874,7 +867,7 @@ snd_azf3328_hw_free(struct snd_pcm_substream *substream) | |||
874 | static void | 867 | static void |
875 | snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, | 868 | snd_azf3328_codec_setfmt(struct snd_azf3328 *chip, |
876 | unsigned reg, | 869 | unsigned reg, |
877 | unsigned int bitrate, | 870 | enum azf_freq_t bitrate, |
878 | unsigned int format_width, | 871 | unsigned int format_width, |
879 | unsigned int channels | 872 | unsigned int channels |
880 | ) | 873 | ) |
@@ -958,15 +951,29 @@ snd_azf3328_codec_setfmt_lowpower(struct snd_azf3328 *chip, | |||
958 | snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1); | 951 | snd_azf3328_codec_setfmt(chip, reg, AZF_FREQ_4000, 8, 1); |
959 | } | 952 | } |
960 | 953 | ||
954 | static void | ||
955 | snd_azf3328_codec_reg_6AH_update(struct snd_azf3328 *chip, | ||
956 | unsigned bitmask, | ||
957 | int enable | ||
958 | ) | ||
959 | { | ||
960 | if (enable) | ||
961 | chip->shadow_reg_codec_6AH &= ~bitmask; | ||
962 | else | ||
963 | chip->shadow_reg_codec_6AH |= bitmask; | ||
964 | snd_azf3328_dbgplay("6AH_update mask 0x%04x enable %d: val 0x%04x\n", | ||
965 | bitmask, enable, chip->shadow_reg_codec_6AH); | ||
966 | snd_azf3328_codec_outw(chip, IDX_IO_6AH, chip->shadow_reg_codec_6AH); | ||
967 | } | ||
968 | |||
961 | static inline void | 969 | static inline void |
962 | snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable) | 970 | snd_azf3328_codec_enable(struct snd_azf3328 *chip, int enable) |
963 | { | 971 | { |
972 | snd_azf3328_dbgplay("codec_enable %d\n", enable); | ||
964 | /* no idea what exactly is being done here, but I strongly assume it's | 973 | /* no idea what exactly is being done here, but I strongly assume it's |
965 | * PM related */ | 974 | * PM related */ |
966 | snd_azf3328_io_reg_setw( | 975 | snd_azf3328_codec_reg_6AH_update( |
967 | chip->codec_io+IDX_IO_6AH, | 976 | chip, IO_6A_PAUSE_PLAYBACK_BIT8, enable |
968 | IO_6A_PAUSE_PLAYBACK_BIT8, | ||
969 | !enable | ||
970 | ); | 977 | ); |
971 | } | 978 | } |
972 | 979 | ||
@@ -1404,10 +1411,8 @@ snd_azf3328_gameport_legacy_address_enable(struct snd_azf3328 *chip, int enable) | |||
1404 | static inline void | 1411 | static inline void |
1405 | snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable) | 1412 | snd_azf3328_gameport_axis_circuit_enable(struct snd_azf3328 *chip, int enable) |
1406 | { | 1413 | { |
1407 | snd_azf3328_io_reg_setw( | 1414 | snd_azf3328_codec_reg_6AH_update( |
1408 | chip->codec_io+IDX_IO_6AH, | 1415 | chip, IO_6A_SOMETHING2_GAMEPORT, enable |
1409 | IO_6A_SOMETHING2_GAMEPORT, | ||
1410 | !enable | ||
1411 | ); | 1416 | ); |
1412 | } | 1417 | } |
1413 | 1418 | ||
@@ -1525,8 +1530,6 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) | |||
1525 | { | 1530 | { |
1526 | struct gameport *gp; | 1531 | struct gameport *gp; |
1527 | 1532 | ||
1528 | int io_port = chip->game_io; | ||
1529 | |||
1530 | chip->gameport = gp = gameport_allocate_port(); | 1533 | chip->gameport = gp = gameport_allocate_port(); |
1531 | if (!gp) { | 1534 | if (!gp) { |
1532 | printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n"); | 1535 | printk(KERN_ERR "azt3328: cannot alloc memory for gameport\n"); |
@@ -1536,7 +1539,7 @@ snd_azf3328_gameport(struct snd_azf3328 *chip, int dev) | |||
1536 | gameport_set_name(gp, "AZF3328 Gameport"); | 1539 | gameport_set_name(gp, "AZF3328 Gameport"); |
1537 | gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); | 1540 | gameport_set_phys(gp, "pci%s/gameport0", pci_name(chip->pci)); |
1538 | gameport_set_dev_parent(gp, &chip->pci->dev); | 1541 | gameport_set_dev_parent(gp, &chip->pci->dev); |
1539 | gp->io = io_port; | 1542 | gp->io = chip->game_io; |
1540 | gameport_set_port_data(gp, chip); | 1543 | gameport_set_port_data(gp, chip); |
1541 | 1544 | ||
1542 | gp->open = snd_azf3328_gameport_open; | 1545 | gp->open = snd_azf3328_gameport_open; |
@@ -1577,6 +1580,15 @@ snd_azf3328_gameport_interrupt(struct snd_azf3328 *chip) | |||
1577 | 1580 | ||
1578 | /******************************************************************/ | 1581 | /******************************************************************/ |
1579 | 1582 | ||
1583 | static inline void | ||
1584 | snd_azf3328_irq_log_unknown_type(u8 which) | ||
1585 | { | ||
1586 | snd_azf3328_dbgplay( | ||
1587 | "azt3328: unknown IRQ type (%x) occurred, please report!\n", | ||
1588 | which | ||
1589 | ); | ||
1590 | } | ||
1591 | |||
1580 | static irqreturn_t | 1592 | static irqreturn_t |
1581 | snd_azf3328_interrupt(int irq, void *dev_id) | 1593 | snd_azf3328_interrupt(int irq, void *dev_id) |
1582 | { | 1594 | { |
@@ -1594,11 +1606,14 @@ snd_azf3328_interrupt(int irq, void *dev_id) | |||
1594 | )) | 1606 | )) |
1595 | return IRQ_NONE; /* must be interrupt for another device */ | 1607 | return IRQ_NONE; /* must be interrupt for another device */ |
1596 | 1608 | ||
1597 | snd_azf3328_dbgplay("Interrupt %ld!\nIDX_IO_PLAY_FLAGS %04x, IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n", | 1609 | snd_azf3328_dbgplay( |
1598 | irq_count++ /* debug-only */, | 1610 | "irq_count %ld! IDX_IO_PLAY_FLAGS %04x, " |
1599 | snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS), | 1611 | "IDX_IO_PLAY_IRQTYPE %04x, IDX_IO_IRQSTATUS %04x\n", |
1600 | snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE), | 1612 | irq_count++ /* debug-only */, |
1601 | status); | 1613 | snd_azf3328_codec_inw(chip, IDX_IO_PLAY_FLAGS), |
1614 | snd_azf3328_codec_inw(chip, IDX_IO_PLAY_IRQTYPE), | ||
1615 | status | ||
1616 | ); | ||
1602 | 1617 | ||
1603 | if (status & IRQ_TIMER) { | 1618 | if (status & IRQ_TIMER) { |
1604 | /* snd_azf3328_dbgplay("timer %ld\n", | 1619 | /* snd_azf3328_dbgplay("timer %ld\n", |
@@ -1631,9 +1646,9 @@ snd_azf3328_interrupt(int irq, void *dev_id) | |||
1631 | ) | 1646 | ) |
1632 | ); | 1647 | ); |
1633 | } else | 1648 | } else |
1634 | snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); | 1649 | printk(KERN_WARNING "azt3328: irq handler problem!\n"); |
1635 | if (which & IRQ_PLAY_SOMETHING) | 1650 | if (which & IRQ_PLAY_SOMETHING) |
1636 | snd_azf3328_dbgplay("azt3328: unknown play IRQ type occurred, please report!\n"); | 1651 | snd_azf3328_irq_log_unknown_type(which); |
1637 | } | 1652 | } |
1638 | if (status & IRQ_RECORDING) { | 1653 | if (status & IRQ_RECORDING) { |
1639 | spin_lock(&chip->reg_lock); | 1654 | spin_lock(&chip->reg_lock); |
@@ -1653,9 +1668,9 @@ snd_azf3328_interrupt(int irq, void *dev_id) | |||
1653 | ) | 1668 | ) |
1654 | ); | 1669 | ); |
1655 | } else | 1670 | } else |
1656 | snd_azf3328_dbgplay("azt3328: ouch, irq handler problem!\n"); | 1671 | printk(KERN_WARNING "azt3328: irq handler problem!\n"); |
1657 | if (which & IRQ_REC_SOMETHING) | 1672 | if (which & IRQ_REC_SOMETHING) |
1658 | snd_azf3328_dbgplay("azt3328: unknown rec IRQ type occurred, please report!\n"); | 1673 | snd_azf3328_irq_log_unknown_type(which); |
1659 | } | 1674 | } |
1660 | if (status & IRQ_GAMEPORT) | 1675 | if (status & IRQ_GAMEPORT) |
1661 | snd_azf3328_gameport_interrupt(chip); | 1676 | snd_azf3328_gameport_interrupt(chip); |
@@ -2311,6 +2326,10 @@ snd_azf3328_suspend(struct pci_dev *pci, pm_message_t state) | |||
2311 | 2326 | ||
2312 | for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg) | 2327 | for (reg = 0; reg < AZF_IO_SIZE_CODEC_PM / 2; ++reg) |
2313 | chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2); | 2328 | chip->saved_regs_codec[reg] = inw(chip->codec_io + reg * 2); |
2329 | |||
2330 | /* manually store the one currently relevant write-only reg, too */ | ||
2331 | chip->saved_regs_codec[IDX_IO_6AH / 2] = chip->shadow_reg_codec_6AH; | ||
2332 | |||
2314 | for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg) | 2333 | for (reg = 0; reg < AZF_IO_SIZE_GAME_PM / 2; ++reg) |
2315 | chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2); | 2334 | chip->saved_regs_game[reg] = inw(chip->game_io + reg * 2); |
2316 | for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg) | 2335 | for (reg = 0; reg < AZF_IO_SIZE_MPU_PM / 2; ++reg) |
diff --git a/sound/pci/azt3328.h b/sound/pci/azt3328.h index 3448fd626f80..7e3e8942d073 100644 --- a/sound/pci/azt3328.h +++ b/sound/pci/azt3328.h | |||
@@ -1,7 +1,8 @@ | |||
1 | #ifndef __SOUND_AZT3328_H | 1 | #ifndef __SOUND_AZT3328_H |
2 | #define __SOUND_AZT3328_H | 2 | #define __SOUND_AZT3328_H |
3 | 3 | ||
4 | /* "PU" == "power-up value", as tested on PCI168 PCI rev. 10 */ | 4 | /* "PU" == "power-up value", as tested on PCI168 PCI rev. 10 |
5 | * "WRITE_ONLY" == register does not indicate actual bit values */ | ||
5 | 6 | ||
6 | /*** main I/O area port indices ***/ | 7 | /*** main I/O area port indices ***/ |
7 | /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ | 8 | /* (only 0x70 of 0x80 bytes saved/restored by Windows driver) */ |
@@ -76,7 +77,7 @@ | |||
76 | #define SOUNDFORMAT_FLAG_2CHANNELS 0x0020 | 77 | #define SOUNDFORMAT_FLAG_2CHANNELS 0x0020 |
77 | 78 | ||
78 | /* define frequency helpers, for maximum value safety */ | 79 | /* define frequency helpers, for maximum value safety */ |
79 | enum { | 80 | enum azf_freq_t { |
80 | #define AZF_FREQ(rate) AZF_FREQ_##rate = rate | 81 | #define AZF_FREQ(rate) AZF_FREQ_##rate = rate |
81 | AZF_FREQ(4000), | 82 | AZF_FREQ(4000), |
82 | AZF_FREQ(4800), | 83 | AZF_FREQ(4800), |
@@ -150,11 +151,18 @@ enum { | |||
150 | #define IO_68_RANDOM_TOGGLE1 0x0100 /* toggles randomly */ | 151 | #define IO_68_RANDOM_TOGGLE1 0x0100 /* toggles randomly */ |
151 | #define IO_68_RANDOM_TOGGLE2 0x0200 /* toggles randomly */ | 152 | #define IO_68_RANDOM_TOGGLE2 0x0200 /* toggles randomly */ |
152 | /* umm, nope, behaviour of these bits changes depending on what we wrote | 153 | /* umm, nope, behaviour of these bits changes depending on what we wrote |
153 | * to 0x6b!! */ | 154 | * to 0x6b!! |
155 | * And they change upon playback/stop, too: | ||
156 | * Writing a value to 0x68 will display this exact value during playback, | ||
157 | * too but when stopped it can fall back to a rather different | ||
158 | * seemingly random value). Hmm, possibly this is a register which | ||
159 | * has a remote shadow which needs proper device supply which only exists | ||
160 | * in case playback is active? Or is this driver-induced? | ||
161 | */ | ||
154 | 162 | ||
155 | /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); | 163 | /* this WORD can be set to have bits 0x0028 activated (FIXME: correct??); |
156 | * actually inhibits PCM playback!!! maybe power management??: */ | 164 | * actually inhibits PCM playback!!! maybe power management??: */ |
157 | #define IDX_IO_6AH 0x6A | 165 | #define IDX_IO_6AH 0x6A /* WRITE_ONLY! */ |
158 | /* bit 5: enabling this will activate permanent counting of bytes 2/3 | 166 | /* bit 5: enabling this will activate permanent counting of bytes 2/3 |
159 | * at gameport I/O (0xb402/3) (equal values each) and cause | 167 | * at gameport I/O (0xb402/3) (equal values each) and cause |
160 | * gameport legacy I/O at 0x0200 to be _DISABLED_! | 168 | * gameport legacy I/O at 0x0200 to be _DISABLED_! |