diff options
author | Hans de Goede <hdegoede@redhat.com> | 2010-04-23 05:26:42 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-04-23 11:09:46 -0400 |
commit | eb581adf25fe9e42197e591926de85459e68b9fd (patch) | |
tree | 9967f82156d9ab138c96adefa4771a420470fabe /sound/pci/maestro3.c | |
parent | 20133d4cd329af7a02ee5af36bba1796d5ff7b1d (diff) |
ALSA: snd-maestro3: Make hardware volume buttons an input device (rev2)
While working on the sound suspend / resume problems with my laptop
I noticed that the hardware volume handling code in essence just detects
key presses, and then does some hardcoded modification of the master volume
based on which key is pressed.
This made me think that clearly the right thing to do here is just report
these keypresses to userspace and let userspace decide what to with them.
This patch adds a Kconfig option which when enabled reports the volume
buttons as keypresses using an input device. When enabled this option
also gets rid of the ugly direct ac97 writes from the tasklet, the ac97lock
and the need for using a tasklet in general.
As an added bonus the keys now work identical to volume keys on a (usb)
keyboard with multimedia keys, providing visual feedback of the volume
level change, and a better range of the volume control (with a properly
configured desktop environment).
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/maestro3.c')
-rw-r--r-- | sound/pci/maestro3.c | 116 |
1 files changed, 114 insertions, 2 deletions
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c index 53d2a5d61baf..217a4dcb259e 100644 --- a/sound/pci/maestro3.c +++ b/sound/pci/maestro3.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <linux/vmalloc.h> | 41 | #include <linux/vmalloc.h> |
42 | #include <linux/moduleparam.h> | 42 | #include <linux/moduleparam.h> |
43 | #include <linux/firmware.h> | 43 | #include <linux/firmware.h> |
44 | #include <linux/input.h> | ||
44 | #include <sound/core.h> | 45 | #include <sound/core.h> |
45 | #include <sound/info.h> | 46 | #include <sound/info.h> |
46 | #include <sound/control.h> | 47 | #include <sound/control.h> |
@@ -844,11 +845,17 @@ struct snd_m3 { | |||
844 | struct m3_dma *substreams; | 845 | struct m3_dma *substreams; |
845 | 846 | ||
846 | spinlock_t reg_lock; | 847 | spinlock_t reg_lock; |
847 | spinlock_t ac97_lock; | ||
848 | 848 | ||
849 | #ifdef CONFIG_SND_MAESTRO3_INPUT | ||
850 | struct input_dev *input_dev; | ||
851 | char phys[64]; /* physical device path */ | ||
852 | #else | ||
853 | spinlock_t ac97_lock; | ||
849 | struct snd_kcontrol *master_switch; | 854 | struct snd_kcontrol *master_switch; |
850 | struct snd_kcontrol *master_volume; | 855 | struct snd_kcontrol *master_volume; |
851 | struct tasklet_struct hwvol_tq; | 856 | struct tasklet_struct hwvol_tq; |
857 | #endif | ||
858 | |||
852 | unsigned int in_suspend; | 859 | unsigned int in_suspend; |
853 | 860 | ||
854 | #ifdef CONFIG_PM | 861 | #ifdef CONFIG_PM |
@@ -1606,7 +1613,9 @@ static void snd_m3_update_hw_volume(unsigned long private_data) | |||
1606 | { | 1613 | { |
1607 | struct snd_m3 *chip = (struct snd_m3 *) private_data; | 1614 | struct snd_m3 *chip = (struct snd_m3 *) private_data; |
1608 | int x, val; | 1615 | int x, val; |
1616 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1609 | unsigned long flags; | 1617 | unsigned long flags; |
1618 | #endif | ||
1610 | 1619 | ||
1611 | /* Figure out which volume control button was pushed, | 1620 | /* Figure out which volume control button was pushed, |
1612 | based on differences from the default register | 1621 | based on differences from the default register |
@@ -1632,6 +1641,7 @@ static void snd_m3_update_hw_volume(unsigned long private_data) | |||
1632 | if (chip->in_suspend) | 1641 | if (chip->in_suspend) |
1633 | return; | 1642 | return; |
1634 | 1643 | ||
1644 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1635 | if (!chip->master_switch || !chip->master_volume) | 1645 | if (!chip->master_switch || !chip->master_volume) |
1636 | return; | 1646 | return; |
1637 | 1647 | ||
@@ -1677,6 +1687,35 @@ static void snd_m3_update_hw_volume(unsigned long private_data) | |||
1677 | break; | 1687 | break; |
1678 | } | 1688 | } |
1679 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | 1689 | spin_unlock_irqrestore(&chip->ac97_lock, flags); |
1690 | #else | ||
1691 | if (!chip->input_dev) | ||
1692 | return; | ||
1693 | |||
1694 | val = 0; | ||
1695 | switch (x) { | ||
1696 | case 0x88: | ||
1697 | /* The counters have not changed, yet we've received a HV | ||
1698 | interrupt. According to tests run by various people this | ||
1699 | happens when pressing the mute button. */ | ||
1700 | val = KEY_MUTE; | ||
1701 | break; | ||
1702 | case 0xaa: | ||
1703 | /* counters increased by 1 -> volume up */ | ||
1704 | val = KEY_VOLUMEUP; | ||
1705 | break; | ||
1706 | case 0x66: | ||
1707 | /* counters decreased by 1 -> volume down */ | ||
1708 | val = KEY_VOLUMEDOWN; | ||
1709 | break; | ||
1710 | } | ||
1711 | |||
1712 | if (val) { | ||
1713 | input_report_key(chip->input_dev, val, 1); | ||
1714 | input_sync(chip->input_dev); | ||
1715 | input_report_key(chip->input_dev, val, 0); | ||
1716 | input_sync(chip->input_dev); | ||
1717 | } | ||
1718 | #endif | ||
1680 | } | 1719 | } |
1681 | 1720 | ||
1682 | static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) | 1721 | static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) |
@@ -1691,7 +1730,11 @@ static irqreturn_t snd_m3_interrupt(int irq, void *dev_id) | |||
1691 | return IRQ_NONE; | 1730 | return IRQ_NONE; |
1692 | 1731 | ||
1693 | if (status & HV_INT_PENDING) | 1732 | if (status & HV_INT_PENDING) |
1733 | #ifdef CONFIG_SND_MAESTRO3_INPUT | ||
1734 | snd_m3_update_hw_volume((unsigned long)chip); | ||
1735 | #else | ||
1694 | tasklet_schedule(&chip->hwvol_tq); | 1736 | tasklet_schedule(&chip->hwvol_tq); |
1737 | #endif | ||
1695 | 1738 | ||
1696 | /* | 1739 | /* |
1697 | * ack an assp int if its running | 1740 | * ack an assp int if its running |
@@ -1957,18 +2000,24 @@ static unsigned short | |||
1957 | snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | 2000 | snd_m3_ac97_read(struct snd_ac97 *ac97, unsigned short reg) |
1958 | { | 2001 | { |
1959 | struct snd_m3 *chip = ac97->private_data; | 2002 | struct snd_m3 *chip = ac97->private_data; |
2003 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1960 | unsigned long flags; | 2004 | unsigned long flags; |
2005 | #endif | ||
1961 | unsigned short data = 0xffff; | 2006 | unsigned short data = 0xffff; |
1962 | 2007 | ||
1963 | if (snd_m3_ac97_wait(chip)) | 2008 | if (snd_m3_ac97_wait(chip)) |
1964 | goto fail; | 2009 | goto fail; |
2010 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1965 | spin_lock_irqsave(&chip->ac97_lock, flags); | 2011 | spin_lock_irqsave(&chip->ac97_lock, flags); |
2012 | #endif | ||
1966 | snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); | 2013 | snd_m3_outb(chip, 0x80 | (reg & 0x7f), CODEC_COMMAND); |
1967 | if (snd_m3_ac97_wait(chip)) | 2014 | if (snd_m3_ac97_wait(chip)) |
1968 | goto fail_unlock; | 2015 | goto fail_unlock; |
1969 | data = snd_m3_inw(chip, CODEC_DATA); | 2016 | data = snd_m3_inw(chip, CODEC_DATA); |
1970 | fail_unlock: | 2017 | fail_unlock: |
2018 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1971 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | 2019 | spin_unlock_irqrestore(&chip->ac97_lock, flags); |
2020 | #endif | ||
1972 | fail: | 2021 | fail: |
1973 | return data; | 2022 | return data; |
1974 | } | 2023 | } |
@@ -1977,14 +2026,20 @@ static void | |||
1977 | snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) | 2026 | snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) |
1978 | { | 2027 | { |
1979 | struct snd_m3 *chip = ac97->private_data; | 2028 | struct snd_m3 *chip = ac97->private_data; |
2029 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1980 | unsigned long flags; | 2030 | unsigned long flags; |
2031 | #endif | ||
1981 | 2032 | ||
1982 | if (snd_m3_ac97_wait(chip)) | 2033 | if (snd_m3_ac97_wait(chip)) |
1983 | return; | 2034 | return; |
2035 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1984 | spin_lock_irqsave(&chip->ac97_lock, flags); | 2036 | spin_lock_irqsave(&chip->ac97_lock, flags); |
2037 | #endif | ||
1985 | snd_m3_outw(chip, val, CODEC_DATA); | 2038 | snd_m3_outw(chip, val, CODEC_DATA); |
1986 | snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); | 2039 | snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND); |
2040 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
1987 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | 2041 | spin_unlock_irqrestore(&chip->ac97_lock, flags); |
2042 | #endif | ||
1988 | } | 2043 | } |
1989 | 2044 | ||
1990 | 2045 | ||
@@ -2091,7 +2146,9 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) | |||
2091 | { | 2146 | { |
2092 | struct snd_ac97_bus *pbus; | 2147 | struct snd_ac97_bus *pbus; |
2093 | struct snd_ac97_template ac97; | 2148 | struct snd_ac97_template ac97; |
2149 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
2094 | struct snd_ctl_elem_id elem_id; | 2150 | struct snd_ctl_elem_id elem_id; |
2151 | #endif | ||
2095 | int err; | 2152 | int err; |
2096 | static struct snd_ac97_bus_ops ops = { | 2153 | static struct snd_ac97_bus_ops ops = { |
2097 | .write = snd_m3_ac97_write, | 2154 | .write = snd_m3_ac97_write, |
@@ -2111,6 +2168,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) | |||
2111 | schedule_timeout_uninterruptible(msecs_to_jiffies(100)); | 2168 | schedule_timeout_uninterruptible(msecs_to_jiffies(100)); |
2112 | snd_ac97_write(chip->ac97, AC97_PCM, 0); | 2169 | snd_ac97_write(chip->ac97, AC97_PCM, 0); |
2113 | 2170 | ||
2171 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
2114 | memset(&elem_id, 0, sizeof(elem_id)); | 2172 | memset(&elem_id, 0, sizeof(elem_id)); |
2115 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | 2173 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
2116 | strcpy(elem_id.name, "Master Playback Switch"); | 2174 | strcpy(elem_id.name, "Master Playback Switch"); |
@@ -2119,6 +2177,7 @@ static int __devinit snd_m3_mixer(struct snd_m3 *chip) | |||
2119 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | 2177 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
2120 | strcpy(elem_id.name, "Master Playback Volume"); | 2178 | strcpy(elem_id.name, "Master Playback Volume"); |
2121 | chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); | 2179 | chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); |
2180 | #endif | ||
2122 | 2181 | ||
2123 | return 0; | 2182 | return 0; |
2124 | } | 2183 | } |
@@ -2398,6 +2457,11 @@ static int snd_m3_free(struct snd_m3 *chip) | |||
2398 | struct m3_dma *s; | 2457 | struct m3_dma *s; |
2399 | int i; | 2458 | int i; |
2400 | 2459 | ||
2460 | #ifdef CONFIG_SND_MAESTRO3_INPUT | ||
2461 | if (chip->input_dev) | ||
2462 | input_unregister_device(chip->input_dev); | ||
2463 | #endif | ||
2464 | |||
2401 | if (chip->substreams) { | 2465 | if (chip->substreams) { |
2402 | spin_lock_irq(&chip->reg_lock); | 2466 | spin_lock_irq(&chip->reg_lock); |
2403 | for (i = 0; i < chip->num_substreams; i++) { | 2467 | for (i = 0; i < chip->num_substreams; i++) { |
@@ -2524,6 +2588,41 @@ static int m3_resume(struct pci_dev *pci) | |||
2524 | } | 2588 | } |
2525 | #endif /* CONFIG_PM */ | 2589 | #endif /* CONFIG_PM */ |
2526 | 2590 | ||
2591 | #ifdef CONFIG_SND_MAESTRO3_INPUT | ||
2592 | static int __devinit snd_m3_input_register(struct snd_m3 *chip) | ||
2593 | { | ||
2594 | struct input_dev *input_dev; | ||
2595 | int err; | ||
2596 | |||
2597 | input_dev = input_allocate_device(); | ||
2598 | if (!input_dev) | ||
2599 | return -ENOMEM; | ||
2600 | |||
2601 | snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0", | ||
2602 | pci_name(chip->pci)); | ||
2603 | |||
2604 | input_dev->name = chip->card->driver; | ||
2605 | input_dev->phys = chip->phys; | ||
2606 | input_dev->id.bustype = BUS_PCI; | ||
2607 | input_dev->id.vendor = chip->pci->vendor; | ||
2608 | input_dev->id.product = chip->pci->device; | ||
2609 | input_dev->dev.parent = &chip->pci->dev; | ||
2610 | |||
2611 | __set_bit(EV_KEY, input_dev->evbit); | ||
2612 | __set_bit(KEY_MUTE, input_dev->keybit); | ||
2613 | __set_bit(KEY_VOLUMEDOWN, input_dev->keybit); | ||
2614 | __set_bit(KEY_VOLUMEUP, input_dev->keybit); | ||
2615 | |||
2616 | err = input_register_device(input_dev); | ||
2617 | if (err) { | ||
2618 | input_free_device(input_dev); | ||
2619 | return err; | ||
2620 | } | ||
2621 | |||
2622 | chip->input_dev = input_dev; | ||
2623 | return 0; | ||
2624 | } | ||
2625 | #endif /* CONFIG_INPUT */ | ||
2527 | 2626 | ||
2528 | /* | 2627 | /* |
2529 | */ | 2628 | */ |
@@ -2567,7 +2666,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, | |||
2567 | } | 2666 | } |
2568 | 2667 | ||
2569 | spin_lock_init(&chip->reg_lock); | 2668 | spin_lock_init(&chip->reg_lock); |
2669 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
2570 | spin_lock_init(&chip->ac97_lock); | 2670 | spin_lock_init(&chip->ac97_lock); |
2671 | #endif | ||
2571 | 2672 | ||
2572 | switch (pci->device) { | 2673 | switch (pci->device) { |
2573 | case PCI_DEVICE_ID_ESS_ALLEGRO: | 2674 | case PCI_DEVICE_ID_ESS_ALLEGRO: |
@@ -2650,7 +2751,9 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, | |||
2650 | 2751 | ||
2651 | snd_m3_hv_init(chip); | 2752 | snd_m3_hv_init(chip); |
2652 | 2753 | ||
2754 | #ifndef CONFIG_SND_MAESTRO3_INPUT | ||
2653 | tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); | 2755 | tasklet_init(&chip->hwvol_tq, snd_m3_update_hw_volume, (unsigned long)chip); |
2756 | #endif | ||
2654 | 2757 | ||
2655 | if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED, | 2758 | if (request_irq(pci->irq, snd_m3_interrupt, IRQF_SHARED, |
2656 | card->driver, chip)) { | 2759 | card->driver, chip)) { |
@@ -2682,7 +2785,16 @@ snd_m3_create(struct snd_card *card, struct pci_dev *pci, | |||
2682 | 2785 | ||
2683 | if ((err = snd_m3_pcm(chip, 0)) < 0) | 2786 | if ((err = snd_m3_pcm(chip, 0)) < 0) |
2684 | return err; | 2787 | return err; |
2685 | 2788 | ||
2789 | #ifdef CONFIG_SND_MAESTRO3_INPUT | ||
2790 | if (chip->hv_config & HV_CTRL_ENABLE) { | ||
2791 | err = snd_m3_input_register(chip); | ||
2792 | if (err) | ||
2793 | snd_printk(KERN_WARNING "Input device registration " | ||
2794 | "failed with error %i", err); | ||
2795 | } | ||
2796 | #endif | ||
2797 | |||
2686 | snd_m3_enable_ints(chip); | 2798 | snd_m3_enable_ints(chip); |
2687 | snd_m3_assp_continue(chip); | 2799 | snd_m3_assp_continue(chip); |
2688 | 2800 | ||