0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <asm/prom.h>
0011 #include <linux/list.h>
0012 #include <linux/module.h>
0013 #include <linux/slab.h>
0014 #include "../aoa.h"
0015 #include "../soundbus/soundbus.h"
0016
0017 MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
0018 MODULE_LICENSE("GPL");
0019 MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");
0020
0021 #define MAX_CODECS_PER_BUS 2
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032 #define CC_SPEAKERS (1<<0)
0033 #define CC_HEADPHONE (1<<1)
0034 #define CC_LINEOUT (1<<2)
0035 #define CC_DIGITALOUT (1<<3)
0036 #define CC_LINEIN (1<<4)
0037 #define CC_MICROPHONE (1<<5)
0038 #define CC_DIGITALIN (1<<6)
0039
0040
0041
0042
0043 #define CC_LINEOUT_LABELLED_HEADPHONE (1<<7)
0044
0045 struct codec_connection {
0046
0047 int connected;
0048
0049
0050
0051
0052 int codec_bit;
0053 };
0054
0055 struct codec_connect_info {
0056 char *name;
0057 struct codec_connection *connections;
0058 };
0059
0060 #define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)
0061
0062 struct layout {
0063 unsigned int layout_id, device_id;
0064 struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
0065 int flags;
0066
0067
0068
0069
0070
0071
0072
0073 char *busname;
0074 int pcmid;
0075 };
0076
0077 MODULE_ALIAS("sound-layout-36");
0078 MODULE_ALIAS("sound-layout-41");
0079 MODULE_ALIAS("sound-layout-45");
0080 MODULE_ALIAS("sound-layout-47");
0081 MODULE_ALIAS("sound-layout-48");
0082 MODULE_ALIAS("sound-layout-49");
0083 MODULE_ALIAS("sound-layout-50");
0084 MODULE_ALIAS("sound-layout-51");
0085 MODULE_ALIAS("sound-layout-56");
0086 MODULE_ALIAS("sound-layout-57");
0087 MODULE_ALIAS("sound-layout-58");
0088 MODULE_ALIAS("sound-layout-60");
0089 MODULE_ALIAS("sound-layout-61");
0090 MODULE_ALIAS("sound-layout-62");
0091 MODULE_ALIAS("sound-layout-64");
0092 MODULE_ALIAS("sound-layout-65");
0093 MODULE_ALIAS("sound-layout-66");
0094 MODULE_ALIAS("sound-layout-67");
0095 MODULE_ALIAS("sound-layout-68");
0096 MODULE_ALIAS("sound-layout-69");
0097 MODULE_ALIAS("sound-layout-70");
0098 MODULE_ALIAS("sound-layout-72");
0099 MODULE_ALIAS("sound-layout-76");
0100 MODULE_ALIAS("sound-layout-80");
0101 MODULE_ALIAS("sound-layout-82");
0102 MODULE_ALIAS("sound-layout-84");
0103 MODULE_ALIAS("sound-layout-86");
0104 MODULE_ALIAS("sound-layout-90");
0105 MODULE_ALIAS("sound-layout-92");
0106 MODULE_ALIAS("sound-layout-94");
0107 MODULE_ALIAS("sound-layout-96");
0108 MODULE_ALIAS("sound-layout-98");
0109 MODULE_ALIAS("sound-layout-100");
0110
0111 MODULE_ALIAS("aoa-device-id-14");
0112 MODULE_ALIAS("aoa-device-id-22");
0113 MODULE_ALIAS("aoa-device-id-31");
0114 MODULE_ALIAS("aoa-device-id-35");
0115 MODULE_ALIAS("aoa-device-id-44");
0116
0117
0118 static struct codec_connection onyx_connections_nomic[] = {
0119 {
0120 .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
0121 .codec_bit = 0,
0122 },
0123 {
0124 .connected = CC_DIGITALOUT,
0125 .codec_bit = 1,
0126 },
0127 {
0128 .connected = CC_LINEIN,
0129 .codec_bit = 2,
0130 },
0131 {}
0132 };
0133
0134
0135 static struct codec_connection onyx_connections_noheadphones[] = {
0136 {
0137 .connected = CC_SPEAKERS | CC_LINEOUT |
0138 CC_LINEOUT_LABELLED_HEADPHONE,
0139 .codec_bit = 0,
0140 },
0141 {
0142 .connected = CC_DIGITALOUT,
0143 .codec_bit = 1,
0144 },
0145
0146
0147 {
0148 .connected = CC_LINEIN,
0149 .codec_bit = 2,
0150 },
0151 {
0152 .connected = CC_MICROPHONE,
0153 .codec_bit = 3,
0154 },
0155 {}
0156 };
0157
0158
0159 static struct codec_connection onyx_connections_reallineout[] = {
0160 {
0161 .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
0162 .codec_bit = 0,
0163 },
0164 {
0165 .connected = CC_DIGITALOUT,
0166 .codec_bit = 1,
0167 },
0168 {
0169 .connected = CC_LINEIN,
0170 .codec_bit = 2,
0171 },
0172 {}
0173 };
0174
0175
0176 static struct codec_connection tas_connections_nolineout[] = {
0177 {
0178 .connected = CC_SPEAKERS | CC_HEADPHONE,
0179 .codec_bit = 0,
0180 },
0181 {
0182 .connected = CC_LINEIN,
0183 .codec_bit = 2,
0184 },
0185 {
0186 .connected = CC_MICROPHONE,
0187 .codec_bit = 3,
0188 },
0189 {}
0190 };
0191
0192
0193 static struct codec_connection tas_connections_noline[] = {
0194 {
0195 .connected = CC_SPEAKERS | CC_HEADPHONE,
0196 .codec_bit = 0,
0197 },
0198 {
0199 .connected = CC_MICROPHONE,
0200 .codec_bit = 3,
0201 },
0202 {}
0203 };
0204
0205
0206 static struct codec_connection tas_connections_nomic[] = {
0207 {
0208 .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
0209 .codec_bit = 0,
0210 },
0211 {
0212 .connected = CC_LINEIN,
0213 .codec_bit = 2,
0214 },
0215 {}
0216 };
0217
0218
0219 static struct codec_connection tas_connections_all[] = {
0220 {
0221 .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
0222 .codec_bit = 0,
0223 },
0224 {
0225 .connected = CC_LINEIN,
0226 .codec_bit = 2,
0227 },
0228 {
0229 .connected = CC_MICROPHONE,
0230 .codec_bit = 3,
0231 },
0232 {}
0233 };
0234
0235 static struct codec_connection toonie_connections[] = {
0236 {
0237 .connected = CC_SPEAKERS | CC_HEADPHONE,
0238 .codec_bit = 0,
0239 },
0240 {}
0241 };
0242
0243 static struct codec_connection topaz_input[] = {
0244 {
0245 .connected = CC_DIGITALIN,
0246 .codec_bit = 0,
0247 },
0248 {}
0249 };
0250
0251 static struct codec_connection topaz_output[] = {
0252 {
0253 .connected = CC_DIGITALOUT,
0254 .codec_bit = 1,
0255 },
0256 {}
0257 };
0258
0259 static struct codec_connection topaz_inout[] = {
0260 {
0261 .connected = CC_DIGITALIN,
0262 .codec_bit = 0,
0263 },
0264 {
0265 .connected = CC_DIGITALOUT,
0266 .codec_bit = 1,
0267 },
0268 {}
0269 };
0270
0271 static struct layout layouts[] = {
0272
0273 { .layout_id = 82,
0274 .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
0275 .codecs[0] = {
0276 .name = "onyx",
0277 .connections = onyx_connections_noheadphones,
0278 },
0279 .codecs[1] = {
0280 .name = "topaz",
0281 .connections = topaz_input,
0282 },
0283 },
0284
0285 { .layout_id = 60,
0286 .codecs[0] = {
0287 .name = "onyx",
0288 .connections = onyx_connections_reallineout,
0289 },
0290 },
0291
0292 { .layout_id = 61,
0293 .codecs[0] = {
0294 .name = "topaz",
0295 .connections = topaz_input,
0296 },
0297 },
0298
0299 { .layout_id = 64,
0300 .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
0301 .codecs[0] = {
0302 .name = "onyx",
0303 .connections = onyx_connections_noheadphones,
0304 },
0305 },
0306
0307 { .layout_id = 65,
0308 .codecs[0] = {
0309 .name = "topaz",
0310 .connections = topaz_input,
0311 },
0312 },
0313
0314 { .layout_id = 84,
0315 .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
0316 .codecs[0] = {
0317 .name = "onyx",
0318 .connections = onyx_connections_noheadphones,
0319 },
0320 .codecs[1] = {
0321 .name = "topaz",
0322 .connections = topaz_input,
0323 },
0324 },
0325
0326 { .layout_id = 45,
0327 .codecs[0] = {
0328 .name = "onyx",
0329 .connections = onyx_connections_noheadphones,
0330 },
0331 .codecs[1] = {
0332 .name = "topaz",
0333 .connections = topaz_input,
0334 },
0335 },
0336
0337 { .layout_id = 68,
0338 .codecs[0] = {
0339 .name = "onyx",
0340 .connections = onyx_connections_nomic,
0341 },
0342 },
0343
0344 { .layout_id = 69,
0345 .codecs[0] = {
0346 .name = "topaz",
0347 .connections = topaz_input,
0348 },
0349 .busname = "digital in", .pcmid = 1 },
0350
0351 { .layout_id = 70,
0352 .codecs[0] = {
0353 .name = "tas",
0354 .connections = tas_connections_nolineout,
0355 },
0356 },
0357
0358 { .layout_id = 51,
0359 .codecs[0] = {
0360 .name = "tas",
0361 .connections = tas_connections_nolineout,
0362 },
0363 },
0364
0365 { .device_id = 31,
0366 .codecs[0] = {
0367 .name = "tas",
0368 .connections = tas_connections_nolineout,
0369 },
0370 },
0371
0372 { .device_id = 44,
0373 .codecs[0] = {
0374 .name = "tas",
0375 .connections = tas_connections_all,
0376 },
0377 },
0378
0379 { .layout_id = 80,
0380 .codecs[0] = {
0381 .name = "tas",
0382 .connections = tas_connections_noline,
0383 },
0384 },
0385
0386 { .layout_id = 72,
0387 .codecs[0] = {
0388 .name = "tas",
0389 .connections = tas_connections_nolineout,
0390 },
0391 },
0392
0393 { .layout_id = 86,
0394 .codecs[0] = {
0395 .name = "onyx",
0396 .connections = onyx_connections_nomic,
0397 },
0398 .codecs[1] = {
0399 .name = "topaz",
0400 .connections = topaz_input,
0401 },
0402 },
0403
0404 { .layout_id = 92,
0405 .codecs[0] = {
0406 .name = "tas",
0407 .connections = tas_connections_nolineout,
0408 },
0409 },
0410
0411 { .layout_id = 58,
0412 .codecs[0] = {
0413 .name = "toonie",
0414 .connections = toonie_connections,
0415 },
0416 },
0417 {
0418 .layout_id = 96,
0419 .codecs[0] = {
0420 .name = "onyx",
0421 .connections = onyx_connections_noheadphones,
0422 },
0423 },
0424
0425 { .layout_id = 41,
0426 .codecs[0] = {
0427 .name = "tas",
0428 .connections = tas_connections_all,
0429 },
0430 },
0431 { .layout_id = 36,
0432 .codecs[0] = {
0433 .name = "tas",
0434 .connections = tas_connections_nomic,
0435 },
0436 .codecs[1] = {
0437 .name = "topaz",
0438 .connections = topaz_inout,
0439 },
0440 },
0441 { .layout_id = 47,
0442 .codecs[0] = {
0443 .name = "onyx",
0444 .connections = onyx_connections_noheadphones,
0445 },
0446 },
0447 { .layout_id = 48,
0448 .codecs[0] = {
0449 .name = "topaz",
0450 .connections = topaz_input,
0451 },
0452 },
0453 { .layout_id = 49,
0454 .codecs[0] = {
0455 .name = "onyx",
0456 .connections = onyx_connections_nomic,
0457 },
0458 },
0459 { .layout_id = 50,
0460 .codecs[0] = {
0461 .name = "topaz",
0462 .connections = topaz_input,
0463 },
0464 },
0465 { .layout_id = 56,
0466 .codecs[0] = {
0467 .name = "onyx",
0468 .connections = onyx_connections_noheadphones,
0469 },
0470 },
0471 { .layout_id = 57,
0472 .codecs[0] = {
0473 .name = "topaz",
0474 .connections = topaz_input,
0475 },
0476 },
0477 { .layout_id = 62,
0478 .codecs[0] = {
0479 .name = "onyx",
0480 .connections = onyx_connections_noheadphones,
0481 },
0482 .codecs[1] = {
0483 .name = "topaz",
0484 .connections = topaz_output,
0485 },
0486 },
0487 { .layout_id = 66,
0488 .codecs[0] = {
0489 .name = "onyx",
0490 .connections = onyx_connections_noheadphones,
0491 },
0492 },
0493 { .layout_id = 67,
0494 .codecs[0] = {
0495 .name = "topaz",
0496 .connections = topaz_input,
0497 },
0498 },
0499 { .layout_id = 76,
0500 .codecs[0] = {
0501 .name = "tas",
0502 .connections = tas_connections_nomic,
0503 },
0504 .codecs[1] = {
0505 .name = "topaz",
0506 .connections = topaz_inout,
0507 },
0508 },
0509 { .layout_id = 90,
0510 .codecs[0] = {
0511 .name = "tas",
0512 .connections = tas_connections_noline,
0513 },
0514 },
0515 { .layout_id = 94,
0516 .codecs[0] = {
0517 .name = "onyx",
0518
0519 .connections = onyx_connections_noheadphones,
0520 },
0521 },
0522 { .layout_id = 98,
0523 .codecs[0] = {
0524 .name = "toonie",
0525 .connections = toonie_connections,
0526 },
0527 },
0528 { .layout_id = 100,
0529 .codecs[0] = {
0530 .name = "topaz",
0531 .connections = topaz_input,
0532 },
0533 .codecs[1] = {
0534 .name = "onyx",
0535 .connections = onyx_connections_noheadphones,
0536 },
0537 },
0538
0539 { .device_id = 14,
0540 .codecs[0] = {
0541 .name = "tas",
0542 .connections = tas_connections_noline,
0543 },
0544 },
0545
0546 { .device_id = 22,
0547 .codecs[0] = {
0548 .name = "tas",
0549 .connections = tas_connections_all,
0550 },
0551 },
0552
0553 { .device_id = 35,
0554 .codecs[0] = {
0555 .name = "tas",
0556 .connections = tas_connections_all,
0557 },
0558 },
0559 {}
0560 };
0561
0562 static struct layout *find_layout_by_id(unsigned int id)
0563 {
0564 struct layout *l;
0565
0566 l = layouts;
0567 while (l->codecs[0].name) {
0568 if (l->layout_id == id)
0569 return l;
0570 l++;
0571 }
0572 return NULL;
0573 }
0574
0575 static struct layout *find_layout_by_device(unsigned int id)
0576 {
0577 struct layout *l;
0578
0579 l = layouts;
0580 while (l->codecs[0].name) {
0581 if (l->device_id == id)
0582 return l;
0583 l++;
0584 }
0585 return NULL;
0586 }
0587
0588 static void use_layout(struct layout *l)
0589 {
0590 int i;
0591
0592 for (i=0; i<MAX_CODECS_PER_BUS; i++) {
0593 if (l->codecs[i].name) {
0594 request_module("snd-aoa-codec-%s", l->codecs[i].name);
0595 }
0596 }
0597
0598 }
0599
0600 struct layout_dev;
0601
0602 struct layout_dev_ptr {
0603 struct layout_dev *ptr;
0604 };
0605
0606 struct layout_dev {
0607 struct list_head list;
0608 struct soundbus_dev *sdev;
0609 struct device_node *sound;
0610 struct aoa_codec *codecs[MAX_CODECS_PER_BUS];
0611 struct layout *layout;
0612 struct gpio_runtime gpio;
0613
0614
0615 struct snd_kcontrol *headphone_ctrl;
0616 struct snd_kcontrol *lineout_ctrl;
0617 struct snd_kcontrol *speaker_ctrl;
0618 struct snd_kcontrol *master_ctrl;
0619 struct snd_kcontrol *headphone_detected_ctrl;
0620 struct snd_kcontrol *lineout_detected_ctrl;
0621
0622 struct layout_dev_ptr selfptr_headphone;
0623 struct layout_dev_ptr selfptr_lineout;
0624
0625 u32 have_lineout_detect:1,
0626 have_headphone_detect:1,
0627 switch_on_headphone:1,
0628 switch_on_lineout:1;
0629 };
0630
0631 static LIST_HEAD(layouts_list);
0632 static int layouts_list_items;
0633
0634
0635 static struct layout_dev *layout_device;
0636
0637 #define control_info snd_ctl_boolean_mono_info
0638
0639 #define AMP_CONTROL(n, description) \
0640 static int n##_control_get(struct snd_kcontrol *kcontrol, \
0641 struct snd_ctl_elem_value *ucontrol) \
0642 { \
0643 struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
0644 if (gpio->methods && gpio->methods->get_##n) \
0645 ucontrol->value.integer.value[0] = \
0646 gpio->methods->get_##n(gpio); \
0647 return 0; \
0648 } \
0649 static int n##_control_put(struct snd_kcontrol *kcontrol, \
0650 struct snd_ctl_elem_value *ucontrol) \
0651 { \
0652 struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \
0653 if (gpio->methods && gpio->methods->set_##n) \
0654 gpio->methods->set_##n(gpio, \
0655 !!ucontrol->value.integer.value[0]); \
0656 return 1; \
0657 } \
0658 static const struct snd_kcontrol_new n##_ctl = { \
0659 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
0660 .name = description, \
0661 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
0662 .info = control_info, \
0663 .get = n##_control_get, \
0664 .put = n##_control_put, \
0665 }
0666
0667 AMP_CONTROL(headphone, "Headphone Switch");
0668 AMP_CONTROL(speakers, "Speakers Switch");
0669 AMP_CONTROL(lineout, "Line-Out Switch");
0670 AMP_CONTROL(master, "Master Switch");
0671
0672 static int detect_choice_get(struct snd_kcontrol *kcontrol,
0673 struct snd_ctl_elem_value *ucontrol)
0674 {
0675 struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
0676
0677 switch (kcontrol->private_value) {
0678 case 0:
0679 ucontrol->value.integer.value[0] = ldev->switch_on_headphone;
0680 break;
0681 case 1:
0682 ucontrol->value.integer.value[0] = ldev->switch_on_lineout;
0683 break;
0684 default:
0685 return -ENODEV;
0686 }
0687 return 0;
0688 }
0689
0690 static int detect_choice_put(struct snd_kcontrol *kcontrol,
0691 struct snd_ctl_elem_value *ucontrol)
0692 {
0693 struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
0694
0695 switch (kcontrol->private_value) {
0696 case 0:
0697 ldev->switch_on_headphone = !!ucontrol->value.integer.value[0];
0698 break;
0699 case 1:
0700 ldev->switch_on_lineout = !!ucontrol->value.integer.value[0];
0701 break;
0702 default:
0703 return -ENODEV;
0704 }
0705 return 1;
0706 }
0707
0708 static const struct snd_kcontrol_new headphone_detect_choice = {
0709 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0710 .name = "Headphone Detect Autoswitch",
0711 .info = control_info,
0712 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
0713 .get = detect_choice_get,
0714 .put = detect_choice_put,
0715 .private_value = 0,
0716 };
0717
0718 static const struct snd_kcontrol_new lineout_detect_choice = {
0719 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0720 .name = "Line-Out Detect Autoswitch",
0721 .info = control_info,
0722 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
0723 .get = detect_choice_get,
0724 .put = detect_choice_put,
0725 .private_value = 1,
0726 };
0727
0728 static int detected_get(struct snd_kcontrol *kcontrol,
0729 struct snd_ctl_elem_value *ucontrol)
0730 {
0731 struct layout_dev *ldev = snd_kcontrol_chip(kcontrol);
0732 int v;
0733
0734 switch (kcontrol->private_value) {
0735 case 0:
0736 v = ldev->gpio.methods->get_detect(&ldev->gpio,
0737 AOA_NOTIFY_HEADPHONE);
0738 break;
0739 case 1:
0740 v = ldev->gpio.methods->get_detect(&ldev->gpio,
0741 AOA_NOTIFY_LINE_OUT);
0742 break;
0743 default:
0744 return -ENODEV;
0745 }
0746 ucontrol->value.integer.value[0] = v;
0747 return 0;
0748 }
0749
0750 static const struct snd_kcontrol_new headphone_detected = {
0751 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0752 .name = "Headphone Detected",
0753 .info = control_info,
0754 .access = SNDRV_CTL_ELEM_ACCESS_READ,
0755 .get = detected_get,
0756 .private_value = 0,
0757 };
0758
0759 static const struct snd_kcontrol_new lineout_detected = {
0760 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
0761 .name = "Line-Out Detected",
0762 .info = control_info,
0763 .access = SNDRV_CTL_ELEM_ACCESS_READ,
0764 .get = detected_get,
0765 .private_value = 1,
0766 };
0767
0768 static int check_codec(struct aoa_codec *codec,
0769 struct layout_dev *ldev,
0770 struct codec_connect_info *cci)
0771 {
0772 const u32 *ref;
0773 char propname[32];
0774 struct codec_connection *cc;
0775
0776
0777 if (of_node_name_eq(codec->node, "codec")) {
0778 snprintf(propname, sizeof(propname),
0779 "platform-%s-codec-ref", codec->name);
0780 ref = of_get_property(ldev->sound, propname, NULL);
0781 if (!ref) {
0782 printk(KERN_INFO "snd-aoa-fabric-layout: "
0783 "required property %s not present\n", propname);
0784 return -ENODEV;
0785 }
0786 if (*ref != codec->node->phandle) {
0787 printk(KERN_INFO "snd-aoa-fabric-layout: "
0788 "%s doesn't match!\n", propname);
0789 return -ENODEV;
0790 }
0791 } else {
0792 if (layouts_list_items != 1) {
0793 printk(KERN_INFO "snd-aoa-fabric-layout: "
0794 "more than one soundbus, but no references.\n");
0795 return -ENODEV;
0796 }
0797 }
0798 codec->soundbus_dev = ldev->sdev;
0799 codec->gpio = &ldev->gpio;
0800
0801 cc = cci->connections;
0802 if (!cc)
0803 return -EINVAL;
0804
0805 printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n");
0806
0807 codec->connected = 0;
0808 codec->fabric_data = cc;
0809
0810 while (cc->connected) {
0811 codec->connected |= 1<<cc->codec_bit;
0812 cc++;
0813 }
0814
0815 return 0;
0816 }
0817
0818 static int layout_found_codec(struct aoa_codec *codec)
0819 {
0820 struct layout_dev *ldev;
0821 int i;
0822
0823 list_for_each_entry(ldev, &layouts_list, list) {
0824 for (i=0; i<MAX_CODECS_PER_BUS; i++) {
0825 if (!ldev->layout->codecs[i].name)
0826 continue;
0827 if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) {
0828 if (check_codec(codec,
0829 ldev,
0830 &ldev->layout->codecs[i]) == 0)
0831 return 0;
0832 }
0833 }
0834 }
0835 return -ENODEV;
0836 }
0837
0838 static void layout_remove_codec(struct aoa_codec *codec)
0839 {
0840 int i;
0841
0842
0843
0844 codec->soundbus_dev = NULL;
0845 codec->gpio = NULL;
0846 for (i=0; i<MAX_CODECS_PER_BUS; i++) {
0847 }
0848 }
0849
0850 static void layout_notify(void *data)
0851 {
0852 struct layout_dev_ptr *dptr = data;
0853 struct layout_dev *ldev;
0854 int v, update;
0855 struct snd_kcontrol *detected, *c;
0856 struct snd_card *card = aoa_get_card();
0857
0858 ldev = dptr->ptr;
0859 if (data == &ldev->selfptr_headphone) {
0860 v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE);
0861 detected = ldev->headphone_detected_ctrl;
0862 update = ldev->switch_on_headphone;
0863 if (update) {
0864 ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
0865 ldev->gpio.methods->set_headphone(&ldev->gpio, v);
0866 ldev->gpio.methods->set_lineout(&ldev->gpio, 0);
0867 }
0868 } else if (data == &ldev->selfptr_lineout) {
0869 v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT);
0870 detected = ldev->lineout_detected_ctrl;
0871 update = ldev->switch_on_lineout;
0872 if (update) {
0873 ldev->gpio.methods->set_speakers(&ldev->gpio, !v);
0874 ldev->gpio.methods->set_headphone(&ldev->gpio, 0);
0875 ldev->gpio.methods->set_lineout(&ldev->gpio, v);
0876 }
0877 } else
0878 return;
0879
0880 if (detected)
0881 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
0882 if (update) {
0883 c = ldev->headphone_ctrl;
0884 if (c)
0885 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
0886 c = ldev->speaker_ctrl;
0887 if (c)
0888 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
0889 c = ldev->lineout_ctrl;
0890 if (c)
0891 snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
0892 }
0893 }
0894
0895 static void layout_attached_codec(struct aoa_codec *codec)
0896 {
0897 struct codec_connection *cc;
0898 struct snd_kcontrol *ctl;
0899 int headphones, lineout;
0900 struct layout_dev *ldev = layout_device;
0901
0902
0903
0904 cc = codec->fabric_data;
0905
0906 headphones = codec->gpio->methods->get_detect(codec->gpio,
0907 AOA_NOTIFY_HEADPHONE);
0908 lineout = codec->gpio->methods->get_detect(codec->gpio,
0909 AOA_NOTIFY_LINE_OUT);
0910
0911 if (codec->gpio->methods->set_master) {
0912 ctl = snd_ctl_new1(&master_ctl, codec->gpio);
0913 ldev->master_ctrl = ctl;
0914 aoa_snd_ctl_add(ctl);
0915 }
0916 while (cc->connected) {
0917 if (cc->connected & CC_SPEAKERS) {
0918 if (headphones <= 0 && lineout <= 0)
0919 ldev->gpio.methods->set_speakers(codec->gpio, 1);
0920 ctl = snd_ctl_new1(&speakers_ctl, codec->gpio);
0921 ldev->speaker_ctrl = ctl;
0922 aoa_snd_ctl_add(ctl);
0923 }
0924 if (cc->connected & CC_HEADPHONE) {
0925 if (headphones == 1)
0926 ldev->gpio.methods->set_headphone(codec->gpio, 1);
0927 ctl = snd_ctl_new1(&headphone_ctl, codec->gpio);
0928 ldev->headphone_ctrl = ctl;
0929 aoa_snd_ctl_add(ctl);
0930 ldev->have_headphone_detect =
0931 !ldev->gpio.methods
0932 ->set_notify(&ldev->gpio,
0933 AOA_NOTIFY_HEADPHONE,
0934 layout_notify,
0935 &ldev->selfptr_headphone);
0936 if (ldev->have_headphone_detect) {
0937 ctl = snd_ctl_new1(&headphone_detect_choice,
0938 ldev);
0939 aoa_snd_ctl_add(ctl);
0940 ctl = snd_ctl_new1(&headphone_detected,
0941 ldev);
0942 ldev->headphone_detected_ctrl = ctl;
0943 aoa_snd_ctl_add(ctl);
0944 }
0945 }
0946 if (cc->connected & CC_LINEOUT) {
0947 if (lineout == 1)
0948 ldev->gpio.methods->set_lineout(codec->gpio, 1);
0949 ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
0950 if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
0951 strscpy(ctl->id.name,
0952 "Headphone Switch", sizeof(ctl->id.name));
0953 ldev->lineout_ctrl = ctl;
0954 aoa_snd_ctl_add(ctl);
0955 ldev->have_lineout_detect =
0956 !ldev->gpio.methods
0957 ->set_notify(&ldev->gpio,
0958 AOA_NOTIFY_LINE_OUT,
0959 layout_notify,
0960 &ldev->selfptr_lineout);
0961 if (ldev->have_lineout_detect) {
0962 ctl = snd_ctl_new1(&lineout_detect_choice,
0963 ldev);
0964 if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
0965 strscpy(ctl->id.name,
0966 "Headphone Detect Autoswitch",
0967 sizeof(ctl->id.name));
0968 aoa_snd_ctl_add(ctl);
0969 ctl = snd_ctl_new1(&lineout_detected,
0970 ldev);
0971 if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
0972 strscpy(ctl->id.name,
0973 "Headphone Detected",
0974 sizeof(ctl->id.name));
0975 ldev->lineout_detected_ctrl = ctl;
0976 aoa_snd_ctl_add(ctl);
0977 }
0978 }
0979 cc++;
0980 }
0981
0982 if (ldev->have_headphone_detect)
0983 layout_notify(&ldev->selfptr_headphone);
0984 if (ldev->have_lineout_detect)
0985 layout_notify(&ldev->selfptr_lineout);
0986 }
0987
0988 static struct aoa_fabric layout_fabric = {
0989 .name = "SoundByLayout",
0990 .owner = THIS_MODULE,
0991 .found_codec = layout_found_codec,
0992 .remove_codec = layout_remove_codec,
0993 .attached_codec = layout_attached_codec,
0994 };
0995
0996 static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
0997 {
0998 struct device_node *sound = NULL;
0999 const unsigned int *id;
1000 struct layout *layout = NULL;
1001 struct layout_dev *ldev = NULL;
1002 int err;
1003
1004
1005 if (layout_device)
1006 return -ENODEV;
1007
1008
1009 for_each_child_of_node(sdev->ofdev.dev.of_node, sound) {
1010 if (of_node_is_type(sound, "soundchip"))
1011 break;
1012 }
1013 if (!sound)
1014 return -ENODEV;
1015
1016 id = of_get_property(sound, "layout-id", NULL);
1017 if (id) {
1018 layout = find_layout_by_id(*id);
1019 } else {
1020 id = of_get_property(sound, "device-id", NULL);
1021 if (id)
1022 layout = find_layout_by_device(*id);
1023 }
1024
1025 if (!layout) {
1026 printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
1027 goto outnodev;
1028 }
1029
1030 ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
1031 if (!ldev)
1032 goto outnodev;
1033
1034 layout_device = ldev;
1035 ldev->sdev = sdev;
1036 ldev->sound = sound;
1037 ldev->layout = layout;
1038 ldev->gpio.node = sound->parent;
1039 switch (layout->layout_id) {
1040 case 0:
1041 case 41:
1042 case 51:
1043 case 58:
1044 ldev->gpio.methods = ftr_gpio_methods;
1045 printk(KERN_DEBUG
1046 "snd-aoa-fabric-layout: Using direct GPIOs\n");
1047 break;
1048 default:
1049 ldev->gpio.methods = pmf_gpio_methods;
1050 printk(KERN_DEBUG
1051 "snd-aoa-fabric-layout: Using PMF GPIOs\n");
1052 }
1053 ldev->selfptr_headphone.ptr = ldev;
1054 ldev->selfptr_lineout.ptr = ldev;
1055 dev_set_drvdata(&sdev->ofdev.dev, ldev);
1056 list_add(&ldev->list, &layouts_list);
1057 layouts_list_items++;
1058
1059
1060
1061
1062 sdev->pcmid = ldev->layout->pcmid;
1063 if (ldev->layout->busname) {
1064 sdev->pcmname = ldev->layout->busname;
1065 } else {
1066 sdev->pcmname = "Master";
1067 }
1068
1069 ldev->gpio.methods->init(&ldev->gpio);
1070
1071 err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);
1072 if (err && err != -EALREADY) {
1073 printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
1074 " another fabric is active!\n");
1075 goto outlistdel;
1076 }
1077
1078 use_layout(layout);
1079 ldev->switch_on_headphone = 1;
1080 ldev->switch_on_lineout = 1;
1081 return 0;
1082 outlistdel:
1083
1084 ldev->gpio.methods->exit(&ldev->gpio);
1085
1086 sdev->pcmname = NULL;
1087 sdev->pcmid = -1;
1088 list_del(&ldev->list);
1089 layouts_list_items--;
1090 kfree(ldev);
1091 outnodev:
1092 of_node_put(sound);
1093 layout_device = NULL;
1094 return -ENODEV;
1095 }
1096
1097 static int aoa_fabric_layout_remove(struct soundbus_dev *sdev)
1098 {
1099 struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
1100 int i;
1101
1102 for (i=0; i<MAX_CODECS_PER_BUS; i++) {
1103 if (ldev->codecs[i]) {
1104 aoa_fabric_unlink_codec(ldev->codecs[i]);
1105 }
1106 ldev->codecs[i] = NULL;
1107 }
1108 list_del(&ldev->list);
1109 layouts_list_items--;
1110 of_node_put(ldev->sound);
1111
1112 ldev->gpio.methods->set_notify(&ldev->gpio,
1113 AOA_NOTIFY_HEADPHONE,
1114 NULL,
1115 NULL);
1116 ldev->gpio.methods->set_notify(&ldev->gpio,
1117 AOA_NOTIFY_LINE_OUT,
1118 NULL,
1119 NULL);
1120
1121 ldev->gpio.methods->exit(&ldev->gpio);
1122 layout_device = NULL;
1123 kfree(ldev);
1124 sdev->pcmid = -1;
1125 sdev->pcmname = NULL;
1126 return 0;
1127 }
1128
1129 #ifdef CONFIG_PM_SLEEP
1130 static int aoa_fabric_layout_suspend(struct device *dev)
1131 {
1132 struct layout_dev *ldev = dev_get_drvdata(dev);
1133
1134 if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
1135 ldev->gpio.methods->all_amps_off(&ldev->gpio);
1136
1137 return 0;
1138 }
1139
1140 static int aoa_fabric_layout_resume(struct device *dev)
1141 {
1142 struct layout_dev *ldev = dev_get_drvdata(dev);
1143
1144 if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
1145 ldev->gpio.methods->all_amps_restore(&ldev->gpio);
1146
1147 return 0;
1148 }
1149
1150 static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
1151 aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
1152
1153 #endif
1154
1155 static struct soundbus_driver aoa_soundbus_driver = {
1156 .name = "snd_aoa_soundbus_drv",
1157 .owner = THIS_MODULE,
1158 .probe = aoa_fabric_layout_probe,
1159 .remove = aoa_fabric_layout_remove,
1160 .driver = {
1161 .owner = THIS_MODULE,
1162 #ifdef CONFIG_PM_SLEEP
1163 .pm = &aoa_fabric_layout_pm_ops,
1164 #endif
1165 }
1166 };
1167
1168 static int __init aoa_fabric_layout_init(void)
1169 {
1170 return soundbus_register_driver(&aoa_soundbus_driver);
1171 }
1172
1173 static void __exit aoa_fabric_layout_exit(void)
1174 {
1175 soundbus_unregister_driver(&aoa_soundbus_driver);
1176 aoa_fabric_unregister(&layout_fabric);
1177 }
1178
1179 module_init(aoa_fabric_layout_init);
1180 module_exit(aoa_fabric_layout_exit);