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