Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/aoa/core/gpio-pmf.c
10817 views
1
/*
2
* Apple Onboard Audio pmf GPIOs
3
*
4
* Copyright 2006 Johannes Berg <[email protected]>
5
*
6
* GPL v2, can be found in COPYING.
7
*/
8
9
#include <linux/slab.h>
10
#include <asm/pmac_feature.h>
11
#include <asm/pmac_pfunc.h>
12
#include "../aoa.h"
13
14
#define PMF_GPIO(name, bit) \
15
static void pmf_gpio_set_##name(struct gpio_runtime *rt, int on)\
16
{ \
17
struct pmf_args args = { .count = 1, .u[0].v = !on }; \
18
int rc; \
19
\
20
if (unlikely(!rt)) return; \
21
rc = pmf_call_function(rt->node, #name "-mute", &args); \
22
if (rc && rc != -ENODEV) \
23
printk(KERN_WARNING "pmf_gpio_set_" #name \
24
" failed, rc: %d\n", rc); \
25
rt->implementation_private &= ~(1<<bit); \
26
rt->implementation_private |= (!!on << bit); \
27
} \
28
static int pmf_gpio_get_##name(struct gpio_runtime *rt) \
29
{ \
30
if (unlikely(!rt)) return 0; \
31
return (rt->implementation_private>>bit)&1; \
32
}
33
34
PMF_GPIO(headphone, 0);
35
PMF_GPIO(amp, 1);
36
PMF_GPIO(lineout, 2);
37
38
static void pmf_gpio_set_hw_reset(struct gpio_runtime *rt, int on)
39
{
40
struct pmf_args args = { .count = 1, .u[0].v = !!on };
41
int rc;
42
43
if (unlikely(!rt)) return;
44
rc = pmf_call_function(rt->node, "hw-reset", &args);
45
if (rc)
46
printk(KERN_WARNING "pmf_gpio_set_hw_reset"
47
" failed, rc: %d\n", rc);
48
}
49
50
static void pmf_gpio_all_amps_off(struct gpio_runtime *rt)
51
{
52
int saved;
53
54
if (unlikely(!rt)) return;
55
saved = rt->implementation_private;
56
pmf_gpio_set_headphone(rt, 0);
57
pmf_gpio_set_amp(rt, 0);
58
pmf_gpio_set_lineout(rt, 0);
59
rt->implementation_private = saved;
60
}
61
62
static void pmf_gpio_all_amps_restore(struct gpio_runtime *rt)
63
{
64
int s;
65
66
if (unlikely(!rt)) return;
67
s = rt->implementation_private;
68
pmf_gpio_set_headphone(rt, (s>>0)&1);
69
pmf_gpio_set_amp(rt, (s>>1)&1);
70
pmf_gpio_set_lineout(rt, (s>>2)&1);
71
}
72
73
static void pmf_handle_notify(struct work_struct *work)
74
{
75
struct gpio_notification *notif =
76
container_of(work, struct gpio_notification, work.work);
77
78
mutex_lock(&notif->mutex);
79
if (notif->notify)
80
notif->notify(notif->data);
81
mutex_unlock(&notif->mutex);
82
}
83
84
static void pmf_gpio_init(struct gpio_runtime *rt)
85
{
86
pmf_gpio_all_amps_off(rt);
87
rt->implementation_private = 0;
88
INIT_DELAYED_WORK(&rt->headphone_notify.work, pmf_handle_notify);
89
INIT_DELAYED_WORK(&rt->line_in_notify.work, pmf_handle_notify);
90
INIT_DELAYED_WORK(&rt->line_out_notify.work, pmf_handle_notify);
91
mutex_init(&rt->headphone_notify.mutex);
92
mutex_init(&rt->line_in_notify.mutex);
93
mutex_init(&rt->line_out_notify.mutex);
94
}
95
96
static void pmf_gpio_exit(struct gpio_runtime *rt)
97
{
98
pmf_gpio_all_amps_off(rt);
99
rt->implementation_private = 0;
100
101
if (rt->headphone_notify.gpio_private)
102
pmf_unregister_irq_client(rt->headphone_notify.gpio_private);
103
if (rt->line_in_notify.gpio_private)
104
pmf_unregister_irq_client(rt->line_in_notify.gpio_private);
105
if (rt->line_out_notify.gpio_private)
106
pmf_unregister_irq_client(rt->line_out_notify.gpio_private);
107
108
/* make sure no work is pending before freeing
109
* all things */
110
cancel_delayed_work_sync(&rt->headphone_notify.work);
111
cancel_delayed_work_sync(&rt->line_in_notify.work);
112
cancel_delayed_work_sync(&rt->line_out_notify.work);
113
114
mutex_destroy(&rt->headphone_notify.mutex);
115
mutex_destroy(&rt->line_in_notify.mutex);
116
mutex_destroy(&rt->line_out_notify.mutex);
117
118
kfree(rt->headphone_notify.gpio_private);
119
kfree(rt->line_in_notify.gpio_private);
120
kfree(rt->line_out_notify.gpio_private);
121
}
122
123
static void pmf_handle_notify_irq(void *data)
124
{
125
struct gpio_notification *notif = data;
126
127
schedule_delayed_work(&notif->work, 0);
128
}
129
130
static int pmf_set_notify(struct gpio_runtime *rt,
131
enum notify_type type,
132
notify_func_t notify,
133
void *data)
134
{
135
struct gpio_notification *notif;
136
notify_func_t old;
137
struct pmf_irq_client *irq_client;
138
char *name;
139
int err = -EBUSY;
140
141
switch (type) {
142
case AOA_NOTIFY_HEADPHONE:
143
notif = &rt->headphone_notify;
144
name = "headphone-detect";
145
break;
146
case AOA_NOTIFY_LINE_IN:
147
notif = &rt->line_in_notify;
148
name = "linein-detect";
149
break;
150
case AOA_NOTIFY_LINE_OUT:
151
notif = &rt->line_out_notify;
152
name = "lineout-detect";
153
break;
154
default:
155
return -EINVAL;
156
}
157
158
mutex_lock(&notif->mutex);
159
160
old = notif->notify;
161
162
if (!old && !notify) {
163
err = 0;
164
goto out_unlock;
165
}
166
167
if (old && notify) {
168
if (old == notify && notif->data == data)
169
err = 0;
170
goto out_unlock;
171
}
172
173
if (old && !notify) {
174
irq_client = notif->gpio_private;
175
pmf_unregister_irq_client(irq_client);
176
kfree(irq_client);
177
notif->gpio_private = NULL;
178
}
179
if (!old && notify) {
180
irq_client = kzalloc(sizeof(struct pmf_irq_client),
181
GFP_KERNEL);
182
if (!irq_client) {
183
err = -ENOMEM;
184
goto out_unlock;
185
}
186
irq_client->data = notif;
187
irq_client->handler = pmf_handle_notify_irq;
188
irq_client->owner = THIS_MODULE;
189
err = pmf_register_irq_client(rt->node,
190
name,
191
irq_client);
192
if (err) {
193
printk(KERN_ERR "snd-aoa: gpio layer failed to"
194
" register %s irq (%d)\n", name, err);
195
kfree(irq_client);
196
goto out_unlock;
197
}
198
notif->gpio_private = irq_client;
199
}
200
notif->notify = notify;
201
notif->data = data;
202
203
err = 0;
204
out_unlock:
205
mutex_unlock(&notif->mutex);
206
return err;
207
}
208
209
static int pmf_get_detect(struct gpio_runtime *rt,
210
enum notify_type type)
211
{
212
char *name;
213
int err = -EBUSY, ret;
214
struct pmf_args args = { .count = 1, .u[0].p = &ret };
215
216
switch (type) {
217
case AOA_NOTIFY_HEADPHONE:
218
name = "headphone-detect";
219
break;
220
case AOA_NOTIFY_LINE_IN:
221
name = "linein-detect";
222
break;
223
case AOA_NOTIFY_LINE_OUT:
224
name = "lineout-detect";
225
break;
226
default:
227
return -EINVAL;
228
}
229
230
err = pmf_call_function(rt->node, name, &args);
231
if (err)
232
return err;
233
return ret;
234
}
235
236
static struct gpio_methods methods = {
237
.init = pmf_gpio_init,
238
.exit = pmf_gpio_exit,
239
.all_amps_off = pmf_gpio_all_amps_off,
240
.all_amps_restore = pmf_gpio_all_amps_restore,
241
.set_headphone = pmf_gpio_set_headphone,
242
.set_speakers = pmf_gpio_set_amp,
243
.set_lineout = pmf_gpio_set_lineout,
244
.set_hw_reset = pmf_gpio_set_hw_reset,
245
.get_headphone = pmf_gpio_get_headphone,
246
.get_speakers = pmf_gpio_get_amp,
247
.get_lineout = pmf_gpio_get_lineout,
248
.set_notify = pmf_set_notify,
249
.get_detect = pmf_get_detect,
250
};
251
252
struct gpio_methods *pmf_gpio_methods = &methods;
253
EXPORT_SYMBOL_GPL(pmf_gpio_methods);
254
255