ALSA: hda_intel: fix handling of non-completion stream interrupts
[safe/jmp/linux-2.6] / sound / pci / fm801.c
index 77e3d5c..e1baad7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *  The driver for the ForteMedia FM801 based soundcards
- *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
  *
  *  Support FM only card by Andy Shevchenko <andy@smile.org.ua>
  *
@@ -20,7 +20,6 @@
  *
  */
 
-#include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -42,7 +41,7 @@
 #define TEA575X_RADIO 1
 #endif
 
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
 MODULE_DESCRIPTION("ForteMedia FM801");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{ForteMedia,FM801},"
@@ -56,7 +55,7 @@ static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;    /* Enable this card *
  *    1 = MediaForte 256-PCS
  *    2 = MediaForte 256-PCPR
  *    3 = MediaForte 64-PCR
- *   16 = setup tuner only (this is additional bit), i.e. SF-64-PCR FM card
+ *   16 = setup tuner only (this is additional bit), i.e. SF64-PCR FM card
  *  High 16-bits are video (radio) device number + 1
  */
 static int tea575x_tuner[SNDRV_CARDS];
@@ -68,7 +67,10 @@ MODULE_PARM_DESC(id, "ID string for the FM801 soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable FM801 soundcard.");
 module_param_array(tea575x_tuner, int, NULL, 0444);
-MODULE_PARM_DESC(tea575x_tuner, "Enable TEA575x tuner.");
+MODULE_PARM_DESC(tea575x_tuner, "TEA575x tuner access method (1 = SF256-PCS, 2=SF256-PCPR, 3=SF64-PCR, +16=tuner-only).");
+
+#define TUNER_ONLY             (1<<4)
+#define TUNER_TYPE_MASK                (~TUNER_ONLY & 0xFFFF)
 
 /*
  *  Direct registers
@@ -161,7 +163,7 @@ struct fm801 {
        unsigned int multichannel: 1,   /* multichannel support */
                     secondary: 1;      /* secondary codec */
        unsigned char secondary_addr;   /* address of the secondary codec */
-       unsigned int tea575x_tuner;     /* tuner flags */
+       unsigned int tea575x_tuner;     /* tuner access method & flags */
 
        unsigned short ply_ctrl; /* playback control */
        unsigned short cap_ctrl; /* capture control */
@@ -203,7 +205,7 @@ struct fm801 {
 #endif
 };
 
