ASoC: Add a GPIO chip for AC'97

GPIOs are part of the AC'97 spec, enable their use on embedded platforms
 using AC'97.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQEcBAABAgAGBQJWX4h6AAoJECTWi3JdVIfQhIoH/2BB+Cdyzob3aAcIarYAPdsZ
 bMXjDYyTyK1HanEUXGueQ7+CruQqXynRHhN3VRhmbjhJ+DgDP2GdgG53bm6q0J5z
 TjoflP51OMX2boVokdyR3omOvuvoBDvhutVrHk5b/3Uye0p2yQ9kqdZgbvc8QE21
 8QvJrm1PUg4OTj+xdUdl/uKweV7VUVMBob11NIM2C8UvURSv0lV+4UG/jy/fMHlz
 KA/U0mPqCAz93XahxDHJzJsAO2O3q7K5Z1y5ZT0YXfrLiOFRGSIQSVjSZjPxAJkm
 TAO6UMrxvLPEWuSqf5NcaMve5gveVbmHfocE/JvnRO8ylgGswF7wSflMPbcuYQ8=
 =Fg8T
 -----END PGP SIGNATURE-----

Merge tag 'asoc-ac97-gpio' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into devel

ASoC: Add a GPIO chip for AC'97

GPIOs are part of the AC'97 spec, enable their use on embedded platforms
using AC'97.
This commit is contained in:
Linus Walleij 2015-12-07 13:31:00 +01:00
commit 286d31f06d
2 changed files with 128 additions and 0 deletions

View File

@ -417,11 +417,13 @@
#define AC97_RATES_MIC_ADC 4
#define AC97_RATES_SPDIF 5
#define AC97_NUM_GPIOS 16
/*
*
*/
struct snd_ac97;
struct snd_ac97_gpio_priv;
struct snd_pcm_chmap;
struct snd_ac97_build_ops {
@ -529,6 +531,7 @@ struct snd_ac97 {
struct delayed_work power_work;
#endif
struct device dev;
struct snd_ac97_gpio_priv *gpio_priv;
struct snd_pcm_chmap *chmaps[2]; /* channel-maps (optional) */
};

View File

@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/export.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/of_gpio.h>
#include <linux/of.h>
@ -38,6 +39,14 @@ struct snd_ac97_reset_cfg {
int gpio_reset;
};
struct snd_ac97_gpio_priv {
#ifdef CONFIG_GPIOLIB
struct gpio_chip gpio_chip;
#endif
unsigned int gpios_set;
struct snd_soc_codec *codec;
};
static struct snd_ac97_bus soc_ac97_bus = {
.ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
};
@ -47,6 +56,117 @@ static void soc_ac97_device_release(struct device *dev)
kfree(to_ac97_t(dev));
}
#ifdef CONFIG_GPIOLIB
static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip)
{
struct snd_ac97_gpio_priv *gpio_priv =
container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
return gpio_priv->codec;
}
static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
{
if (offset >= AC97_NUM_GPIOS)
return -EINVAL;
return 0;
}
static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
unsigned offset)
{
struct snd_soc_codec *codec = gpio_to_codec(chip);
dev_dbg(codec->dev, "set gpio %d to output\n", offset);
return snd_soc_update_bits(codec, AC97_GPIO_CFG,
1 << offset, 1 << offset);
}
static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
{
struct snd_soc_codec *codec = gpio_to_codec(chip);
int ret;
ret = snd_soc_read(codec, AC97_GPIO_STATUS);
dev_dbg(codec->dev, "get gpio %d : %d\n", offset,
ret < 0 ? ret : ret & (1 << offset));
return ret < 0 ? ret : ret & (1 << offset);
}
static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
int value)
{
struct snd_ac97_gpio_priv *gpio_priv =
container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
struct snd_soc_codec *codec = gpio_to_codec(chip);
gpio_priv->gpios_set &= ~(1 << offset);
gpio_priv->gpios_set |= (!!value) << offset;
snd_soc_write(codec, AC97_GPIO_STATUS, gpio_priv->gpios_set);
dev_dbg(codec->dev, "set gpio %d to %d\n", offset, !!value);
}
static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct snd_soc_codec *codec = gpio_to_codec(chip);
dev_dbg(codec->dev, "set gpio %d to output\n", offset);
snd_soc_ac97_gpio_set(chip, offset, value);
return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0);
}
static struct gpio_chip snd_soc_ac97_gpio_chip = {
.label = "snd_soc_ac97",
.owner = THIS_MODULE,
.request = snd_soc_ac97_gpio_request,
.direction_input = snd_soc_ac97_gpio_direction_in,
.get = snd_soc_ac97_gpio_get,
.direction_output = snd_soc_ac97_gpio_direction_out,
.set = snd_soc_ac97_gpio_set,
.can_sleep = 1,
};
static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
struct snd_soc_codec *codec)
{
struct snd_ac97_gpio_priv *gpio_priv;
int ret;
gpio_priv = devm_kzalloc(codec->dev, sizeof(*gpio_priv), GFP_KERNEL);
if (!gpio_priv)
return -ENOMEM;
ac97->gpio_priv = gpio_priv;
gpio_priv->codec = codec;
gpio_priv->gpio_chip = snd_soc_ac97_gpio_chip;
gpio_priv->gpio_chip.ngpio = AC97_NUM_GPIOS;
gpio_priv->gpio_chip.dev = codec->dev;
gpio_priv->gpio_chip.base = -1;
ret = gpiochip_add(&gpio_priv->gpio_chip);
if (ret != 0)
dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
return ret;
}
static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
{
gpiochip_remove(&ac97->gpio_priv->gpio_chip);
}
#else
static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
struct snd_soc_codec *codec)
{
return 0;
}
static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
{
}
#endif
/**
* snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device
* @codec: The CODEC for which to create the AC'97 device
@ -119,6 +239,10 @@ struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
if (ret)
goto err_put_device;
ret = snd_soc_ac97_init_gpio(ac97, codec);
if (ret)
goto err_put_device;
return ac97;
err_put_device:
@ -135,6 +259,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
*/
void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
{
snd_soc_ac97_free_gpio(ac97);
device_del(&ac97->dev);
ac97->bus = NULL;
put_device(&ac97->dev);