Subject: RFC: [PATCH Upated]: snd-pmac-gpio interfaces for snd-powermac

From: Ben Collins <[email-masked]>
Date: Wed, 18 Jan 2006 19:28:04 -0500

This corrects a problem in the tumbler and toonie conversion. Also fixes
a small naming change requested by BenH in snd-pmac-gpio.c

diff -urN linux-2.6/sound/ppc/Makefile linux-source-2.6.15-2.6.15/sound/ppc/Makefile
--- linux-2.6/sound/ppc/Makefile	2006-01-17 20:19:28.000000000 -0500
+++ linux-source-2.6.15-2.6.15/sound/ppc/Makefile	2006-01-17 20:30:40.000000000 -0500
@@ -3,7 +3,8 @@
 # Copyright (c) 2001 by Jaroslav Kysela <[email-masked]>
 #
 
-snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o toonie.o keywest.o beep.o
+snd-powermac-objs := powermac.o pmac.o awacs.o burgundy.o daca.o tumbler.o \
+	toonie.o keywest.o beep.o snd-pmac-gpio.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_POWERMAC) += snd-powermac.o
diff -urN linux-2.6/sound/ppc/snd-pmac-gpio.c linux-source-2.6.15-2.6.15/sound/ppc/snd-pmac-gpio.c
--- linux-2.6/sound/ppc/snd-pmac-gpio.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-source-2.6.15-2.6.15/sound/ppc/snd-pmac-gpio.c	2006-01-18 19:08:17.000000000 -0500
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2005 by Benjamin Herrenschmidt <[email-masked]>
+ * Copyright (c) 2006 by Ben Collins <[email-masked]>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <asm/pmac_feature.h>
+#include "snd-pmac-gpio.h"
+#include "pmac.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define DBG(fmt...) printk(fmt)
+#else
+#define DBG(fmt...)
+#endif
+
+
+static struct pmf_function *get_audio_pfunc(const char *name, const char *altname)
+{
+	struct device_node *np;
+	struct pmf_function *pfunc = NULL;
+
+	if (! (np = find_devices("i2s-a")))
+		return NULL;
+
+	pfunc = pmf_find_function(np, name);
+	if (pfunc == NULL && altname != NULL)
+		pfunc = pmf_find_function(np, altname);
+
+	return pfunc;
+}
+
+static struct device_node *find_audio_gpio(const char *name,
+					   const char *altname)
+{
+	struct device_node *np;
+
+	if (! (np = find_devices("gpio")))
+		return NULL;
+
+	for (np = np->child; np; np = np->sibling) {
+		char *property = get_property(np, "audio-gpio", NULL);
+		if (property && (strcmp(property, name) == 0 ||
+		    strcmp(property, altname) == 0))
+			break;
+		if (device_is_compatible(np, name) ||
+		    device_is_compatible(np, altname))
+			break;
+        }
+
+	return np;
+}
+
+static int get_audio_gpio(const char *name, const char *altname,
+			  snd_pmac_gpio_t *gp)
+{
+	struct device_node *np;
+	u32 *base, addr;
+
+	if (!(np = find_audio_gpio(name, altname)))
+		return -ENODEV;
+
+	base = (u32 *)get_property(np, "AAPL,address", NULL);
+	if (! base) {
+		base = (u32 *)get_property(np, "reg", NULL);
+		if (!base) {
+			DBG("(E) cannot find address for device %s !\n", name);
+			return -ENODEV;
+		}
+		addr = *base;
+		if (addr < 0x50)
+			addr += 0x50;
+	} else
+		addr = *base;
+
+	gp->addr = addr & 0x0000ffff;
+
+	/* Try to find the active state, default to 0 ! */
+	base = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
+	if (base) {
+		gp->active_state = *base;
+		gp->active_val = (*base) ? 0x5 : 0x4;
+		gp->inactive_val = (*base) ? 0x4 : 0x5;
+	} else {
+		/* Don't expect this to work. If platform-do isn't
+		 * available (pmac_pfunc), and the above didn't work, then
+		 * these are probably wrong.
+		 */
+		gp->active_state = 0;
+		gp->active_val = 0x4;
+		gp->inactive_val = 0x5;
+	}
+
+	DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
+	    name, gp->addr, gp->active_state);
+
+	gp->irq = (np->n_intrs > 0) ? np->intrs[0].line : 0;
+
+	return 0;
+}
+
+int snd_pmac_get_gpio(const char *name, const char *altname,
+                            snd_pmac_gpio_t *gp)
+{
+        memset(gp, 0, sizeof(*gp));
+
+	gp->name = name;
+	gp->altname = altname;
+
+        /* Platform functions are prefered */
+        if ((gp->pfunc = get_audio_pfunc(name, altname)))
+                return 0;
+
+	/* Else, fallback to direct gpio */
+	return get_audio_gpio(name, altname, gp);
+}
+
+void snd_pmac_free_gpio(snd_pmac_gpio_t *gp)
+{
+	if (gp->pfunc != NULL) {
+		if (gp->irq_client.owner == THIS_MODULE) {
+			/* XXX: pmf_unregister_irq_client doesn't use its
+			 * first two arguments. We only need to send it
+			 * the irq_client. WATCH FOR THIS CHANGING!
+			 */
+			pmf_unregister_irq_client(NULL, NULL, &gp->irq_client);
+			gp->irq_client.owner = NULL;
+		}
+
+		pmf_put_function(gp->pfunc);
+		gp->pfunc = NULL;
+	} else if (gp->addr) {
+		if (gp->irq > 0) {
+			free_irq(gp->irq, gp);
+			gp->irq = 0;
+		}
+		gp->addr = 0;
+	}
+}
+
+int snd_pmac_write_gpio(snd_pmac_gpio_t *gp, u32 val)
+{
+	int ret = -ENODEV;
+
+	if (gp->pfunc) {
+		struct pmf_args args;
+
+		args.count = 1;
+		args.u[0].v = val;
+
+		ret = pmf_call_one(gp->pfunc, &args);
+	} else if (gp->addr) {
+		val = val ? gp->active_val : gp->inactive_val;
+
+		pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, gp->addr, val);
+
+		ret = 0;
+	}
+
+	if (!ret)
+		gp->state = val;
+
+	return -EINVAL;
+}
+
+int snd_pmac_read_gpio(snd_pmac_gpio_t *gp, u32 *val)
+{
+	int ret = -EINVAL;
+
+	if (gp->pfunc) {
+		struct pmf_args args;
+
+		args.count = 1;
+		args.u[0].p = val;
+
+		ret = pmf_call_one(gp->pfunc, &args);
+	} else if (gp->addr) {
+		int ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,
+					    gp->addr, 0);
+
+		ret = ((ret & 0x02) != 0);
+		*val = ret == gp->active_state;
+
+		ret = 0;
+	}
+
+	return -EINVAL;
+}
+
+u32 snd_pmac_gpio_internal_state(snd_pmac_gpio_t *gp)
+{
+	return gp->state;
+}
+
+static irqreturn_t snd_pmac_intr(int irq, void *data, struct pt_regs *regs)
+{
+	snd_pmac_gpio_t *gp = data;
+
+	gp->irq_client.handler(gp->irq_client.data);
+
+	return IRQ_HANDLED;
+}
+
+int snd_pmac_request_irq(snd_pmac_gpio_t *gp, void (*handler)(void *),
+			 void *data)
+{
+	int ret = -ENODEV;
+	struct device_node *np;
+
+	gp->irq_client.handler = handler;
+	gp->irq_client.data = data;
+	gp->irq_client.owner = NULL;
+
+	if (gp->pfunc) {
+		gp->irq_client.owner = THIS_MODULE;
+
+		if ((np = find_devices("i2s-a"))) {
+			ret = pmf_register_irq_client(np, gp->name, &gp->irq_client);
+			if (ret < 0)
+				ret = pmf_register_irq_client(np, gp->altname, &gp->irq_client);
+		}
+		if (ret < 0)
+			gp->irq_client.owner = NULL;
+	} else if (gp->irq > 0) {
+		ret = request_irq(gp->irq, snd_pmac_intr, 0, gp->name, gp);
+		if (ret < 0)
+			gp->irq = 0;
+	}
+
+	return ret;
+}
diff -urN linux-2.6/sound/ppc/snd-pmac-gpio.h linux-source-2.6.15-2.6.15/sound/ppc/snd-pmac-gpio.h
--- linux-2.6/sound/ppc/snd-pmac-gpio.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-source-2.6.15-2.6.15/sound/ppc/snd-pmac-gpio.h	2006-01-18 17:56:07.000000000 -0500
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2005 by Benjamin Herrenschmidt <[email-masked]>
+ * Copyright (c) 2006 by Ben Collins <[email-masked]>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#ifndef SND_PMAC_GPIO_H
+#define SND_PMAC_GPIO_H 1
+
+#include <asm/pmac_pfunc.h>
+
+typedef struct snd_pmac_gpio {
+	struct pmf_function *pfunc;
+        unsigned int addr;
+        u8 active_val;
+        u8 inactive_val;
+        u8 active_state;
+	u32 state;
+
+	/* Used by irq functions */
+	int irq;
+	struct pmf_irq_client irq_client;
+	const char *name;
+	const char *altname;
+} snd_pmac_gpio_t;
+
+/* Return a handle for access to the named gpio */
+int snd_pmac_get_gpio(const char *name, const char *altname,
+		      snd_pmac_gpio_t *gp);
+
+/* Frees resources related to the gpio handle */
+void snd_pmac_free_gpio(snd_pmac_gpio_t *gp);
+
+/* GPIO Operations */
+int snd_pmac_write_gpio(snd_pmac_gpio_t *gp, u32 val);
+int snd_pmac_read_gpio(snd_pmac_gpio_t *gp, u32 *val);
+
+/* Used to get the internal state of a write only gpio */
+u32 snd_pmac_gpio_internal_state(snd_pmac_gpio_t *gp);
+
+/* Register an irq for a previously allocated gpio. This is automaticlly
+ * freed in snd_pmac_free_cpio. */
+int snd_pmac_request_irq(snd_pmac_gpio_t *gp, void (*handler)(void *),
+			 void *data);
+
+static inline int snd_pmac_gpio_valid(snd_pmac_gpio_t *gp)
+{
+	return (gp->pfunc || gp->addr) ? 1 : 0;
+}
+#endif /* SND_PMAC_GPIO_H */

