Merge rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[deliverable/linux.git] / sound / aoa / core / snd-aoa-gpio-pmf.c
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) \
14 static 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 } \
23 static 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
29 PMF_GPIO(headphone, 0);
30 PMF_GPIO(amp, 1);
31 PMF_GPIO(lineout, 2);
32
33 static 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
41 static 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
53 static 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
64 static 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
74 static 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
89 static 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
120 static void pmf_handle_notify_irq(void *data)
121 {
122 struct gpio_notification *notif = data;
123
124 schedule_work(&notif->work);
125 }
126
127 static 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
202 static 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
229 static 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
245 struct gpio_methods *pmf_gpio_methods = &methods;
246 EXPORT_SYMBOL_GPL(pmf_gpio_methods);
This page took 0.035389 seconds and 6 git commands to generate.