Skip to content

Commit

Permalink
ASoC: Add support for WM8962 GPIO outputs
Browse files Browse the repository at this point in the history
The WM8962 features five GPIOs, add support for controlling their output
state via gpiolib.

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
  • Loading branch information
Mark Brown committed Oct 2, 2010
1 parent 205d231 commit 3367b8d
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions include/sound/wm8962.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define WM8962_GPIO_SET 0x10000

struct wm8962_pdata {
int gpio_base;
u32 gpio_init[WM8962_MAX_GPIO];

/* Setup for microphone detection, raw value to be written to
Expand Down
119 changes: 119 additions & 0 deletions sound/soc/codecs/wm8962.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/platform_device.h>
Expand Down Expand Up @@ -70,6 +71,10 @@ struct wm8962_priv {
struct work_struct beep_work;
int beep_rate;
#endif

#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
};

/* We can't use the same notifier block for more than one supply and
Expand Down Expand Up @@ -1646,6 +1651,118 @@ static void wm8962_free_beep(struct snd_soc_codec *codec)
}
#endif

#ifdef CONFIG_GPIOLIB
static inline struct wm8962_priv *gpio_to_wm8962(struct gpio_chip *chip)
{
return container_of(chip, struct wm8962_priv, gpio_chip);
}

static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
{
struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
struct snd_soc_codec *codec = wm8962->codec;
int mask = 0;
int val;

/* The WM8962 GPIOs aren't linearly numbered. For simplicity
* we export linear numbers and error out if the unsupported
* ones are requsted.
*/
switch (offset + 1) {
case 2:
mask = WM8962_CLKOUT2_SEL_MASK;
val = 1 << WM8962_CLKOUT2_SEL_SHIFT;
break;
case 3:
mask = WM8962_CLKOUT3_SEL_MASK;
val = 1 << WM8962_CLKOUT3_SEL_SHIFT;
break;
case 5:
case 6:
break;
default:
return -EINVAL;
}

/* Some of the GPIOs are behind MFP configuration */
if (mask)
snd_soc_update_bits(codec, WM8962_ANALOGUE_CLOCKING1,
mask, val);

return 0;
}

static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
struct snd_soc_codec *codec = wm8962->codec;

snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
WM8962_GP2_LVL, value << WM8962_GP2_LVL_SHIFT);
}

static int wm8962_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct wm8962_priv *wm8962 = gpio_to_wm8962(chip);
struct snd_soc_codec *codec = wm8962->codec;
int val;

/* Force function 1 (logic output) */
val = (1 << WM8962_GP2_FN_SHIFT) | (value << WM8962_GP2_LVL_SHIFT);

return snd_soc_update_bits(codec, WM8962_GPIO_BASE + offset,
WM8962_GP2_FN_MASK | WM8962_GP2_LVL, val);
}

static struct gpio_chip wm8962_template_chip = {
.label = "wm8962",
.owner = THIS_MODULE,
.request = wm8962_gpio_request,
.direction_output = wm8962_gpio_direction_out,
.set = wm8962_gpio_set,
.can_sleep = 1,
};

static void wm8962_init_gpio(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
struct wm8962_pdata *pdata = dev_get_platdata(codec->dev);
int ret;

wm8962->gpio_chip = wm8962_template_chip;
wm8962->gpio_chip.ngpio = WM8962_MAX_GPIO;
wm8962->gpio_chip.dev = codec->dev;

if (pdata && pdata->gpio_base)
wm8962->gpio_chip.base = pdata->gpio_base;
else
wm8962->gpio_chip.base = -1;

ret = gpiochip_add(&wm8962->gpio_chip);
if (ret != 0)
dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
}

static void wm8962_free_gpio(struct snd_soc_codec *codec)
{
struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
int ret;

ret = gpiochip_remove(&wm8962->gpio_chip);
if (ret != 0)
dev_err(codec->dev, "Failed to remove GPIOs: %d\n", ret);
}
#else
static void wm8962_init_gpio(struct snd_soc_codec *codec)
{
}

static void wm8962_free_gpio(struct snd_soc_codec *codec)
{
}
#endif

static int wm8962_probe(struct snd_soc_codec *codec)
{
int ret;
Expand Down Expand Up @@ -1778,6 +1895,7 @@ static int wm8962_probe(struct snd_soc_codec *codec)
wm8962_add_widgets(codec);

wm8962_init_beep(codec);
wm8962_init_gpio(codec);

if (i2c->irq) {
if (pdata && pdata->irq_active_low) {
Expand Down Expand Up @@ -1828,6 +1946,7 @@ static int wm8962_remove(struct snd_soc_codec *codec)
if (i2c->irq)
free_irq(i2c->irq, codec);

wm8962_free_gpio(codec);
wm8962_free_beep(codec);
for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++)
regulator_unregister_notifier(wm8962->supplies[i].consumer,
Expand Down
1 change: 1 addition & 0 deletions sound/soc/codecs/wm8962.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
#define WM8962_EQ39 0x175
#define WM8962_EQ40 0x176
#define WM8962_EQ41 0x177
#define WM8962_GPIO_BASE 0x200
#define WM8962_GPIO_2 0x201
#define WM8962_GPIO_3 0x202
#define WM8962_GPIO_5 0x204
Expand Down

0 comments on commit 3367b8d

Please sign in to comment.