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