]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - sound/pci/es1968.c
ALSA: hda - Fix GPIO LED setup for IDT 92HD75 codecs
[linux-2.6.git] / sound / pci / es1968.c
index 1bf298d214b90462cfe7b5253c7d4d0f6c097fd1..99ea9320c6b5592ac22ca11cdce0a9347ca37f37 100644 (file)
 #include <linux/gameport.h>
 #include <linux/moduleparam.h>
 #include <linux/mutex.h>
+#include <linux/input.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 
+#ifdef CONFIG_SND_ES1968_RADIO
+#include <sound/tea575x-tuner.h>
+#endif
+
 #define CARD_NAME "ESS Maestro1/2"
 #define DRIVER_NAME "ES1968"
 
@@ -219,7 +224,7 @@ MODULE_PARM_DESC(joystick, "Enable joystick.");
 #define        RINGB_EN_2CODEC         0x0020
 #define RINGB_SING_BIT_DUAL    0x0040
 
-/* ****Port Adresses**** */
+/* ****Port Addresses**** */
 
 /*   Write & Read */
 #define ESM_INDEX              0x02
@@ -517,14 +522,9 @@ struct es1968 {
 
        /* ALSA Stuff */
        struct snd_ac97 *ac97;
-       struct snd_kcontrol *master_switch; /* for h/w volume control */
-       struct snd_kcontrol *master_volume;
-
        struct snd_rawmidi *rmidi;
 
        spinlock_t reg_lock;
-       spinlock_t ac97_lock;
-       struct tasklet_struct hwvol_tq;
        unsigned int in_suspend;
 
        /* Maestro Stuff */
@@ -547,11 +547,24 @@ struct es1968 {
 #ifdef SUPPORT_JOYSTICK
        struct gameport *gameport;
 #endif
+
+#ifdef CONFIG_SND_ES1968_INPUT
+       struct input_dev *input_dev;
+       char phys[64];                  /* physical device path */
+#else
+       struct snd_kcontrol *master_switch; /* for h/w volume control */
+       struct snd_kcontrol *master_volume;
+#endif
+       struct work_struct hwvol_work;
+
+#ifdef CONFIG_SND_ES1968_RADIO
+       struct snd_tea575x tea;
+#endif
 };
 
 static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
 
-static struct pci_device_id snd_es1968_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_es1968_ids) = {
        /* Maestro 1 */
         { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
        /* Maestro 2 */
@@ -632,28 +645,23 @@ static int snd_es1968_ac97_wait_poll(struct es1968 *chip)
 static void snd_es1968_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
 {
        struct es1968 *chip = ac97->private_data;
-       unsigned long flags;
 
        snd_es1968_ac97_wait(chip);
 
        /* Write the bus */
-       spin_lock_irqsave(&chip->ac97_lock, flags);
        outw(val, chip->io_port + ESM_AC97_DATA);
        /*msleep(1);*/
        outb(reg, chip->io_port + ESM_AC97_INDEX);
        /*msleep(1);*/
-       spin_unlock_irqrestore(&chip->ac97_lock, flags);
 }
 
 static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 {
        u16 data = 0;
        struct es1968 *chip = ac97->private_data;
-       unsigned long flags;
 
        snd_es1968_ac97_wait(chip);
 
-       spin_lock_irqsave(&chip->ac97_lock, flags);
        outb(reg | 0x80, chip->io_port + ESM_AC97_INDEX);
        /*msleep(1);*/
 
@@ -661,7 +669,6 @@ static unsigned short snd_es1968_ac97_read(struct snd_ac97 *ac97, unsigned short
                data = inw(chip->io_port + ESM_AC97_DATA);
                /*msleep(1);*/
        }
-       spin_unlock_irqrestore(&chip->ac97_lock, flags);
 
        return data;
 }
@@ -692,7 +699,8 @@ static void apu_data_set(struct es1968 *chip, u16 data)
 /* no spinlock */
 static void __apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
 {
-       snd_assert(channel < NR_APUS, return);
+       if (snd_BUG_ON(channel >= NR_APUS))
+               return;
 #ifdef CONFIG_PM
        chip->apu_map[channel][reg] = data;
 #endif
@@ -711,7 +719,8 @@ static void apu_set_register(struct es1968 *chip, u16 channel, u8 reg, u16 data)
 
 static u16 __apu_get_register(struct es1968 *chip, u16 channel, u8 reg)
 {
-       snd_assert(channel < NR_APUS, return 0);
+       if (snd_BUG_ON(channel >= NR_APUS))
+               return 0;
        reg |= (channel << 4);
        apu_index_set(chip, reg);
        return __maestro_read(chip, IDR0_DATA_PORT);
@@ -1872,13 +1881,14 @@ static void snd_es1968_update_pcm(struct es1968 *chip, struct esschan *es)
        }
 }
 
-/*
- */
-static void es1968_update_hw_volume(unsigned long private_data)
+/* The hardware volume works by incrementing / decrementing 2 counters
+   (without wrap around) in response to volume button presses and then
+   generating an interrupt. The pair of counters is stored in bits 1-3 and 5-7
+   of a byte wide register. The meaning of bits 0 and 4 is unknown. */
+static void es1968_update_hw_volume(struct work_struct *work)
 {
-       struct es1968 *chip = (struct es1968 *) private_data;
+       struct es1968 *chip = container_of(work, struct es1968, hwvol_work);
        int x, val;
-       unsigned long flags;
 
        /* Figure out which volume control button was pushed,
           based on differences from the default register
@@ -1893,21 +1903,15 @@ static void es1968_update_hw_volume(unsigned long private_data)
        if (chip->in_suspend)
                return;
 
+#ifndef CONFIG_SND_ES1968_INPUT
        if (! chip->master_switch || ! chip->master_volume)
                return;
 
-       /* FIXME: we can't call snd_ac97_* functions since here is in tasklet. */
-       spin_lock_irqsave(&chip->ac97_lock, flags);
-       val = chip->ac97->regs[AC97_MASTER];
+       val = snd_ac97_read(chip->ac97, AC97_MASTER);
        switch (x) {
        case 0x88:
                /* mute */
                val ^= 0x8000;
-               chip->ac97->regs[AC97_MASTER] = val;
-               outw(val, chip->io_port + ESM_AC97_DATA);
-               outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
-               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-                              &chip->master_switch->id);
                break;
        case 0xaa:
                /* volume up */
@@ -1915,11 +1919,6 @@ static void es1968_update_hw_volume(unsigned long private_data)
                        val--;
                if ((val & 0x7f00) > 0)
                        val -= 0x0100;
-               chip->ac97->regs[AC97_MASTER] = val;
-               outw(val, chip->io_port + ESM_AC97_DATA);
-               outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
-               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-                              &chip->master_volume->id);
                break;
        case 0x66:
                /* volume down */
@@ -1927,14 +1926,40 @@ static void es1968_update_hw_volume(unsigned long private_data)
                        val++;
                if ((val & 0x7f00) < 0x1f00)
                        val += 0x0100;
-               chip->ac97->regs[AC97_MASTER] = val;
-               outw(val, chip->io_port + ESM_AC97_DATA);
-               outb(AC97_MASTER, chip->io_port + ESM_AC97_INDEX);
+               break;
+       }
+       if (snd_ac97_update(chip->ac97, AC97_MASTER, val))
                snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
                               &chip->master_volume->id);
