diff options
author | Linus Walleij <linus.walleij@linaro.org> | 2015-12-07 07:31:00 -0500 |
---|---|---|
committer | Linus Walleij <linus.walleij@linaro.org> | 2015-12-07 07:31:00 -0500 |
commit | 286d31f06d6cbc07c08c8b9df8ab42f3287eebba (patch) | |
tree | f62c6ae8c6467843ce000ee6eae5f6eebd9efb7f /sound | |
parent | 6ddbaed3eff9f60d29805413404251670d2e8f0c (diff) | |
parent | 9bf5c3d11f1fbaf43399d189f05fb20ceb46ee5d (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.c | 125 |
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 | ||
42 | struct 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 | |||
41 | static struct snd_ac97_bus soc_ac97_bus = { | 50 | static 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 | ||
60 | static 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 | |||
68 | static 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 | |||
76 | static 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 | |||
86 | static 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 | |||
98 | static 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 | |||
111 | static 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 | |||
121 | static 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 | |||
132 | static 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 | |||
154 | static void snd_soc_ac97_free_gpio(struct snd_ac97 *ac97) | ||
155 | { | ||
156 | gpiochip_remove(&ac97->gpio_priv->gpio_chip); | ||
157 | } | ||
158 | #else | ||
159 | static int snd_soc_ac97_init_gpio(struct snd_ac97 *ac97, | ||
160 | struct snd_soc_codec *codec) | ||
161 | { | ||
162 | return 0; | ||
163 | } | ||
164 | |||
165 | static 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 | ||
124 | err_put_device: | 248 | err_put_device: |
@@ -135,6 +259,7 @@ EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec); | |||
135 | */ | 259 | */ |
136 | void snd_soc_free_ac97_codec(struct snd_ac97 *ac97) | 260 | void 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); |