Skip to content

Commit

Permalink
ASoC: Add GPIO support for jack reporting interface
Browse files Browse the repository at this point in the history
Add GPIO support to jack reporting framework in ASoC using gpiolib calls.
The gpio support exports two new functions: snd_soc_jack_add_gpios and
snd_soc_jack_free_gpios.

Client drivers using gpio feature must pass an array of jack_gpio pins
belonging to a specific jack to the snd_soc_jack_add_gpios function. The
framework will request the gpios, set the data direction and request irq.
The framework will update power status of related jack_pins when an event on
the gpio pins comes according to the reporting bits defined for each gpio.

All gpio resources allocated when adding jack_gpio pins can be released
using snd_soc_jack_free_gpios function.

Signed-off-by: Misael Lopez Cruz <x0052729@ti.com>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
  • Loading branch information
Lopez Cruz, Misael authored and Mark Brown committed Mar 4, 2009
1 parent 5f2a938 commit ec67624
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 0 deletions.
32 changes: 32 additions & 0 deletions include/sound/soc.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/control.h>
Expand Down Expand Up @@ -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);
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
129 changes: 129 additions & 0 deletions sound/soc/soc-jack.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
#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
Expand Down Expand Up @@ -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 */

0 comments on commit ec67624

Please sign in to comment.