|
@@ -20,6 +20,7 @@
|
|
#include <linux/delay.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/export.h>
|
|
#include <linux/export.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/gpio.h>
|
|
|
|
+#include <linux/gpio/driver.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of_gpio.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of.h>
|
|
@@ -38,6 +39,14 @@ struct snd_ac97_reset_cfg {
|
|
int gpio_reset;
|
|
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 = {
|
|
static struct snd_ac97_bus soc_ac97_bus = {
|
|
.ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
|
|
.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));
|
|
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
|
|
* snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device
|
|
* @codec: The CODEC for which to create the 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)
|
|
if (ret)
|
|
goto err_put_device;
|
|
goto err_put_device;
|
|
|
|
|
|
|
|
+ ret = snd_soc_ac97_init_gpio(ac97, codec);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto err_put_device;
|
|
|
|
+
|
|
return ac97;
|
|
return ac97;
|
|
|
|
|
|
err_put_device:
|
|
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)
|
|
void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
|
|
{
|
|
{
|
|
|
|
+ snd_soc_ac97_free_gpio(ac97);
|
|
device_del(&ac97->dev);
|
|
device_del(&ac97->dev);
|
|
ac97->bus = NULL;
|
|
ac97->bus = NULL;
|
|
put_device(&ac97->dev);
|
|
put_device(&ac97->dev);
|