diff -urN linux-2.6/sound/ppc/toonie.c linux-source-2.6.15-2.6.15/sound/ppc/toonie.c
--- linux-2.6/sound/ppc/toonie.c	2006-01-17 20:19:28.000000000 -0500
+++ linux-source-2.6.15-2.6.15/sound/ppc/toonie.c	2006-01-18 19:18:33.000000000 -0500
@@ -31,7 +31,9 @@
 #include <asm/irq.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
+
 #include "pmac.h"
+#include "snd-pmac-gpio.h"
 
 #undef DEBUG
 
@@ -41,61 +43,34 @@
 #define DBG(fmt...)
 #endif
 
-struct pmac_gpio {
-	unsigned int addr;
-	u8 active_val;
-	u8 inactive_val;
-	u8 active_state;
-};
-
 struct pmac_toonie
 {
-	struct pmac_gpio	hp_detect_gpio;
-	struct pmac_gpio	hp_mute_gpio;
-	struct pmac_gpio	amp_mute_gpio;
-	int			hp_detect_irq;
+	snd_pmac_gpio_t		hp_detect_gpio;
+	snd_pmac_gpio_t		hp_mute_gpio;
+	snd_pmac_gpio_t		amp_mute_gpio;
+
 	int			auto_mute_notify;
 	struct work_struct	detect_work;
 };

