diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-10-23 09:47:56 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2008-10-23 12:43:42 -0400 |
commit | 888dcb7cb26fb85dfe3486d28a2431d69d3e8148 (patch) | |
tree | aad6f0dba4bdb63bc72280ed51a79f7dea284278 /sound/aoa/fabrics/snd-aoa-fabric-layout.c | |
parent | 72474be62d6ec2e0337ff01ecbd737f9c5c242c7 (diff) |
ALSA: aoa: clean up file names
This cleans up the apple onboard audio driver filenames.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/aoa/fabrics/snd-aoa-fabric-layout.c')
-rw-r--r-- | sound/aoa/fabrics/snd-aoa-fabric-layout.c | 1120 |
1 files changed, 0 insertions, 1120 deletions
diff --git a/sound/aoa/fabrics/snd-aoa-fabric-layout.c b/sound/aoa/fabrics/snd-aoa-fabric-layout.c deleted file mode 100644 index dea7abb082c..00000000000 --- a/sound/aoa/fabrics/snd-aoa-fabric-layout.c +++ /dev/null | |||
@@ -1,1120 +0,0 @@ | |||
1 | /* | ||
2 | * Apple Onboard Audio driver -- layout fabric | ||
3 | * | ||
4 | * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * | ||
6 | * GPL v2, can be found in COPYING. | ||
7 | * | ||
8 | * | ||
9 | * This fabric module looks for sound codecs | ||
10 | * based on the layout-id property in the device tree. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <asm/prom.h> | ||
15 | #include <linux/list.h> | ||
16 | #include <linux/module.h> | ||
17 | #include "../aoa.h" | ||
18 | #include "../soundbus/soundbus.h" | ||
19 | |||
20 | MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); | ||
21 | MODULE_LICENSE("GPL"); | ||
22 | MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa"); | ||
23 | |||
24 | #define MAX_CODECS_PER_BUS 2 | ||
25 | |||
26 | /* These are the connections the layout fabric | ||
27 | * knows about. It doesn't really care about the | ||
28 | * input ones, but I thought I'd separate them | ||
29 | * to give them proper names. The thing is that | ||
30 | * Apple usually will distinguish the active output | ||
31 | * by GPIOs, while the active input is set directly | ||
32 | * on the codec. Hence we here tell the codec what | ||
33 | * we think is connected. This information is hard- | ||
34 | * coded below ... */ | ||
35 | #define CC_SPEAKERS (1<<0) | ||
36 | #define CC_HEADPHONE (1<<1) | ||
37 | #define CC_LINEOUT (1<<2) | ||
38 | #define CC_DIGITALOUT (1<<3) | ||
39 | #define CC_LINEIN (1<<4) | ||
40 | #define CC_MICROPHONE (1<<5) | ||
41 | #define CC_DIGITALIN (1<<6) | ||
42 | /* pretty bogus but users complain... | ||
43 | * This is a flag saying that the LINEOUT | ||
44 | * should be renamed to HEADPHONE. | ||
45 | * be careful with input detection! */ | ||
46 | #define CC_LINEOUT_LABELLED_HEADPHONE (1<<7) | ||
47 | |||
48 | struct codec_connection { | ||
49 | /* CC_ flags from above */ | ||
50 | int connected; | ||
51 | /* codec dependent bit to be set in the aoa_codec.connected field. | ||
52 | * This intentionally doesn't have any generic flags because the | ||
53 | * fabric has to know the codec anyway and all codecs might have | ||
54 | * different connectors */ | ||
55 | int codec_bit; | ||
56 | }; | ||
57 | |||
58 | struct codec_connect_info { | ||
59 | char *name; | ||
60 | struct codec_connection *connections; | ||
61 | }; | ||
62 | |||
63 | #define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0) | ||
64 | |||
65 | struct layout { | ||
66 | unsigned int layout_id; | ||
67 | struct codec_connect_info codecs[MAX_CODECS_PER_BUS]; | ||
68 | int flags; | ||
69 | |||
70 | /* if busname is not assigned, we use 'Master' below, | ||
71 | * so that our layout table doesn't need to be filled | ||
72 | * too much. | ||
73 | * We only assign these two if we expect to find more | ||
74 | * than one soundbus, i.e. on those machines with | ||
75 | * multiple layout-ids */ | ||
76 | char *busname; | ||
77 | int pcmid; | ||
78 | }; | ||
79 | |||
80 | MODULE_ALIAS("sound-layout-36"); | ||
81 | MODULE_ALIAS("sound-layout-41"); | ||
82 | MODULE_ALIAS("sound-layout-45"); | ||
83 | MODULE_ALIAS("sound-layout-47"); | ||
84 | MODULE_ALIAS("sound-layout-48"); | ||
85 | MODULE_ALIAS("sound-layout-49"); | ||
86 | MODULE_ALIAS("sound-layout-50"); | ||
87 | MODULE_ALIAS("sound-layout-51"); | ||
88 | MODULE_ALIAS("sound-layout-56"); | ||
89 | MODULE_ALIAS("sound-layout-57"); | ||
90 | MODULE_ALIAS("sound-layout-58"); | ||
91 | MODULE_ALIAS("sound-layout-60"); | ||
92 | MODULE_ALIAS("sound-layout-61"); | ||
93 | MODULE_ALIAS("sound-layout-62"); | ||
94 | MODULE_ALIAS("sound-layout-64"); | ||
95 | MODULE_ALIAS("sound-layout-65"); | ||
96 | MODULE_ALIAS("sound-layout-66"); | ||
97 | MODULE_ALIAS("sound-layout-67"); | ||
98 | MODULE_ALIAS("sound-layout-68"); | ||
99 | MODULE_ALIAS("sound-layout-69"); | ||
100 | MODULE_ALIAS("sound-layout-70"); | ||
101 | MODULE_ALIAS("sound-layout-72"); | ||
102 | MODULE_ALIAS("sound-layout-76"); | ||
103 | MODULE_ALIAS("sound-layout-80"); | ||
104 | MODULE_ALIAS("sound-layout-82"); | ||
105 | MODULE_ALIAS("sound-layout-84"); | ||
106 | MODULE_ALIAS("sound-layout-86"); | ||
107 | MODULE_ALIAS("sound-layout-90"); | ||
108 | MODULE_ALIAS("sound-layout-92"); | ||
109 | MODULE_ALIAS("sound-layout-94"); | ||
110 | MODULE_ALIAS("sound-layout-96"); | ||
111 | MODULE_ALIAS("sound-layout-98"); | ||
112 | MODULE_ALIAS("sound-layout-100"); | ||
113 | |||
114 | /* onyx with all but microphone connected */ | ||
115 | static struct codec_connection onyx_connections_nomic[] = { | ||
116 | { | ||
117 | .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, | ||
118 | .codec_bit = 0, | ||
119 | }, | ||
120 | { | ||
121 | .connected = CC_DIGITALOUT, | ||
122 | .codec_bit = 1, | ||
123 | }, | ||
124 | { | ||
125 | .connected = CC_LINEIN, | ||
126 | .codec_bit = 2, | ||
127 | }, | ||
128 | {} /* terminate array by .connected == 0 */ | ||
129 | }; | ||
130 | |||
131 | /* onyx on machines without headphone */ | ||
132 | static struct codec_connection onyx_connections_noheadphones[] = { | ||
133 | { | ||
134 | .connected = CC_SPEAKERS | CC_LINEOUT | | ||
135 | CC_LINEOUT_LABELLED_HEADPHONE, | ||
136 | .codec_bit = 0, | ||
137 | }, | ||
138 | { | ||
139 | .connected = CC_DIGITALOUT, | ||
140 | .codec_bit = 1, | ||
141 | }, | ||
142 | /* FIXME: are these correct? probably not for all the machines | ||
143 | * below ... If not this will need separating. */ | ||
144 | { | ||
145 | .connected = CC_LINEIN, | ||
146 | .codec_bit = 2, | ||
147 | }, | ||
148 | { | ||
149 | .connected = CC_MICROPHONE, | ||
150 | .codec_bit = 3, | ||
151 | }, | ||
152 | {} /* terminate array by .connected == 0 */ | ||
153 | }; | ||
154 | |||
155 | /* onyx on machines with real line-out */ | ||
156 | static struct codec_connection onyx_connections_reallineout[] = { | ||
157 | { | ||
158 | .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE, | ||
159 | .codec_bit = 0, | ||
160 | }, | ||
161 | { | ||
162 | .connected = CC_DIGITALOUT, | ||
163 | .codec_bit = 1, | ||
164 | }, | ||
165 | { | ||
166 | .connected = CC_LINEIN, | ||
167 | .codec_bit = 2, | ||
168 | }, | ||
169 | {} /* terminate array by .connected == 0 */ | ||
170 | }; | ||
171 | |||
172 | /* tas on machines without line out */ | ||
173 | static struct codec_connection tas_connections_nolineout[] = { | ||
174 | { | ||
175 | .connected = CC_SPEAKERS | CC_HEADPHONE, | ||
176 | .codec_bit = 0, | ||
177 | }, | ||
178 | { | ||
179 | .connected = CC_LINEIN, | ||
180 | .codec_bit = 2, | ||
181 | }, | ||
182 | { | ||
183 | .connected = CC_MICROPHONE, | ||
184 | .codec_bit = 3, | ||
185 | }, | ||
186 | {} /* terminate array by .connected == 0 */ | ||
187 | }; | ||
188 | |||
189 | /* tas on machines with neither line out nor line in */ | ||
190 | static struct codec_connection tas_connections_noline[] = { | ||
191 | { | ||
192 | .connected = CC_SPEAKERS | CC_HEADPHONE, | ||
193 | .codec_bit = 0, | ||
194 | }, | ||
195 | { | ||
196 | .connected = CC_MICROPHONE, | ||
197 | .codec_bit = 3, | ||
198 | }, | ||
199 | {} /* terminate array by .connected == 0 */ | ||
200 | }; | ||
201 | |||
202 | /* tas on machines without microphone */ | ||
203 | static struct codec_connection tas_connections_nomic[] = { | ||
204 | { | ||
205 | .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, | ||
206 | .codec_bit = 0, | ||
207 | }, | ||
208 | { | ||
209 | .connected = CC_LINEIN, | ||
210 | .codec_bit = 2, | ||
211 | }, | ||
212 | {} /* terminate array by .connected == 0 */ | ||
213 | }; | ||
214 | |||
215 | /* tas on machines with everything connected */ | ||
216 | static struct codec_connection tas_connections_all[] = { | ||
217 | { | ||
218 | .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, | ||
219 | .codec_bit = 0, | ||
220 | }, | ||
221 | { | ||
222 | .connected = CC_LINEIN, | ||
223 | .codec_bit = 2, | ||
224 | }, | ||
225 | { | ||
226 | .connected = CC_MICROPHONE, | ||
227 | .codec_bit = 3, | ||
228 | }, | ||
229 | {} /* terminate array by .connected == 0 */ | ||
230 | }; | ||
231 | |||
232 | static struct codec_connection toonie_connections[] = { | ||
233 | { | ||
234 | .connected = CC_SPEAKERS | CC_HEADPHONE, | ||
235 | .codec_bit = 0, | ||
236 | }, | ||
237 | {} /* terminate array by .connected == 0 */ | ||
238 | }; | ||
239 | |||
240 | static struct codec_connection topaz_input[] = { | ||
241 | { | ||
242 | .connected = CC_DIGITALIN, | ||
243 | .codec_bit = 0, | ||
244 | }, | ||
245 | {} /* terminate array by .connected == 0 */ | ||
246 | }; | ||
247 | |||
248 | static struct codec_connection topaz_output[] = { | ||
249 | { | ||
250 | .connected = CC_DIGITALOUT, | ||
251 | .codec_bit = 1, | ||
252 | }, | ||
253 | {} /* terminate array by .connected == 0 */ | ||
254 | }; | ||
255 | |||
256 | static struct codec_connection topaz_inout[] = { | ||
257 | { | ||
258 | .connected = CC_DIGITALIN, | ||
259 | .codec_bit = 0, | ||
260 | }, | ||
261 | { | ||
262 | .connected = CC_DIGITALOUT, | ||
263 | .codec_bit = 1, | ||
264 | }, | ||
265 | {} /* terminate array by .connected == 0 */ | ||
266 | }; | ||
267 | |||
268 | static struct layout layouts[] = { | ||
269 | /* last PowerBooks (15" Oct 2005) */ | ||
270 | { .layout_id = 82, | ||
271 | .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, | ||
272 | .codecs[0] = { | ||
273 | .name = "onyx", | ||
274 | .connections = onyx_connections_noheadphones, | ||
275 | }, | ||
276 | .codecs[1] = { | ||
277 | .name = "topaz", | ||
278 | .connections = topaz_input, | ||
279 | }, | ||
280 | }, | ||
281 | /* PowerMac9,1 */ | ||
282 | { .layout_id = 60, | ||
283 | .codecs[0] = { | ||
284 | .name = "onyx", | ||
285 | .connections = onyx_connections_reallineout, | ||
286 | }, | ||
287 | }, | ||
288 | /* PowerMac9,1 */ | ||
289 | { .layout_id = 61, | ||
290 | .codecs[0] = { | ||
291 | .name = "topaz", | ||
292 | .connections = topaz_input, | ||
293 | }, | ||
294 | }, | ||
295 | /* PowerBook5,7 */ | ||
296 | { .layout_id = 64, | ||
297 | .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, | ||
298 | .codecs[0] = { | ||
299 | .name = "onyx", | ||
300 | .connections = onyx_connections_noheadphones, | ||
301 | }, | ||
302 | }, | ||
303 | /* PowerBook5,7 */ | ||
304 | { .layout_id = 65, | ||
305 | .codecs[0] = { | ||
306 | .name = "topaz", | ||
307 | .connections = topaz_input, | ||
308 | }, | ||
309 | }, | ||
310 | /* PowerBook5,9 [17" Oct 2005] */ | ||
311 | { .layout_id = 84, | ||
312 | .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, | ||
313 | .codecs[0] = { | ||
314 | .name = "onyx", | ||
315 | .connections = onyx_connections_noheadphones, | ||
316 | }, | ||
317 | .codecs[1] = { | ||
318 | .name = "topaz", | ||
319 | .connections = topaz_input, | ||
320 | }, | ||
321 | }, | ||
322 | /* PowerMac8,1 */ | ||
323 | { .layout_id = 45, | ||
324 | .codecs[0] = { | ||
325 | .name = "onyx", | ||
326 | .connections = onyx_connections_noheadphones, | ||
327 | }, | ||
328 | .codecs[1] = { | ||
329 | .name = "topaz", | ||
330 | .connections = topaz_input, | ||
331 | }, | ||
332 | }, | ||
333 | /* Quad PowerMac (analog in, analog/digital out) */ | ||
334 | { .layout_id = 68, | ||
335 | .codecs[0] = { | ||
336 | .name = "onyx", | ||
337 | .connections = onyx_connections_nomic, | ||
338 | }, | ||
339 | }, | ||
340 | /* Quad PowerMac (digital in) */ | ||
341 | { .layout_id = 69, | ||
342 | .codecs[0] = { | ||
343 | .name = "topaz", | ||
344 | .connections = topaz_input, | ||
345 | }, | ||
346 | .busname = "digital in", .pcmid = 1 }, | ||
347 | /* Early 2005 PowerBook (PowerBook 5,6) */ | ||
348 | { .layout_id = 70, | ||
349 | .codecs[0] = { | ||
350 | .name = "tas", | ||
351 | .connections = tas_connections_nolineout, | ||
352 | }, | ||
353 | }, | ||
354 | /* PowerBook 5,4 */ | ||
355 | { .layout_id = 51, | ||
356 | .codecs[0] = { | ||
357 | .name = "tas", | ||
358 | .connections = tas_connections_nolineout, | ||
359 | }, | ||
360 | }, | ||
361 | /* PowerBook6,7 */ | ||
362 | { .layout_id = 80, | ||
363 | .codecs[0] = { | ||
364 | .name = "tas", | ||
365 | .connections = tas_connections_noline, | ||
366 | }, | ||
367 | }, | ||
368 | /* PowerBook6,8 */ | ||
369 | { .layout_id = 72, | ||
370 | .codecs[0] = { | ||
371 | .name = "tas", | ||
372 | .connections = tas_connections_nolineout, | ||
373 | }, | ||
374 | }, | ||
375 | /* PowerMac8,2 */ | ||
376 | { .layout_id = 86, | ||
377 | .codecs[0] = { | ||
378 | .name = "onyx", | ||
379 | .connections = onyx_connections_nomic, | ||
380 | }, | ||
381 | .codecs[1] = { | ||
382 | .name = "topaz", | ||
383 | .connections = topaz_input, | ||
384 | }, | ||
385 | }, | ||
386 | /* PowerBook6,7 */ | ||
387 | { .layout_id = 92, | ||
388 | .codecs[0] = { | ||
389 | .name = "tas", | ||
390 | .connections = tas_connections_nolineout, | ||
391 | }, | ||
392 | }, | ||
393 | /* PowerMac10,1 (Mac Mini) */ | ||
394 | { .layout_id = 58, | ||
395 | .codecs[0] = { | ||
396 | .name = "toonie", | ||
397 | .connections = toonie_connections, | ||
398 | }, | ||
399 | }, | ||
400 | { | ||
401 | .layout_id = 96, | ||
402 | .codecs[0] = { | ||
403 | .name = "onyx", | ||
404 | .connections = onyx_connections_noheadphones, | ||
405 | }, | ||
406 | }, | ||
407 | /* unknown, untested, but this comes from Apple */ | ||
408 | { .layout_id = 41, | ||
409 | .codecs[0] = { | ||
410 | .name = "tas", | ||
411 | .connections = tas_connections_all, | ||
412 | }, | ||
413 | }, | ||
414 | { .layout_id = 36, | ||
415 | .codecs[0] = { | ||
416 | .name = "tas", | ||
417 | .connections = tas_connections_nomic, | ||
418 | }, | ||
419 | .codecs[1] = { | ||
420 | .name = "topaz", | ||
421 | .connections = topaz_inout, | ||
422 | }, | ||
423 | }, | ||
424 | { .layout_id = 47, | ||
425 | .codecs[0] = { | ||
426 | .name = "onyx", | ||
427 | .connections = onyx_connections_noheadphones, | ||
428 | }, | ||
429 | }, | ||
430 | { .layout_id = 48, | ||
431 | .codecs[0] = { | ||
432 | .name = "topaz", | ||
433 | .connections = topaz_input, | ||
434 | }, | ||
435 | }, | ||
436 | { .layout_id = 49, | ||
437 | .codecs[0] = { | ||
438 | .name = "onyx", | ||
439 | .connections = onyx_connections_nomic, | ||
440 | }, | ||
441 | }, | ||
442 | { .layout_id = 50, | ||
443 | .codecs[0] = { | ||
444 | .name = "topaz", | ||
445 | .connections = topaz_input, | ||
446 | }, | ||
447 | }, | ||
448 | { .layout_id = 56, | ||
449 | .codecs[0] = { | ||
450 | .name = "onyx", | ||
451 | .connections = onyx_connections_noheadphones, | ||
452 | }, | ||
453 | }, | ||
454 | { .layout_id = 57, | ||
455 | .codecs[0] = { | ||
456 | .name = "topaz", | ||
457 | .connections = topaz_input, | ||
458 | }, | ||
459 | }, | ||
460 | { .layout_id = 62, | ||
461 | .codecs[0] = { | ||
462 | .name = "onyx", | ||
463 | .connections = onyx_connections_noheadphones, | ||
464 | }, | ||
465 | .codecs[1] = { | ||
466 | .name = "topaz", | ||
467 | .connections = topaz_output, | ||
468 | }, | ||
469 | }, | ||
470 | { .layout_id = 66, | ||
471 | .codecs[0] = { | ||
472 | .name = "onyx", | ||
473 | .connections = onyx_connections_noheadphones, | ||
474 | }, | ||
475 | }, | ||
476 | { .layout_id = 67, | ||
477 | .codecs[0] = { | ||
478 | .name = "topaz", | ||
479 | .connections = topaz_input, | ||
480 | }, | ||
481 | }, | ||
482 | { .layout_id = 76, | ||
483 | .codecs[0] = { | ||
484 | .name = "tas", | ||
485 | .connections = tas_connections_nomic, | ||
486 | }, | ||
487 | .codecs[1] = { | ||
488 | .name = "topaz", | ||
489 | .connections = topaz_inout, | ||
490 | }, | ||
491 | }, | ||
492 | { .layout_id = 90, | ||
493 | .codecs[0] = { | ||
494 | .name = "tas", | ||
495 | .connections = tas_connections_noline, | ||
496 | }, | ||
497 | }, | ||
498 | { .layout_id = 94, | ||
499 | .codecs[0] = { | ||
500 | .name = "onyx", | ||
501 | /* but it has an external mic?? how to select? */ | ||
502 | .connections = onyx_connections_noheadphones, | ||
503 | }, | ||
504 | }, | ||
505 | { .layout_id = 98, | ||
506 | .codecs[0] = { | ||
507 | .name = "toonie", | ||
508 | .connections = toonie_connections, | ||
509 | }, | ||
510 | }, | ||
511 | { .layout_id = 100, | ||
512 | .codecs[0] = { | ||
513 | .name = "topaz", | ||
514 | .connections = topaz_input, | ||
515 | }, | ||
516 | .codecs[1] = { | ||
517 | .name = "onyx", | ||
518 | .connections = onyx_connections_noheadphones, | ||
519 | }, | ||
520 | }, | ||
521 | {} | ||
522 | }; | ||
523 | |||
524 | static struct layout *find_layout_by_id(unsigned int id) | ||
525 | { | ||
526 | struct layout *l; | ||
527 | |||
528 | l = layouts; | ||
529 | while (l->layout_id) { | ||
530 | if (l->layout_id == id) | ||
531 | return l; | ||
532 | l++; | ||
533 | } | ||
534 | return NULL; | ||
535 | } | ||
536 | |||
537 | static void use_layout(struct layout *l) | ||
538 | { | ||
539 | int i; | ||
540 | |||
541 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
542 | if (l->codecs[i].name) { | ||
543 | request_module("snd-aoa-codec-%s", l->codecs[i].name); | ||
544 | } | ||
545 | } | ||
546 | /* now we wait for the codecs to call us back */ | ||
547 | } | ||
548 | |||
549 | struct layout_dev; | ||
550 | |||
551 | struct layout_dev_ptr { | ||
552 | struct layout_dev *ptr; | ||
553 | }; | ||
554 | |||
555 | struct layout_dev { | ||
556 | struct list_head list; | ||
557 | struct soundbus_dev *sdev; | ||
558 | struct device_node *sound; | ||
559 | struct aoa_codec *codecs[MAX_CODECS_PER_BUS]; | ||
560 | struct layout *layout; | ||
561 | struct gpio_runtime gpio; | ||
562 | |||
563 | /* we need these for headphone/lineout detection */ | ||
564 | struct snd_kcontrol *headphone_ctrl; | ||
565 | struct snd_kcontrol *lineout_ctrl; | ||
566 | struct snd_kcontrol *speaker_ctrl; | ||
567 | struct snd_kcontrol *headphone_detected_ctrl; | ||
568 | struct snd_kcontrol *lineout_detected_ctrl; | ||
569 | |||
570 | struct layout_dev_ptr selfptr_headphone; | ||
571 | struct layout_dev_ptr selfptr_lineout; | ||
572 | |||
573 | u32 have_lineout_detect:1, | ||
574 | have_headphone_detect:1, | ||
575 | switch_on_headphone:1, | ||
576 | switch_on_lineout:1; | ||
577 | }; | ||
578 | |||
579 | static LIST_HEAD(layouts_list); | ||
580 | static int layouts_list_items; | ||
581 | /* this can go away but only if we allow multiple cards, | ||
582 | * make the fabric handle all the card stuff, etc... */ | ||
583 | static struct layout_dev *layout_device; | ||
584 | |||
585 | #define control_info snd_ctl_boolean_mono_info | ||
586 | |||
587 | #define AMP_CONTROL(n, description) \ | ||
588 | static int n##_control_get(struct snd_kcontrol *kcontrol, \ | ||
589 | struct snd_ctl_elem_value *ucontrol) \ | ||
590 | { \ | ||
591 | struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ | ||
592 | if (gpio->methods && gpio->methods->get_##n) \ | ||
593 | ucontrol->value.integer.value[0] = \ | ||
594 | gpio->methods->get_##n(gpio); \ | ||
595 | return 0; \ | ||
596 | } \ | ||
597 | static int n##_control_put(struct snd_kcontrol *kcontrol, \ | ||
598 | struct snd_ctl_elem_value *ucontrol) \ | ||
599 | { \ | ||
600 | struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ | ||
601 | if (gpio->methods && gpio->methods->get_##n) \ | ||
602 | gpio->methods->set_##n(gpio, \ | ||
603 | !!ucontrol->value.integer.value[0]); \ | ||
604 | return 1; \ | ||
605 | } \ | ||
606 | static struct snd_kcontrol_new n##_ctl = { \ | ||
607 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ | ||
608 | .name = description, \ | ||
609 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ | ||
610 | .info = control_info, \ | ||
611 | .get = n##_control_get, \ | ||
612 | .put = n##_control_put, \ | ||
613 | } | ||
614 | |||
615 | AMP_CONTROL(headphone, "Headphone Switch"); | ||
616 | AMP_CONTROL(speakers, "Speakers Switch"); | ||
617 | AMP_CONTROL(lineout, "Line-Out Switch"); | ||
618 | |||
619 | static int detect_choice_get(struct snd_kcontrol *kcontrol, | ||
620 | struct snd_ctl_elem_value *ucontrol) | ||
621 | { | ||
622 | struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); | ||
623 | |||
624 | switch (kcontrol->private_value) { | ||
625 | case 0: | ||
626 | ucontrol->value.integer.value[0] = ldev->switch_on_headphone; | ||
627 | break; | ||
628 | case 1: | ||
629 | ucontrol->value.integer.value[0] = ldev->switch_on_lineout; | ||
630 | break; | ||
631 | default: | ||
632 | return -ENODEV; | ||
633 | } | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | static int detect_choice_put(struct snd_kcontrol *kcontrol, | ||
638 | struct snd_ctl_elem_value *ucontrol) | ||
639 | { | ||
640 | struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); | ||
641 | |||
642 | switch (kcontrol->private_value) { | ||
643 | case 0: | ||
644 | ldev->switch_on_headphone = !!ucontrol->value.integer.value[0]; | ||
645 | break; | ||
646 | case 1: | ||
647 | ldev->switch_on_lineout = !!ucontrol->value.integer.value[0]; | ||
648 | break; | ||
649 | default: | ||
650 | return -ENODEV; | ||
651 | } | ||
652 | return 1; | ||
653 | } | ||
654 | |||
655 | static struct snd_kcontrol_new headphone_detect_choice = { | ||
656 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
657 | .name = "Headphone Detect Autoswitch", | ||
658 | .info = control_info, | ||
659 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
660 | .get = detect_choice_get, | ||
661 | .put = detect_choice_put, | ||
662 | .private_value = 0, | ||
663 | }; | ||
664 | |||
665 | static struct snd_kcontrol_new lineout_detect_choice = { | ||
666 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
667 | .name = "Line-Out Detect Autoswitch", | ||
668 | .info = control_info, | ||
669 | .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
670 | .get = detect_choice_get, | ||
671 | .put = detect_choice_put, | ||
672 | .private_value = 1, | ||
673 | }; | ||
674 | |||
675 | static int detected_get(struct snd_kcontrol *kcontrol, | ||
676 | struct snd_ctl_elem_value *ucontrol) | ||
677 | { | ||
678 | struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); | ||
679 | int v; | ||
680 | |||
681 | switch (kcontrol->private_value) { | ||
682 | case 0: | ||
683 | v = ldev->gpio.methods->get_detect(&ldev->gpio, | ||
684 | AOA_NOTIFY_HEADPHONE); | ||
685 | break; | ||
686 | case 1: | ||
687 | v = ldev->gpio.methods->get_detect(&ldev->gpio, | ||
688 | AOA_NOTIFY_LINE_OUT); | ||
689 | break; | ||
690 | default: | ||
691 | return -ENODEV; | ||
692 | } | ||
693 | ucontrol->value.integer.value[0] = v; | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | static struct snd_kcontrol_new headphone_detected = { | ||
698 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
699 | .name = "Headphone Detected", | ||
700 | .info = control_info, | ||
701 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
702 | .get = detected_get, | ||
703 | .private_value = 0, | ||
704 | }; | ||
705 | |||
706 | static struct snd_kcontrol_new lineout_detected = { | ||
707 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
708 | .name = "Line-Out Detected", | ||
709 | .info = control_info, | ||
710 | .access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
711 | .get = detected_get, | ||
712 | .private_value = 1, | ||
713 | }; | ||
714 | |||
715 | static int check_codec(struct aoa_codec *codec, | ||
716 | struct layout_dev *ldev, | ||
717 | struct codec_connect_info *cci) | ||
718 | { | ||
719 | const u32 *ref; | ||
720 | char propname[32]; | ||
721 | struct codec_connection *cc; | ||
722 | |||
723 | /* if the codec has a 'codec' node, we require a reference */ | ||
724 | if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { | ||
725 | snprintf(propname, sizeof(propname), | ||
726 | "platform-%s-codec-ref", codec->name); | ||
727 | ref = of_get_property(ldev->sound, propname, NULL); | ||
728 | if (!ref) { | ||
729 | printk(KERN_INFO "snd-aoa-fabric-layout: " | ||
730 | "required property %s not present\n", propname); | ||
731 | return -ENODEV; | ||
732 | } | ||
733 | if (*ref != codec->node->linux_phandle) { | ||
734 | printk(KERN_INFO "snd-aoa-fabric-layout: " | ||
735 | "%s doesn't match!\n", propname); | ||
736 | return -ENODEV; | ||
737 | } | ||
738 | } else { | ||
739 | if (layouts_list_items != 1) { | ||
740 | printk(KERN_INFO "snd-aoa-fabric-layout: " | ||
741 | "more than one soundbus, but no references.\n"); | ||
742 | return -ENODEV; | ||
743 | } | ||
744 | } | ||
745 | codec->soundbus_dev = ldev->sdev; | ||
746 | codec->gpio = &ldev->gpio; | ||
747 | |||
748 | cc = cci->connections; | ||
749 | if (!cc) | ||
750 | return -EINVAL; | ||
751 | |||
752 | printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n"); | ||
753 | |||
754 | codec->connected = 0; | ||
755 | codec->fabric_data = cc; | ||
756 | |||
757 | while (cc->connected) { | ||
758 | codec->connected |= 1<<cc->codec_bit; | ||
759 | cc++; | ||
760 | } | ||
761 | |||
762 | return 0; | ||
763 | } | ||
764 | |||
765 | static int layout_found_codec(struct aoa_codec *codec) | ||
766 | { | ||
767 | struct layout_dev *ldev; | ||
768 | int i; | ||
769 | |||
770 | list_for_each_entry(ldev, &layouts_list, list) { | ||
771 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
772 | if (!ldev->layout->codecs[i].name) | ||
773 | continue; | ||
774 | if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) { | ||
775 | if (check_codec(codec, | ||
776 | ldev, | ||
777 | &ldev->layout->codecs[i]) == 0) | ||
778 | return 0; | ||
779 | } | ||
780 | } | ||
781 | } | ||
782 | return -ENODEV; | ||
783 | } | ||
784 | |||
785 | static void layout_remove_codec(struct aoa_codec *codec) | ||
786 | { | ||
787 | int i; | ||
788 | /* here remove the codec from the layout dev's | ||
789 | * codec reference */ | ||
790 | |||
791 | codec->soundbus_dev = NULL; | ||
792 | codec->gpio = NULL; | ||
793 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
794 | } | ||
795 | } | ||
796 | |||
797 | static void layout_notify(void *data) | ||
798 | { | ||
799 | struct layout_dev_ptr *dptr = data; | ||
800 | struct layout_dev *ldev; | ||
801 | int v, update; | ||
802 | struct snd_kcontrol *detected, *c; | ||
803 | struct snd_card *card = aoa_get_card(); | ||
804 | |||
805 | ldev = dptr->ptr; | ||
806 | if (data == &ldev->selfptr_headphone) { | ||
807 | v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE); | ||
808 | detected = ldev->headphone_detected_ctrl; | ||
809 | update = ldev->switch_on_headphone; | ||
810 | if (update) { | ||
811 | ldev->gpio.methods->set_speakers(&ldev->gpio, !v); | ||
812 | ldev->gpio.methods->set_headphone(&ldev->gpio, v); | ||
813 | ldev->gpio.methods->set_lineout(&ldev->gpio, 0); | ||
814 | } | ||
815 | } else if (data == &ldev->selfptr_lineout) { | ||
816 | v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT); | ||
817 | detected = ldev->lineout_detected_ctrl; | ||
818 | update = ldev->switch_on_lineout; | ||
819 | if (update) { | ||
820 | ldev->gpio.methods->set_speakers(&ldev->gpio, !v); | ||
821 | ldev->gpio.methods->set_headphone(&ldev->gpio, 0); | ||
822 | ldev->gpio.methods->set_lineout(&ldev->gpio, v); | ||
823 | } | ||
824 | } else | ||
825 | return; | ||
826 | |||
827 | if (detected) | ||
828 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id); | ||
829 | if (update) { | ||
830 | c = ldev->headphone_ctrl; | ||
831 | if (c) | ||
832 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); | ||
833 | c = ldev->speaker_ctrl; | ||
834 | if (c) | ||
835 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); | ||
836 | c = ldev->lineout_ctrl; | ||
837 | if (c) | ||
838 | snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); | ||
839 | } | ||
840 | } | ||
841 | |||
842 | static void layout_attached_codec(struct aoa_codec *codec) | ||
843 | { | ||
844 | struct codec_connection *cc; | ||
845 | struct snd_kcontrol *ctl; | ||
846 | int headphones, lineout; | ||
847 | struct layout_dev *ldev = layout_device; | ||
848 | |||
849 | /* need to add this codec to our codec array! */ | ||
850 | |||
851 | cc = codec->fabric_data; | ||
852 | |||
853 | headphones = codec->gpio->methods->get_detect(codec->gpio, | ||
854 | AOA_NOTIFY_HEADPHONE); | ||
855 | lineout = codec->gpio->methods->get_detect(codec->gpio, | ||
856 | AOA_NOTIFY_LINE_OUT); | ||
857 | |||
858 | while (cc->connected) { | ||
859 | if (cc->connected & CC_SPEAKERS) { | ||
860 | if (headphones <= 0 && lineout <= 0) | ||
861 | ldev->gpio.methods->set_speakers(codec->gpio, 1); | ||
862 | ctl = snd_ctl_new1(&speakers_ctl, codec->gpio); | ||
863 | ldev->speaker_ctrl = ctl; | ||
864 | aoa_snd_ctl_add(ctl); | ||
865 | } | ||
866 | if (cc->connected & CC_HEADPHONE) { | ||
867 | if (headphones == 1) | ||
868 | ldev->gpio.methods->set_headphone(codec->gpio, 1); | ||
869 | ctl = snd_ctl_new1(&headphone_ctl, codec->gpio); | ||
870 | ldev->headphone_ctrl = ctl; | ||
871 | aoa_snd_ctl_add(ctl); | ||
872 | ldev->have_headphone_detect = | ||
873 | !ldev->gpio.methods | ||
874 | ->set_notify(&ldev->gpio, | ||
875 | AOA_NOTIFY_HEADPHONE, | ||
876 | layout_notify, | ||
877 | &ldev->selfptr_headphone); | ||
878 | if (ldev->have_headphone_detect) { | ||
879 | ctl = snd_ctl_new1(&headphone_detect_choice, | ||
880 | ldev); | ||
881 | aoa_snd_ctl_add(ctl); | ||
882 | ctl = snd_ctl_new1(&headphone_detected, | ||
883 | ldev); | ||
884 | ldev->headphone_detected_ctrl = ctl; | ||
885 | aoa_snd_ctl_add(ctl); | ||
886 | } | ||
887 | } | ||
888 | if (cc->connected & CC_LINEOUT) { | ||
889 | if (lineout == 1) | ||
890 | ldev->gpio.methods->set_lineout(codec->gpio, 1); | ||
891 | ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); | ||
892 | if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) | ||
893 | strlcpy(ctl->id.name, | ||
894 | "Headphone Switch", sizeof(ctl->id.name)); | ||
895 | ldev->lineout_ctrl = ctl; | ||
896 | aoa_snd_ctl_add(ctl); | ||
897 | ldev->have_lineout_detect = | ||
898 | !ldev->gpio.methods | ||
899 | ->set_notify(&ldev->gpio, | ||
900 | AOA_NOTIFY_LINE_OUT, | ||
901 | layout_notify, | ||
902 | &ldev->selfptr_lineout); | ||
903 | if (ldev->have_lineout_detect) { | ||
904 | ctl = snd_ctl_new1(&lineout_detect_choice, | ||
905 | ldev); | ||
906 | if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) | ||
907 | strlcpy(ctl->id.name, | ||
908 | "Headphone Detect Autoswitch", | ||
909 | sizeof(ctl->id.name)); | ||
910 | aoa_snd_ctl_add(ctl); | ||
911 | ctl = snd_ctl_new1(&lineout_detected, | ||
912 | ldev); | ||
913 | if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) | ||
914 | strlcpy(ctl->id.name, | ||
915 | "Headphone Detected", | ||
916 | sizeof(ctl->id.name)); | ||
917 | ldev->lineout_detected_ctrl = ctl; | ||
918 | aoa_snd_ctl_add(ctl); | ||
919 | } | ||
920 | } | ||
921 | cc++; | ||
922 | } | ||
923 | /* now update initial state */ | ||
924 | if (ldev->have_headphone_detect) | ||
925 | layout_notify(&ldev->selfptr_headphone); | ||
926 | if (ldev->have_lineout_detect) | ||
927 | layout_notify(&ldev->selfptr_lineout); | ||
928 | } | ||
929 | |||
930 | static struct aoa_fabric layout_fabric = { | ||
931 | .name = "SoundByLayout", | ||
932 | .owner = THIS_MODULE, | ||
933 | .found_codec = layout_found_codec, | ||
934 | .remove_codec = layout_remove_codec, | ||
935 | .attached_codec = layout_attached_codec, | ||
936 | }; | ||
937 | |||
938 | static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) | ||
939 | { | ||
940 | struct device_node *sound = NULL; | ||
941 | const unsigned int *layout_id; | ||
942 | struct layout *layout; | ||
943 | struct layout_dev *ldev = NULL; | ||
944 | int err; | ||
945 | |||
946 | /* hm, currently we can only have one ... */ | ||
947 | if (layout_device) | ||
948 | return -ENODEV; | ||
949 | |||
950 | /* by breaking out we keep a reference */ | ||
951 | while ((sound = of_get_next_child(sdev->ofdev.node, sound))) { | ||
952 | if (sound->type && strcasecmp(sound->type, "soundchip") == 0) | ||
953 | break; | ||
954 | } | ||
955 | if (!sound) return -ENODEV; | ||
956 | |||
957 | layout_id = of_get_property(sound, "layout-id", NULL); | ||
958 | if (!layout_id) | ||
959 | goto outnodev; | ||
960 | printk(KERN_INFO "snd-aoa-fabric-layout: found bus with layout %d\n", | ||
961 | *layout_id); | ||
962 | |||
963 | layout = find_layout_by_id(*layout_id); | ||
964 | if (!layout) { | ||
965 | printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n"); | ||
966 | goto outnodev; | ||
967 | } | ||
968 | |||
969 | ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL); | ||
970 | if (!ldev) | ||
971 | goto outnodev; | ||
972 | |||
973 | layout_device = ldev; | ||
974 | ldev->sdev = sdev; | ||
975 | ldev->sound = sound; | ||
976 | ldev->layout = layout; | ||
977 | ldev->gpio.node = sound->parent; | ||
978 | switch (layout->layout_id) { | ||
979 | case 41: /* that unknown machine no one seems to have */ | ||
980 | case 51: /* PowerBook5,4 */ | ||
981 | case 58: /* Mac Mini */ | ||
982 | ldev->gpio.methods = ftr_gpio_methods; | ||
983 | printk(KERN_DEBUG | ||
984 | "snd-aoa-fabric-layout: Using direct GPIOs\n"); | ||
985 | break; | ||
986 | default: | ||
987 | ldev->gpio.methods = pmf_gpio_methods; | ||
988 | printk(KERN_DEBUG | ||
989 | "snd-aoa-fabric-layout: Using PMF GPIOs\n"); | ||
990 | } | ||
991 | ldev->selfptr_headphone.ptr = ldev; | ||
992 | ldev->selfptr_lineout.ptr = ldev; | ||
993 | sdev->ofdev.dev.driver_data = ldev; | ||
994 | list_add(&ldev->list, &layouts_list); | ||
995 | layouts_list_items++; | ||
996 | |||
997 | /* assign these before registering ourselves, so | ||
998 | * callbacks that are done during registration | ||
999 | * already have the values */ | ||
1000 | sdev->pcmid = ldev->layout->pcmid; | ||
1001 | if (ldev->layout->busname) { | ||
1002 | sdev->pcmname = ldev->layout->busname; | ||
1003 | } else { | ||
1004 | sdev->pcmname = "Master"; | ||
1005 | } | ||
1006 | |||
1007 | ldev->gpio.methods->init(&ldev->gpio); | ||
1008 | |||
1009 | err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev); | ||
1010 | if (err && err != -EALREADY) { | ||
1011 | printk(KERN_INFO "snd-aoa-fabric-layout: can't use," | ||
1012 | " another fabric is active!\n"); | ||
1013 | goto outlistdel; | ||
1014 | } | ||
1015 | |||
1016 | use_layout(layout); | ||
1017 | ldev->switch_on_headphone = 1; | ||
1018 | ldev->switch_on_lineout = 1; | ||
1019 | return 0; | ||
1020 | outlistdel: | ||
1021 | /* we won't be using these then... */ | ||
1022 | ldev->gpio.methods->exit(&ldev->gpio); | ||
1023 | /* reset if we didn't use it */ | ||
1024 | sdev->pcmname = NULL; | ||
1025 | sdev->pcmid = -1; | ||
1026 | list_del(&ldev->list); | ||
1027 | layouts_list_items--; | ||
1028 | outnodev: | ||
1029 | of_node_put(sound); | ||
1030 | layout_device = NULL; | ||
1031 | kfree(ldev); | ||
1032 | return -ENODEV; | ||
1033 | } | ||
1034 | |||
1035 | static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) | ||
1036 | { | ||
1037 | struct layout_dev *ldev = sdev->ofdev.dev.driver_data; | ||
1038 | int i; | ||
1039 | |||
1040 | for (i=0; i<MAX_CODECS_PER_BUS; i++) { | ||
1041 | if (ldev->codecs[i]) { | ||
1042 | aoa_fabric_unlink_codec(ldev->codecs[i]); | ||
1043 | } | ||
1044 | ldev->codecs[i] = NULL; | ||
1045 | } | ||
1046 | list_del(&ldev->list); | ||
1047 | layouts_list_items--; | ||
1048 | of_node_put(ldev->sound); | ||
1049 | |||
1050 | ldev->gpio.methods->set_notify(&ldev->gpio, | ||
1051 | AOA_NOTIFY_HEADPHONE, | ||
1052 | NULL, | ||
1053 | NULL); | ||
1054 | ldev->gpio.methods->set_notify(&ldev->gpio, | ||
1055 | AOA_NOTIFY_LINE_OUT, | ||
1056 | NULL, | ||
1057 | NULL); | ||
1058 | |||
1059 | ldev->gpio.methods->exit(&ldev->gpio); | ||
1060 | layout_device = NULL; | ||
1061 | kfree(ldev); | ||
1062 | sdev->pcmid = -1; | ||
1063 | sdev->pcmname = NULL; | ||
1064 | return 0; | ||
1065 | } | ||
1066 | |||
1067 | #ifdef CONFIG_PM | ||
1068 | static int aoa_fabric_layout_suspend(struct soundbus_dev *sdev, pm_message_t state) | ||
1069 | { | ||
1070 | struct layout_dev *ldev = sdev->ofdev.dev.driver_data; | ||
1071 | |||
1072 | if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) | ||
1073 | ldev->gpio.methods->all_amps_off(&ldev->gpio); | ||
1074 | |||
1075 | return 0; | ||
1076 | } | ||
1077 | |||
1078 | static int aoa_fabric_layout_resume(struct soundbus_dev *sdev) | ||
1079 | { | ||
1080 | struct layout_dev *ldev = sdev->ofdev.dev.driver_data; | ||
1081 | |||
1082 | if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) | ||
1083 | ldev->gpio.methods->all_amps_restore(&ldev->gpio); | ||
1084 | |||
1085 | return 0; | ||
1086 | } | ||
1087 | #endif | ||
1088 | |||
1089 | static struct soundbus_driver aoa_soundbus_driver = { | ||
1090 | .name = "snd_aoa_soundbus_drv", | ||
1091 | .owner = THIS_MODULE, | ||
1092 | .probe = aoa_fabric_layout_probe, | ||
1093 | .remove = aoa_fabric_layout_remove, | ||
1094 | #ifdef CONFIG_PM | ||
1095 | .suspend = aoa_fabric_layout_suspend, | ||
1096 | .resume = aoa_fabric_layout_resume, | ||
1097 | #endif | ||
1098 | .driver = { | ||
1099 | .owner = THIS_MODULE, | ||
1100 | } | ||
1101 | }; | ||
1102 | |||
1103 | static int __init aoa_fabric_layout_init(void) | ||
1104 | { | ||
1105 | int err; | ||
1106 | |||
1107 | err = soundbus_register_driver(&aoa_soundbus_driver); | ||
1108 | if (err) | ||
1109 | return err; | ||
1110 | return 0; | ||
1111 | } | ||
1112 | |||
1113 | static void __exit aoa_fabric_layout_exit(void) | ||
1114 | { | ||
1115 | soundbus_unregister_driver(&aoa_soundbus_driver); | ||
1116 | aoa_fabric_unregister(&layout_fabric); | ||
1117 | } | ||
1118 | |||
1119 | module_init(aoa_fabric_layout_init); | ||
1120 | module_exit(aoa_fabric_layout_exit); | ||