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