+#else
+       if (!chip->input_dev)
+               return;
+
+       val = 0;
+       switch (x) {
+       case 0x88:
+               /* The counters have not changed, yet we've received a HV
+                  interrupt. According to tests run by various people this
+                  happens when pressing the mute button. */
+               val = KEY_MUTE;
+               break;
+       case 0xaa:
+               /* counters increased by 1 -> volume up */
+               val = KEY_VOLUMEUP;
+               break;
+       case 0x66:
+               /* counters decreased by 1 -> volume down */
+               val = KEY_VOLUMEDOWN;
                break;
        }
-       spin_unlock_irqrestore(&chip->ac97_lock, flags);
+
+       if (val) {
+               input_report_key(chip->input_dev, val, 1);
+               input_sync(chip->input_dev);
+               input_report_key(chip->input_dev, val, 0);
+               input_sync(chip->input_dev);
+       }
+#endif
 }
 
 /*
@@ -1951,7 +1976,7 @@ static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id)
        outw(inw(chip->io_port + 4) & 1, chip->io_port + 4);
 
        if (event & ESM_HWVOL_IRQ)
-               tasklet_hi_schedule(&chip->hwvol_tq); /* we'll do this later */
+               schedule_work(&chip->hwvol_work);
 
        /* else ack 'em all, i imagine */
        outb(0xFF, chip->io_port + 0x1A);
