aboutsummaryrefslogtreecommitdiffstats
path: root/sound/aoa/core
diff options
context:
space:
mode:
Diffstat (limited to 'sound/aoa/core')
-rw-r--r--sound/aoa/core/Makefile5
-rw-r--r--sound/aoa/core/snd-aoa-alsa.c98
-rw-r--r--sound/aoa/core/snd-aoa-alsa.h16
-rw-r--r--sound/aoa/core/snd-aoa-core.c162
-rw-r--r--sound/aoa/core/snd-aoa-gpio-feature.c399
-rw-r--r--sound/aoa/core/snd-aoa-gpio-pmf.c246
6 files changed, 926 insertions, 0 deletions
diff --git a/sound/aoa/core/Makefile b/sound/aoa/core/Makefile
new file mode 100644
index 00000000000..62dc7287f66
--- /dev/null
+++ b/sound/aoa/core/Makefile
@@ -0,0 +1,5 @@
1obj-$(CONFIG_SND_AOA) += snd-aoa.o
2snd-aoa-objs := snd-aoa-core.o \
3 snd-aoa-alsa.o \
4 snd-aoa-gpio-pmf.o \
5 snd-aoa-gpio-feature.o
diff --git a/sound/aoa/core/snd-aoa-alsa.c b/sound/aoa/core/snd-aoa-alsa.c
new file mode 100644
index 00000000000..b42fdea77ed
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.c
@@ -0,0 +1,98 @@
1/*
2 * Apple Onboard Audio Alsa helpers
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 */
8#include <linux/module.h>
9#include "snd-aoa-alsa.h"
10
11static int index = -1;
12module_param(index, int, 0444);
13MODULE_PARM_DESC(index, "index for AOA sound card.");
14
15static struct aoa_card *aoa_card;
16
17int aoa_alsa_init(char *name, struct module *mod)
18{
19 struct snd_card *alsa_card;
20 int err;
21
22 if (aoa_card)
23 /* cannot be EEXIST due to usage in aoa_fabric_register */
24 return -EBUSY;
25
26 alsa_card = snd_card_new(index, name, mod, sizeof(struct aoa_card));
27 if (!alsa_card)
28 return -ENOMEM;
29 aoa_card = alsa_card->private_data;
30 aoa_card->alsa_card = alsa_card;
31 strlcpy(alsa_card->driver, "AppleOnbdAudio", sizeof(alsa_card->driver));
32 strlcpy(alsa_card->shortname, name, sizeof(alsa_card->shortname));
33 strlcpy(alsa_card->longname, name, sizeof(alsa_card->longname));
34 strlcpy(alsa_card->mixername, name, sizeof(alsa_card->mixername));
35 err = snd_card_register(aoa_card->alsa_card);
36 if (err < 0) {
37 printk(KERN_ERR "snd-aoa: couldn't register alsa card\n");
38 snd_card_free(aoa_card->alsa_card);
39 aoa_card = NULL;
40 return err;
41 }
42 return 0;
43}
44
45struct snd_card *aoa_get_card(void)
46{
47 if (aoa_card)
48 return aoa_card->alsa_card;
49 return NULL;
50}
51EXPORT_SYMBOL_GPL(aoa_get_card);
52
53void aoa_alsa_cleanup(void)
54{
55 if (aoa_card) {
56 snd_card_free(aoa_card->alsa_card);
57 aoa_card = NULL;
58 }
59}
60
61int aoa_snd_device_new(snd_device_type_t type,
62 void * device_data, struct snd_device_ops * ops)
63{
64 struct snd_card *card = aoa_get_card();
65 int err;
66
67 if (!card) return -ENOMEM;
68
69 err = snd_device_new(card, type, device_data, ops);
70 if (err) {
71 printk(KERN_ERR "snd-aoa: failed to create snd device (%d)\n", err);
72 return err;
73 }
74 err = snd_device_register(card, device_data);
75 if (err) {
76 printk(KERN_ERR "snd-aoa: failed to register "
77 "snd device (%d)\n", err);
78 printk(KERN_ERR "snd-aoa: have you forgotten the "
79 "dev_register callback?\n");
80 snd_device_free(card, device_data);
81 }
82 return err;
83}
84EXPORT_SYMBOL_GPL(aoa_snd_device_new);
85
86int aoa_snd_ctl_add(struct snd_kcontrol* control)
87{
88 int err;
89
90 if (!aoa_card) return -ENODEV;
91
92 err = snd_ctl_add(aoa_card->alsa_card, control);
93 if (err)
94 printk(KERN_ERR "snd-aoa: failed to add alsa control (%d)\n",
95 err);
96 return err;
97}
98EXPORT_SYMBOL_GPL(aoa_snd_ctl_add);
diff --git a/sound/aoa/core/snd-aoa-alsa.h b/sound/aoa/core/snd-aoa-alsa.h
new file mode 100644
index 00000000000..660d2f1793b
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-alsa.h
@@ -0,0 +1,16 @@
1/*
2 * Apple Onboard Audio Alsa private helpers
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 */
8
9#ifndef __SND_AOA_ALSA_H
10#define __SND_AOA_ALSA_H
11#include "../aoa.h"
12
13extern int aoa_alsa_init(char *name, struct module *mod);
14extern void aoa_alsa_cleanup(void);
15
16#endif /* __SND_AOA_ALSA_H */
diff --git a/sound/aoa/core/snd-aoa-core.c b/sound/aoa/core/snd-aoa-core.c
new file mode 100644
index 00000000000..ecd2d8263f2
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-core.c
@@ -0,0 +1,162 @@
1/*
2 * Apple Onboard Audio driver core
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 */
8
9#include <linux/init.h>
10#include <linux/module.h>
11#include <linux/list.h>
12#include "../aoa.h"
13#include "snd-aoa-alsa.h"
14
15MODULE_DESCRIPTION("Apple Onboard Audio Sound Driver");
16MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
17MODULE_LICENSE("GPL");
18
19/* We allow only one fabric. This simplifies things,
20 * and more don't really make that much sense */
21static struct aoa_fabric *fabric;
22static LIST_HEAD(codec_list);
23
24static int attach_codec_to_fabric(struct aoa_codec *c)
25{
26 int err;
27
28 if (!try_module_get(c->owner))
29 return -EBUSY;
30 /* found_codec has to be assigned */
31 err = -ENOENT;
32 if (fabric->found_codec)
33 err = fabric->found_codec(c);
34 if (err) {
35 module_put(c->owner);
36 printk(KERN_ERR "snd-aoa: fabric didn't like codec %s\n",
37 c->name);
38 return err;
39 }
40 c->fabric = fabric;
41
42 err = 0;
43 if (c->init)
44 err = c->init(c);
45 if (err) {
46 printk(KERN_ERR "snd-aoa: codec %s didn't init\n", c->name);
47 c->fabric = NULL;
48 if (fabric->remove_codec)
49 fabric->remove_codec(c);
50 module_put(c->owner);
51 return err;
52 }
53 if (fabric->attached_codec)
54 fabric->attached_codec(c);
55 return 0;
56}
57
58int aoa_codec_register(struct aoa_codec *codec)
59{
60 int err = 0;
61
62 /* if there's a fabric already, we can tell if we
63 * will want to have this codec, so propagate error
64 * through. Otherwise, this will happen later... */
65 if (fabric)
66 err = attach_codec_to_fabric(codec);
67 if (!err)
68 list_add(&codec->list, &codec_list);
69 return err;
70}
71EXPORT_SYMBOL_GPL(aoa_codec_register);
72
73void aoa_codec_unregister(struct aoa_codec *codec)
74{
75 list_del(&codec->list);
76 if (codec->fabric && codec->exit)
77 codec->exit(codec);
78 if (fabric && fabric->remove_codec)
79 fabric->remove_codec(codec);
80 codec->fabric = NULL;
81 module_put(codec->owner);
82}
83EXPORT_SYMBOL_GPL(aoa_codec_unregister);
84
85int aoa_fabric_register(struct aoa_fabric *new_fabric)
86{
87 struct aoa_codec *c;
88 int err;
89
90 /* allow querying for presence of fabric
91 * (i.e. do this test first!) */
92 if (new_fabric == fabric) {
93 err = -EALREADY;
94 goto attach;
95 }
96 if (fabric)
97 return -EEXIST;
98 if (!new_fabric)
99 return -EINVAL;
100
101 err = aoa_alsa_init(new_fabric->name, new_fabric->owner);
102 if (err)
103 return err;
104
105 fabric = new_fabric;
106
107 attach:
108 list_for_each_entry(c, &codec_list, list) {
109 if (c->fabric != fabric)
110 attach_codec_to_fabric(c);
111 }
112 return err;
113}
114EXPORT_SYMBOL_GPL(aoa_fabric_register);
115
116void aoa_fabric_unregister(struct aoa_fabric *old_fabric)
117{
118 struct aoa_codec *c;
119
120 if (fabric != old_fabric)
121 return;
122
123 list_for_each_entry(c, &codec_list, list) {
124 if (c->fabric)
125 aoa_fabric_unlink_codec(c);
126 }
127
128 aoa_alsa_cleanup();
129
130 fabric = NULL;
131}
132EXPORT_SYMBOL_GPL(aoa_fabric_unregister);
133
134void aoa_fabric_unlink_codec(struct aoa_codec *codec)
135{
136 if (!codec->fabric) {
137 printk(KERN_ERR "snd-aoa: fabric unassigned "
138 "in aoa_fabric_unlink_codec\n");
139 dump_stack();
140 return;
141 }
142 if (codec->exit)
143 codec->exit(codec);
144 if (codec->fabric->remove_codec)
145 codec->fabric->remove_codec(codec);
146 codec->fabric = NULL;
147 module_put(codec->owner);
148}
149EXPORT_SYMBOL_GPL(aoa_fabric_unlink_codec);
150
151static int __init aoa_init(void)
152{
153 return 0;
154}
155
156static void __exit aoa_exit(void)
157{
158 aoa_alsa_cleanup();
159}
160
161module_init(aoa_init);
162module_exit(aoa_exit);
diff --git a/sound/aoa/core/snd-aoa-gpio-feature.c b/sound/aoa/core/snd-aoa-gpio-feature.c
new file mode 100644
index 00000000000..2c6eb7784cc
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-gpio-feature.c
@@ -0,0 +1,399 @@
1/*
2 * Apple Onboard Audio feature call GPIO control
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 *
8 * This file contains the GPIO control routines for
9 * direct (through feature calls) access to the GPIO
10 * registers.
11 */
12
13#include <asm/pmac_feature.h>
14#include <linux/interrupt.h>
15#include "../aoa.h"
16
17/* TODO: these are 20 global variables
18 * that aren't used on most machines...
19 * Move them into a dynamically allocated
20 * structure and use that.
21 */
22
23/* these are the GPIO numbers (register addresses as offsets into
24 * the GPIO space) */
25static int headphone_mute_gpio;
26static int amp_mute_gpio;
27static int lineout_mute_gpio;
28static int hw_reset_gpio;
29static int lineout_detect_gpio;
30static int headphone_detect_gpio;
31static int linein_detect_gpio;
32
33/* see the SWITCH_GPIO macro */
34static int headphone_mute_gpio_activestate;
35static int amp_mute_gpio_activestate;
36static int lineout_mute_gpio_activestate;
37static int hw_reset_gpio_activestate;
38static int lineout_detect_gpio_activestate;
39static int headphone_detect_gpio_activestate;
40static int linein_detect_gpio_activestate;
41
42/* node pointers that we save when getting the GPIO number
43 * to get the interrupt later */
44static struct device_node *lineout_detect_node;
45static struct device_node *linein_detect_node;
46static struct device_node *headphone_detect_node;
47
48static int lineout_detect_irq;
49static int linein_detect_irq;
50static int headphone_detect_irq;
51
52static struct device_node *get_gpio(char *name,
53 char *altname,
54 int *gpioptr,
55 int *gpioactiveptr)
56{
57 struct device_node *np, *gpio;
58 u32 *reg;
59 char *audio_gpio;
60
61 *gpioptr = -1;
62
63 /* check if we can get it the easy way ... */
64 np = of_find_node_by_name(NULL, name);
65 if (!np) {
66 /* some machines have only gpioX/extint-gpioX nodes,
67 * and an audio-gpio property saying what it is ...
68 * So what we have to do is enumerate all children
69 * of the gpio node and check them all. */
70 gpio = of_find_node_by_name(NULL, "gpio");
71 if (!gpio)
72 return NULL;
73 while ((np = of_get_next_child(gpio, np))) {
74 audio_gpio = get_property(np, "audio-gpio", NULL);
75 if (!audio_gpio)
76 continue;
77 if (strcmp(audio_gpio, name) == 0)
78 break;
79 if (altname && (strcmp(audio_gpio, altname) == 0))
80 break;
81 }
82 /* still not found, assume not there */
83 if (!np)
84 return NULL;
85 }
86
87 reg = (u32 *)get_property(np, "reg", NULL);
88 if (!reg)
89 return NULL;
90
91 *gpioptr = *reg;
92
93 /* this is a hack, usually the GPIOs 'reg' property
94 * should have the offset based from the GPIO space
95 * which is at 0x50, but apparently not always... */
96 if (*gpioptr < 0x50)
97 *gpioptr += 0x50;
98
99 reg = (u32 *)get_property(np, "audio-gpio-active-state", NULL);
100 if (!reg)
101 /* Apple seems to default to 1, but
102 * that doesn't seem right at least on most
103 * machines. So until proven that the opposite
104 * is necessary, we default to 0
105 * (which, incidentally, snd-powermac also does...) */
106 *gpioactiveptr = 0;
107 else
108 *gpioactiveptr = *reg;
109
110 return np;
111}
112
113static void get_irq(struct device_node * np, int *irqptr)
114{
115 *irqptr = -1;
116 if (!np)
117 return;
118 if (np->n_intrs != 1)
119 return;
120 *irqptr = np->intrs[0].line;
121}
122
123/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
124#define SWITCH_GPIO(name, v, on) \
125 (((v)&~1) | ((on)? \
126 (name##_gpio_activestate==0?4:5): \
127 (name##_gpio_activestate==0?5:4)))
128
129#define FTR_GPIO(name, bit) \
130static void ftr_gpio_set_##name(struct gpio_runtime *rt, int on)\
131{ \
132 int v; \
133 \
134 if (unlikely(!rt)) return; \
135 \
136 if (name##_mute_gpio < 0) \
137 return; \
138 \
139 v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, \
140 name##_mute_gpio, \
141 0); \
142 \
143 /* muted = !on... */ \
144 v = SWITCH_GPIO(name##_mute, v, !on); \
145 \
146 pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL, \
147 name##_mute_gpio, v); \
148 \
149 rt->implementation_private &= ~(1<<bit); \
150 rt->implementation_private |= (!!on << bit); \
151} \
152static int ftr_gpio_get_##name(struct gpio_runtime *rt) \
153{ \
154 if (unlikely(!rt)) return 0; \
155 return (rt->implementation_private>>bit)&1; \
156}
157
158FTR_GPIO(headphone, 0);
159FTR_GPIO(amp, 1);
160FTR_GPIO(lineout, 2);
161
162static void ftr_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
163{
164 int v;
165
166 if (unlikely(!rt)) return;
167 if (hw_reset_gpio < 0)
168 return;
169
170 v = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL,
171 hw_reset_gpio, 0);
172 v = SWITCH_GPIO(hw_reset, v, on);
173 pmac_call_feature(PMAC_FTR_WRITE_GPIO, NULL,
174 hw_reset_gpio, v);
175}
176
177static void ftr_gpio_all_amps_off(struct gpio_runtime *rt)
178{
179 int saved;
180
181 if (unlikely(!rt)) return;
182 saved = rt->implementation_private;
183 ftr_gpio_set_headphone(rt, 0);
184 ftr_gpio_set_amp(rt, 0);
185 ftr_gpio_set_lineout(rt, 0);
186 rt->implementation_private = saved;
187}
188
189static void ftr_gpio_all_amps_restore(struct gpio_runtime *rt)
190{
191 int s;
192
193 if (unlikely(!rt)) return;
194 s = rt->implementation_private;
195 ftr_gpio_set_headphone(rt, (s>>0)&1);
196 ftr_gpio_set_amp(rt, (s>>1)&1);
197 ftr_gpio_set_lineout(rt, (s>>2)&1);
198}
199
200static void ftr_handle_notify(void *data)
201{
202 struct gpio_notification *notif = data;
203
204 mutex_lock(&notif->mutex);
205 if (notif->notify)
206 notif->notify(notif->data);
207 mutex_unlock(&notif->mutex);
208}
209
210static void ftr_gpio_init(struct gpio_runtime *rt)
211{
212 get_gpio("headphone-mute", NULL,
213 &headphone_mute_gpio,
214 &headphone_mute_gpio_activestate);
215 get_gpio("amp-mute", NULL,
216 &amp_mute_gpio,
217 &amp_mute_gpio_activestate);
218 get_gpio("lineout-mute", NULL,
219 &lineout_mute_gpio,
220 &lineout_mute_gpio_activestate);
221 get_gpio("hw-reset", "audio-hw-reset",
222 &hw_reset_gpio,
223 &hw_reset_gpio_activestate);
224
225 headphone_detect_node = get_gpio("headphone-detect", NULL,
226 &headphone_detect_gpio,
227 &headphone_detect_gpio_activestate);
228 /* go Apple, and thanks for giving these different names
229 * across the board... */
230 lineout_detect_node = get_gpio("lineout-detect", "line-output-detect",
231 &lineout_detect_gpio,
232 &lineout_detect_gpio_activestate);
233 linein_detect_node = get_gpio("linein-detect", "line-input-detect",
234 &linein_detect_gpio,
235 &linein_detect_gpio_activestate);
236
237 get_irq(headphone_detect_node, &headphone_detect_irq);
238 get_irq(lineout_detect_node, &lineout_detect_irq);
239 get_irq(linein_detect_node, &linein_detect_irq);
240
241 ftr_gpio_all_amps_off(rt);
242 rt->implementation_private = 0;
243 INIT_WORK(&rt->headphone_notify.work, ftr_handle_notify,
244 &rt->headphone_notify);
245 INIT_WORK(&rt->line_in_notify.work, ftr_handle_notify,
246 &rt->line_in_notify);
247 INIT_WORK(&rt->line_out_notify.work, ftr_handle_notify,
248 &rt->line_out_notify);
249 mutex_init(&rt->headphone_notify.mutex);
250 mutex_init(&rt->line_in_notify.mutex);
251 mutex_init(&rt->line_out_notify.mutex);
252}
253
254static void ftr_gpio_exit(struct gpio_runtime *rt)
255{
256 ftr_gpio_all_amps_off(rt);
257 rt->implementation_private = 0;
258 if (rt->headphone_notify.notify)
259 free_irq(headphone_detect_irq, &rt->headphone_notify);
260 if (rt->line_in_notify.gpio_private)
261 free_irq(linein_detect_irq, &rt->line_in_notify);
262 if (rt->line_out_notify.gpio_private)
263 free_irq(lineout_detect_irq, &rt->line_out_notify);
264 cancel_delayed_work(&rt->headphone_notify.work);
265 cancel_delayed_work(&rt->line_in_notify.work);
266 cancel_delayed_work(&rt->line_out_notify.work);
267 flush_scheduled_work();
268 mutex_destroy(&rt->headphone_notify.mutex);
269 mutex_destroy(&rt->line_in_notify.mutex);
270 mutex_destroy(&rt->line_out_notify.mutex);
271}
272
273static irqreturn_t ftr_handle_notify_irq(int xx,
274 void *data,
275 struct pt_regs *regs)
276{
277 struct gpio_notification *notif = data;
278
279 schedule_work(&notif->work);
280
281 return IRQ_HANDLED;
282}
283
284static int ftr_set_notify(struct gpio_runtime *rt,
285 enum notify_type type,
286 notify_func_t notify,
287 void *data)
288{
289 struct gpio_notification *notif;
290 notify_func_t old;
291 int irq;
292 char *name;
293 int err = -EBUSY;
294
295 switch (type) {
296 case AOA_NOTIFY_HEADPHONE:
297 notif = &rt->headphone_notify;
298 name = "headphone-detect";
299 irq = headphone_detect_irq;
300 break;
301 case AOA_NOTIFY_LINE_IN:
302 notif = &rt->line_in_notify;
303 name = "linein-detect";
304 irq = linein_detect_irq;
305 break;
306 case AOA_NOTIFY_LINE_OUT:
307 notif = &rt->line_out_notify;
308 name = "lineout-detect";
309 irq = lineout_detect_irq;
310 break;
311 default:
312 return -EINVAL;
313 }
314
315 if (irq == -1)
316 return -ENODEV;
317
318 mutex_lock(&notif->mutex);
319
320 old = notif->notify;
321
322 if (!old && !notify) {
323 err = 0;
324 goto out_unlock;
325 }
326
327 if (old && notify) {
328 if (old == notify && notif->data == data)
329 err = 0;
330 goto out_unlock;
331 }
332
333 if (old && !notify)
334 free_irq(irq, notif);
335
336 if (!old && notify) {
337 err = request_irq(irq, ftr_handle_notify_irq, 0, name, notif);
338 if (err)
339 goto out_unlock;
340 }
341
342 notif->notify = notify;
343 notif->data = data;
344
345 err = 0;
346 out_unlock:
347 mutex_unlock(&notif->mutex);
348 return err;
349}
350
351static int ftr_get_detect(struct gpio_runtime *rt,
352 enum notify_type type)
353{
354 int gpio, ret, active;
355
356 switch (type) {
357 case AOA_NOTIFY_HEADPHONE:
358 gpio = headphone_detect_gpio;
359 active = headphone_detect_gpio_activestate;
360 break;
361 case AOA_NOTIFY_LINE_IN:
362 gpio = linein_detect_gpio;
363 active = linein_detect_gpio_activestate;
364 break;
365 case AOA_NOTIFY_LINE_OUT:
366 gpio = lineout_detect_gpio;
367 active = lineout_detect_gpio_activestate;
368 break;
369 default:
370 return -EINVAL;
371 }
372
373 if (gpio == -1)
374 return -ENODEV;
375
376 ret = pmac_call_feature(PMAC_FTR_READ_GPIO, NULL, gpio, 0);
377 if (ret < 0)
378 return ret;
379 return ((ret >> 1) & 1) == active;
380}
381
382static struct gpio_methods methods = {
383 .init = ftr_gpio_init,
384 .exit = ftr_gpio_exit,
385 .all_amps_off = ftr_gpio_all_amps_off,
386 .all_amps_restore = ftr_gpio_all_amps_restore,
387 .set_headphone = ftr_gpio_set_headphone,
388 .set_speakers = ftr_gpio_set_amp,
389 .set_lineout = ftr_gpio_set_lineout,
390 .set_hw_reset = ftr_gpio_set_hw_reset,
391 .get_headphone = ftr_gpio_get_headphone,
392 .get_speakers = ftr_gpio_get_amp,
393 .get_lineout = ftr_gpio_get_lineout,
394 .set_notify = ftr_set_notify,
395 .get_detect = ftr_get_detect,
396};
397
398struct gpio_methods *ftr_gpio_methods = &methods;
399EXPORT_SYMBOL_GPL(ftr_gpio_methods);
diff --git a/sound/aoa/core/snd-aoa-gpio-pmf.c b/sound/aoa/core/snd-aoa-gpio-pmf.c
new file mode 100644
index 00000000000..0e9b9bb2a6d
--- /dev/null
+++ b/sound/aoa/core/snd-aoa-gpio-pmf.c
@@ -0,0 +1,246 @@
1/*
2 * Apple Onboard Audio pmf GPIOs
3 *
4 * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
5 *
6 * GPL v2, can be found in COPYING.
7 */
8
9#include <asm/pmac_feature.h>
10#include <asm/pmac_pfunc.h>
11#include "../aoa.h"
12
13#define PMF_GPIO(name, bit) \
14static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
15{ \
16 struct pmf_args args = { .count = 1, .u[0].v = !on }; \
17 \
18 if (unlikely(!rt)) return; \
19 pmf_call_function(rt->node, #name "-mute", &args); \
20 rt->implementation_private &= ~(1<<bit); \
21 rt->implementation_private |= (!!on << bit); \
22} \
23static int pmf_gpio_get_##name(struct gpio_runtime *rt) \
24{ \
25 if (unlikely(!rt)) return 0; \
26 return (rt->implementation_private>>bit)&1; \
27}
28
29PMF_GPIO(headphone, 0);
30PMF_GPIO(amp, 1);
31PMF_GPIO(lineout, 2);
32
33static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
34{
35 struct pmf_args args = { .count = 1, .u[0].v = !!on };
36
37 if (unlikely(!rt)) return;
38 pmf_call_function(rt->node, "hw-reset", &args);
39}
40
41static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
42{
43 int saved;
44
45 if (unlikely(!rt)) return;
46 saved = rt->implementation_private;
47 pmf_gpio_set_headphone(rt, 0);
48 pmf_gpio_set_amp(rt, 0);
49 pmf_gpio_set_lineout(rt, 0);
50 rt->implementation_private = saved;
51}
52
53static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
54{
55 int s;
56
57 if (unlikely(!rt)) return;
58 s = rt->implementation_private;
59 pmf_gpio_set_headphone(rt, (s>>0)&1);
60 pmf_gpio_set_amp(rt, (s>>1)&1);
61 pmf_gpio_set_lineout(rt, (s>>2)&1);
62}
63
64static void pmf_handle_notify(void *data)
65{
66 struct gpio_notification *notif = data;
67
68 mutex_lock(&notif->mutex);
69 if (notif->notify)
70 notif->notify(notif->data);
71 mutex_unlock(&notif->mutex);
72}
73
74static void pmf_gpio_init(struct gpio_runtime *rt)
75{
76 pmf_gpio_all_amps_off(rt);
77 rt->implementation_private = 0;
78 INIT_WORK(&rt->headphone_notify.work, pmf_handle_notify,
79 &rt->headphone_notify);
80 INIT_WORK(&rt->line_in_notify.work, pmf_handle_notify,
81 &rt->line_in_notify);
82 INIT_WORK(&rt->line_out_notify.work, pmf_handle_notify,
83 &rt->line_out_notify);
84 mutex_init(&rt->headphone_notify.mutex);
85 mutex_init(&rt->line_in_notify.mutex);
86 mutex_init(&rt->line_out_notify.mutex);
87}
88
89static void pmf_gpio_exit(struct gpio_runtime *rt)
90{
91 pmf_gpio_all_amps_off(rt);
92 rt->implementation_private = 0;
93
94 if (rt->headphone_notify.gpio_private)
95 pmf_unregister_irq_client(rt->headphone_notify.gpio_private);
96 if (rt->line_in_notify.gpio_private)
97 pmf_unregister_irq_client(rt->line_in_notify.gpio_private);
98 if (rt->line_out_notify.gpio_private)
99 pmf_unregister_irq_client(rt->line_out_notify.gpio_private);
100
101 /* make sure no work is pending before freeing
102 * all things */
103 cancel_delayed_work(&rt->headphone_notify.work);
104 cancel_delayed_work(&rt->line_in_notify.work);
105 cancel_delayed_work(&rt->line_out_notify.work);
106 flush_scheduled_work();
107
108 mutex_destroy(&rt->headphone_notify.mutex);
109 mutex_destroy(&rt->line_in_notify.mutex);
110 mutex_destroy(&rt->line_out_notify.mutex);
111
112 if (rt->headphone_notify.gpio_private)
113 kfree(rt->headphone_notify.gpio_private);
114 if (rt->line_in_notify.gpio_private)
115 kfree(rt->line_in_notify.gpio_private);
116 if (rt->line_out_notify.gpio_private)
117 kfree(rt->line_out_notify.gpio_private);
118}
119
120static void pmf_handle_notify_irq(void *data)
121{
122 struct gpio_notification *notif = data;
123
124 schedule_work(&notif->work);
125}
126
127static int pmf_set_notify(struct gpio_runtime *rt,
128 enum notify_type type,
129 notify_func_t notify,
130 void *data)
131{
132 struct gpio_notification *notif;
133 notify_func_t old;
134 struct pmf_irq_client *irq_client;
135 char *name;
136 int err = -EBUSY;
137
138 switch (type) {
139 case AOA_NOTIFY_HEADPHONE:
140 notif = &rt->headphone_notify;
141 name = "headphone-detect";
142 break;
143 case AOA_NOTIFY_LINE_IN:
144 notif = &rt->line_in_notify;
145 name = "linein-detect";
146 break;
147 case AOA_NOTIFY_LINE_OUT:
148 notif = &rt->line_out_notify;
149 name = "lineout-detect";
150 break;
151 default:
152 return -EINVAL;
153 }
154
155 mutex_lock(&notif->mutex);
156
157 old = notif->notify;
158
159 if (!old && !notify) {
160 err = 0;
161 goto out_unlock;
162 }
163
164 if (old && notify) {
165 if (old == notify && notif->data == data)
166 err = 0;
167 goto out_unlock;
168 }
169
170 if (old && !notify) {
171 irq_client = notif->gpio_private;
172 pmf_unregister_irq_client(irq_client);
173 kfree(irq_client);
174 notif->gpio_private = NULL;
175 }
176 if (!old && notify) {
177 irq_client = kzalloc(sizeof(struct pmf_irq_client),
178 GFP_KERNEL);
179 irq_client->data = notif;
180 irq_client->handler = pmf_handle_notify_irq;
181 irq_client->owner = THIS_MODULE;
182 err = pmf_register_irq_client(rt->node,
183 name,
184 irq_client);
185 if (err) {
186 printk(KERN_ERR "snd-aoa: gpio layer failed to"
187 " register %s irq (%d)\n", name, err);
188 kfree(irq_client);
189 goto out_unlock;
190 }
191 notif->gpio_private = irq_client;
192 }
193 notif->notify = notify;
194 notif->data = data;
195
196 err = 0;
197 out_unlock:
198 mutex_unlock(&notif->mutex);
199 return err;
200}
201
202static int pmf_get_detect(struct gpio_runtime *rt,
203 enum notify_type type)
204{
205 char *name;
206 int err = -EBUSY, ret;
207 struct pmf_args args = { .count = 1, .u[0].p = &ret };
208
209 switch (type) {
210 case AOA_NOTIFY_HEADPHONE:
211 name = "headphone-detect";
212 break;
213 case AOA_NOTIFY_LINE_IN:
214 name = "linein-detect";
215 break;
216 case AOA_NOTIFY_LINE_OUT:
217 name = "lineout-detect";
218 break;
219 default:
220 return -EINVAL;
221 }
222
223 err = pmf_call_function(rt->node, name, &args);
224 if (err)
225 return err;
226 return ret;
227}
228
229static struct gpio_methods methods = {
230 .init = pmf_gpio_init,
231 .exit = pmf_gpio_exit,
232 .all_amps_off = pmf_gpio_all_amps_off,
233 .all_amps_restore = pmf_gpio_all_amps_restore,
234 .set_headphone = pmf_gpio_set_headphone,
235 .set_speakers = pmf_gpio_set_amp,
236 .set_lineout = pmf_gpio_set_lineout,
237 .set_hw_reset = pmf_gpio_set_hw_reset,
238 .get_headphone = pmf_gpio_get_headphone,
239 .get_speakers = pmf_gpio_get_amp,
240 .get_lineout = pmf_gpio_get_lineout,
241 .set_notify = pmf_set_notify,
242 .get_detect = pmf_get_detect,
243};
244
245struct gpio_methods *pmf_gpio_methods = &methods;
246EXPORT_SYMBOL_GPL(pmf_gpio_methods);