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