@@ -1991,7 +2016,9 @@ snd_es1968_mixer(struct es1968 *chip)
 {
        struct snd_ac97_bus *pbus;
        struct snd_ac97_template ac97;
+#ifndef CONFIG_SND_ES1968_INPUT
        struct snd_ctl_elem_id elem_id;
+#endif
        int err;
        static struct snd_ac97_bus_ops ops = {
                .write = snd_es1968_ac97_write,
@@ -2007,6 +2034,7 @@ snd_es1968_mixer(struct es1968 *chip)
        if ((err = snd_ac97_mixer(pbus, &ac97, &chip->ac97)) < 0)
                return err;
 
+#ifndef CONFIG_SND_ES1968_INPUT
        /* attach master switch / volumes for h/w volume control */
        memset(&elem_id, 0, sizeof(elem_id));
        elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
@@ -2016,6 +2044,7 @@ snd_es1968_mixer(struct es1968 *chip)
        elem_id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
        strcpy(elem_id.name, "Master Playback Volume");
        chip->master_volume = snd_ctl_find_id(chip->card, &elem_id);
+#endif
 
        return 0;
 }
@@ -2339,6 +2368,7 @@ static void snd_es1968_start_irq(struct es1968 *chip)
        w = ESM_HIRQ_DSIE | ESM_HIRQ_HW_VOLUME;
        if (chip->rmidi)
                w |= ESM_HIRQ_MPU401;
+       outb(w, chip->io_port + 0x1A);
        outw(w, chip->io_port + ESM_PORT_HOST_IRQ);
 }
 
@@ -2355,6 +2385,7 @@ static int es1968_suspend(struct pci_dev *pci, pm_message_t state)
                return 0;
 
        chip->in_suspend = 1;
+       cancel_work_sync(&chip->hwvol_work);
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
        snd_pcm_suspend_all(chip->pcm);
        snd_ac97_suspend(chip->ac97);
