aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2015-12-07 07:31:00 -0500
committerLinus Walleij <linus.walleij@linaro.org>2015-12-07 07:31:00 -0500
commit286d31f06d6cbc07c08c8b9df8ab42f3287eebba (patch)
treef62c6ae8c6467843ce000ee6eae5f6eebd9efb7f /sound
parent6ddbaed3eff9f60d29805413404251670d2e8f0c (diff)
parent9bf5c3d11f1fbaf43399d189f05fb20ceb46ee5d (diff)
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.
Diffstat (limited to 'sound')
-rw-r--r--sound/soc/soc-ac97.c125
1 files changed, 125 insertions, 0 deletions
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index d40efc9fe0a9..ae563e379a72 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -20,6 +20,7 @@
20#include <linux/delay.h> 20#include <linux/delay.h>
21#include <linux/export.h> 21#include <linux/export.h>
22#include <linux/gpio.h> 22#include <linux/gpio.h>
23#include <linux/gpio/driver.h>
23#include <linux/init.h> 24#include <linux/init.h>
24#include <linux/of_gpio.h> 25#include <linux/of_gpio.h>
25#include <linux/of.h> 26#include <linux/of.h>
@@ -38,6 +39,14 @@ struct snd_ac97_reset_cfg {
38 int gpio_reset; 39 int gpio_reset;
39}; 40};
40 41
42struct snd_ac97_gpio_priv {
43#ifdef CONFIG_GPIOLIB
44 struct gpio_chip gpio_chip;
45#endif
46 unsigned int gpios_set;
47 struct snd_soc_codec *codec;
48};
49
41static struct snd_ac97_bus soc_ac97_bus = { 50static struct snd_ac97_bus soc_ac97_bus = {
42 .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */ 51 .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
43}; 52};
@@ -47,6 +56,117 @@ static void soc_ac97_device_release(struct device *dev)
47 kfree(to_ac97_t(dev)); 56 kfree(to_ac97_t(dev));
48} 57}
49 58
59#ifdef CONFIG_GPIOLIB
60static inline struct snd_soc_codec *gpio_to_codec(struct gpio_chip *chip)
61{
62 struct snd_ac97_gpio_priv *gpio_priv =
63 container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
64
65 return gpio_priv->codec;
66}
67
68static int snd_soc_ac97_gpio_request(struct gpio_chip *chip, unsigned offset)
69{
70 if (offset >= AC97_NUM_GPIOS)
71 return -EINVAL;
72
73 return 0;
74}
75
76static int snd_soc_ac97_gpio_direction_in(struct gpio_chip *chip,
77 unsigned offset)
78{
79 struct snd_soc_codec *codec = gpio_to_codec(chip);
80
81 dev_dbg(codec->dev, "set gpio %d to output\n", offset);
82 return snd_soc_update_bits(codec, AC97_GPIO_CFG,
83 1 << offset, 1 << offset);
84}
85
86static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned offset)
87{
88 struct snd_soc_codec *codec = gpio_to_codec(chip);
89 int ret;
90
91 ret = snd_soc_read(codec, AC97_GPIO_STATUS);
92 dev_dbg(codec->dev, "get gpio %d : %d\n", offset,
93 ret < 0 ? ret : ret & (1 << offset));
94
95 return ret < 0 ? ret : ret & (1 << offset);
96}
97
98static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned offset,
99 int value)
100{
101 struct snd_ac97_gpio_priv *gpio_priv =
102 container_of(chip, struct snd_ac97_gpio_priv, gpio_chip);
103 struct snd_soc_codec *codec = gpio_to_codec(chip);
104
105 gpio_priv->gpios_set &= ~(1 << offset);
106 gpio_priv->gpios_set |= (!!value) << offset;
107 snd_soc_write(codec, AC97_GPIO_STATUS, gpio_priv->gpios_set);
108 dev_dbg(codec->dev, "set gpio %d to %d\n", offset, !!value);
109}
110
111static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
112 unsigned offset, int value)
113{
114 struct snd_soc_codec *codec = gpio_to_codec(chip);
115
116 dev_dbg(codec->dev, "set gpio %d to output\n", offset);
117 snd_soc_ac97_gpio_set(chip, offset, value);
118 return snd_soc_update_bits(codec, AC97_GPIO_CFG, 1 << offset, 0);
119}
120
121static struct gpio_chip snd_soc_ac97_gpio_chip = {
122 .label = "snd_soc_ac97",
123 .owner = THIS_MODULE,
124 .request = snd_soc_ac97_gpio_request,
125 .direction_input = snd_soc_ac97_gpio_direction_in,
126 .get = snd_soc_ac97_gpio_get,
127 .direction_output = snd_soc_ac97_gpio_direction_out,
128 .set = snd_soc_ac97_gpio_set,
129 .can_sleep = 1,
130};
131
132static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
133 struct snd_soc_codec *codec)
134{
135 struct snd_ac97_gpio_priv *gpio_priv;
136 int ret;
137
138 gpio_priv = devm_kzalloc(codec->dev, sizeof(*gpio_priv), GFP_KERNEL);
139 if (!gpio_priv)
140 return -ENOMEM;
141 ac97->gpio_priv = gpio_priv;
142 gpio_priv->codec = codec;
143 gpio_priv->gpio_chip = snd_soc_ac97_gpio_chip;
144 gpio_priv->gpio_chip.ngpio = AC97_NUM_GPIOS;
145 gpio_priv->gpio_chip.dev = codec->dev;
146 gpio_priv->gpio_chip.base = -1;
147
148 ret = gpiochip_add(&gpio_priv->gpio_chip);
149 if (ret != 0)
150 dev_err(codec->dev, "Failed to add GPIOs: %d\n", ret);
151 return ret;
152}
153
154static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
155{
156 gpiochip_remove(&ac97->gpio_priv->gpio_chip);
157}
158#else
159static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97,
160 struct snd_soc_codec *codec)
161{
162 return 0;
163}
164
165static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97)
166{
167}
168#endif
169
50/** 170/**
51 * snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device 171 * snd_soc_alloc_ac97_codec() - Allocate new a AC'97 device
52 * @codec: The CODEC for which to create the AC'97 device 172 * @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,
119 if (ret) 239 if (ret)
120 goto err_put_device; 240 goto err_put_device;
121 241
242 ret = snd_soc_ac97_init_gpio(ac97, codec);
243 if (ret)
244 goto err_put_device;
245
122 return ac97; 246 return ac97;
123 247
124err_put_device: 248err_put_device:
@@ -135,6 +259,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
135 */ 259 */
136void snd_soc_free_ac97_codec(struct snd_ac97 *ac97) 260void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
137{ 261{
262 snd_soc_ac97_free_gpio(ac97);
138 device_del(&ac97->dev); 263 device_del(&ac97->dev);
139 ac97->bus = NULL; 264 ac97->bus = NULL;
140 put_device(&ac97->dev); 265 put_device(&ac97->dev);