diff options
author | Hans de Goede <hdegoede@redhat.com> | 2010-04-23 05:26:43 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2010-04-23 11:09:59 -0400 |
commit | 5a5e02e5095ed89a0a1f4031e7440078c209442b (patch) | |
tree | 54a6238359ec63d2221802e23e4ce6581c060d77 /sound/pci/es1968.c | |
parent | eb581adf25fe9e42197e591926de85459e68b9fd (diff) |
ALSA: snd-es1968: Make hardware volume buttons an input device (rev2)
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.
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.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/es1968.c')
-rw-r--r-- | sound/pci/es1968.c | 128 |
1 files changed, 118 insertions, 10 deletions
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c index ecaea9fb48ec..aa973cee8155 100644 --- a/sound/pci/es1968.c +++ b/sound/pci/es1968.c | |||
@@ -104,6 +104,7 @@ | |||
104 | #include <linux/gameport.h> | 104 | #include <linux/gameport.h> |
105 | #include <linux/moduleparam.h> | 105 | #include <linux/moduleparam.h> |
106 | #include <linux/mutex.h> | 106 | #include <linux/mutex.h> |
107 | #include <linux/input.h> | ||
107 | 108 | ||
108 | #include <sound/core.h> | 109 | #include <sound/core.h> |
109 | #include <sound/pcm.h> | 110 | #include <sound/pcm.h> |
@@ -517,14 +518,9 @@ struct es1968 { | |||
517 | 518 | ||
518 | /* ALSA Stuff */ | 519 | /* ALSA Stuff */ |
519 | struct snd_ac97 *ac97; | 520 | struct snd_ac97 *ac97; |
520 | struct snd_kcontrol *master_switch; /* for h/w volume control */ | ||
521 | struct snd_kcontrol *master_volume; | ||
522 | |||
523 | struct snd_rawmidi *rmidi; | 521 | struct snd_rawmidi *rmidi; |
524 | 522 | ||
525 | spinlock_t reg_lock; | 523 | spinlock_t reg_lock; |
526 | spinlock_t ac97_lock; | ||
527 | struct tasklet_struct hwvol_tq; | ||
528 | unsigned int in_suspend; | 524 | unsigned int in_suspend; |
529 | 525 | ||
530 | /* Maestro Stuff */ | 526 | /* Maestro Stuff */ |
@@ -547,6 +543,16 @@ struct es1968 { | |||
547 | #ifdef SUPPORT_JOYSTICK | 543 | #ifdef SUPPORT_JOYSTICK |
548 | struct gameport *gameport; | 544 | struct gameport *gameport; |
549 | #endif | 545 | #endif |
546 | |||
547 | #ifdef CONFIG_SND_ES1968_INPUT | ||
548 | struct input_dev *input_dev; | ||
549 | char phys[64]; /* physical device path */ | ||
550 | #else | ||
551 | struct snd_kcontrol *master_switch; /* for h/w volume control */ | ||
552 | struct snd_kcontrol *master_volume; | ||
553 | spinlock_t ac97_lock; | ||
554 | struct tasklet_struct hwvol_tq; | ||
555 | #endif | ||
550 | }; | 556 | }; |
551 | 557 | ||
552 | static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id); | 558 | static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id); |
@@ -632,28 +638,38 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip) | |||
632 | static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) | 638 | static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) |
633 | { | 639 | { |
634 | struct es1968 *chip = ac97->private_data; | 640 | struct es1968 *chip = ac97->private_data; |
641 | #ifndef CONFIG_SND_ES1968_INPUT | ||
635 | unsigned long flags; | 642 | unsigned long flags; |
643 | #endif | ||
636 | 644 | ||
637 | snd_es1968_ac97_wait(chip); | 645 | snd_es1968_ac97_wait(chip); |
638 | 646 | ||
639 | /* Write the bus */ | 647 | /* Write the bus */ |
648 | #ifndef CONFIG_SND_ES1968_INPUT | ||
640 | spin_lock_irqsave(&chip->ac97_lock, flags); | 649 | spin_lock_irqsave(&chip->ac97_lock, flags); |
650 | #endif | ||
641 | outw(val, chip->io_port + ESM_AC97_DATA); | 651 | outw(val, chip->io_port + ESM_AC97_DATA); |
642 | /*msleep(1);*/ | 652 | /*msleep(1);*/ |
643 | outb(reg, chip->io_port + ESM_AC97_INDEX); | 653 | outb(reg, chip->io_port + ESM_AC97_INDEX); |
644 | /*msleep(1);*/ | 654 | /*msleep(1);*/ |
655 | #ifndef CONFIG_SND_ES1968_INPUT | ||
645 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | 656 | spin_unlock_irqrestore(&chip->ac97_lock, flags); |
657 | #endif | ||
646 | } | 658 | } |
647 | 659 | ||
648 | static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg) | 660 | static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg) |
649 | { | 661 | { |
650 | u16 data = 0; | 662 | u16 data = 0; |
651 | struct es1968 *chip = ac97->private_data; | 663 | struct es1968 *chip = ac97->private_data; |
664 | #ifndef CONFIG_SND_ES1968_INPUT | ||
652 | unsigned long flags; | 665 | unsigned long flags; |
666 | #endif | ||
653 | 667 | ||
654 | snd_es1968_ac97_wait(chip); | 668 | snd_es1968_ac97_wait(chip); |
655 | 669 | ||
670 | #ifndef CONFIG_SND_ES1968_INPUT | ||
656 | spin_lock_irqsave(&chip->ac97_lock, flags); | 671 | spin_lock_irqsave(&chip->ac97_lock, flags); |
672 | #endif | ||
657 | outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); | 673 | outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX); |
658 | /*msleep(1);*/ | 674 | /*msleep(1);*/ |
659 | 675 | ||
@@ -661,7 +677,9 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short | |||
661 | data = inw(chip->io_port + ESM_AC97_DATA); | 677 | data = inw(chip->io_port + ESM_AC97_DATA); |
662 | /*msleep(1);*/ | 678 | /*msleep(1);*/ |
663 | } | 679 | } |
680 | #ifndef CONFIG_SND_ES1968_INPUT | ||
664 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | 681 | spin_unlock_irqrestore(&chip->ac97_lock, flags); |
682 | #endif | ||
665 | 683 | ||
666 | return data; | 684 | return data; |
667 | } | 685 | } |
@@ -1874,13 +1892,17 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es) | |||
1874 | } | 1892 | } |
1875 | } | 1893 | } |
1876 | 1894 | ||
1877 | /* | 1895 | /* The hardware volume works by incrementing / decrementing 2 counters |
1878 | */ | 1896 | (without wrap around) in response to volume button presses and then |
1897 | generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7 | ||
1898 | of a byte wide register. The meaning of bits 0 and 4 is unknown. */ | ||
1879 | static void es1968_update_hw_volume(unsigned long private_data) | 1899 | static void es1968_update_hw_volume(unsigned long private_data) |
1880 | { | 1900 | { |
1881 | struct es1968 *chip = (struct es1968 *) private_data; | 1901 | struct es1968 *chip = (struct es1968 *) private_data; |
1882 | int x, val; | 1902 | int x, val; |
1903 | #ifndef CONFIG_SND_ES1968_INPUT | ||
1883 | unsigned long flags; | 1904 | unsigned long flags; |
1905 | #endif | ||
1884 | 1906 | ||
1885 | /* Figure out which volume control button was pushed, | 1907 | /* Figure out which volume control button was pushed, |
1886 | based on differences from the default register | 1908 | based on differences from the default register |
@@ -1895,6 +1917,7 @@ static void es1968_update_hw_volume(unsigned long private_data) | |||
1895 | if (chip->in_suspend) | 1917 | if (chip->in_suspend) |
1896 | return; | 1918 | return; |
1897 | 1919 | ||
1920 | #ifndef CONFIG_SND_ES1968_INPUT | ||
1898 | if (! chip->master_switch || ! chip->master_volume) | 1921 | if (! chip->master_switch || ! chip->master_volume) |
1899 | return; | 1922 | return; |
1900 | 1923 | ||
@@ -1937,6 +1960,35 @@ static void es1968_update_hw_volume(unsigned long private_data) | |||
1937 | break; | 1960 | break; |
1938 | } | 1961 | } |
1939 | spin_unlock_irqrestore(&chip->ac97_lock, flags); | 1962 | spin_unlock_irqrestore(&chip->ac97_lock, flags); |
1963 | #else | ||
1964 | if (!chip->input_dev) | ||
1965 | return; | ||
1966 | |||
1967 | val = 0; | ||
1968 | switch (x) { | ||
1969 | case 0x88: | ||
1970 | /* The counters have not changed, yet we've received a HV | ||
1971 | interrupt. According to tests run by various people this | ||
1972 | happens when pressing the mute button. */ | ||
1973 | val = KEY_MUTE; | ||
1974 | break; | ||
1975 | case 0xaa: | ||
1976 | /* counters increased by 1 -> volume up */ | ||
1977 | val = KEY_VOLUMEUP; | ||
1978 | break; | ||
1979 | case 0x66: | ||
1980 | /* counters decreased by 1 -> volume down */ | ||
1981 | val = KEY_VOLUMEDOWN; | ||
1982 | break; | ||
1983 | } | ||
1984 | |||
1985 | if (val) { | ||
1986 | input_report_key(chip->input_dev, val, 1); | ||
1987 | input_sync(chip->input_dev); | ||
1988 | input_report_key(chip->input_dev, val, 0); | ||
1989 | input_sync(chip->input_dev); | ||
1990 | } | ||
1991 | #endif | ||
1940 | } | 1992 | } |
1941 | 1993 | ||
1942 | /* | 1994 | /* |
@@ -1953,7 +2005,11 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id) | |||
1953 | outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); | 2005 | outw(inw(chip->io_port + 4) & 1, chip->io_port + 4); |
1954 | 2006 | ||
1955 | if (event & ESM_HWVOL_IRQ) | 2007 | if (event & ESM_HWVOL_IRQ) |
2008 | #ifdef CONFIG_SND_ES1968_INPUT | ||
2009 | es1968_update_hw_volume((unsigned long)chip); | ||
2010 | #else | ||
1956 | tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */ | 2011 | tasklet_schedule(&chip->hwvol_tq); /* we'll do this later */ |
2012 | #endif | ||
1957 | 2013 | ||
1958 | /* else ack 'em all, i imagine */ | 2014 | /* else ack 'em all, i imagine */ |
1959 | outb(0xFF, chip->io_port + 0x1A); | 2015 | outb(0xFF, chip->io_port + 0x1A); |
@@ -1993,7 +2049,9 @@ snd_es1968_mixer(struct es1968 *chip) | |||
1993 | { | 2049 | { |
1994 | struct snd_ac97_bus *pbus; | 2050 | struct snd_ac97_bus *pbus; |
1995 | struct snd_ac97_template ac97; | 2051 | struct snd_ac97_template ac97; |
2052 | #ifndef CONFIG_SND_ES1968_INPUT | ||
1996 | struct snd_ctl_elem_id elem_id; | 2053 | struct snd_ctl_elem_id elem_id; |
2054 | #endif | ||
1997 | int err; | 2055 | int err; |
1998 | static struct snd_ac97_bus_ops ops = { | 2056 | static struct snd_ac97_bus_ops ops = { |
1999 | .write = snd_es1968_ac97_write, | 2057 | .write = snd_es1968_ac97_write, |
@@ -2009,6 +2067,7 @@ snd_es1968_mixer(struct es1968 *chip) | |||
2009 | if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0) | 2067 | if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0) |
2010 | return err; | 2068 | return err; |
2011 | 2069 | ||
2070 | #ifndef CONFIG_SND_ES1968_INPUT | ||
2012 | /* attach master switch / volumes for h/w volume control */ | 2071 | /* attach master switch / volumes for h/w volume control */ |
2013 | memset(&elem_id, 0, sizeof(elem_id)); | 2072 | memset(&elem_id, 0, sizeof(elem_id)); |
2014 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | 2073 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
@@ -2018,6 +2077,7 @@ snd_es1968_mixer(struct es1968 *chip) | |||
2018 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; | 2077 | elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER; |
2019 | strcpy(elem_id.name, "Master Playback Volume"); | 2078 | strcpy(elem_id.name, "Master Playback Volume"); |
2020 | chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); | 2079 | chip->master_volume = snd_ctl_find_id(chip->card, &elem_id); |
2080 | #endif | ||
2021 | 2081 | ||
2022 | return 0; | 2082 | return 0; |
2023 | } | 2083 | } |
@@ -2474,8 +2534,49 @@ static inline int snd_es1968_create_gameport(struct es1968 *chip, int dev) { ret | |||
2474 | static inline void snd_es1968_free_gameport(struct es1968 *chip) { } | 2534 | static inline void snd_es1968_free_gameport(struct es1968 *chip) { } |
2475 | #endif | 2535 | #endif |
2476 | 2536 | ||
2537 | #ifdef CONFIG_SND_ES1968_INPUT | ||
2538 | static int __devinit snd_es1968_input_register(struct es1968 *chip) | ||
2539 | { | ||
2540 | struct input_dev *input_dev; | ||
2541 | int err; | ||
2542 | |||
2543 | input_dev = input_allocate_device(); | ||
2544 | if (!input_dev) | ||
2545 | return -ENOMEM; | ||
2546 | |||
2547 | snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0", | ||
2548 | pci_name(chip->pci)); | ||
2549 | |||
2550 | input_dev->name = chip->card->driver; | ||
2551 | input_dev->phys = chip->phys; | ||
2552 | input_dev->id.bustype = BUS_PCI; | ||
2553 | input_dev->id.vendor = chip->pci->vendor; | ||
2554 | input_dev->id.product = chip->pci->device; | ||
2555 | input_dev->dev.parent = &chip->pci->dev; | ||
2556 | |||
2557 | __set_bit(EV_KEY, input_dev->evbit); | ||
2558 | __set_bit(KEY_MUTE, input_dev->keybit); | ||
2559 | __set_bit(KEY_VOLUMEDOWN, input_dev->keybit); | ||
2560 | __set_bit(KEY_VOLUMEUP, input_dev->keybit); | ||
2561 | |||
2562 | err = input_register_device(input_dev); | ||
2563 | if (err) { | ||
2564 | input_free_device(input_dev); | ||
2565 | return err; | ||
2566 | } | ||
2567 | |||
2568 | chip->input_dev = input_dev; | ||
2569 | return 0; | ||
2570 | } | ||
2571 | #endif /* CONFIG_SND_ES1968_INPUT */ | ||
2572 | |||
2477 | static int snd_es1968_free(struct es1968 *chip) | 2573 | static int snd_es1968_free(struct es1968 *chip) |
2478 | { | 2574 | { |
2575 | #ifdef CONFIG_SND_ES1968_INPUT | ||
2576 | if (chip->input_dev) | ||
2577 | input_unregister_device(chip->input_dev); | ||
2578 | #endif | ||
2579 | |||
2479 | if (chip->io_port) { | 2580 | if (chip->io_port) { |
2480 | if (chip->irq >= 0) | 2581 | if (chip->irq >= 0) |
2481 | synchronize_irq(chip->irq); | 2582 | synchronize_irq(chip->irq); |
@@ -2486,8 +2587,6 @@ static int snd_es1968_free(struct es1968 *chip) | |||
2486 | if (chip->irq >= 0) | 2587 | if (chip->irq >= 0) |
2487 | free_irq(chip->irq, chip); | 2588 | free_irq(chip->irq, chip); |
2488 | snd_es1968_free_gameport(chip); | 2589 | snd_es1968_free_gameport(chip); |
2489 | chip->master_switch = NULL; | ||
2490 | chip->master_volume = NULL; | ||
2491 | pci_release_regions(chip->pci); | 2590 | pci_release_regions(chip->pci); |
2492 | pci_disable_device(chip->pci); | 2591 | pci_disable_device(chip->pci); |
2493 | kfree(chip); | 2592 | kfree(chip); |
@@ -2558,9 +2657,11 @@ static int __devinit snd_es1968_create(struct snd_card *card, | |||
2558 | spin_lock_init(&chip->substream_lock); | 2657 | spin_lock_init(&chip->substream_lock); |
2559 | INIT_LIST_HEAD(&chip->buf_list); | 2658 | INIT_LIST_HEAD(&chip->buf_list); |
2560 | INIT_LIST_HEAD(&chip->substream_list); | 2659 | INIT_LIST_HEAD(&chip->substream_list); |
2561 | spin_lock_init(&chip->ac97_lock); | ||
2562 | mutex_init(&chip->memory_mutex); | 2660 | mutex_init(&chip->memory_mutex); |
2661 | #ifndef CONFIG_SND_ES1968_INPUT | ||
2662 | spin_lock_init(&chip->ac97_lock); | ||
2563 | tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); | 2663 | tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip); |
2664 | #endif | ||
2564 | chip->card = card; | 2665 | chip->card = card; |
2565 | chip->pci = pci; | 2666 | chip->pci = pci; |
2566 | chip->irq = -1; | 2667 | chip->irq = -1; |
@@ -2713,6 +2814,13 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci, | |||
2713 | 2814 | ||
2714 | snd_es1968_create_gameport(chip, dev); | 2815 | snd_es1968_create_gameport(chip, dev); |
2715 | 2816 | ||
2817 | #ifdef CONFIG_SND_ES1968_INPUT | ||
2818 | err = snd_es1968_input_register(chip); | ||
2819 | if (err) | ||
2820 | snd_printk(KERN_WARNING "Input device registration " | ||
2821 | "failed with error %i", err); | ||
2822 | #endif | ||
2823 | |||
2716 | snd_es1968_start_irq(chip); | 2824 | snd_es1968_start_irq(chip); |
2717 | 2825 | ||
2718 | chip->clock = clock[dev]; | 2826 | chip->clock = clock[dev]; |