pnp: add ISAPnP MPU option quirks
Rene Herman [Wed, 14 May 2008 23:05:36 +0000 (16:05 -0700)]
The AD181x and AZT230 chips don't support an IRQ-less MPU401 option but
work fine without one.  This adds (priority functional) IRQ-less options
for each port option to help systems with few available IRQs.

The AD1815 quirk can't use pnp_register_irq_resource() due to doubly
penalizing the IRQ.  Also, while not a practical issue due to no IRQ
option being present for the dependents, this needs to add in front, not
back.

Doesn't use pnp_register_port_resource() for symetry with above.

This does not delete the AD1815 independent option even though it should
be empty after the IRQ transfer due to AD1816 coming with an empty but
still present independent option by default.

Was tested on AD1815, AD1816 and AZT2320.  The ALSA snd-ad1818a driver
also support the AZT2002 ID for MPU401 but this doesn't as I was unable to
test it.

Signed-off-by: Rene Herman <rene.herman@gmail.com>
Tested-by: Uwe Bugla <uwe.bugla@gmx.de>
Acked-by: Uwe Bugla <uwe.bugla@gmx.de>
Acked-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Cc: Takashi Iwai <tiwai@suse.de>
Cc: Len Brown <len.brown@intel.com
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

drivers/pnp/quirks.c

index a1af2f9..ffdb12a 100644 (file)
@@ -111,6 +111,113 @@ static void quirk_sb16audio_resources(struct pnp_dev *dev)
                dev_info(&dev->dev, "SB audio device quirk - increased port range\n");
 }
 
+static struct pnp_option *quirk_isapnp_mpu_options(struct pnp_dev *dev)
+{
+       struct pnp_option *head = NULL;
+       struct pnp_option *prev = NULL;
+       struct pnp_option *res;
+
+       /*
+        * Build a functional IRQ-less variant of each MPU option.
+        */
+
+       for (res = dev->dependent; res; res = res->next) {
+               struct pnp_option *curr;
+               struct pnp_port *port;
+               struct pnp_port *copy;
+
+               port = res->port;
+               if (!port || !res->irq)
+                       continue;
+
+               copy = pnp_alloc(sizeof *copy);
+               if (!copy)
+                       break;
+
+               copy->min = port->min;
+               copy->max = port->max;
+               copy->align = port->align;
+               copy->size = port->size;
+               copy->flags = port->flags;
+
+               curr = pnp_build_option(PNP_RES_PRIORITY_FUNCTIONAL);
+               if (!curr) {
+                       kfree(copy);
+                       break;
+               }
+               curr->port = copy;
+
+               if (prev)
+                       prev->next = curr;
+               else
+                       head = curr;
+               prev = curr;
+       }
+       if (head)
+               dev_info(&dev->dev, "adding IRQ-less MPU options\n");
+
+       return head;
+}
+
+static void quirk_ad1815_mpu_resources(struct pnp_dev *dev)
+{
+       struct pnp_option *res;
+       struct pnp_irq *irq;
+
+       /*
+        * Distribute the independent IRQ over the dependent options
+        */
+
+       res = dev->independent;
+       if (!res)
+               return;
+
+       irq = res->irq;
+       if (!irq || irq->next)
+               return;
+
+       res = dev->dependent;
+       if (!res)
+               return;
+
+       while (1) {
+               struct pnp_irq *copy;
+
+               copy = pnp_alloc(sizeof *copy);
+               if (!copy)
+                       break;
+
+               memcpy(copy->map, irq->map, sizeof copy->map);
+               copy->flags = irq->flags;
+
+               copy->next = res->irq; /* Yes, this is NULL */
+               res->irq = copy;
+
+               if (!res->next)
+                       break;
+               res = res->next;
+       }
+       kfree(irq);
+
+       res->next = quirk_isapnp_mpu_options(dev);
+
+       res = dev->independent;
+       res->irq = NULL;
+}
+
+static void quirk_isapnp_mpu_resources(struct pnp_dev *dev)
+{
+       struct pnp_option *res;
+
+       res = dev->dependent;
+       if (!res)
+               return;
+
+       while (res->next)
+               res = res->next;
+
+       res->next = quirk_isapnp_mpu_options(dev);
+}
 
 #include <linux/pci.h>
 
@@ -205,6 +312,11 @@ static struct pnp_fixup pnp_fixups[] = {
        {"CTL0043", quirk_sb16audio_resources},
        {"CTL0044", quirk_sb16audio_resources},
        {"CTL0045", quirk_sb16audio_resources},
+       /* Add IRQ-less MPU options */
+       {"ADS7151", quirk_ad1815_mpu_resources},
+       {"ADS7181", quirk_isapnp_mpu_resources},
+       {"AZT0002", quirk_isapnp_mpu_resources},
+       /* PnP resources that might overlap PCI BARs */
        {"PNP0c01", quirk_system_pci_resources},
        {"PNP0c02", quirk_system_pci_resources},
        {""}