-/*
- * gpio access
- */
-#define do_gpio_write(gp, val) \
-	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val)
-#define do_gpio_read(gp) \
-	pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0)
-#define tumbler_gpio_free(gp) /* NOP */
-
-static void write_audio_gpio(struct pmac_gpio *gp, int active)
-{
-	if (! gp->addr)
-		return;
-	active = active ? gp->active_val : gp->inactive_val;
-	do_gpio_write(gp, active);
-	DBG("(I) gpio %x write %d\n", gp->addr, active);
-}
-
-static int check_audio_gpio(struct pmac_gpio *gp)
+static void toonie_reset_audio(pmac_t *chip)
 {
-	int ret;
+	snd_pmac_gpio_t gpio;
 
-	if (! gp->addr)
-		return 0;
+	if (snd_pmac_get_gpio("audio-hw-reset", "hw-reset", &gpio))
+		return;
 
-	ret = do_gpio_read(gp);
+	DBG("(I) codec normal reset !\n");
 
-	return (ret & 0xd) == (gp->active_val & 0xd);
-}
+	snd_pmac_write_gpio(&gpio, 0);
+	msleep(200);
+	snd_pmac_write_gpio(&gpio, 1);
+	msleep(100);
+	snd_pmac_write_gpio(&gpio, 0);
+	msleep(100);
 
-static int read_audio_gpio(struct pmac_gpio *gp)
-{
-	int ret;
-	if (! gp->addr)
-		return 0;
-	ret = ((do_gpio_read(gp) & 0x02) !=0);
-	return ret == gp->active_state;
+	snd_pmac_free_gpio(&gpio);
 }

@@ -106,10 +81,11 @@
 {
 	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	struct pmac_toonie *mix = chip->mixer_data;
-	struct pmac_gpio *gp;
+	snd_pmac_gpio_t *gp;
 
 	if (mix == NULL)
 		return -ENODEV;
+
 	switch(kcontrol->private_value) {
 	case TOONIE_MUTE_HP:
 		gp = &mix->hp_mute_gpio;
@@ -120,7 +96,7 @@
 	default:
 		return -EINVAL;;
 	}
-	ucontrol->value.integer.value[0] = !check_audio_gpio(gp);
+	ucontrol->value.integer.value[0] = !snd_pmac_gpio_internal_state(gp);
 	return 0;
 }
 
@@ -129,8 +105,7 @@
 {
 	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	struct pmac_toonie *mix = chip->mixer_data;
-	struct pmac_gpio *gp;
-	int val;
+	snd_pmac_gpio_t *gp;
 
 	if (chip->update_automute && chip->auto_mute)
 		return 0; /* don't touch in the auto-mute mode */
@@ -148,9 +123,8 @@
 	default:
 		return -EINVAL;;
 	}
-	val = ! check_audio_gpio(gp);
-	if (val != ucontrol->value.integer.value[0]) {
-		write_audio_gpio(gp, ! ucontrol->value.integer.value[0]);
+	if (!snd_pmac_gpio_internal_state(gp) != ucontrol->value.integer.value[0]) {
+		snd_pmac_write_gpio(gp, ! ucontrol->value.integer.value[0]);
 		return 1;
 	}
 	return 0;
@@ -181,20 +155,21 @@
 	struct pmac_toonie *mix = chip->mixer_data;
 	int detect = 0;
 
-	if (mix->hp_detect_gpio.addr)
-		detect |= read_audio_gpio(&mix->hp_detect_gpio);
+	snd_pmac_read_gpio(&mix->hp_detect_gpio, &detect);
+
 	return detect;
 }
 
-static void toonie_check_mute(pmac_t *chip, struct pmac_gpio *gp, int val,
-			      int do_notify, snd_kcontrol_t *sw)
+static int toonie_do_mute(pmac_t *chip, snd_pmac_gpio_t *gp, int val,
+			   int do_notify, snd_kcontrol_t *sw)
 {
-	if (check_audio_gpio(gp) != val) {
-		write_audio_gpio(gp, val);
-		if (do_notify)
-			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
-				       &sw->id);
-	}
+	snd_pmac_write_gpio(gp, val);
+
+	if (do_notify)
+		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       &sw->id);
+
+	return val;
 }
 
 static void toonie_detect_handler(void *self)
@@ -211,20 +186,20 @@
 
 	headphone = toonie_detect_headphone(chip);
 
