ASoC: Add GPIO support for jack reporting interface
[linux-2.6.git] / sound / soc / soc-jack.c
index ab64a30..bdf2484 100644 (file)
 #include <sound/jack.h>
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
 
 /**
  * snd_soc_jack_new - Create a new jack
@@ -136,3 +140,128 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_jack_add_pins);
+
+#ifdef CONFIG_GPIOLIB
+/* gpio detect */
+void snd_soc_jack_gpio_detect(struct snd_soc_jack_gpio *gpio)
+{
+       struct snd_soc_jack *jack = gpio->jack;
+       int enable;
+       int report;
+
+       if (gpio->debounce_time > 0)
+               mdelay(gpio->debounce_time);
+
+       enable = gpio_get_value(gpio->gpio);
+       if (gpio->invert)
+               enable = !enable;
+
+       if (enable)
+               report = gpio->report;
+       else
+               report = 0;
+
+       snd_soc_jack_report(jack, report, gpio->report);
+}
+
+/* irq handler for gpio pin */
+static irqreturn_t gpio_handler(int irq, void *data)
+{
+       struct snd_soc_jack_gpio *gpio = data;
+
+       schedule_work(&gpio->work);
+
+       return IRQ_HANDLED;
+}
+
+/* gpio work */
+static void gpio_work(struct work_struct *work)
+{
+       struct snd_soc_jack_gpio *gpio;
+
+       gpio = container_of(work, struct snd_soc_jack_gpio, work);
+       snd_soc_jack_gpio_detect(gpio);
+}
+
+/**
+ * snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack
+ *
+ * @jack:  ASoC jack
+ * @count: number of pins
+ * @gpios: array of gpio pins
+ *
+ * This function will request gpio, set data direction and request irq
+ * for each gpio in the array.
+ */
+int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
+                       struct snd_soc_jack_gpio *gpios)
+{
+       int i, ret;
+
+       for (i = 0; i < count; i++) {
+               if (!gpio_is_valid(gpios[i].gpio)) {
+                       printk(KERN_ERR "Invalid gpio %d\n",
+                               gpios[i].gpio);
+                       ret = -EINVAL;
+                       goto undo;
+               }
+               if (!gpios[i].name) {
+                       printk(KERN_ERR "No name for gpio %d\n",
+                               gpios[i].gpio);
+                       ret = -EINVAL;
+                       goto undo;
+               }
+
+               ret = gpio_request(gpios[i].gpio, gpios[i].name);
+               if (ret)
+                       goto undo;
+
+               ret = gpio_direction_input(gpios[i].gpio);
+               if (ret)
+                       goto err;
+
+               ret = request_irq(gpio_to_irq(gpios[i].gpio),
+                               gpio_handler,
+                               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+                               jack->card->dev->driver->name,
+                               &gpios[i]);
+               if (ret)
+                       goto err;
+
+               INIT_WORK(&gpios[i].work, gpio_work);
+               gpios[i].jack = jack;
+       }
+
+       return 0;
+
+err:
+       gpio_free(gpios[i].gpio);
+undo:
+       snd_soc_jack_free_gpios(jack, i, gpios);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_add_gpios);
+
+/**
+ * snd_soc_jack_free_gpios - Release GPIO pins' resources of an ASoC jack
+ *
+ * @jack:  ASoC jack
+ * @count: number of pins
+ * @gpios: array of gpio pins
+ *
+ * Release gpio and irq resources for gpio pins associated with an ASoC jack.
+ */
+void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
+                       struct snd_soc_jack_gpio *gpios)
+{
+       int i;
+
+       for (i = 0; i < count; i++) {
+               free_irq(gpio_to_irq(gpios[i].gpio), &gpios[i]);
+               gpio_free(gpios[i].gpio);
+               gpios[i].jack = NULL;
+       }
+}
+EXPORT_SYMBOL_GPL(snd_soc_jack_free_gpios);
+#endif /* CONFIG_GPIOLIB */