diff --git a/include/sound/soc.h b/include/sound/soc.h index 0e7735264169..a40bc6f316fc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -16,6 +16,8 @@ #include #include #include +#include +#include #include #include #include @@ -168,6 +170,9 @@ struct soc_enum; struct snd_soc_ac97_ops; struct snd_soc_jack; struct snd_soc_jack_pin; +#ifdef CONFIG_GPIOLIB +struct snd_soc_jack_gpio; +#endif typedef int (*hw_write_t)(void *,const char* ,int); typedef int (*hw_read_t)(void *,char* ,int); @@ -194,6 +199,12 @@ int snd_soc_jack_new(struct snd_soc_card *card, const char *id, int type, void snd_soc_jack_report(struct snd_soc_jack *jack, int status, int mask); int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count, struct snd_soc_jack_pin *pins); +#ifdef CONFIG_GPIOLIB +int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_gpio *gpios); +void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count, + struct snd_soc_jack_gpio *gpios); +#endif /* codec IO */ #define snd_soc_read(codec, reg) codec->read(codec, reg) @@ -264,6 +275,27 @@ struct snd_soc_jack_pin { bool invert; }; +/** + * struct snd_soc_jack_gpio - Describes a gpio pin for jack detection + * + * @gpio: gpio number + * @name: gpio name + * @report: value to report when jack detected + * @invert: report presence in low state + * @debouce_time: debouce time in ms + */ +#ifdef CONFIG_GPIOLIB +struct snd_soc_jack_gpio { + unsigned int gpio; + const char *name; + int report; + int invert; + int debounce_time; + struct snd_soc_jack *jack; + struct work_struct work; +}; +#endif + struct snd_soc_jack { struct snd_jack *jack; struct snd_soc_card *card; diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c index ab64a30bedde..bdf2484c2220 100644 --- a/sound/soc/soc-jack.c +++ b/sound/soc/soc-jack.c @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include +#include /** * 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 */