-	DBG("headphone: %d, lineout: %d\n", headphone, lineout);
+	DBG("headphone: %d\n", headphone);
 
 	if (headphone) {
 		/* unmute headphone/lineout & mute speaker */
-		toonie_check_mute(chip, &mix->hp_mute_gpio, 0,
-				  mix->auto_mute_notify, chip->master_sw_ctl);
-		toonie_check_mute(chip, &mix->amp_mute_gpio, 1,
-				  mix->auto_mute_notify, chip->speaker_sw_ctl);
+		toonie_do_mute(chip, &mix->hp_mute_gpio, 0,
+			       mix->auto_mute_notify, chip->master_sw_ctl);
+		toonie_do_mute(chip, &mix->amp_mute_gpio, 1,
+			       mix->auto_mute_notify, chip->speaker_sw_ctl);
 	} else {
 		/* unmute speaker, mute others */
-		toonie_check_mute(chip, &mix->amp_mute_gpio, 0,
-				  mix->auto_mute_notify, chip->speaker_sw_ctl);
-		toonie_check_mute(chip, &mix->hp_mute_gpio, 1,
-				  mix->auto_mute_notify, chip->master_sw_ctl);
+		toonie_do_mute(chip, &mix->amp_mute_gpio, 0,
+			       mix->auto_mute_notify, chip->speaker_sw_ctl);
+		toonie_do_mute(chip, &mix->hp_mute_gpio, 1,
+			       mix->auto_mute_notify, chip->master_sw_ctl);
 	}
 	if (mix->auto_mute_notify) {
 		snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
@@ -244,85 +219,12 @@
 }
 
 /* interrupt - headphone plug changed */
-static irqreturn_t toonie_hp_intr(int irq, void *devid, struct pt_regs *regs)
+static void toonie_hp_intr(void *devid)
 {
 	pmac_t *chip = devid;
 
-	if (chip->update_automute && chip->initialized) {
+	if (chip->update_automute && chip->initialized)
 		chip->update_automute(chip, 1);
-		return IRQ_HANDLED;
-	}
-	return IRQ_NONE;
-}
-
-/* look for audio gpio device */
-static int find_audio_gpio(const char *name, const char *platform,
-			   struct pmac_gpio *gp)
-{
-	struct device_node *np;
-  	u32 *base, addr;
-
-	if (! (np = find_devices("gpio")))
-		return -ENODEV;
-
-	for (np = np->child; np; np = np->sibling) {
-		char *property = get_property(np, "audio-gpio", NULL);
-		if (property && strcmp(property, name) == 0)
-			break;
-		if (device_is_compatible(np, name))
-			break;
-	}
-	if (np == NULL)
-		return -ENODEV;
-
-	base = (u32 *)get_property(np, "AAPL,address", NULL);
-	if (! base) {
-		base = (u32 *)get_property(np, "reg", NULL);
-		if (!base) {
-			DBG("(E) cannot find address for device %s !\n", name);
-			return -ENODEV;
-		}
-		addr = *base;
-		if (addr < 0x50)
-			addr += 0x50;
-	} else
-		addr = *base;
-
-	gp->addr = addr & 0x0000ffff;
-
-	/* Try to find the active state, default to 0 ! */
-	base = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
-	if (base) {
-		gp->active_state = *base;
-		gp->active_val = (*base) ? 0x5 : 0x4;
-		gp->inactive_val = (*base) ? 0x4 : 0x5;
-	} else {
-		u32 *prop = NULL;
-		gp->active_state = 0;
-		gp->active_val = 0x4;
-		gp->inactive_val = 0x5;
-		/* Here are some crude hacks to extract the GPIO polarity and
-		 * open collector informations out of the do-platform script
-		 * as we don't yet have an interpreter for these things
-		 */
-		if (platform)
-			prop = (u32 *)get_property(np, platform, NULL);
-		if (prop) {
-			if (prop[3] == 0x9 && prop[4] == 0x9) {
-				gp->active_val = 0xd;
-				gp->inactive_val = 0xc;
-			}
-			if (prop[3] == 0x1 && prop[4] == 0x1) {
-				gp->active_val = 0x5;
-				gp->inactive_val = 0x4;
-			}
-		}
-	}
-
-	DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
-	    name, gp->addr, gp->active_state);
-
-	return (np->n_intrs > 0) ? np->intrs[0].line : 0;
 }
 
 static void toonie_cleanup(pmac_t *chip)
@@ -330,8 +232,11 @@
 	struct pmac_toonie *mix = chip->mixer_data;
 	if (! mix)
 		return;
-	if (mix->hp_detect_irq >= 0)
-		free_irq(mix->hp_detect_irq, chip);
+
+	snd_pmac_free_gpio(&mix->hp_mute_gpio);
+	snd_pmac_free_gpio(&mix->amp_mute_gpio);
+	snd_pmac_free_gpio(&mix->hp_detect_gpio);
+
 	kfree(mix);
 	chip->mixer_data = NULL;
 }