-static struct pci_device_id snd_fm801_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_fm801_ids) = {
        { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
        { 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* Gallant Odyssey Sound 4 */
        { 0, }
@@ -979,6 +981,27 @@ static unsigned int snd_fm801_tea575x_64pcr_read(struct snd_tea575x *tea)
        return val;
 }
 
+static void snd_fm801_tea575x_64pcr_mute(struct snd_tea575x *tea,
+                                         unsigned int mute)
+{
+       struct fm801 *chip = tea->private_data;
+       unsigned short reg;
+
+       spin_lock_irq(&chip->reg_lock);
+
+       reg = inw(FM801_REG(chip, GPIO_CTRL));
+       if (mute)
+               /* 0xf800 (mute) */
+               reg &= ~FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+       else
+               /* 0xf802 (unmute) */
+               reg |= FM801_GPIO_GP(TEA_64PCR_WRITE_ENABLE);
+       outw(reg, FM801_REG(chip, GPIO_CTRL));
+       udelay(1);
+
+       spin_unlock_irq(&chip->reg_lock);
+}
+
 static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
        {
                /* 1 = MediaForte 256-PCS */
@@ -994,6 +1017,7 @@ static struct snd_tea575x_ops snd_fm801_tea_ops[3] = {
                /* 3 = MediaForte 64-PCR */
                .write = snd_fm801_tea575x_64pcr_write,
                .read = snd_fm801_tea575x_64pcr_read,
+               .mute = snd_fm801_tea575x_64pcr_mute,
        }
 };
 #endif
@@ -1157,7 +1181,7 @@ static int snd_fm801_put_mux(struct snd_kcontrol *kcontrol,
        return snd_fm801_update_bits(chip, FM801_REC_SRC, 7, val);
 }
 
-static DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -3450, 150, 0);
 
 #define FM801_CONTROLS ARRAY_SIZE(snd_fm801_controls)
 
@@ -1264,10 +1288,9 @@ static int wait_for_codec(struct fm801 *chip, unsigned int codec_id,
 
 static int snd_fm801_chip_init(struct fm801 *chip, int resume)
 {
-       int id;
        unsigned short cmdw;
 
-       if (chip->tea575x_tuner & 0x0010)
+       if (chip->tea575x_tuner & TUNER_ONLY)
                goto __ac97_ok;
 
        /* codec cold reset + AC'97 warm reset */
@@ -1276,11 +1299,13 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
        udelay(100);
        outw(0, FM801_REG(chip, CODEC_CTRL));
 
-       if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0) {
-               snd_printk(KERN_ERR "Primary AC'97 codec not found\n");
-               if (! resume)
-                       return -EIO;
-       }
+       if (wait_for_codec(chip, 0, AC97_RESET, msecs_to_jiffies(750)) < 0)
+               if (!resume) {
+                       snd_printk(KERN_INFO "Primary AC'97 codec not found, "
+                                           "assume SF64-PCR (tuner-only)\n");
+                       chip->tea575x_tuner = 3 | TUNER_ONLY;
+                       goto __ac97_ok;
+               }
 
        if (chip->multichannel) {
                if (chip->secondary_addr) {
@@ -1289,13 +1314,14 @@ static int snd_fm801_chip_init(struct fm801 *chip, int resume)
                } else {
                        /* my card has the secondary codec */
                        /* at address #3, so the loop is inverted */
-                       for (id = 3; id > 0; id--) {
-                               if (! wait_for_codec(chip, id, AC97_VENDOR_ID1,
+                       int i;
+                       for (i = 3; i > 0; i--) {
+                               if (!wait_for_codec(chip, i, AC97_VENDOR_ID1,
                                                     msecs_to_jiffies(50))) {
                                        cmdw = inw(FM801_REG(chip, AC97_DATA));
                                        if (cmdw != 0xffff && cmdw != 0) {
                                                chip->secondary = 1;
-                                               chip->secondary_addr = id;
+                                               chip->secondary_addr = i;
                                                break;
                                        }
                                }
@@ -1369,7 +1395,6 @@ static int __devinit snd_fm801_create(struct snd_card *card,
                                      struct fm801 ** rchip)
 {
        struct fm801 *chip;
-       unsigned char rev;
        int err;
        static struct snd_device_ops ops = {
                .dev_free =     snd_fm801_dev_free,
@@ -1394,8 +1419,8 @@ static int __devinit snd_fm801_create(struct snd_card *card,
                return err;
        }
        chip->port = pci_resource_start(pci, 0);
-       if ((tea575x_tuner & 0x0010) == 0) {
-               if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_DISABLED|IRQF_SHARED,
+       if ((tea575x_tuner & TUNER_ONLY) == 0) {
+               if (request_irq(pci->irq, snd_fm801_interrupt, IRQF_SHARED,
                                "FM801", chip)) {
                        snd_printk(KERN_ERR "unable to grab IRQ %d\n", chip->irq);
                        snd_fm801_free(chip);
@@ -1405,11 +1430,18 @@ static int __devinit snd_fm801_create(struct snd_card *card,
                pci_set_master(pci);
        }
 
-       pci_read_config_byte(pci, PCI_REVISION_ID, &rev);
-       if (rev >= 0xb1)        /* FM801-AU */
+       if (pci->revision >= 0xb1)      /* FM801-AU */
                chip->multichannel = 1;
 
        snd_fm801_chip_init(chip, 0);
+       /* init might set tuner access method */
+       tea575x_tuner = chip->tea575x_tuner;
+
+       if (chip->irq >= 0 && (tea575x_tuner & TUNER_ONLY)) {
+               pci_clear_master(pci);
+               free_irq(chip->irq, chip);
+               chip->irq = -1;
+       }
 
        if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
                snd_fm801_free(chip);
@@ -1419,12 +1451,13 @@ static int __devinit snd_fm801_create(struct snd_card *card,
        snd_card_set_dev(card, &pci->dev);
 
 #ifdef TEA575X_RADIO
-       if (tea575x_tuner > 0 && (tea575x_tuner & 0x000f) < 4) {
+       if ((tea575x_tuner & TUNER_TYPE_MASK) > 0 &&
+           (tea575x_tuner & TUNER_TYPE_MASK) < 4) {
                chip->tea.dev_nr = tea575x_tuner >> 16;
                chip->tea.card = card;
                chip->tea.freq_fixup = 10700;
                chip->tea.private_data = chip;
-               chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & 0x000f) - 1];
+               chip->tea.ops = &snd_fm801_tea_ops[(tea575x_tuner & TUNER_TYPE_MASK) - 1];
                snd_tea575x_init(&chip->tea);
        }
 #endif
@@ -1449,9 +1482,9 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
                return -ENOENT;
        }
 
-       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
-       if (card == NULL)
-               return -ENOMEM;
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
+       if (err < 0)
+               return err;
        if ((err = snd_fm801_create(card, pci, tea575x_tuner[dev], &chip)) < 0) {
                snd_card_free(card);
                return err;
@@ -1464,7 +1497,7 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
        sprintf(card->longname, "%s at 0x%lx, irq %i",
                card->shortname, chip->port, chip->irq);
 
-       if (tea575x_tuner[dev] & 0x0010)
+       if (chip->tea575x_tuner & TUNER_ONLY)
                goto __fm801_tuner_only;
 
        if ((err = snd_fm801_pcm(chip, 0, NULL)) < 0) {