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 <johannes@sipsolutions.net> 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 <johannes@sipsolutions.net>"); 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.