@@ -340,17 +245,20 @@
 {
 	struct pmac_toonie *mix;
 
-	mix = kmalloc(sizeof(*mix), GFP_KERNEL);
+	mix = kzalloc(sizeof(*mix), GFP_KERNEL);
 	if (! mix)
 		return -ENOMEM;
 
 	chip->mixer_data = mix;
 	chip->mixer_free = toonie_cleanup;
 
-	find_audio_gpio("headphone-mute", NULL, &mix->hp_mute_gpio);
-	find_audio_gpio("amp-mute", NULL, &mix->amp_mute_gpio);
-	mix->hp_detect_irq = find_audio_gpio("headphone-detect",
-					     NULL, &mix->hp_detect_gpio);
+	/* Atleast have to have these two */
+	if (snd_pmac_get_gpio("headphone-mute", "lineout-mute",
+			      &mix->hp_mute_gpio) ||
+	    snd_pmac_get_gpio("amp-mute", NULL,
+			      &mix->amp_mute_gpio)) {
+		return -ENODEV;
+	}
 
 	strcpy(chip->card->mixername, "PowerMac Toonie");
 
@@ -362,18 +270,18 @@
 
 	INIT_WORK(&mix->detect_work, toonie_detect_handler, (void *)chip);
 
-	if (mix->hp_detect_irq >= 0) {
+	if (!snd_pmac_get_gpio("headphone-detect", "lineout-detect",
+			       &mix->hp_detect_gpio)) {
 		snd_pmac_add_automute(chip);
 
 		chip->detect_headphone = toonie_detect_headphone;
 		chip->update_automute = toonie_update_automute;
 		toonie_update_automute(chip, 0);
 
-		if (request_irq(mix->hp_detect_irq, toonie_hp_intr, 0,
-				"Sound Headphone Detection", chip) < 0)
-			mix->hp_detect_irq = -1;
+		snd_pmac_request_irq(&mix->hp_detect_gpio, toonie_hp_intr, chip);
 	}
 
+	toonie_reset_audio(chip);
+
 	return 0;
 }
-

diff -urN linux-2.6/sound/ppc/tumbler.c linux-source-2.6.15-2.6.15/sound/ppc/tumbler.c
--- linux-2.6/sound/ppc/tumbler.c	2006-01-17 20:19:28.000000000 -0500
+++ linux-source-2.6.15-2.6.15/sound/ppc/tumbler.c	2006-01-18 19:19:00.000000000 -0500
@@ -37,7 +37,9 @@
 #include <asm/irq.h>
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
+
 #include "pmac.h"
+#include "snd-pmac-gpio.h"
 #include "tumbler_volume.h"
 
 #undef DEBUG
@@ -82,23 +84,15 @@
 	VOL_IDX_LAST_MIX
 };
 
-typedef struct pmac_gpio {
-	unsigned int addr;
-	u8 active_val;
-	u8 inactive_val;
-	u8 active_state;
-} pmac_gpio_t;
-
 typedef struct pmac_tumbler_t {
 	pmac_keywest_t i2c;
-	pmac_gpio_t audio_reset;
-	pmac_gpio_t amp_mute;
-	pmac_gpio_t line_mute;
-	pmac_gpio_t line_detect;
-	pmac_gpio_t hp_mute;
-	pmac_gpio_t hp_detect;
-	int headphone_irq;
-	int lineout_irq;
+	snd_pmac_gpio_t audio_reset;
+	snd_pmac_gpio_t amp_mute;
+	snd_pmac_gpio_t line_mute;
+	snd_pmac_gpio_t line_detect;
+	snd_pmac_gpio_t hp_mute;
+	snd_pmac_gpio_t hp_detect;
+
 	unsigned int save_master_vol[2];
 	unsigned int master_vol[2];
 	unsigned int save_master_switch[2];
@@ -163,45 +157,6 @@
 	DBG("(I) snapper init client\n");
 	return send_init_client(i2c, regs);
 }
-	
-/*
- * gpio access
- */
-#define do_gpio_write(gp, val) \
-	pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, (gp)->addr, val)
-#define do_gpio_read(gp) \
-	pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, (gp)->addr, 0)
-#define tumbler_gpio_free(gp) /* NOP */
-
-static void write_audio_gpio(pmac_gpio_t *gp, int active)
-{
-	if (! gp->addr)
-		return;
-	active = active ? gp->active_val : gp->inactive_val;
-	do_gpio_write(gp, active);
-	DBG("(I) gpio %x write %d\n", gp->addr, active);
-}
-
-static int check_audio_gpio(pmac_gpio_t *gp)
-{
-	int ret;
-
-	if (! gp->addr)
-		return 0;
-
-	ret = do_gpio_read(gp);
-
-	return (ret & 0xd) == (gp->active_val & 0xd);
-}
-
-static int read_audio_gpio(pmac_gpio_t *gp)
-{
-	int ret;
-	if (! gp->addr)
-		return 0;
-	ret = ((do_gpio_read(gp) & 0x02) !=0);
-	return ret == gp->active_state;
-}
 
 /*
  * update master volume
@@ -680,7 +635,8 @@
 {
 	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	pmac_tumbler_t *mix;
-	pmac_gpio_t *gp;
+	snd_pmac_gpio_t *gp;
+
 	if (! (mix = chip->mixer_data))
 		return -ENODEV;
 	switch(kcontrol->private_value) {
@@ -695,7 +651,8 @@
 	}
 	if (gp == NULL)
 		return -EINVAL;
-	ucontrol->value.integer.value[0] = !check_audio_gpio(gp);
+
+	ucontrol->value.integer.value[0] = !snd_pmac_gpio_internal_state(gp);
 	return 0;
 }
 
@@ -703,8 +660,7 @@
 {
 	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	pmac_tumbler_t *mix;
-	pmac_gpio_t *gp;
-	int val;
+	snd_pmac_gpio_t *gp;
 #ifdef PMAC_SUPPORT_AUTOMUTE
 	if (chip->update_automute && chip->auto_mute)
 		return 0; /* don't touch in the auto-mute mode */
@@ -723,9 +679,9 @@
 	}
 	if (gp == NULL)
 		return -EINVAL;
-	val = ! check_audio_gpio(gp);
-	if (val != ucontrol->value.integer.value[0]) {
-		write_audio_gpio(gp, ! ucontrol->value.integer.value[0]);
+
+	if (!snd_pmac_gpio_internal_state(gp) != ucontrol->value.integer.value[0]) {
+		snd_pmac_write_gpio(gp, ! ucontrol->value.integer.value[0]);
 		return 1;
 	}
 	return 0;
@@ -893,7 +849,7 @@
 	int detect = 0;
 
 	if (mix->hp_detect.addr)
-		detect |= read_audio_gpio(&mix->hp_detect);
+		snd_pmac_read_gpio(&mix->hp_detect, &detect);
 	return detect;
 }
 