@@ -2472,8 +2503,107 @@ static inline int snd_es1968_create_gameport(struct es1968 *chip, int dev) { ret
 static inline void snd_es1968_free_gameport(struct es1968 *chip) { }
 #endif
 
+#ifdef CONFIG_SND_ES1968_INPUT
+static int __devinit snd_es1968_input_register(struct es1968 *chip)
+{
+       struct input_dev *input_dev;
+       int err;
+
+       input_dev = input_allocate_device();
+       if (!input_dev)
+               return -ENOMEM;
+
+       snprintf(chip->phys, sizeof(chip->phys), "pci-%s/input0",
+                pci_name(chip->pci));
+
+       input_dev->name = chip->card->driver;
+       input_dev->phys = chip->phys;
+       input_dev->id.bustype = BUS_PCI;
+       input_dev->id.vendor  = chip->pci->vendor;
+       input_dev->id.product = chip->pci->device;
+       input_dev->dev.parent = &chip->pci->dev;
+
+       __set_bit(EV_KEY, input_dev->evbit);
+       __set_bit(KEY_MUTE, input_dev->keybit);
+       __set_bit(KEY_VOLUMEDOWN, input_dev->keybit);
+       __set_bit(KEY_VOLUMEUP, input_dev->keybit);
+
+       err = input_register_device(input_dev);
+       if (err) {
+               input_free_device(input_dev);
+               return err;
+       }
+
+       chip->input_dev = input_dev;
+       return 0;
+}
+#endif /* CONFIG_SND_ES1968_INPUT */
+
+#ifdef CONFIG_SND_ES1968_RADIO
+#define GPIO_DATA      0x60
+#define IO_MASK                4      /* mask      register offset from GPIO_DATA
+                               bits 1=unmask write to given bit */
+#define IO_DIR         8      /* direction register offset from GPIO_DATA
+                               bits 0/1=read/write direction */
+/* mask bits for GPIO lines */
+#define STR_DATA       0x0040 /* GPIO6 */
+#define STR_CLK                0x0080 /* GPIO7 */
+#define STR_WREN       0x0100 /* GPIO8 */
+#define STR_MOST       0x0200 /* GPIO9 */
+
+static void snd_es1968_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
+{
+       struct es1968 *chip = tea->private_data;
+       unsigned long io = chip->io_port + GPIO_DATA;
+       u16 val = 0;
+
+       val |= (pins & TEA575X_DATA) ? STR_DATA : 0;
+       val |= (pins & TEA575X_CLK)  ? STR_CLK  : 0;
+       val |= (pins & TEA575X_WREN) ? STR_WREN : 0;
+
+       outw(val, io);
+}
+
+static u8 snd_es1968_tea575x_get_pins(struct snd_tea575x *tea)
+{
+       struct es1968 *chip = tea->private_data;
+       unsigned long io = chip->io_port + GPIO_DATA;
+       u16 val = inw(io);
+
+       return  (val & STR_DATA) ? TEA575X_DATA : 0 |
+               (val & STR_MOST) ? TEA575X_MOST : 0;
+}
+
+static void snd_es1968_tea575x_set_direction(struct snd_tea575x *tea, bool output)
+{
+       struct es1968 *chip = tea->private_data;
+       unsigned long io = chip->io_port + GPIO_DATA;
+       u16 odir = inw(io + IO_DIR);
+
+       if (output) {
+               outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+               outw(odir | STR_DATA | STR_CLK | STR_WREN, io + IO_DIR);
+       } else {
+               outw(~(STR_CLK | STR_WREN | STR_DATA | STR_MOST), io + IO_MASK);
+               outw((odir & ~(STR_DATA | STR_MOST)) | STR_CLK | STR_WREN, io + IO_DIR);
+       }
+}
+
+static struct snd_tea575x_ops snd_es1968_tea_ops = {
+       .set_pins = snd_es1968_tea575x_set_pins,
+       .get_pins = snd_es1968_tea575x_get_pins,
+       .set_direction = snd_es1968_tea575x_set_direction,
+};
+#endif
+
 static int snd_es1968_free(struct es1968 *chip)
 {
+       cancel_work_sync(&chip->hwvol_work);
+#ifdef CONFIG_SND_ES1968_INPUT
+       if (chip->input_dev)
+               input_unregister_device(chip->input_dev);
+#endif
+
        if (chip->io_port) {
                if (chip->irq >= 0)
                        synchronize_irq(chip->irq);
@@ -2481,11 +2611,13 @@ static int snd_es1968_free(struct es1968 *chip)
                outw(0, chip->io_port + ESM_PORT_HOST_IRQ); /* disable IRQ */
        }
 
+#ifdef CONFIG_SND_ES1968_RADIO
+       snd_tea575x_exit(&chip->tea);
+#endif
+
        if (chip->irq >= 0)
                free_irq(chip->irq, chip);
        snd_es1968_free_gameport(chip);
-       chip->master_switch = NULL;
-       chip->master_volume = NULL;
        pci_release_regions(chip->pci);
        pci_disable_device(chip->pci);
        kfree(chip);
@@ -2537,8 +2669,8 @@ static int __devinit snd_es1968_create(struct snd_card *card,
        if ((err = pci_enable_device(pci)) < 0)
                return err;
        /* check, if we can restrict PCI DMA transfers to 28 bits */
-       if (pci_set_dma_mask(pci, DMA_28BIT_MASK) < 0 ||
-           pci_set_consistent_dma_mask(pci, DMA_28BIT_MASK) < 0) {
+       if (pci_set_dma_mask(pci, DMA_BIT_MASK(28)) < 0 ||
+           pci_set_consistent_dma_mask(pci, DMA_BIT_MASK(28)) < 0) {
                snd_printk(KERN_ERR "architecture does not support 28bit PCI busmaster DMA\n");
                pci_disable_device(pci);
                return -ENXIO;
@@ -2556,9 +2688,8 @@ static int __devinit snd_es1968_create(struct snd_card *card,
        spin_lock_init(&chip->substream_lock);
        INIT_LIST_HEAD(&chip->buf_list);
        INIT_LIST_HEAD(&chip->substream_list);
-       spin_lock_init(&chip->ac97_lock);
        mutex_init(&chip->memory_mutex);
-       tasklet_init(&chip->hwvol_tq, es1968_update_hw_volume, (unsigned long)chip);
+       INIT_WORK(&chip->hwvol_work, es1968_update_hw_volume);
        chip->card = card;
        chip->pci = pci;
        chip->irq = -1;
@@ -2573,7 +2704,7 @@ static int __devinit snd_es1968_create(struct snd_card *card,
        }
        chip->io_port = pci_resource_start(pci, 0);
        if (request_irq(pci->irq, snd_es1968_interrupt, IRQF_SHARED,
-                       "ESS Maestro", chip)) {
+                       KBUILD_MODNAME, chip)) {
                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
                snd_es1968_free(chip);
                return -EBUSY;
@@ -2619,6 +2750,15 @@ static int __devinit snd_es1968_create(struct snd_card *card,
 
        snd_card_set_dev(card, &pci->dev);
 
+#ifdef CONFIG_SND_ES1968_RADIO
+       chip->tea.private_data = chip;
+       chip->tea.ops = &snd_es1968_tea_ops;
+       strlcpy(chip->tea.card, "SF64-PCE2", sizeof(chip->tea.card));
+       sprintf(chip->tea.bus_info, "PCI:%s", pci_name(pci));
+       if (!snd_tea575x_init(&chip->tea))
+               printk(KERN_INFO "es1968: detected TEA575x radio\n");
+#endif
+
        *chip_ret = chip;
 
        return 0;
@@ -2643,9 +2783,9 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
                return -ENOENT;
        }
 
-       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
-       if (!card)
-               return -ENOMEM;
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+       if (err < 0)
+               return err;
                 
        if (total_bufsize[dev] < 128)
                total_bufsize[dev] = 128;
@@ -2711,6 +2851,13 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
 
        snd_es1968_create_gameport(chip, dev);
 
+#ifdef CONFIG_SND_ES1968_INPUT
+       err = snd_es1968_input_register(chip);
+       if (err)
+               snd_printk(KERN_WARNING "Input device registration "
+                       "failed with error %i", err);
+#endif
+
        snd_es1968_start_irq(chip);
 
        chip->clock = clock[dev];
@@ -2736,7 +2883,7 @@ static void __devexit snd_es1968_remove(struct pci_dev *pci)
 }
 
 static struct pci_driver driver = {
-       .name = "ES1968 (ESS Maestro)",
+       .name = KBUILD_MODNAME,
        .id_table = snd_es1968_ids,
        .probe = snd_es1968_probe,
        .remove = __devexit_p(snd_es1968_remove),