OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [tags/] [linux-2.6/] [linux-2.6.24_or32_unified_v2.3/] [sound/] [aoa/] [fabrics/] [snd-aoa-fabric-layout.c] - Blame information for rev 8

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.