fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / sound / pci / es1938.c
index 0d556b0..66ac26c 100644 (file)
 #include <linux/gameport.h>
 #include <linux/moduleparam.h>
 #include <linux/delay.h>
+#include <linux/dma-mapping.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
 #include <sound/opl3.h>
 #include <sound/mpu401.h>
 #include <sound/initval.h>
+#include <sound/tlv.h>
 
 #include <asm/io.h>
 
@@ -239,7 +241,7 @@ struct es1938 {
 #endif
 };
 
-static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);
 
 static struct pci_device_id snd_es1938_ids[] = {
         { 0x125d, 0x1969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* Solo-1 */
@@ -1163,6 +1165,14 @@ static int snd_es1938_reg_read(struct es1938 *chip, unsigned char reg)
                return snd_es1938_read(chip, reg);
 }
 
+#define ES1938_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv)    \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
+  .name = xname, .index = xindex, \
+  .info = snd_es1938_info_single, \
+  .get = snd_es1938_get_single, .put = snd_es1938_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+  .tlv = { .p = xtlv } }
 #define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
   .info = snd_es1938_info_single, \
@@ -1216,6 +1226,14 @@ static int snd_es1938_put_single(struct snd_kcontrol *kcontrol,
        return snd_es1938_reg_bits(chip, reg, mask, val) != val;
 }
 
+#define ES1938_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,\
+  .name = xname, .index = xindex, \
+  .info = snd_es1938_info_double, \
+  .get = snd_es1938_get_double, .put = snd_es1938_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = xtlv } }
 #define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
   .info = snd_es1938_info_double, \