@@ -903,14 +859,14 @@
 	int detect = 0;
 
 	if (mix->line_detect.addr)
-		detect |= read_audio_gpio(&mix->line_detect);
+		snd_pmac_read_gpio(&mix->line_detect, &detect);
 	return detect;
 }
 
-static void check_mute(pmac_t *chip, pmac_gpio_t *gp, int val, int do_notify, snd_kcontrol_t *sw)
+static void check_mute(pmac_t *chip, snd_pmac_gpio_t *gp, int val, int do_notify, snd_kcontrol_t *sw)
 {
-	if (check_audio_gpio(gp) != val) {
-		write_audio_gpio(gp, val);
+	if (snd_pmac_gpio_internal_state(gp) != val) {
+		snd_pmac_write_gpio(gp, val);
 		if (do_notify)
 			snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE,
 				       &sw->id);
@@ -941,7 +897,7 @@
 		if (headphone)
 			check_mute(chip, &mix->hp_mute, 0, mix->auto_mute_notify,
 				   chip->master_sw_ctl);
-		if (lineout && mix->line_mute.addr != 0)
+		if (lineout && snd_pmac_gpio_valid(&mix->line_mute))
 			check_mute(chip, &mix->line_mute, 0, mix->auto_mute_notify,
 				   chip->lineout_sw_ctl);
 		if (mix->anded_reset)
@@ -956,7 +912,7 @@
 			msleep(10);
 		check_mute(chip, &mix->hp_mute, 1, mix->auto_mute_notify,
 			   chip->master_sw_ctl);
-		if (mix->line_mute.addr != 0)
+		if (snd_pmac_gpio_valid(&mix->line_mute))
 			check_mute(chip, &mix->line_mute, 1, mix->auto_mute_notify,
 				   chip->lineout_sw_ctl);
 	}
@@ -993,111 +949,11 @@

 /* interrupt - headphone plug changed */
-static irqreturn_t headphone_intr(int irq, void *devid, struct pt_regs *regs)
+static void headphone_intr(void *devid)
 {
 	pmac_t *chip = devid;
-	if (chip->update_automute && chip->initialized) {
+	if (chip->update_automute && chip->initialized)
 		chip->update_automute(chip, 1);
-		return IRQ_HANDLED;
-	}
-	return IRQ_NONE;
-}
-
-/* look for audio-gpio device */
-static struct device_node *find_audio_device(const char *name)
-{
-	struct device_node *np;
-  
-	if (! (np = find_devices("gpio")))
-		return NULL;
-  
-	for (np = np->child; np; np = np->sibling) {
-		char *property = get_property(np, "audio-gpio", NULL);
-		if (property && strcmp(property, name) == 0)
-			return np;
-	}  
-	return NULL;
-}
-
-/* look for audio-gpio device */
-static struct device_node *find_compatible_audio_device(const char *name)
-{
-	struct device_node *np;
-  
-	if (! (np = find_devices("gpio")))
-		return NULL;
-  
-	for (np = np->child; np; np = np->sibling) {
-		if (device_is_compatible(np, name))
-			return np;
-	}  
-	return NULL;
-}
-
-/* find an audio device and get its address */
-static long tumbler_find_device(const char *device, const char *platform, pmac_gpio_t *gp, int is_compatible)
-{
-	struct device_node *node;
-	u32 *base, addr;
-
-	if (is_compatible)
-		node = find_compatible_audio_device(device);
-	else
-		node = find_audio_device(device);
-	if (! node) {
-		DBG("(W) cannot find audio device %s !\n", device);
-		snd_printdd("cannot find device %s\n", device);
-		return -ENODEV;
-	}
-
-	base = (u32 *)get_property(node, "AAPL,address", NULL);
-	if (! base) {
-		base = (u32 *)get_property(node, "reg", NULL);
-		if (!base) {
-			DBG("(E) cannot find address for device %s !\n", device);
-			snd_printd("cannot find address for device %s\n", device);
-			return -ENODEV;
-		}
-		addr = *base;
-		if (addr < 0x50)
-			addr += 0x50;
-	} else
-		addr = *base;
-
-	gp->addr = addr & 0x0000ffff;
-	/* Try to find the active state, default to 0 ! */
-	base = (u32 *)get_property(node, "audio-gpio-active-state", NULL);
-	if (base) {
-		gp->active_state = *base;
-		gp->active_val = (*base) ? 0x5 : 0x4;
-		gp->inactive_val = (*base) ? 0x4 : 0x5;
-	} else {
-		u32 *prop = NULL;
-		gp->active_state = 0;
-		gp->active_val = 0x4;
-		gp->inactive_val = 0x5;
-		/* Here are some crude hacks to extract the GPIO polarity and
-		 * open collector informations out of the do-platform script
-		 * as we don't yet have an interpreter for these things
-		 */
-		if (platform)
-			prop = (u32 *)get_property(node, platform, NULL);
-		if (prop) {
-			if (prop[3] == 0x9 && prop[4] == 0x9) {
-				gp->active_val = 0xd;
-				gp->inactive_val = 0xc;
-			}
-			if (prop[3] == 0x1 && prop[4] == 0x1) {
-				gp->active_val = 0x5;
-				gp->inactive_val = 0x4;
-			}
-		}
-	}
-
-	DBG("(I) GPIO device %s found, offset: %x, active state: %d !\n",
-	    device, gp->addr, gp->active_state);
-
-	return (node->n_intrs > 0) ? node->intrs[0].line : 0;
 }
 
 /* reset audio */
@@ -1107,23 +963,23 @@
 
 	if (mix->anded_reset) {
 		DBG("(I) codec anded reset !\n");
-		write_audio_gpio(&mix->hp_mute, 0);
-		write_audio_gpio(&mix->amp_mute, 0);
+		snd_pmac_write_gpio(&mix->hp_mute, 0);
+		snd_pmac_write_gpio(&mix->amp_mute, 0);
 		msleep(200);
-		write_audio_gpio(&mix->hp_mute, 1);
-		write_audio_gpio(&mix->amp_mute, 1);
+		snd_pmac_write_gpio(&mix->hp_mute, 1);
+		snd_pmac_write_gpio(&mix->amp_mute, 1);
 		msleep(100);
-		write_audio_gpio(&mix->hp_mute, 0);
-		write_audio_gpio(&mix->amp_mute, 0);
+		snd_pmac_write_gpio(&mix->hp_mute, 0);
+		snd_pmac_write_gpio(&mix->amp_mute, 0);
 		msleep(100);
 	} else {
 		DBG("(I) codec normal reset !\n");
 
-		write_audio_gpio(&mix->audio_reset, 0);
+		snd_pmac_write_gpio(&mix->audio_reset, 0);
 		msleep(200);
-		write_audio_gpio(&mix->audio_reset, 1);
+		snd_pmac_write_gpio(&mix->audio_reset, 1);
 		msleep(100);
-		write_audio_gpio(&mix->audio_reset, 0);
+		snd_pmac_write_gpio(&mix->audio_reset, 0);
 		msleep(100);
 	}
 }
