1176 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Apple Onboard Audio driver -- layout/machine id fabric4 *5 * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>6 *7 * This fabric module looks for sound codecs based on the8 * 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 18MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");19MODULE_LICENSE("GPL");20MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");21 22#define MAX_CODECS_PER_BUS 223 24/* These are the connections the layout fabric25 * knows about. It doesn't really care about the26 * input ones, but I thought I'd separate them27 * to give them proper names. The thing is that28 * Apple usually will distinguish the active output29 * by GPIOs, while the active input is set directly30 * on the codec. Hence we here tell the codec what31 * 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 LINEOUT42 * should be renamed to HEADPHONE.43 * be careful with input detection! */44#define CC_LINEOUT_LABELLED_HEADPHONE (1<<7)45 46struct 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 the51 * fabric has to know the codec anyway and all codecs might have52 * different connectors */53 int codec_bit;54};55 56struct codec_connect_info {57 char *name;58 struct codec_connection *connections;59};60 61#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0)62 63struct 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 filled70 * too much.71 * We only assign these two if we expect to find more72 * than one soundbus, i.e. on those machines with73 * multiple layout-ids */74 char *busname;75 int pcmid;76};77 78MODULE_ALIAS("sound-layout-36");79MODULE_ALIAS("sound-layout-41");80MODULE_ALIAS("sound-layout-45");81MODULE_ALIAS("sound-layout-47");82MODULE_ALIAS("sound-layout-48");83MODULE_ALIAS("sound-layout-49");84MODULE_ALIAS("sound-layout-50");85MODULE_ALIAS("sound-layout-51");86MODULE_ALIAS("sound-layout-56");87MODULE_ALIAS("sound-layout-57");88MODULE_ALIAS("sound-layout-58");89MODULE_ALIAS("sound-layout-60");90MODULE_ALIAS("sound-layout-61");91MODULE_ALIAS("sound-layout-62");92MODULE_ALIAS("sound-layout-64");93MODULE_ALIAS("sound-layout-65");94MODULE_ALIAS("sound-layout-66");95MODULE_ALIAS("sound-layout-67");96MODULE_ALIAS("sound-layout-68");97MODULE_ALIAS("sound-layout-69");98MODULE_ALIAS("sound-layout-70");99MODULE_ALIAS("sound-layout-72");100MODULE_ALIAS("sound-layout-76");101MODULE_ALIAS("sound-layout-80");102MODULE_ALIAS("sound-layout-82");103MODULE_ALIAS("sound-layout-84");104MODULE_ALIAS("sound-layout-86");105MODULE_ALIAS("sound-layout-90");106MODULE_ALIAS("sound-layout-92");107MODULE_ALIAS("sound-layout-94");108MODULE_ALIAS("sound-layout-96");109MODULE_ALIAS("sound-layout-98");110MODULE_ALIAS("sound-layout-100");111 112MODULE_ALIAS("aoa-device-id-14");113MODULE_ALIAS("aoa-device-id-22");114MODULE_ALIAS("aoa-device-id-31");115MODULE_ALIAS("aoa-device-id-35");116MODULE_ALIAS("aoa-device-id-44");117 118/* onyx with all but microphone connected */119static 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 */136static 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 machines147 * 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 */160static 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 */177static 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 */194static 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 */207static 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 */220static 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 236static 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 244static struct codec_connection topaz_input[] = {245 {246 .connected = CC_DIGITALIN,247 .codec_bit = 0,248 },249 {} /* terminate array by .connected == 0 */250};251 252static struct codec_connection topaz_output[] = {253 {254 .connected = CC_DIGITALOUT,255 .codec_bit = 1,256 },257 {} /* terminate array by .connected == 0 */258};259 260static 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 272static 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 563static 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 576static 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 589static 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 601struct layout_dev;602 603struct layout_dev_ptr {604 struct layout_dev *ptr;605};606 607struct 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 632static LIST_HEAD(layouts_list);633static 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... */636static struct layout_dev *layout_device;637 638#define control_info snd_ctl_boolean_mono_info639 640#define AMP_CONTROL(n, description) \641static 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} \650static 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} \659static 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 668AMP_CONTROL(headphone, "Headphone Switch");669AMP_CONTROL(speakers, "Speakers Switch");670AMP_CONTROL(lineout, "Line-Out Switch");671AMP_CONTROL(master, "Master Switch");672 673static 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 691static 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 709static 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 719static 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 729static 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 751static 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 760static 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 769static 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 819static 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 839static void layout_remove_codec(struct aoa_codec *codec)840{841 int i;842 /* here remove the codec from the layout dev's843 * 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 851static 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 } else879 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 896static 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.methods933 ->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.methods958 ->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 989static 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 997static 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_DEBUG1047 "snd-aoa-fabric-layout: Using direct GPIOs\n");1048 break;1049 default:1050 ldev->gpio.methods = pmf_gpio_methods;1051 printk(KERN_DEBUG1052 "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, so1061 * callbacks that are done during registration1062 * 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 1098static 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 1129static 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 1139static 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 1149static DEFINE_SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,1150 aoa_fabric_layout_suspend, aoa_fabric_layout_resume);1151 1152static 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 1163static int __init aoa_fabric_layout_init(void)1164{1165 return soundbus_register_driver(&aoa_soundbus_driver);1166}1167 1168static void __exit aoa_fabric_layout_exit(void)1169{1170 soundbus_unregister_driver(&aoa_soundbus_driver);1171 aoa_fabric_unregister(&layout_fabric);1172}1173 1174module_init(aoa_fabric_layout_init);1175module_exit(aoa_fabric_layout_exit);1176