@@ -1296,8 +1314,41 @@ static int snd_es1938_put_double(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+static unsigned int db_scale_master[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 54, TLV_DB_SCALE_ITEM(-3600, 50, 1),
+       54, 63, TLV_DB_SCALE_ITEM(-900, 100, 0),
+};
+
+static unsigned int db_scale_audio1[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 8, TLV_DB_SCALE_ITEM(-3300, 300, 1),
+       8, 15, TLV_DB_SCALE_ITEM(-900, 150, 0),
+};
+
+static unsigned int db_scale_audio2[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 8, TLV_DB_SCALE_ITEM(-3450, 300, 1),
+       8, 15, TLV_DB_SCALE_ITEM(-1050, 150, 0),
+};
+
+static unsigned int db_scale_mic[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 8, TLV_DB_SCALE_ITEM(-2400, 300, 1),
+       8, 15, TLV_DB_SCALE_ITEM(0, 150, 0),
+};
+
+static unsigned int db_scale_line[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 8, TLV_DB_SCALE_ITEM(-3150, 300, 1),
+       8, 15, TLV_DB_SCALE_ITEM(-750, 150, 0),
+};
+
+static DECLARE_TLV_DB_SCALE(db_scale_capture, 0, 150, 0);
+
 static struct snd_kcontrol_new snd_es1938_controls[] = {
-ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
+ES1938_DOUBLE_TLV("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0,
+                 db_scale_master),
 ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
 {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1308,19 +1359,27 @@ ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
 },
 {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = (SNDRV_CTL_ELEM_ACCESS_READ |
+                  SNDRV_CTL_ELEM_ACCESS_TLV_READ),
        .name = "Hardware Master Playback Switch",
-       .access = SNDRV_CTL_ELEM_ACCESS_READ,
        .info = snd_es1938_info_hw_switch,
        .get = snd_es1938_get_hw_switch,
+       .tlv = { .p = db_scale_master },
 },
 ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
-ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0,
+                 db_scale_line),
 ES1938_DOUBLE("CD Playback Volume", 0, 0x38, 0x38, 4, 0, 15, 0),
-ES1938_DOUBLE("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0),
-ES1938_DOUBLE("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
-ES1938_DOUBLE("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0),
-ES1938_DOUBLE("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0),
-ES1938_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("FM Playback Volume", 0, 0x36, 0x36, 4, 0, 15, 0,
+                 db_scale_mic),
+ES1938_DOUBLE_TLV("Mono Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("Mic Playback Volume", 0, 0x1a, 0x1a, 4, 0, 15, 0,
+                 db_scale_mic),
+ES1938_DOUBLE_TLV("Aux Playback Volume", 0, 0x3a, 0x3a, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0,
+                 db_scale_capture),
 ES1938_SINGLE("PC Speaker Volume", 0, 0x3c, 0, 7, 0),
 ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
 ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
@@ -1331,16 +1390,26 @@ ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
        .get = snd_es1938_get_mux,
        .put = snd_es1938_put_mux,
 },
-ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
-ES1938_DOUBLE("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0),
-ES1938_DOUBLE("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0),
-ES1938_DOUBLE("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0),
-ES1938_DOUBLE("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0),
-ES1938_DOUBLE("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0),
-ES1938_DOUBLE("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0),
-ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
+ES1938_DOUBLE_TLV("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0,
+                 db_scale_audio2),
+ES1938_DOUBLE_TLV("Mic Capture Volume", 0, 0x68, 0x68, 4, 0, 15, 0,
+                 db_scale_mic),
+ES1938_DOUBLE_TLV("Line Capture Volume", 0, 0x6e, 0x6e, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("FM Capture Volume", 0, 0x6b, 0x6b, 4, 0, 15, 0,
+                 db_scale_mic),
+ES1938_DOUBLE_TLV("Mono Capture Volume", 0, 0x6f, 0x6f, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("CD Capture Volume", 0, 0x6a, 0x6a, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("Aux Capture Volume", 0, 0x6c, 0x6c, 4, 0, 15, 0,
+                 db_scale_line),
+ES1938_DOUBLE_TLV("PCM Playback Volume", 0, 0x7c, 0x7c, 4, 0, 15, 0,
+                 db_scale_audio2),
+ES1938_DOUBLE_TLV("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0,
+                 db_scale_audio1),
 ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
 {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1412,10 +1481,14 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state)
                *d = snd_es1938_reg_read(chip, *s);
 
        outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */
-       if (chip->irq >= 0)
+       if (chip->irq >= 0) {
+               synchronize_irq(chip->irq);
                free_irq(chip->irq, chip);
+               chip->irq = -1;
+       }
        pci_disable_device(pci);
        pci_save_state(pci);
+       pci_set_power_state(pci, pci_choose_state(pci, state));
        return 0;
 }
 
@@ -1425,10 +1498,22 @@ static int es1938_resume(struct pci_dev *pci)
        struct es1938 *chip = card->private_data;
        unsigned char *s, *d;
 
+       pci_set_power_state(pci, PCI_D0);
        pci_restore_state(pci);
-       pci_enable_device(pci);
-       request_irq(pci->irq, snd_es1938_interrupt,
-                   SA_INTERRUPT|SA_SHIRQ, "ES1938", chip);
+       if (pci_enable_device(pci) < 0) {
+               printk(KERN_ERR "es1938: pci_enable_device failed, "
+                      "disabling device\n");
+               snd_card_disconnect(card);
+               return -EIO;
+       }
+
+       if (request_irq(pci->irq, snd_es1938_interrupt,
+                       IRQF_SHARED, "ES1938", chip)) {
+               printk(KERN_ERR "es1938: unable to grab IRQ %d, "
+                      "disabling device\n", pci->irq);
+               snd_card_disconnect(card);
+               return -EIO;
+       }
        chip->irq = pci->irq;
        snd_es1938_chip_init(chip);
 
@@ -1487,8 +1572,10 @@ static int snd_es1938_free(struct es1938 *chip)
 
        snd_es1938_free_gameport(chip);
 
-       if (chip->irq >= 0)
+       if (chip->irq >= 0) {
+               synchronize_irq(chip->irq);
                free_irq(chip->irq, chip);
+       }
        pci_release_regions(chip->pci);
        pci_disable_device(chip->pci);
        kfree(chip);
@@ -1517,8 +1604,8 @@ static int __devinit snd_es1938_create(struct snd_card *card,
        if ((err = pci_enable_device(pci)) < 0)
                return err;
         /* check, if we can restrict PCI DMA transfers to 24 bits */
-       if (pci_set_dma_mask(pci, 0x00ffffff) < 0 ||
-           pci_set_consistent_dma_mask(pci, 0x00ffffff) < 0) {
+       if (pci_set_dma_mask(pci, DMA_24BIT_MASK) < 0 ||
+           pci_set_consistent_dma_mask(pci, DMA_24BIT_MASK) < 0) {
                snd_printk(KERN_ERR "architecture does not support 24bit PCI busmaster DMA\n");
                pci_disable_device(pci);
                 return -ENXIO;
@@ -1533,6 +1620,7 @@ static int __devinit snd_es1938_create(struct snd_card *card,
        spin_lock_init(&chip->mixer_lock);
        chip->card = card;
        chip->pci = pci;
+       chip->irq = -1;
        if ((err = pci_request_regions(pci, "ESS Solo-1")) < 0) {
                kfree(chip);
                pci_disable_device(pci);
@@ -1543,7 +1631,7 @@ static int __devinit snd_es1938_create(struct snd_card *card,
        chip->vc_port = pci_resource_start(pci, 2);
        chip->mpu_port = pci_resource_start(pci, 3);
        chip->game_port = pci_resource_start(pci, 4);
-       if (request_irq(pci->irq, snd_es1938_interrupt, SA_INTERRUPT|SA_SHIRQ,
+       if (request_irq(pci->irq, snd_es1938_interrupt, IRQF_SHARED,
                        "ES1938", chip)) {
                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
                snd_es1938_free(chip);
@@ -1573,7 +1661,7 @@ static int __devinit snd_es1938_create(struct snd_card *card,
 /* --------------------------------------------------------------------
  * Interrupt handler
  * -------------------------------------------------------------------- */
-static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id)
 {
        struct es1938 *chip = dev_id;
        unsigned char status, audiostatus;
@@ -1645,7 +1733,7 @@ static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id, struct pt_regs *r
                // snd_es1938_mixer_bits(chip, ESSSB_IREG_MPU401CONTROL, 0x40, 0); /* ack? */
                if (chip->rmidi) {
                        handled = 1;
-                       snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+                       snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data);
                }
        }
        return IRQ_RETVAL(handled);
@@ -1755,7 +1843,8 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
                }
        }
        if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
-                               chip->mpu_port, 1, chip->irq, 0, &chip->rmidi) < 0) {
+                               chip->mpu_port, MPU401_INFO_INTEGRATED,
+                               chip->irq, 0, &chip->rmidi) < 0) {
                printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
        } else {
                // this line is vital for MIDI interrupt handling on ess-solo1