drm/nouveau/gpio: reimplement as nouveau_gpio.c, fixing a number of issues

- moves out of nouveau_bios.c and demagics the logical state definitions
- simplifies chipset-specific driver interface
- makes most of gpio irq handling common, will use for nv4x hpd later
- api extended to allow both direct gpio access, and access using the
  logical function states
- api extended to allow for future use of gpio extender chips
- pre-nv50 was handled very badly, the main issue being that all GPIOs
  were being treated as output-only.
- fixes nvd0 so gpio changes actually stick, magic reg needs bashing

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c
index 788ba33..aba3362 100644
--- a/drivers/gpu/drm/nouveau/nouveau_pm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_pm.c
@@ -26,6 +26,7 @@
 
 #include "nouveau_drv.h"
 #include "nouveau_pm.h"
+#include "nouveau_gpio.h"
 
 #ifdef CONFIG_ACPI
 #include <linux/acpi.h>
@@ -38,27 +39,25 @@
 nouveau_pwmfan_get(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
 	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
-	struct dcb_gpio_entry *gpio = NULL;
+	struct gpio_func gpio;
 	u32 divs, duty;
 	int ret;
 
 	if (!pm->pwm_get)
 		return -ENODEV;
 
-	gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN);
-	if (gpio) {
-		ret = pm->pwm_get(dev, gpio->line, &divs, &duty);
+	ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
+	if (ret == 0) {
+		ret = pm->pwm_get(dev, gpio.line, &divs, &duty);
 		if (ret == 0) {
 			divs = max(divs, duty);
-			if (dev_priv->card_type <= NV_40 ||
-			    (gpio->state[0] & 1))
+			if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
 				duty = divs - duty;
 			return (duty * 100) / divs;
 		}
 
-		return pgpio->get(dev, gpio->tag) * 100;
+		return nouveau_gpio_func_get(dev, gpio.func) * 100;
 	}
 
 	return -ENODEV;
@@ -69,14 +68,15 @@
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
-	struct dcb_gpio_entry *gpio;
+	struct gpio_func gpio;
 	u32 divs, duty;
+	int ret;
 
 	if (!pm->pwm_set)
 		return -ENODEV;
 
-	gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_PWM_FAN);
-	if (gpio) {
+	ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
+	if (ret == 0) {
 		divs = pm->pwm_divisor;
 		if (pm->fan.pwm_freq) {
 			/*XXX: PNVIO clock more than likely... */
@@ -86,11 +86,10 @@
 		}
 
 		duty = ((divs * percent) + 99) / 100;
-		if (dev_priv->card_type <= NV_40 ||
-		    (gpio->state[0] & 1))
+		if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
 			duty = divs - duty;
 
-		return pm->pwm_set(dev, gpio->line, divs, duty);
+		return pm->pwm_set(dev, gpio.line, divs, duty);
 	}
 
 	return -ENODEV;
@@ -472,24 +471,24 @@
 	struct drm_device *dev = dev_get_drvdata(d);
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
-	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
-	struct dcb_gpio_entry *gpio;
+	struct gpio_func gpio;
 	u32 cycles, cur, prev;
 	u64 start;
+	int ret;
 
-	gpio = nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE);
-	if (!gpio)
-		return -ENODEV;
+	ret = nouveau_gpio_find(dev, 0, DCB_GPIO_FAN_SENSE, 0xff, &gpio);
+	if (ret)
+		return ret;
 
 	/* Monitor the GPIO input 0x3b for 250ms.
 	 * When the fan spins, it changes the value of GPIO FAN_SENSE.
 	 * We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation.
 	 */
 	start = ptimer->read(dev);
-	prev = pgpio->get(dev, DCB_GPIO_FAN_SENSE);
+	prev = nouveau_gpio_sense(dev, 0, gpio.line);
 	cycles = 0;
 	do {
-		cur = pgpio->get(dev, DCB_GPIO_FAN_SENSE);
+		cur = nouveau_gpio_sense(dev, 0, gpio.line);
 		if (prev != cur) {
 			cycles++;
 			prev = cur;
@@ -701,7 +700,7 @@
 	}
 
 	/* if the card can read the fan rpm */
-	if (nouveau_bios_gpio_entry(dev, DCB_GPIO_FAN_SENSE)) {
+	if (nouveau_gpio_func_valid(dev, DCB_GPIO_FAN_SENSE)) {
 		ret = sysfs_create_group(&dev->pdev->dev.kobj,
 					 &hwmon_fan_rpm_attrgroup);
 		if (ret)