diff options
author | Ville Syrjala <syrjala@sci.fi> | 2005-05-12 08:19:32 -0400 |
---|---|---|
committer | Jaroslav Kysela <perex@suse.cz> | 2005-05-29 04:08:23 -0400 |
commit | db68d15da00f64bef2c8c822baab42aff39ae774 (patch) | |
tree | 193afef1ba4936b7265886360008305024429b5a /sound/pci | |
parent | 88491386be67f659e6b2c76c7463c997ce4c4ebb (diff) |
[ALSA] maestro3: Add HW volume button support
Maestro3 driver
Add support for hardware volume buttons.
Signed-off-by: Ville Syrjala <syrjala@sci.fi>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci')
-rw-r--r-- | sound/pci/maestro3.c | 107 |
1 files changed, 103 insertions, 4 deletions
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 293cc10e3cbb..c1c7eeeb199b 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c | |||
@@ -851,7 +851,12 @@ struct snd_m3 { | |||
851 | m3_dma_t *substreams; | 851 | m3_dma_t *substreams; |
852 | 852 | ||
853 | spinlock_t reg_lock; | 853 | spinlock_t reg_lock; |
854 | spinlock_t ac97_lock; | ||
854 | 855 | ||
856 | snd_kcontrol_t *master_switch; | ||
857 | snd_kcontrol_t *master_volume; | ||
858 | struct tasklet_struct hwvol_tq; | ||
859 | |||
855 | #ifdef CONFIG_PM | 860 | #ifdef CONFIG_PM |
856 | u16 *suspend_mem; | 861 | u16 *suspend_mem; |
857 | #endif | 862 | #endif |
@@ -1565,6 +1570,68 @@ static void snd_m3_update_ptr(m3_t *chip, m3_dma_t *s) | |||
1565 | } | 1570 | } |
1566 | } | 1571 | } |
1567 | 1572 | ||
1573 | static void snd_m3_update_hw_volume(unsigned long private_data) | ||
1574 | { | ||
1575 | m3_t *chip = (m3_t *) private_data; | ||
1576 | int x, val; | ||
1577 | unsigned long flags; | ||
1578 | |||
1579 | /* Figure out which volume control button was pushed, | ||
1580 | based on differences from the default register | ||
1581 | values. */ | ||
1582 | x = inb(chip->iobase + SHADOW_MIX_REG_VOICE) & 0xee; | ||
1583 | |||
1584 | /* Reset the volume control registers. */ | ||
1585 | outb(0x88, chip->iobase + SHADOW_MIX_REG_VOICE); | ||
1586 | outb(0x88, chip->iobase + HW_VOL_COUNTER_VOICE); | ||
1587 | outb(0x88, chip->iobase + SHADOW_MIX_REG_MASTER); | ||
1588 | outb(0x88, chip->iobase + HW_VOL_COUNTER_MASTER); | ||
1589 | |||
1590 | if (!chip->master_switch || !chip->master_volume) | ||
1591 | return; | ||
1592 | |||
1593 | /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */ | ||
1594 | spin_lock_irqsave(&chip->ac97_lock, flags); | ||
1595 | |||
1596 | val = chip->ac97->regs[AC97_MASTER_VOL]; | ||
1597 | switch (x) { | ||
1598 | case 0x88: | ||
1599 | /* mute */ | ||
1600 | val ^= 0x8000; | ||
1601 | chip->ac97->regs[AC97_MASTER_VOL] = val; | ||
1602 | outw(val, chip->iobase + CODEC_DATA); | ||
1603 | outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); | ||
1604 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
1605 | &chip->master_switch->id); | ||
1606 | break; | ||
1607 | case 0xaa: | ||
1608 | /* volume up */ | ||
1609 | if ((val & 0x7f) > 0) | ||
1610 | val--; | ||
1611 | if ((val & 0x7f00) > 0) | ||
1612 | val -= 0x0100; | ||
1613 | chip->ac97->regs[AC97_MASTER_VOL] = val; | ||
1614 | outw(val, chip->iobase + CODEC_DATA); | ||
1615 | outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); | ||
1616 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
1617 | &chip->master_volume->id); | ||
1618 | break; | ||
1619 | case 0x66: | ||
1620 | /* volume down */ | ||
1621 | if ((val & 0x7f) < 0x1f) | ||
1622 | val++; | ||
1623 | if ((val & 0x7f00) < 0x1f00) | ||
1624 | val += 0x0100; | ||
1625 | chip->ac97->regs[AC97_MASTER_VOL] = val; | ||
1626 | outw(val, chip->iobase + CODEC_DATA); | ||
1627 | outb(AC97_MASTER_VOL, chip->iobase + CODEC_COMMAND); | ||
1628 | snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE, | ||
1629 | &chip->master_volume->id); | ||
1630 | break; | ||
1631 | } | ||
1632 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | ||
1633 | } | ||
1634 | |||
1568 | static irqreturn_t | 1635 | static irqreturn_t |
1569 | snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 1636 | snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) |
1570 | { | 1637 | { |
@@ -1576,7 +1643,10 @@ snd_m3_interrupt(int irq, void *dev_id, struct pt_regs *regs) | |||
1576 | 1643 | ||
1577 | if (status == 0xff) | 1644 | if (status == 0xff) |
1578 | return IRQ_NONE; | 1645 | return IRQ_NONE; |
1579 | 1646 | ||
1647 | if (status & HV_INT_PENDING) | ||
1648 | tasklet_hi_schedule(&chip->hwvol_tq); | ||
1649 | |||
1580 | /* | 1650 | /* |
1581 | * ack an assp int if its running | 1651 | * ack an assp int if its running |
1582 | * and has an int pending | 1652 | * and has an int pending |
@@ -1842,24 +1912,32 @@ static unsigned short | |||
1842 | snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) | 1912 | snd_m3_ac97_read(ac97_t *ac97, unsigned short reg) |
1843 | { | 1913 | { |
1844 | m3_t *chip = ac97->private_data; | 1914 | m3_t *chip = ac97->private_data; |
1915 | unsigned long flags; | ||
1916 | unsigned short data; | ||
1845 | 1917 | ||
1846 | if (snd_m3_ac97_wait(chip)) | 1918 | if (snd_m3_ac97_wait(chip)) |
1847 | return 0xffff; | 1919 | return 0xffff; |
1920 | spin_lock_irqsave(&chip->ac97_lock, flags); | ||
1848 | snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); | 1921 | snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); |
1849 | if (snd_m3_ac97_wait(chip)) | 1922 | if (snd_m3_ac97_wait(chip)) |
1850 | return 0xffff; | 1923 | return 0xffff; |
1851 | return snd_m3_inw(chip, CODEC_DATA); | 1924 | data = snd_m3_inw(chip, CODEC_DATA); |
1925 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | ||
1926 | return data; | ||
1852 | } | 1927 | } |
1853 | 1928 | ||
1854 | static void | 1929 | static void |
1855 | snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) | 1930 | snd_m3_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val) |
1856 | { | 1931 | { |
1857 | m3_t *chip = ac97->private_data; | 1932 | m3_t *chip = ac97->private_data; |
1933 | unsigned long flags; | ||
1858 | 1934 | ||
1859 | if (snd_m3_ac97_wait(chip)) | 1935 | if (snd_m3_ac97_wait(chip)) |
1860 | return; | 1936 | return; |
1937 | spin_lock_irqsave(&chip->ac97_lock, flags); | ||
1861 | snd_m3_outw(chip, val, CODEC_DATA); | 1938 | snd_m3_outw(chip, val, CODEC_DATA); |
1862 | snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); | 1939 | snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); |
1940 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | ||
1863 | } | 1941 | } |
1864 | 1942 | ||
1865 | 1943 | ||
@@ -1968,6 +2046,7 @@ static int __devinit snd_m3_mixer(m3_t *chip) | |||
1968 | { | 2046 | { |
1969 | ac97_bus_t *pbus; | 2047 | ac97_bus_t *pbus; |
1970 | ac97_template_t ac97; | 2048 | ac97_template_t ac97; |
2049 | snd_ctl_elem_id_t id; | ||
1971 | int err; | 2050 | int err; |
1972 | static ac97_bus_ops_t ops = { | 2051 | static ac97_bus_ops_t ops = { |
1973 | .write = snd_m3_ac97_write, | 2052 | .write = snd_m3_ac97_write, |
@@ -1988,6 +2067,15 @@ static int __devinit snd_m3_mixer(m3_t *chip) | |||
1988 | schedule_timeout(HZ / 10); | 2067 | schedule_timeout(HZ / 10); |
1989 | snd_ac97_write(chip->ac97, AC97_PCM, 0); | 2068 | snd_ac97_write(chip->ac97, AC97_PCM, 0); |
1990 | 2069 | ||
2070 | memset(&id, 0, sizeof(id)); | ||
2071 | id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
2072 | strcpy(id.name, "Master Playback Switch"); | ||
2073 | chip->master_switch = snd_ctl_find_id(chip->card, &id); | ||
2074 | memset(&id, 0, sizeof(id)); | ||
2075 | id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | ||
2076 | strcpy(id.name, "Master Playback Volume"); | ||
2077 | chip->master_volume = snd_ctl_find_id(chip->card, &id); | ||
2078 | |||
1991 | return 0; | 2079 | return 0; |
1992 | } | 2080 | } |
1993 | 2081 | ||
@@ -2293,6 +2381,7 @@ static int | |||
2293 | snd_m3_chip_init(m3_t *chip) | 2381 | snd_m3_chip_init(m3_t *chip) |
2294 | { | 2382 | { |
2295 | struct pci_dev *pcidev = chip->pci; | 2383 | struct pci_dev *pcidev = chip->pci; |
2384 | unsigned long io = chip->iobase; | ||
2296 | u32 n; | 2385 | u32 n; |
2297 | u16 w; | 2386 | u16 w; |
2298 | u8 t; /* makes as much sense as 'n', no? */ | 2387 | u8 t; /* makes as much sense as 'n', no? */ |
@@ -2304,7 +2393,8 @@ snd_m3_chip_init(m3_t *chip) | |||
2304 | pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w); | 2393 | pci_write_config_word(pcidev, PCI_LEGACY_AUDIO_CTRL, w); |
2305 | 2394 | ||
2306 | pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); | 2395 | pci_read_config_dword(pcidev, PCI_ALLEGRO_CONFIG, &n); |
2307 | n &= REDUCED_DEBOUNCE; | 2396 | n &= ~HV_BUTTON_FROM_GD; |
2397 | n |= HV_CTRL_ENABLE | REDUCED_DEBOUNCE; | ||
2308 | n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; | 2398 | n |= PM_CTRL_ENABLE | CLK_DIV_BY_49 | USE_PCI_TIMING; |
2309 | pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); | 2399 | pci_write_config_dword(pcidev, PCI_ALLEGRO_CONFIG, n); |
2310 | 2400 | ||
@@ -2332,6 +2422,12 @@ snd_m3_chip_init(m3_t *chip) | |||
2332 | 2422 | ||
2333 | outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); | 2423 | outb(RUN_ASSP, chip->iobase + ASSP_CONTROL_B); |
2334 | 2424 | ||
2425 | outb(0x00, io + HARDWARE_VOL_CTRL); | ||
2426 | outb(0x88, io + SHADOW_MIX_REG_VOICE); | ||
2427 | outb(0x88, io + HW_VOL_COUNTER_VOICE); | ||
2428 | outb(0x88, io + SHADOW_MIX_REG_MASTER); | ||
2429 | outb(0x88, io + HW_VOL_COUNTER_MASTER); | ||
2430 | |||
2335 | return 0; | 2431 | return 0; |
2336 | } | 2432 | } |
2337 | 2433 | ||
@@ -2341,7 +2437,7 @@ snd_m3_enable_ints(m3_t *chip) | |||
2341 | unsigned long io = chip->iobase; | 2437 | unsigned long io = chip->iobase; |
2342 | 2438 | ||
2343 | /* TODO: MPU401 not supported yet */ | 2439 | /* TODO: MPU401 not supported yet */ |
2344 | outw(ASSP_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL); | 2440 | outw(ASSP_INT_ENABLE | HV_INT_ENABLE /*| MPU401_INT_ENABLE*/, io + HOST_INT_CTRL); |
2345 | outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, | 2441 | outb(inb(io + ASSP_CONTROL_C) | ASSP_HOST_INT_ENABLE, |
2346 | io + ASSP_CONTROL_C); | 2442 | io + ASSP_CONTROL_C); |
2347 | } | 2443 | } |
@@ -2593,6 +2689,9 @@ snd_m3_create(snd_card_t *card, struct pci_dev *pci, | |||
2593 | return err; | 2689 | return err; |
2594 | } | 2690 | } |
2595 | 2691 | ||
2692 | spin_lock_init(&chip->ac97_lock); | ||
2693 | tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); | ||
2694 | |||
2596 | if ((err = snd_m3_mixer(chip)) < 0) | 2695 | if ((err = snd_m3_mixer(chip)) < 0) |
2597 | return err; | 2696 | return err; |
2598 | 2697 | ||