@@ -1134,10 +990,6 @@
 {
 	pmac_tumbler_t *mix = chip->mixer_data;
 
-	if (mix->headphone_irq >= 0)
-		disable_irq(mix->headphone_irq);
-	if (mix->lineout_irq >= 0)
-		disable_irq(mix->lineout_irq);
 	mix->save_master_switch[0] = mix->master_switch[0];
 	mix->save_master_switch[1] = mix->master_switch[1];
 	mix->save_master_vol[0] = mix->master_vol[0];
@@ -1145,18 +997,32 @@
 	mix->master_switch[0] = mix->master_switch[1] = 0;
 	tumbler_set_master_volume(mix);
 	if (!mix->anded_reset) {
-		write_audio_gpio(&mix->amp_mute, 1);
-		write_audio_gpio(&mix->hp_mute, 1);
+		snd_pmac_write_gpio(&mix->amp_mute, 1);
+		snd_pmac_write_gpio(&mix->hp_mute, 1);
 	}
 	if (chip->model == PMAC_SNAPPER) {
 		mix->acs |= 1;
 		i2c_smbus_write_byte_data(mix->i2c.client, TAS_REG_ACS, mix->acs);
 	}
 	if (mix->anded_reset) {
-		write_audio_gpio(&mix->amp_mute, 1);
-		write_audio_gpio(&mix->hp_mute, 1);
+		snd_pmac_write_gpio(&mix->amp_mute, 1);
+		snd_pmac_write_gpio(&mix->hp_mute, 1);
 	} else
-		write_audio_gpio(&mix->audio_reset, 1);
+		snd_pmac_write_gpio(&mix->audio_reset, 1);
+}
+
+static void activate_status_interrupts(snd_pmac_gpio_t *gp)
+{
+	int val;
+
+	/* XXX: This is broken with platform functions. We can't write a a
+	 * read function, and this will actuall fail (oops). */
+	return;
+
+	if (snd_pmac_gpio_valid(gp)) {
+		snd_pmac_read_gpio(gp, &val);
+		snd_pmac_write_gpio(gp, val | 0x80);
+	}
 }
 
 /* resume mixer */
@@ -1194,69 +1060,34 @@
 	tumbler_set_master_volume(mix);
 	if (chip->update_automute)
 		chip->update_automute(chip, 0);
-	if (mix->headphone_irq >= 0) {
-		unsigned char val;
 
-		enable_irq(mix->headphone_irq);
-		/* activate headphone status interrupts */
-		val = do_gpio_read(&mix->hp_detect);
-		do_gpio_write(&mix->hp_detect, val | 0x80);
-	}
-	if (mix->lineout_irq >= 0)
-		enable_irq(mix->lineout_irq);
+	activate_status_interrupts(&mix->hp_detect);
 }
 #endif
 
 /* initialize tumbler */
 static int __init tumbler_init(pmac_t *chip)
 {
-	int irq;
+	int ret = 0;
+
 	pmac_tumbler_t *mix = chip->mixer_data;
 	snd_assert(mix, return -EINVAL);
 
-	if (tumbler_find_device("audio-hw-reset",
-				"platform-do-hw-reset",
-				&mix->audio_reset, 0) < 0)
-		tumbler_find_device("hw-reset",
-				    "platform-do-hw-reset",
-				    &mix->audio_reset, 1);
-	if (tumbler_find_device("amp-mute",
-				"platform-do-amp-mute",
-				&mix->amp_mute, 0) < 0)
-		tumbler_find_device("amp-mute",
-				    "platform-do-amp-mute",
-				    &mix->amp_mute, 1);
-	if (tumbler_find_device("headphone-mute",
-				"platform-do-headphone-mute",
-				&mix->hp_mute, 0) < 0)
-		tumbler_find_device("headphone-mute",
-				    "platform-do-headphone-mute",
-				    &mix->hp_mute, 1);
-	if (tumbler_find_device("line-output-mute",
-				"platform-do-lineout-mute",
-				&mix->line_mute, 0) < 0)
-		tumbler_find_device("line-output-mute",
-				   "platform-do-lineout-mute",
-				    &mix->line_mute, 1);
-	irq = tumbler_find_device("headphone-detect",
-				  NULL, &mix->hp_detect, 0);
-	if (irq < 0)
-		irq = tumbler_find_device("headphone-detect",
-					  NULL, &mix->hp_detect, 1);
-	if (irq < 0)
-		irq = tumbler_find_device("keywest-gpio15",
-					  NULL, &mix->hp_detect, 1);
-	mix->headphone_irq = irq;
- 	irq = tumbler_find_device("line-output-detect",
-				  NULL, &mix->line_detect, 0);
- 	if (irq < 0)
-		irq = tumbler_find_device("line-output-detect",
-					  NULL, &mix->line_detect, 1);
-	mix->lineout_irq = irq;
+	ret |= snd_pmac_get_gpio("audio-hw-reset", "hw-reset",
+				 &mix->audio_reset);
+	ret |= snd_pmac_get_gpio("amp-mute", NULL, &mix->amp_mute);
+	ret |= snd_pmac_get_gpio("headphone-mute", NULL, &mix->hp_mute);
+	ret |= snd_pmac_get_gpio("line-output-mute", "lineout-mute",
+				 &mix->line_mute);
+
+	snd_pmac_get_gpio("headphone-detect", "keywest-gpio15",
+			  &mix->hp_detect);
+	snd_pmac_get_gpio("line-output-detect", "lineout-detect",
+			  &mix->line_detect);
 
 	tumbler_reset_audio(chip);
   
-	return 0;
+	return ret;
 }
 
 static void tumbler_cleanup(pmac_t *chip)
@@ -1265,15 +1096,14 @@
 	if (! mix)
 		return;
 
-	if (mix->headphone_irq >= 0)
-		free_irq(mix->headphone_irq, chip);
-	if (mix->lineout_irq >= 0)
-		free_irq(mix->lineout_irq, chip);
-	tumbler_gpio_free(&mix->audio_reset);
-	tumbler_gpio_free(&mix->amp_mute);
-	tumbler_gpio_free(&mix->hp_mute);
-	tumbler_gpio_free(&mix->hp_detect);
+	snd_pmac_free_gpio(&mix->audio_reset);
+	snd_pmac_free_gpio(&mix->amp_mute);
+	snd_pmac_free_gpio(&mix->hp_mute);
+	snd_pmac_free_gpio(&mix->hp_detect);
+	snd_pmac_free_gpio(&mix->line_detect);
+
 	snd_pmac_keywest_cleanup(&mix->i2c);
+
 	kfree(mix);
 	chip->mixer_data = NULL;
 }
@@ -1296,7 +1126,6 @@
 	if (! mix)
 		return -ENOMEM;
 	memset(mix, 0, sizeof(*mix));
-	mix->headphone_irq = -1;
 
 	chip->mixer_data = mix;
 	chip->mixer_free = tumbler_cleanup;
@@ -1367,7 +1196,7 @@
 	chip->speaker_sw_ctl = snd_ctl_new1(&tumbler_speaker_sw, chip);
 	if ((err = snd_ctl_add(chip->card, chip->speaker_sw_ctl)) < 0)
 		return err;
-	if (mix->line_mute.addr != 0) {
+	if (snd_pmac_gpio_valid(&mix->line_mute)) {
 		chip->lineout_sw_ctl = snd_ctl_new1(&tumbler_lineout_sw, chip);
 		if ((err = snd_ctl_add(chip->card, chip->lineout_sw_ctl)) < 0)
 			return err;
@@ -1395,7 +1224,7 @@
 	INIT_WORK(&device_change, device_change_handler, (void *)chip);
 
 #ifdef PMAC_SUPPORT_AUTOMUTE
-	if ((mix->headphone_irq >=0 || mix->lineout_irq >= 0)
+	if ((snd_pmac_gpio_valid(&mix->hp_detect) || snd_pmac_gpio_valid(&mix->line_detect))
 	    && (err = snd_pmac_add_automute(chip)) < 0)
 		return err;
 	chip->detect_headphone = tumbler_detect_headphone;
@@ -1403,23 +1232,15 @@
 	tumbler_update_automute(chip, 0); /* update the status only */
 
 	/* activate headphone status interrupts */
-  	if (mix->headphone_irq >= 0) {
-		unsigned char val;
-		if ((err = request_irq(mix->headphone_irq, headphone_intr, 0,
-				       "Sound Headphone Detection", chip)) < 0)
+  	if (snd_pmac_gpio_valid(&mix->hp_detect)) {
+		if (snd_pmac_request_irq(&mix->hp_detect, headphone_intr, chip) < 0)
 			return 0;
-		/* activate headphone status interrupts */
-		val = do_gpio_read(&mix->hp_detect);
-		do_gpio_write(&mix->hp_detect, val | 0x80);
-	}
-  	if (mix->lineout_irq >= 0) {
-		unsigned char val;
-		if ((err = request_irq(mix->lineout_irq, headphone_intr, 0,
-				       "Sound Lineout Detection", chip)) < 0)
+		activate_status_interrupts(&mix->hp_detect);
+	}
+  	if (snd_pmac_gpio_valid(&mix->line_detect)) {
+		if (snd_pmac_request_irq(&mix->line_detect, headphone_intr, chip) < 0)
 			return 0;
-		/* activate headphone status interrupts */
-		val = do_gpio_read(&mix->line_detect);
-		do_gpio_write(&mix->line_detect, val | 0x80);
+		activate_status_interrupts(&mix->line_detect);
 	}
 #endif