18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Apple Onboard Audio driver -- layout/machine id fabric 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This fabric module looks for sound codecs based on the 88c2ecf20Sopenharmony_ci * layout-id or device-id property in the device tree. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <asm/prom.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include "../aoa.h" 158c2ecf20Sopenharmony_ci#include "../soundbus/soundbus.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); 188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 198c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Layout-ID fabric for snd-aoa"); 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define MAX_CODECS_PER_BUS 2 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* These are the connections the layout fabric 248c2ecf20Sopenharmony_ci * knows about. It doesn't really care about the 258c2ecf20Sopenharmony_ci * input ones, but I thought I'd separate them 268c2ecf20Sopenharmony_ci * to give them proper names. The thing is that 278c2ecf20Sopenharmony_ci * Apple usually will distinguish the active output 288c2ecf20Sopenharmony_ci * by GPIOs, while the active input is set directly 298c2ecf20Sopenharmony_ci * on the codec. Hence we here tell the codec what 308c2ecf20Sopenharmony_ci * we think is connected. This information is hard- 318c2ecf20Sopenharmony_ci * coded below ... */ 328c2ecf20Sopenharmony_ci#define CC_SPEAKERS (1<<0) 338c2ecf20Sopenharmony_ci#define CC_HEADPHONE (1<<1) 348c2ecf20Sopenharmony_ci#define CC_LINEOUT (1<<2) 358c2ecf20Sopenharmony_ci#define CC_DIGITALOUT (1<<3) 368c2ecf20Sopenharmony_ci#define CC_LINEIN (1<<4) 378c2ecf20Sopenharmony_ci#define CC_MICROPHONE (1<<5) 388c2ecf20Sopenharmony_ci#define CC_DIGITALIN (1<<6) 398c2ecf20Sopenharmony_ci/* pretty bogus but users complain... 408c2ecf20Sopenharmony_ci * This is a flag saying that the LINEOUT 418c2ecf20Sopenharmony_ci * should be renamed to HEADPHONE. 428c2ecf20Sopenharmony_ci * be careful with input detection! */ 438c2ecf20Sopenharmony_ci#define CC_LINEOUT_LABELLED_HEADPHONE (1<<7) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct codec_connection { 468c2ecf20Sopenharmony_ci /* CC_ flags from above */ 478c2ecf20Sopenharmony_ci int connected; 488c2ecf20Sopenharmony_ci /* codec dependent bit to be set in the aoa_codec.connected field. 498c2ecf20Sopenharmony_ci * This intentionally doesn't have any generic flags because the 508c2ecf20Sopenharmony_ci * fabric has to know the codec anyway and all codecs might have 518c2ecf20Sopenharmony_ci * different connectors */ 528c2ecf20Sopenharmony_ci int codec_bit; 538c2ecf20Sopenharmony_ci}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct codec_connect_info { 568c2ecf20Sopenharmony_ci char *name; 578c2ecf20Sopenharmony_ci struct codec_connection *connections; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF (1<<0) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistruct layout { 638c2ecf20Sopenharmony_ci unsigned int layout_id, device_id; 648c2ecf20Sopenharmony_ci struct codec_connect_info codecs[MAX_CODECS_PER_BUS]; 658c2ecf20Sopenharmony_ci int flags; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci /* if busname is not assigned, we use 'Master' below, 688c2ecf20Sopenharmony_ci * so that our layout table doesn't need to be filled 698c2ecf20Sopenharmony_ci * too much. 708c2ecf20Sopenharmony_ci * We only assign these two if we expect to find more 718c2ecf20Sopenharmony_ci * than one soundbus, i.e. on those machines with 728c2ecf20Sopenharmony_ci * multiple layout-ids */ 738c2ecf20Sopenharmony_ci char *busname; 748c2ecf20Sopenharmony_ci int pcmid; 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-36"); 788c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-41"); 798c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-45"); 808c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-47"); 818c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-48"); 828c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-49"); 838c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-50"); 848c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-51"); 858c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-56"); 868c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-57"); 878c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-58"); 888c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-60"); 898c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-61"); 908c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-62"); 918c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-64"); 928c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-65"); 938c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-66"); 948c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-67"); 958c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-68"); 968c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-69"); 978c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-70"); 988c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-72"); 998c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-76"); 1008c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-80"); 1018c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-82"); 1028c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-84"); 1038c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-86"); 1048c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-90"); 1058c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-92"); 1068c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-94"); 1078c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-96"); 1088c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-98"); 1098c2ecf20Sopenharmony_ciMODULE_ALIAS("sound-layout-100"); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ciMODULE_ALIAS("aoa-device-id-14"); 1128c2ecf20Sopenharmony_ciMODULE_ALIAS("aoa-device-id-22"); 1138c2ecf20Sopenharmony_ciMODULE_ALIAS("aoa-device-id-31"); 1148c2ecf20Sopenharmony_ciMODULE_ALIAS("aoa-device-id-35"); 1158c2ecf20Sopenharmony_ciMODULE_ALIAS("aoa-device-id-44"); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci/* onyx with all but microphone connected */ 1188c2ecf20Sopenharmony_cistatic struct codec_connection onyx_connections_nomic[] = { 1198c2ecf20Sopenharmony_ci { 1208c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, 1218c2ecf20Sopenharmony_ci .codec_bit = 0, 1228c2ecf20Sopenharmony_ci }, 1238c2ecf20Sopenharmony_ci { 1248c2ecf20Sopenharmony_ci .connected = CC_DIGITALOUT, 1258c2ecf20Sopenharmony_ci .codec_bit = 1, 1268c2ecf20Sopenharmony_ci }, 1278c2ecf20Sopenharmony_ci { 1288c2ecf20Sopenharmony_ci .connected = CC_LINEIN, 1298c2ecf20Sopenharmony_ci .codec_bit = 2, 1308c2ecf20Sopenharmony_ci }, 1318c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/* onyx on machines without headphone */ 1358c2ecf20Sopenharmony_cistatic struct codec_connection onyx_connections_noheadphones[] = { 1368c2ecf20Sopenharmony_ci { 1378c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_LINEOUT | 1388c2ecf20Sopenharmony_ci CC_LINEOUT_LABELLED_HEADPHONE, 1398c2ecf20Sopenharmony_ci .codec_bit = 0, 1408c2ecf20Sopenharmony_ci }, 1418c2ecf20Sopenharmony_ci { 1428c2ecf20Sopenharmony_ci .connected = CC_DIGITALOUT, 1438c2ecf20Sopenharmony_ci .codec_bit = 1, 1448c2ecf20Sopenharmony_ci }, 1458c2ecf20Sopenharmony_ci /* FIXME: are these correct? probably not for all the machines 1468c2ecf20Sopenharmony_ci * below ... If not this will need separating. */ 1478c2ecf20Sopenharmony_ci { 1488c2ecf20Sopenharmony_ci .connected = CC_LINEIN, 1498c2ecf20Sopenharmony_ci .codec_bit = 2, 1508c2ecf20Sopenharmony_ci }, 1518c2ecf20Sopenharmony_ci { 1528c2ecf20Sopenharmony_ci .connected = CC_MICROPHONE, 1538c2ecf20Sopenharmony_ci .codec_bit = 3, 1548c2ecf20Sopenharmony_ci }, 1558c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 1568c2ecf20Sopenharmony_ci}; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci/* onyx on machines with real line-out */ 1598c2ecf20Sopenharmony_cistatic struct codec_connection onyx_connections_reallineout[] = { 1608c2ecf20Sopenharmony_ci { 1618c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE, 1628c2ecf20Sopenharmony_ci .codec_bit = 0, 1638c2ecf20Sopenharmony_ci }, 1648c2ecf20Sopenharmony_ci { 1658c2ecf20Sopenharmony_ci .connected = CC_DIGITALOUT, 1668c2ecf20Sopenharmony_ci .codec_bit = 1, 1678c2ecf20Sopenharmony_ci }, 1688c2ecf20Sopenharmony_ci { 1698c2ecf20Sopenharmony_ci .connected = CC_LINEIN, 1708c2ecf20Sopenharmony_ci .codec_bit = 2, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/* tas on machines without line out */ 1768c2ecf20Sopenharmony_cistatic struct codec_connection tas_connections_nolineout[] = { 1778c2ecf20Sopenharmony_ci { 1788c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_HEADPHONE, 1798c2ecf20Sopenharmony_ci .codec_bit = 0, 1808c2ecf20Sopenharmony_ci }, 1818c2ecf20Sopenharmony_ci { 1828c2ecf20Sopenharmony_ci .connected = CC_LINEIN, 1838c2ecf20Sopenharmony_ci .codec_bit = 2, 1848c2ecf20Sopenharmony_ci }, 1858c2ecf20Sopenharmony_ci { 1868c2ecf20Sopenharmony_ci .connected = CC_MICROPHONE, 1878c2ecf20Sopenharmony_ci .codec_bit = 3, 1888c2ecf20Sopenharmony_ci }, 1898c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* tas on machines with neither line out nor line in */ 1938c2ecf20Sopenharmony_cistatic struct codec_connection tas_connections_noline[] = { 1948c2ecf20Sopenharmony_ci { 1958c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_HEADPHONE, 1968c2ecf20Sopenharmony_ci .codec_bit = 0, 1978c2ecf20Sopenharmony_ci }, 1988c2ecf20Sopenharmony_ci { 1998c2ecf20Sopenharmony_ci .connected = CC_MICROPHONE, 2008c2ecf20Sopenharmony_ci .codec_bit = 3, 2018c2ecf20Sopenharmony_ci }, 2028c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2038c2ecf20Sopenharmony_ci}; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* tas on machines without microphone */ 2068c2ecf20Sopenharmony_cistatic struct codec_connection tas_connections_nomic[] = { 2078c2ecf20Sopenharmony_ci { 2088c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, 2098c2ecf20Sopenharmony_ci .codec_bit = 0, 2108c2ecf20Sopenharmony_ci }, 2118c2ecf20Sopenharmony_ci { 2128c2ecf20Sopenharmony_ci .connected = CC_LINEIN, 2138c2ecf20Sopenharmony_ci .codec_bit = 2, 2148c2ecf20Sopenharmony_ci }, 2158c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/* tas on machines with everything connected */ 2198c2ecf20Sopenharmony_cistatic struct codec_connection tas_connections_all[] = { 2208c2ecf20Sopenharmony_ci { 2218c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT, 2228c2ecf20Sopenharmony_ci .codec_bit = 0, 2238c2ecf20Sopenharmony_ci }, 2248c2ecf20Sopenharmony_ci { 2258c2ecf20Sopenharmony_ci .connected = CC_LINEIN, 2268c2ecf20Sopenharmony_ci .codec_bit = 2, 2278c2ecf20Sopenharmony_ci }, 2288c2ecf20Sopenharmony_ci { 2298c2ecf20Sopenharmony_ci .connected = CC_MICROPHONE, 2308c2ecf20Sopenharmony_ci .codec_bit = 3, 2318c2ecf20Sopenharmony_ci }, 2328c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2338c2ecf20Sopenharmony_ci}; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic struct codec_connection toonie_connections[] = { 2368c2ecf20Sopenharmony_ci { 2378c2ecf20Sopenharmony_ci .connected = CC_SPEAKERS | CC_HEADPHONE, 2388c2ecf20Sopenharmony_ci .codec_bit = 0, 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2418c2ecf20Sopenharmony_ci}; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic struct codec_connection topaz_input[] = { 2448c2ecf20Sopenharmony_ci { 2458c2ecf20Sopenharmony_ci .connected = CC_DIGITALIN, 2468c2ecf20Sopenharmony_ci .codec_bit = 0, 2478c2ecf20Sopenharmony_ci }, 2488c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic struct codec_connection topaz_output[] = { 2528c2ecf20Sopenharmony_ci { 2538c2ecf20Sopenharmony_ci .connected = CC_DIGITALOUT, 2548c2ecf20Sopenharmony_ci .codec_bit = 1, 2558c2ecf20Sopenharmony_ci }, 2568c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2578c2ecf20Sopenharmony_ci}; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic struct codec_connection topaz_inout[] = { 2608c2ecf20Sopenharmony_ci { 2618c2ecf20Sopenharmony_ci .connected = CC_DIGITALIN, 2628c2ecf20Sopenharmony_ci .codec_bit = 0, 2638c2ecf20Sopenharmony_ci }, 2648c2ecf20Sopenharmony_ci { 2658c2ecf20Sopenharmony_ci .connected = CC_DIGITALOUT, 2668c2ecf20Sopenharmony_ci .codec_bit = 1, 2678c2ecf20Sopenharmony_ci }, 2688c2ecf20Sopenharmony_ci {} /* terminate array by .connected == 0 */ 2698c2ecf20Sopenharmony_ci}; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic struct layout layouts[] = { 2728c2ecf20Sopenharmony_ci /* last PowerBooks (15" Oct 2005) */ 2738c2ecf20Sopenharmony_ci { .layout_id = 82, 2748c2ecf20Sopenharmony_ci .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, 2758c2ecf20Sopenharmony_ci .codecs[0] = { 2768c2ecf20Sopenharmony_ci .name = "onyx", 2778c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 2788c2ecf20Sopenharmony_ci }, 2798c2ecf20Sopenharmony_ci .codecs[1] = { 2808c2ecf20Sopenharmony_ci .name = "topaz", 2818c2ecf20Sopenharmony_ci .connections = topaz_input, 2828c2ecf20Sopenharmony_ci }, 2838c2ecf20Sopenharmony_ci }, 2848c2ecf20Sopenharmony_ci /* PowerMac9,1 */ 2858c2ecf20Sopenharmony_ci { .layout_id = 60, 2868c2ecf20Sopenharmony_ci .codecs[0] = { 2878c2ecf20Sopenharmony_ci .name = "onyx", 2888c2ecf20Sopenharmony_ci .connections = onyx_connections_reallineout, 2898c2ecf20Sopenharmony_ci }, 2908c2ecf20Sopenharmony_ci }, 2918c2ecf20Sopenharmony_ci /* PowerMac9,1 */ 2928c2ecf20Sopenharmony_ci { .layout_id = 61, 2938c2ecf20Sopenharmony_ci .codecs[0] = { 2948c2ecf20Sopenharmony_ci .name = "topaz", 2958c2ecf20Sopenharmony_ci .connections = topaz_input, 2968c2ecf20Sopenharmony_ci }, 2978c2ecf20Sopenharmony_ci }, 2988c2ecf20Sopenharmony_ci /* PowerBook5,7 */ 2998c2ecf20Sopenharmony_ci { .layout_id = 64, 3008c2ecf20Sopenharmony_ci .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, 3018c2ecf20Sopenharmony_ci .codecs[0] = { 3028c2ecf20Sopenharmony_ci .name = "onyx", 3038c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 3048c2ecf20Sopenharmony_ci }, 3058c2ecf20Sopenharmony_ci }, 3068c2ecf20Sopenharmony_ci /* PowerBook5,7 */ 3078c2ecf20Sopenharmony_ci { .layout_id = 65, 3088c2ecf20Sopenharmony_ci .codecs[0] = { 3098c2ecf20Sopenharmony_ci .name = "topaz", 3108c2ecf20Sopenharmony_ci .connections = topaz_input, 3118c2ecf20Sopenharmony_ci }, 3128c2ecf20Sopenharmony_ci }, 3138c2ecf20Sopenharmony_ci /* PowerBook5,9 [17" Oct 2005] */ 3148c2ecf20Sopenharmony_ci { .layout_id = 84, 3158c2ecf20Sopenharmony_ci .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF, 3168c2ecf20Sopenharmony_ci .codecs[0] = { 3178c2ecf20Sopenharmony_ci .name = "onyx", 3188c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 3198c2ecf20Sopenharmony_ci }, 3208c2ecf20Sopenharmony_ci .codecs[1] = { 3218c2ecf20Sopenharmony_ci .name = "topaz", 3228c2ecf20Sopenharmony_ci .connections = topaz_input, 3238c2ecf20Sopenharmony_ci }, 3248c2ecf20Sopenharmony_ci }, 3258c2ecf20Sopenharmony_ci /* PowerMac8,1 */ 3268c2ecf20Sopenharmony_ci { .layout_id = 45, 3278c2ecf20Sopenharmony_ci .codecs[0] = { 3288c2ecf20Sopenharmony_ci .name = "onyx", 3298c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 3308c2ecf20Sopenharmony_ci }, 3318c2ecf20Sopenharmony_ci .codecs[1] = { 3328c2ecf20Sopenharmony_ci .name = "topaz", 3338c2ecf20Sopenharmony_ci .connections = topaz_input, 3348c2ecf20Sopenharmony_ci }, 3358c2ecf20Sopenharmony_ci }, 3368c2ecf20Sopenharmony_ci /* Quad PowerMac (analog in, analog/digital out) */ 3378c2ecf20Sopenharmony_ci { .layout_id = 68, 3388c2ecf20Sopenharmony_ci .codecs[0] = { 3398c2ecf20Sopenharmony_ci .name = "onyx", 3408c2ecf20Sopenharmony_ci .connections = onyx_connections_nomic, 3418c2ecf20Sopenharmony_ci }, 3428c2ecf20Sopenharmony_ci }, 3438c2ecf20Sopenharmony_ci /* Quad PowerMac (digital in) */ 3448c2ecf20Sopenharmony_ci { .layout_id = 69, 3458c2ecf20Sopenharmony_ci .codecs[0] = { 3468c2ecf20Sopenharmony_ci .name = "topaz", 3478c2ecf20Sopenharmony_ci .connections = topaz_input, 3488c2ecf20Sopenharmony_ci }, 3498c2ecf20Sopenharmony_ci .busname = "digital in", .pcmid = 1 }, 3508c2ecf20Sopenharmony_ci /* Early 2005 PowerBook (PowerBook 5,6) */ 3518c2ecf20Sopenharmony_ci { .layout_id = 70, 3528c2ecf20Sopenharmony_ci .codecs[0] = { 3538c2ecf20Sopenharmony_ci .name = "tas", 3548c2ecf20Sopenharmony_ci .connections = tas_connections_nolineout, 3558c2ecf20Sopenharmony_ci }, 3568c2ecf20Sopenharmony_ci }, 3578c2ecf20Sopenharmony_ci /* PowerBook 5,4 */ 3588c2ecf20Sopenharmony_ci { .layout_id = 51, 3598c2ecf20Sopenharmony_ci .codecs[0] = { 3608c2ecf20Sopenharmony_ci .name = "tas", 3618c2ecf20Sopenharmony_ci .connections = tas_connections_nolineout, 3628c2ecf20Sopenharmony_ci }, 3638c2ecf20Sopenharmony_ci }, 3648c2ecf20Sopenharmony_ci /* PowerBook6,1 */ 3658c2ecf20Sopenharmony_ci { .device_id = 31, 3668c2ecf20Sopenharmony_ci .codecs[0] = { 3678c2ecf20Sopenharmony_ci .name = "tas", 3688c2ecf20Sopenharmony_ci .connections = tas_connections_nolineout, 3698c2ecf20Sopenharmony_ci }, 3708c2ecf20Sopenharmony_ci }, 3718c2ecf20Sopenharmony_ci /* PowerBook6,5 */ 3728c2ecf20Sopenharmony_ci { .device_id = 44, 3738c2ecf20Sopenharmony_ci .codecs[0] = { 3748c2ecf20Sopenharmony_ci .name = "tas", 3758c2ecf20Sopenharmony_ci .connections = tas_connections_all, 3768c2ecf20Sopenharmony_ci }, 3778c2ecf20Sopenharmony_ci }, 3788c2ecf20Sopenharmony_ci /* PowerBook6,7 */ 3798c2ecf20Sopenharmony_ci { .layout_id = 80, 3808c2ecf20Sopenharmony_ci .codecs[0] = { 3818c2ecf20Sopenharmony_ci .name = "tas", 3828c2ecf20Sopenharmony_ci .connections = tas_connections_noline, 3838c2ecf20Sopenharmony_ci }, 3848c2ecf20Sopenharmony_ci }, 3858c2ecf20Sopenharmony_ci /* PowerBook6,8 */ 3868c2ecf20Sopenharmony_ci { .layout_id = 72, 3878c2ecf20Sopenharmony_ci .codecs[0] = { 3888c2ecf20Sopenharmony_ci .name = "tas", 3898c2ecf20Sopenharmony_ci .connections = tas_connections_nolineout, 3908c2ecf20Sopenharmony_ci }, 3918c2ecf20Sopenharmony_ci }, 3928c2ecf20Sopenharmony_ci /* PowerMac8,2 */ 3938c2ecf20Sopenharmony_ci { .layout_id = 86, 3948c2ecf20Sopenharmony_ci .codecs[0] = { 3958c2ecf20Sopenharmony_ci .name = "onyx", 3968c2ecf20Sopenharmony_ci .connections = onyx_connections_nomic, 3978c2ecf20Sopenharmony_ci }, 3988c2ecf20Sopenharmony_ci .codecs[1] = { 3998c2ecf20Sopenharmony_ci .name = "topaz", 4008c2ecf20Sopenharmony_ci .connections = topaz_input, 4018c2ecf20Sopenharmony_ci }, 4028c2ecf20Sopenharmony_ci }, 4038c2ecf20Sopenharmony_ci /* PowerBook6,7 */ 4048c2ecf20Sopenharmony_ci { .layout_id = 92, 4058c2ecf20Sopenharmony_ci .codecs[0] = { 4068c2ecf20Sopenharmony_ci .name = "tas", 4078c2ecf20Sopenharmony_ci .connections = tas_connections_nolineout, 4088c2ecf20Sopenharmony_ci }, 4098c2ecf20Sopenharmony_ci }, 4108c2ecf20Sopenharmony_ci /* PowerMac10,1 (Mac Mini) */ 4118c2ecf20Sopenharmony_ci { .layout_id = 58, 4128c2ecf20Sopenharmony_ci .codecs[0] = { 4138c2ecf20Sopenharmony_ci .name = "toonie", 4148c2ecf20Sopenharmony_ci .connections = toonie_connections, 4158c2ecf20Sopenharmony_ci }, 4168c2ecf20Sopenharmony_ci }, 4178c2ecf20Sopenharmony_ci { 4188c2ecf20Sopenharmony_ci .layout_id = 96, 4198c2ecf20Sopenharmony_ci .codecs[0] = { 4208c2ecf20Sopenharmony_ci .name = "onyx", 4218c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 4228c2ecf20Sopenharmony_ci }, 4238c2ecf20Sopenharmony_ci }, 4248c2ecf20Sopenharmony_ci /* unknown, untested, but this comes from Apple */ 4258c2ecf20Sopenharmony_ci { .layout_id = 41, 4268c2ecf20Sopenharmony_ci .codecs[0] = { 4278c2ecf20Sopenharmony_ci .name = "tas", 4288c2ecf20Sopenharmony_ci .connections = tas_connections_all, 4298c2ecf20Sopenharmony_ci }, 4308c2ecf20Sopenharmony_ci }, 4318c2ecf20Sopenharmony_ci { .layout_id = 36, 4328c2ecf20Sopenharmony_ci .codecs[0] = { 4338c2ecf20Sopenharmony_ci .name = "tas", 4348c2ecf20Sopenharmony_ci .connections = tas_connections_nomic, 4358c2ecf20Sopenharmony_ci }, 4368c2ecf20Sopenharmony_ci .codecs[1] = { 4378c2ecf20Sopenharmony_ci .name = "topaz", 4388c2ecf20Sopenharmony_ci .connections = topaz_inout, 4398c2ecf20Sopenharmony_ci }, 4408c2ecf20Sopenharmony_ci }, 4418c2ecf20Sopenharmony_ci { .layout_id = 47, 4428c2ecf20Sopenharmony_ci .codecs[0] = { 4438c2ecf20Sopenharmony_ci .name = "onyx", 4448c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 4458c2ecf20Sopenharmony_ci }, 4468c2ecf20Sopenharmony_ci }, 4478c2ecf20Sopenharmony_ci { .layout_id = 48, 4488c2ecf20Sopenharmony_ci .codecs[0] = { 4498c2ecf20Sopenharmony_ci .name = "topaz", 4508c2ecf20Sopenharmony_ci .connections = topaz_input, 4518c2ecf20Sopenharmony_ci }, 4528c2ecf20Sopenharmony_ci }, 4538c2ecf20Sopenharmony_ci { .layout_id = 49, 4548c2ecf20Sopenharmony_ci .codecs[0] = { 4558c2ecf20Sopenharmony_ci .name = "onyx", 4568c2ecf20Sopenharmony_ci .connections = onyx_connections_nomic, 4578c2ecf20Sopenharmony_ci }, 4588c2ecf20Sopenharmony_ci }, 4598c2ecf20Sopenharmony_ci { .layout_id = 50, 4608c2ecf20Sopenharmony_ci .codecs[0] = { 4618c2ecf20Sopenharmony_ci .name = "topaz", 4628c2ecf20Sopenharmony_ci .connections = topaz_input, 4638c2ecf20Sopenharmony_ci }, 4648c2ecf20Sopenharmony_ci }, 4658c2ecf20Sopenharmony_ci { .layout_id = 56, 4668c2ecf20Sopenharmony_ci .codecs[0] = { 4678c2ecf20Sopenharmony_ci .name = "onyx", 4688c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 4698c2ecf20Sopenharmony_ci }, 4708c2ecf20Sopenharmony_ci }, 4718c2ecf20Sopenharmony_ci { .layout_id = 57, 4728c2ecf20Sopenharmony_ci .codecs[0] = { 4738c2ecf20Sopenharmony_ci .name = "topaz", 4748c2ecf20Sopenharmony_ci .connections = topaz_input, 4758c2ecf20Sopenharmony_ci }, 4768c2ecf20Sopenharmony_ci }, 4778c2ecf20Sopenharmony_ci { .layout_id = 62, 4788c2ecf20Sopenharmony_ci .codecs[0] = { 4798c2ecf20Sopenharmony_ci .name = "onyx", 4808c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 4818c2ecf20Sopenharmony_ci }, 4828c2ecf20Sopenharmony_ci .codecs[1] = { 4838c2ecf20Sopenharmony_ci .name = "topaz", 4848c2ecf20Sopenharmony_ci .connections = topaz_output, 4858c2ecf20Sopenharmony_ci }, 4868c2ecf20Sopenharmony_ci }, 4878c2ecf20Sopenharmony_ci { .layout_id = 66, 4888c2ecf20Sopenharmony_ci .codecs[0] = { 4898c2ecf20Sopenharmony_ci .name = "onyx", 4908c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 4918c2ecf20Sopenharmony_ci }, 4928c2ecf20Sopenharmony_ci }, 4938c2ecf20Sopenharmony_ci { .layout_id = 67, 4948c2ecf20Sopenharmony_ci .codecs[0] = { 4958c2ecf20Sopenharmony_ci .name = "topaz", 4968c2ecf20Sopenharmony_ci .connections = topaz_input, 4978c2ecf20Sopenharmony_ci }, 4988c2ecf20Sopenharmony_ci }, 4998c2ecf20Sopenharmony_ci { .layout_id = 76, 5008c2ecf20Sopenharmony_ci .codecs[0] = { 5018c2ecf20Sopenharmony_ci .name = "tas", 5028c2ecf20Sopenharmony_ci .connections = tas_connections_nomic, 5038c2ecf20Sopenharmony_ci }, 5048c2ecf20Sopenharmony_ci .codecs[1] = { 5058c2ecf20Sopenharmony_ci .name = "topaz", 5068c2ecf20Sopenharmony_ci .connections = topaz_inout, 5078c2ecf20Sopenharmony_ci }, 5088c2ecf20Sopenharmony_ci }, 5098c2ecf20Sopenharmony_ci { .layout_id = 90, 5108c2ecf20Sopenharmony_ci .codecs[0] = { 5118c2ecf20Sopenharmony_ci .name = "tas", 5128c2ecf20Sopenharmony_ci .connections = tas_connections_noline, 5138c2ecf20Sopenharmony_ci }, 5148c2ecf20Sopenharmony_ci }, 5158c2ecf20Sopenharmony_ci { .layout_id = 94, 5168c2ecf20Sopenharmony_ci .codecs[0] = { 5178c2ecf20Sopenharmony_ci .name = "onyx", 5188c2ecf20Sopenharmony_ci /* but it has an external mic?? how to select? */ 5198c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 5208c2ecf20Sopenharmony_ci }, 5218c2ecf20Sopenharmony_ci }, 5228c2ecf20Sopenharmony_ci { .layout_id = 98, 5238c2ecf20Sopenharmony_ci .codecs[0] = { 5248c2ecf20Sopenharmony_ci .name = "toonie", 5258c2ecf20Sopenharmony_ci .connections = toonie_connections, 5268c2ecf20Sopenharmony_ci }, 5278c2ecf20Sopenharmony_ci }, 5288c2ecf20Sopenharmony_ci { .layout_id = 100, 5298c2ecf20Sopenharmony_ci .codecs[0] = { 5308c2ecf20Sopenharmony_ci .name = "topaz", 5318c2ecf20Sopenharmony_ci .connections = topaz_input, 5328c2ecf20Sopenharmony_ci }, 5338c2ecf20Sopenharmony_ci .codecs[1] = { 5348c2ecf20Sopenharmony_ci .name = "onyx", 5358c2ecf20Sopenharmony_ci .connections = onyx_connections_noheadphones, 5368c2ecf20Sopenharmony_ci }, 5378c2ecf20Sopenharmony_ci }, 5388c2ecf20Sopenharmony_ci /* PowerMac3,4 */ 5398c2ecf20Sopenharmony_ci { .device_id = 14, 5408c2ecf20Sopenharmony_ci .codecs[0] = { 5418c2ecf20Sopenharmony_ci .name = "tas", 5428c2ecf20Sopenharmony_ci .connections = tas_connections_noline, 5438c2ecf20Sopenharmony_ci }, 5448c2ecf20Sopenharmony_ci }, 5458c2ecf20Sopenharmony_ci /* PowerMac3,6 */ 5468c2ecf20Sopenharmony_ci { .device_id = 22, 5478c2ecf20Sopenharmony_ci .codecs[0] = { 5488c2ecf20Sopenharmony_ci .name = "tas", 5498c2ecf20Sopenharmony_ci .connections = tas_connections_all, 5508c2ecf20Sopenharmony_ci }, 5518c2ecf20Sopenharmony_ci }, 5528c2ecf20Sopenharmony_ci /* PowerBook5,2 */ 5538c2ecf20Sopenharmony_ci { .device_id = 35, 5548c2ecf20Sopenharmony_ci .codecs[0] = { 5558c2ecf20Sopenharmony_ci .name = "tas", 5568c2ecf20Sopenharmony_ci .connections = tas_connections_all, 5578c2ecf20Sopenharmony_ci }, 5588c2ecf20Sopenharmony_ci }, 5598c2ecf20Sopenharmony_ci {} 5608c2ecf20Sopenharmony_ci}; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_cistatic struct layout *find_layout_by_id(unsigned int id) 5638c2ecf20Sopenharmony_ci{ 5648c2ecf20Sopenharmony_ci struct layout *l; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci l = layouts; 5678c2ecf20Sopenharmony_ci while (l->codecs[0].name) { 5688c2ecf20Sopenharmony_ci if (l->layout_id == id) 5698c2ecf20Sopenharmony_ci return l; 5708c2ecf20Sopenharmony_ci l++; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci return NULL; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic struct layout *find_layout_by_device(unsigned int id) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci struct layout *l; 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci l = layouts; 5808c2ecf20Sopenharmony_ci while (l->codecs[0].name) { 5818c2ecf20Sopenharmony_ci if (l->device_id == id) 5828c2ecf20Sopenharmony_ci return l; 5838c2ecf20Sopenharmony_ci l++; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci return NULL; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_cistatic void use_layout(struct layout *l) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci int i; 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_ci for (i=0; i<MAX_CODECS_PER_BUS; i++) { 5938c2ecf20Sopenharmony_ci if (l->codecs[i].name) { 5948c2ecf20Sopenharmony_ci request_module("snd-aoa-codec-%s", l->codecs[i].name); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci /* now we wait for the codecs to call us back */ 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_cistruct layout_dev; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistruct layout_dev_ptr { 6038c2ecf20Sopenharmony_ci struct layout_dev *ptr; 6048c2ecf20Sopenharmony_ci}; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_cistruct layout_dev { 6078c2ecf20Sopenharmony_ci struct list_head list; 6088c2ecf20Sopenharmony_ci struct soundbus_dev *sdev; 6098c2ecf20Sopenharmony_ci struct device_node *sound; 6108c2ecf20Sopenharmony_ci struct aoa_codec *codecs[MAX_CODECS_PER_BUS]; 6118c2ecf20Sopenharmony_ci struct layout *layout; 6128c2ecf20Sopenharmony_ci struct gpio_runtime gpio; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci /* we need these for headphone/lineout detection */ 6158c2ecf20Sopenharmony_ci struct snd_kcontrol *headphone_ctrl; 6168c2ecf20Sopenharmony_ci struct snd_kcontrol *lineout_ctrl; 6178c2ecf20Sopenharmony_ci struct snd_kcontrol *speaker_ctrl; 6188c2ecf20Sopenharmony_ci struct snd_kcontrol *master_ctrl; 6198c2ecf20Sopenharmony_ci struct snd_kcontrol *headphone_detected_ctrl; 6208c2ecf20Sopenharmony_ci struct snd_kcontrol *lineout_detected_ctrl; 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci struct layout_dev_ptr selfptr_headphone; 6238c2ecf20Sopenharmony_ci struct layout_dev_ptr selfptr_lineout; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci u32 have_lineout_detect:1, 6268c2ecf20Sopenharmony_ci have_headphone_detect:1, 6278c2ecf20Sopenharmony_ci switch_on_headphone:1, 6288c2ecf20Sopenharmony_ci switch_on_lineout:1; 6298c2ecf20Sopenharmony_ci}; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic LIST_HEAD(layouts_list); 6328c2ecf20Sopenharmony_cistatic int layouts_list_items; 6338c2ecf20Sopenharmony_ci/* this can go away but only if we allow multiple cards, 6348c2ecf20Sopenharmony_ci * make the fabric handle all the card stuff, etc... */ 6358c2ecf20Sopenharmony_cistatic struct layout_dev *layout_device; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci#define control_info snd_ctl_boolean_mono_info 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci#define AMP_CONTROL(n, description) \ 6408c2ecf20Sopenharmony_cistatic int n##_control_get(struct snd_kcontrol *kcontrol, \ 6418c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) \ 6428c2ecf20Sopenharmony_ci{ \ 6438c2ecf20Sopenharmony_ci struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ 6448c2ecf20Sopenharmony_ci if (gpio->methods && gpio->methods->get_##n) \ 6458c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = \ 6468c2ecf20Sopenharmony_ci gpio->methods->get_##n(gpio); \ 6478c2ecf20Sopenharmony_ci return 0; \ 6488c2ecf20Sopenharmony_ci} \ 6498c2ecf20Sopenharmony_cistatic int n##_control_put(struct snd_kcontrol *kcontrol, \ 6508c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) \ 6518c2ecf20Sopenharmony_ci{ \ 6528c2ecf20Sopenharmony_ci struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ 6538c2ecf20Sopenharmony_ci if (gpio->methods && gpio->methods->set_##n) \ 6548c2ecf20Sopenharmony_ci gpio->methods->set_##n(gpio, \ 6558c2ecf20Sopenharmony_ci !!ucontrol->value.integer.value[0]); \ 6568c2ecf20Sopenharmony_ci return 1; \ 6578c2ecf20Sopenharmony_ci} \ 6588c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new n##_ctl = { \ 6598c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ 6608c2ecf20Sopenharmony_ci .name = description, \ 6618c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ 6628c2ecf20Sopenharmony_ci .info = control_info, \ 6638c2ecf20Sopenharmony_ci .get = n##_control_get, \ 6648c2ecf20Sopenharmony_ci .put = n##_control_put, \ 6658c2ecf20Sopenharmony_ci} 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ciAMP_CONTROL(headphone, "Headphone Switch"); 6688c2ecf20Sopenharmony_ciAMP_CONTROL(speakers, "Speakers Switch"); 6698c2ecf20Sopenharmony_ciAMP_CONTROL(lineout, "Line-Out Switch"); 6708c2ecf20Sopenharmony_ciAMP_CONTROL(master, "Master Switch"); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic int detect_choice_get(struct snd_kcontrol *kcontrol, 6738c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6748c2ecf20Sopenharmony_ci{ 6758c2ecf20Sopenharmony_ci struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci switch (kcontrol->private_value) { 6788c2ecf20Sopenharmony_ci case 0: 6798c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = ldev->switch_on_headphone; 6808c2ecf20Sopenharmony_ci break; 6818c2ecf20Sopenharmony_ci case 1: 6828c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = ldev->switch_on_lineout; 6838c2ecf20Sopenharmony_ci break; 6848c2ecf20Sopenharmony_ci default: 6858c2ecf20Sopenharmony_ci return -ENODEV; 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci return 0; 6888c2ecf20Sopenharmony_ci} 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_cistatic int detect_choice_put(struct snd_kcontrol *kcontrol, 6918c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 6928c2ecf20Sopenharmony_ci{ 6938c2ecf20Sopenharmony_ci struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci switch (kcontrol->private_value) { 6968c2ecf20Sopenharmony_ci case 0: 6978c2ecf20Sopenharmony_ci ldev->switch_on_headphone = !!ucontrol->value.integer.value[0]; 6988c2ecf20Sopenharmony_ci break; 6998c2ecf20Sopenharmony_ci case 1: 7008c2ecf20Sopenharmony_ci ldev->switch_on_lineout = !!ucontrol->value.integer.value[0]; 7018c2ecf20Sopenharmony_ci break; 7028c2ecf20Sopenharmony_ci default: 7038c2ecf20Sopenharmony_ci return -ENODEV; 7048c2ecf20Sopenharmony_ci } 7058c2ecf20Sopenharmony_ci return 1; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new headphone_detect_choice = { 7098c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7108c2ecf20Sopenharmony_ci .name = "Headphone Detect Autoswitch", 7118c2ecf20Sopenharmony_ci .info = control_info, 7128c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 7138c2ecf20Sopenharmony_ci .get = detect_choice_get, 7148c2ecf20Sopenharmony_ci .put = detect_choice_put, 7158c2ecf20Sopenharmony_ci .private_value = 0, 7168c2ecf20Sopenharmony_ci}; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new lineout_detect_choice = { 7198c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7208c2ecf20Sopenharmony_ci .name = "Line-Out Detect Autoswitch", 7218c2ecf20Sopenharmony_ci .info = control_info, 7228c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 7238c2ecf20Sopenharmony_ci .get = detect_choice_get, 7248c2ecf20Sopenharmony_ci .put = detect_choice_put, 7258c2ecf20Sopenharmony_ci .private_value = 1, 7268c2ecf20Sopenharmony_ci}; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic int detected_get(struct snd_kcontrol *kcontrol, 7298c2ecf20Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 7308c2ecf20Sopenharmony_ci{ 7318c2ecf20Sopenharmony_ci struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); 7328c2ecf20Sopenharmony_ci int v; 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci switch (kcontrol->private_value) { 7358c2ecf20Sopenharmony_ci case 0: 7368c2ecf20Sopenharmony_ci v = ldev->gpio.methods->get_detect(&ldev->gpio, 7378c2ecf20Sopenharmony_ci AOA_NOTIFY_HEADPHONE); 7388c2ecf20Sopenharmony_ci break; 7398c2ecf20Sopenharmony_ci case 1: 7408c2ecf20Sopenharmony_ci v = ldev->gpio.methods->get_detect(&ldev->gpio, 7418c2ecf20Sopenharmony_ci AOA_NOTIFY_LINE_OUT); 7428c2ecf20Sopenharmony_ci break; 7438c2ecf20Sopenharmony_ci default: 7448c2ecf20Sopenharmony_ci return -ENODEV; 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci ucontrol->value.integer.value[0] = v; 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new headphone_detected = { 7518c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7528c2ecf20Sopenharmony_ci .name = "Headphone Detected", 7538c2ecf20Sopenharmony_ci .info = control_info, 7548c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 7558c2ecf20Sopenharmony_ci .get = detected_get, 7568c2ecf20Sopenharmony_ci .private_value = 0, 7578c2ecf20Sopenharmony_ci}; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_cistatic const struct snd_kcontrol_new lineout_detected = { 7608c2ecf20Sopenharmony_ci .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 7618c2ecf20Sopenharmony_ci .name = "Line-Out Detected", 7628c2ecf20Sopenharmony_ci .info = control_info, 7638c2ecf20Sopenharmony_ci .access = SNDRV_CTL_ELEM_ACCESS_READ, 7648c2ecf20Sopenharmony_ci .get = detected_get, 7658c2ecf20Sopenharmony_ci .private_value = 1, 7668c2ecf20Sopenharmony_ci}; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic int check_codec(struct aoa_codec *codec, 7698c2ecf20Sopenharmony_ci struct layout_dev *ldev, 7708c2ecf20Sopenharmony_ci struct codec_connect_info *cci) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci const u32 *ref; 7738c2ecf20Sopenharmony_ci char propname[32]; 7748c2ecf20Sopenharmony_ci struct codec_connection *cc; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci /* if the codec has a 'codec' node, we require a reference */ 7778c2ecf20Sopenharmony_ci if (of_node_name_eq(codec->node, "codec")) { 7788c2ecf20Sopenharmony_ci snprintf(propname, sizeof(propname), 7798c2ecf20Sopenharmony_ci "platform-%s-codec-ref", codec->name); 7808c2ecf20Sopenharmony_ci ref = of_get_property(ldev->sound, propname, NULL); 7818c2ecf20Sopenharmony_ci if (!ref) { 7828c2ecf20Sopenharmony_ci printk(KERN_INFO "snd-aoa-fabric-layout: " 7838c2ecf20Sopenharmony_ci "required property %s not present\n", propname); 7848c2ecf20Sopenharmony_ci return -ENODEV; 7858c2ecf20Sopenharmony_ci } 7868c2ecf20Sopenharmony_ci if (*ref != codec->node->phandle) { 7878c2ecf20Sopenharmony_ci printk(KERN_INFO "snd-aoa-fabric-layout: " 7888c2ecf20Sopenharmony_ci "%s doesn't match!\n", propname); 7898c2ecf20Sopenharmony_ci return -ENODEV; 7908c2ecf20Sopenharmony_ci } 7918c2ecf20Sopenharmony_ci } else { 7928c2ecf20Sopenharmony_ci if (layouts_list_items != 1) { 7938c2ecf20Sopenharmony_ci printk(KERN_INFO "snd-aoa-fabric-layout: " 7948c2ecf20Sopenharmony_ci "more than one soundbus, but no references.\n"); 7958c2ecf20Sopenharmony_ci return -ENODEV; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci } 7988c2ecf20Sopenharmony_ci codec->soundbus_dev = ldev->sdev; 7998c2ecf20Sopenharmony_ci codec->gpio = &ldev->gpio; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci cc = cci->connections; 8028c2ecf20Sopenharmony_ci if (!cc) 8038c2ecf20Sopenharmony_ci return -EINVAL; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n"); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci codec->connected = 0; 8088c2ecf20Sopenharmony_ci codec->fabric_data = cc; 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci while (cc->connected) { 8118c2ecf20Sopenharmony_ci codec->connected |= 1<<cc->codec_bit; 8128c2ecf20Sopenharmony_ci cc++; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return 0; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_cistatic int layout_found_codec(struct aoa_codec *codec) 8198c2ecf20Sopenharmony_ci{ 8208c2ecf20Sopenharmony_ci struct layout_dev *ldev; 8218c2ecf20Sopenharmony_ci int i; 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci list_for_each_entry(ldev, &layouts_list, list) { 8248c2ecf20Sopenharmony_ci for (i=0; i<MAX_CODECS_PER_BUS; i++) { 8258c2ecf20Sopenharmony_ci if (!ldev->layout->codecs[i].name) 8268c2ecf20Sopenharmony_ci continue; 8278c2ecf20Sopenharmony_ci if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) { 8288c2ecf20Sopenharmony_ci if (check_codec(codec, 8298c2ecf20Sopenharmony_ci ldev, 8308c2ecf20Sopenharmony_ci &ldev->layout->codecs[i]) == 0) 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci } 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci return -ENODEV; 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic void layout_remove_codec(struct aoa_codec *codec) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci int i; 8418c2ecf20Sopenharmony_ci /* here remove the codec from the layout dev's 8428c2ecf20Sopenharmony_ci * codec reference */ 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci codec->soundbus_dev = NULL; 8458c2ecf20Sopenharmony_ci codec->gpio = NULL; 8468c2ecf20Sopenharmony_ci for (i=0; i<MAX_CODECS_PER_BUS; i++) { 8478c2ecf20Sopenharmony_ci } 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_cistatic void layout_notify(void *data) 8518c2ecf20Sopenharmony_ci{ 8528c2ecf20Sopenharmony_ci struct layout_dev_ptr *dptr = data; 8538c2ecf20Sopenharmony_ci struct layout_dev *ldev; 8548c2ecf20Sopenharmony_ci int v, update; 8558c2ecf20Sopenharmony_ci struct snd_kcontrol *detected, *c; 8568c2ecf20Sopenharmony_ci struct snd_card *card = aoa_get_card(); 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci ldev = dptr->ptr; 8598c2ecf20Sopenharmony_ci if (data == &ldev->selfptr_headphone) { 8608c2ecf20Sopenharmony_ci v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE); 8618c2ecf20Sopenharmony_ci detected = ldev->headphone_detected_ctrl; 8628c2ecf20Sopenharmony_ci update = ldev->switch_on_headphone; 8638c2ecf20Sopenharmony_ci if (update) { 8648c2ecf20Sopenharmony_ci ldev->gpio.methods->set_speakers(&ldev->gpio, !v); 8658c2ecf20Sopenharmony_ci ldev->gpio.methods->set_headphone(&ldev->gpio, v); 8668c2ecf20Sopenharmony_ci ldev->gpio.methods->set_lineout(&ldev->gpio, 0); 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci } else if (data == &ldev->selfptr_lineout) { 8698c2ecf20Sopenharmony_ci v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT); 8708c2ecf20Sopenharmony_ci detected = ldev->lineout_detected_ctrl; 8718c2ecf20Sopenharmony_ci update = ldev->switch_on_lineout; 8728c2ecf20Sopenharmony_ci if (update) { 8738c2ecf20Sopenharmony_ci ldev->gpio.methods->set_speakers(&ldev->gpio, !v); 8748c2ecf20Sopenharmony_ci ldev->gpio.methods->set_headphone(&ldev->gpio, 0); 8758c2ecf20Sopenharmony_ci ldev->gpio.methods->set_lineout(&ldev->gpio, v); 8768c2ecf20Sopenharmony_ci } 8778c2ecf20Sopenharmony_ci } else 8788c2ecf20Sopenharmony_ci return; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci if (detected) 8818c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id); 8828c2ecf20Sopenharmony_ci if (update) { 8838c2ecf20Sopenharmony_ci c = ldev->headphone_ctrl; 8848c2ecf20Sopenharmony_ci if (c) 8858c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); 8868c2ecf20Sopenharmony_ci c = ldev->speaker_ctrl; 8878c2ecf20Sopenharmony_ci if (c) 8888c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); 8898c2ecf20Sopenharmony_ci c = ldev->lineout_ctrl; 8908c2ecf20Sopenharmony_ci if (c) 8918c2ecf20Sopenharmony_ci snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); 8928c2ecf20Sopenharmony_ci } 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic void layout_attached_codec(struct aoa_codec *codec) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci struct codec_connection *cc; 8988c2ecf20Sopenharmony_ci struct snd_kcontrol *ctl; 8998c2ecf20Sopenharmony_ci int headphones, lineout; 9008c2ecf20Sopenharmony_ci struct layout_dev *ldev = layout_device; 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci /* need to add this codec to our codec array! */ 9038c2ecf20Sopenharmony_ci 9048c2ecf20Sopenharmony_ci cc = codec->fabric_data; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci headphones = codec->gpio->methods->get_detect(codec->gpio, 9078c2ecf20Sopenharmony_ci AOA_NOTIFY_HEADPHONE); 9088c2ecf20Sopenharmony_ci lineout = codec->gpio->methods->get_detect(codec->gpio, 9098c2ecf20Sopenharmony_ci AOA_NOTIFY_LINE_OUT); 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci if (codec->gpio->methods->set_master) { 9128c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&master_ctl, codec->gpio); 9138c2ecf20Sopenharmony_ci ldev->master_ctrl = ctl; 9148c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9158c2ecf20Sopenharmony_ci } 9168c2ecf20Sopenharmony_ci while (cc->connected) { 9178c2ecf20Sopenharmony_ci if (cc->connected & CC_SPEAKERS) { 9188c2ecf20Sopenharmony_ci if (headphones <= 0 && lineout <= 0) 9198c2ecf20Sopenharmony_ci ldev->gpio.methods->set_speakers(codec->gpio, 1); 9208c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&speakers_ctl, codec->gpio); 9218c2ecf20Sopenharmony_ci ldev->speaker_ctrl = ctl; 9228c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9238c2ecf20Sopenharmony_ci } 9248c2ecf20Sopenharmony_ci if (cc->connected & CC_HEADPHONE) { 9258c2ecf20Sopenharmony_ci if (headphones == 1) 9268c2ecf20Sopenharmony_ci ldev->gpio.methods->set_headphone(codec->gpio, 1); 9278c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&headphone_ctl, codec->gpio); 9288c2ecf20Sopenharmony_ci ldev->headphone_ctrl = ctl; 9298c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9308c2ecf20Sopenharmony_ci ldev->have_headphone_detect = 9318c2ecf20Sopenharmony_ci !ldev->gpio.methods 9328c2ecf20Sopenharmony_ci ->set_notify(&ldev->gpio, 9338c2ecf20Sopenharmony_ci AOA_NOTIFY_HEADPHONE, 9348c2ecf20Sopenharmony_ci layout_notify, 9358c2ecf20Sopenharmony_ci &ldev->selfptr_headphone); 9368c2ecf20Sopenharmony_ci if (ldev->have_headphone_detect) { 9378c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&headphone_detect_choice, 9388c2ecf20Sopenharmony_ci ldev); 9398c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9408c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&headphone_detected, 9418c2ecf20Sopenharmony_ci ldev); 9428c2ecf20Sopenharmony_ci ldev->headphone_detected_ctrl = ctl; 9438c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9448c2ecf20Sopenharmony_ci } 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci if (cc->connected & CC_LINEOUT) { 9478c2ecf20Sopenharmony_ci if (lineout == 1) 9488c2ecf20Sopenharmony_ci ldev->gpio.methods->set_lineout(codec->gpio, 1); 9498c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); 9508c2ecf20Sopenharmony_ci if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) 9518c2ecf20Sopenharmony_ci strlcpy(ctl->id.name, 9528c2ecf20Sopenharmony_ci "Headphone Switch", sizeof(ctl->id.name)); 9538c2ecf20Sopenharmony_ci ldev->lineout_ctrl = ctl; 9548c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9558c2ecf20Sopenharmony_ci ldev->have_lineout_detect = 9568c2ecf20Sopenharmony_ci !ldev->gpio.methods 9578c2ecf20Sopenharmony_ci ->set_notify(&ldev->gpio, 9588c2ecf20Sopenharmony_ci AOA_NOTIFY_LINE_OUT, 9598c2ecf20Sopenharmony_ci layout_notify, 9608c2ecf20Sopenharmony_ci &ldev->selfptr_lineout); 9618c2ecf20Sopenharmony_ci if (ldev->have_lineout_detect) { 9628c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&lineout_detect_choice, 9638c2ecf20Sopenharmony_ci ldev); 9648c2ecf20Sopenharmony_ci if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) 9658c2ecf20Sopenharmony_ci strlcpy(ctl->id.name, 9668c2ecf20Sopenharmony_ci "Headphone Detect Autoswitch", 9678c2ecf20Sopenharmony_ci sizeof(ctl->id.name)); 9688c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9698c2ecf20Sopenharmony_ci ctl = snd_ctl_new1(&lineout_detected, 9708c2ecf20Sopenharmony_ci ldev); 9718c2ecf20Sopenharmony_ci if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) 9728c2ecf20Sopenharmony_ci strlcpy(ctl->id.name, 9738c2ecf20Sopenharmony_ci "Headphone Detected", 9748c2ecf20Sopenharmony_ci sizeof(ctl->id.name)); 9758c2ecf20Sopenharmony_ci ldev->lineout_detected_ctrl = ctl; 9768c2ecf20Sopenharmony_ci aoa_snd_ctl_add(ctl); 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci } 9798c2ecf20Sopenharmony_ci cc++; 9808c2ecf20Sopenharmony_ci } 9818c2ecf20Sopenharmony_ci /* now update initial state */ 9828c2ecf20Sopenharmony_ci if (ldev->have_headphone_detect) 9838c2ecf20Sopenharmony_ci layout_notify(&ldev->selfptr_headphone); 9848c2ecf20Sopenharmony_ci if (ldev->have_lineout_detect) 9858c2ecf20Sopenharmony_ci layout_notify(&ldev->selfptr_lineout); 9868c2ecf20Sopenharmony_ci} 9878c2ecf20Sopenharmony_ci 9888c2ecf20Sopenharmony_cistatic struct aoa_fabric layout_fabric = { 9898c2ecf20Sopenharmony_ci .name = "SoundByLayout", 9908c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 9918c2ecf20Sopenharmony_ci .found_codec = layout_found_codec, 9928c2ecf20Sopenharmony_ci .remove_codec = layout_remove_codec, 9938c2ecf20Sopenharmony_ci .attached_codec = layout_attached_codec, 9948c2ecf20Sopenharmony_ci}; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_cistatic int aoa_fabric_layout_probe(struct soundbus_dev *sdev) 9978c2ecf20Sopenharmony_ci{ 9988c2ecf20Sopenharmony_ci struct device_node *sound = NULL; 9998c2ecf20Sopenharmony_ci const unsigned int *id; 10008c2ecf20Sopenharmony_ci struct layout *layout = NULL; 10018c2ecf20Sopenharmony_ci struct layout_dev *ldev = NULL; 10028c2ecf20Sopenharmony_ci int err; 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci /* hm, currently we can only have one ... */ 10058c2ecf20Sopenharmony_ci if (layout_device) 10068c2ecf20Sopenharmony_ci return -ENODEV; 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci /* by breaking out we keep a reference */ 10098c2ecf20Sopenharmony_ci for_each_child_of_node(sdev->ofdev.dev.of_node, sound) { 10108c2ecf20Sopenharmony_ci if (of_node_is_type(sound, "soundchip")) 10118c2ecf20Sopenharmony_ci break; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci if (!sound) 10148c2ecf20Sopenharmony_ci return -ENODEV; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci id = of_get_property(sound, "layout-id", NULL); 10178c2ecf20Sopenharmony_ci if (id) { 10188c2ecf20Sopenharmony_ci layout = find_layout_by_id(*id); 10198c2ecf20Sopenharmony_ci } else { 10208c2ecf20Sopenharmony_ci id = of_get_property(sound, "device-id", NULL); 10218c2ecf20Sopenharmony_ci if (id) 10228c2ecf20Sopenharmony_ci layout = find_layout_by_device(*id); 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (!layout) { 10268c2ecf20Sopenharmony_ci printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n"); 10278c2ecf20Sopenharmony_ci goto outnodev; 10288c2ecf20Sopenharmony_ci } 10298c2ecf20Sopenharmony_ci 10308c2ecf20Sopenharmony_ci ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL); 10318c2ecf20Sopenharmony_ci if (!ldev) 10328c2ecf20Sopenharmony_ci goto outnodev; 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci layout_device = ldev; 10358c2ecf20Sopenharmony_ci ldev->sdev = sdev; 10368c2ecf20Sopenharmony_ci ldev->sound = sound; 10378c2ecf20Sopenharmony_ci ldev->layout = layout; 10388c2ecf20Sopenharmony_ci ldev->gpio.node = sound->parent; 10398c2ecf20Sopenharmony_ci switch (layout->layout_id) { 10408c2ecf20Sopenharmony_ci case 0: /* anything with device_id, not layout_id */ 10418c2ecf20Sopenharmony_ci case 41: /* that unknown machine no one seems to have */ 10428c2ecf20Sopenharmony_ci case 51: /* PowerBook5,4 */ 10438c2ecf20Sopenharmony_ci case 58: /* Mac Mini */ 10448c2ecf20Sopenharmony_ci ldev->gpio.methods = ftr_gpio_methods; 10458c2ecf20Sopenharmony_ci printk(KERN_DEBUG 10468c2ecf20Sopenharmony_ci "snd-aoa-fabric-layout: Using direct GPIOs\n"); 10478c2ecf20Sopenharmony_ci break; 10488c2ecf20Sopenharmony_ci default: 10498c2ecf20Sopenharmony_ci ldev->gpio.methods = pmf_gpio_methods; 10508c2ecf20Sopenharmony_ci printk(KERN_DEBUG 10518c2ecf20Sopenharmony_ci "snd-aoa-fabric-layout: Using PMF GPIOs\n"); 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci ldev->selfptr_headphone.ptr = ldev; 10548c2ecf20Sopenharmony_ci ldev->selfptr_lineout.ptr = ldev; 10558c2ecf20Sopenharmony_ci dev_set_drvdata(&sdev->ofdev.dev, ldev); 10568c2ecf20Sopenharmony_ci list_add(&ldev->list, &layouts_list); 10578c2ecf20Sopenharmony_ci layouts_list_items++; 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci /* assign these before registering ourselves, so 10608c2ecf20Sopenharmony_ci * callbacks that are done during registration 10618c2ecf20Sopenharmony_ci * already have the values */ 10628c2ecf20Sopenharmony_ci sdev->pcmid = ldev->layout->pcmid; 10638c2ecf20Sopenharmony_ci if (ldev->layout->busname) { 10648c2ecf20Sopenharmony_ci sdev->pcmname = ldev->layout->busname; 10658c2ecf20Sopenharmony_ci } else { 10668c2ecf20Sopenharmony_ci sdev->pcmname = "Master"; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ldev->gpio.methods->init(&ldev->gpio); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev); 10728c2ecf20Sopenharmony_ci if (err && err != -EALREADY) { 10738c2ecf20Sopenharmony_ci printk(KERN_INFO "snd-aoa-fabric-layout: can't use," 10748c2ecf20Sopenharmony_ci " another fabric is active!\n"); 10758c2ecf20Sopenharmony_ci goto outlistdel; 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci use_layout(layout); 10798c2ecf20Sopenharmony_ci ldev->switch_on_headphone = 1; 10808c2ecf20Sopenharmony_ci ldev->switch_on_lineout = 1; 10818c2ecf20Sopenharmony_ci return 0; 10828c2ecf20Sopenharmony_ci outlistdel: 10838c2ecf20Sopenharmony_ci /* we won't be using these then... */ 10848c2ecf20Sopenharmony_ci ldev->gpio.methods->exit(&ldev->gpio); 10858c2ecf20Sopenharmony_ci /* reset if we didn't use it */ 10868c2ecf20Sopenharmony_ci sdev->pcmname = NULL; 10878c2ecf20Sopenharmony_ci sdev->pcmid = -1; 10888c2ecf20Sopenharmony_ci list_del(&ldev->list); 10898c2ecf20Sopenharmony_ci layouts_list_items--; 10908c2ecf20Sopenharmony_ci kfree(ldev); 10918c2ecf20Sopenharmony_ci outnodev: 10928c2ecf20Sopenharmony_ci of_node_put(sound); 10938c2ecf20Sopenharmony_ci layout_device = NULL; 10948c2ecf20Sopenharmony_ci return -ENODEV; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic int aoa_fabric_layout_remove(struct soundbus_dev *sdev) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); 11008c2ecf20Sopenharmony_ci int i; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci for (i=0; i<MAX_CODECS_PER_BUS; i++) { 11038c2ecf20Sopenharmony_ci if (ldev->codecs[i]) { 11048c2ecf20Sopenharmony_ci aoa_fabric_unlink_codec(ldev->codecs[i]); 11058c2ecf20Sopenharmony_ci } 11068c2ecf20Sopenharmony_ci ldev->codecs[i] = NULL; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci list_del(&ldev->list); 11098c2ecf20Sopenharmony_ci layouts_list_items--; 11108c2ecf20Sopenharmony_ci of_node_put(ldev->sound); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci ldev->gpio.methods->set_notify(&ldev->gpio, 11138c2ecf20Sopenharmony_ci AOA_NOTIFY_HEADPHONE, 11148c2ecf20Sopenharmony_ci NULL, 11158c2ecf20Sopenharmony_ci NULL); 11168c2ecf20Sopenharmony_ci ldev->gpio.methods->set_notify(&ldev->gpio, 11178c2ecf20Sopenharmony_ci AOA_NOTIFY_LINE_OUT, 11188c2ecf20Sopenharmony_ci NULL, 11198c2ecf20Sopenharmony_ci NULL); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci ldev->gpio.methods->exit(&ldev->gpio); 11228c2ecf20Sopenharmony_ci layout_device = NULL; 11238c2ecf20Sopenharmony_ci kfree(ldev); 11248c2ecf20Sopenharmony_ci sdev->pcmid = -1; 11258c2ecf20Sopenharmony_ci sdev->pcmname = NULL; 11268c2ecf20Sopenharmony_ci return 0; 11278c2ecf20Sopenharmony_ci} 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11308c2ecf20Sopenharmony_cistatic int aoa_fabric_layout_suspend(struct device *dev) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci struct layout_dev *ldev = dev_get_drvdata(dev); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) 11358c2ecf20Sopenharmony_ci ldev->gpio.methods->all_amps_off(&ldev->gpio); 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci return 0; 11388c2ecf20Sopenharmony_ci} 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_cistatic int aoa_fabric_layout_resume(struct device *dev) 11418c2ecf20Sopenharmony_ci{ 11428c2ecf20Sopenharmony_ci struct layout_dev *ldev = dev_get_drvdata(dev); 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore) 11458c2ecf20Sopenharmony_ci ldev->gpio.methods->all_amps_restore(&ldev->gpio); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci return 0; 11488c2ecf20Sopenharmony_ci} 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, 11518c2ecf20Sopenharmony_ci aoa_fabric_layout_suspend, aoa_fabric_layout_resume); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci#endif 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic struct soundbus_driver aoa_soundbus_driver = { 11568c2ecf20Sopenharmony_ci .name = "snd_aoa_soundbus_drv", 11578c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11588c2ecf20Sopenharmony_ci .probe = aoa_fabric_layout_probe, 11598c2ecf20Sopenharmony_ci .remove = aoa_fabric_layout_remove, 11608c2ecf20Sopenharmony_ci .driver = { 11618c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 11628c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11638c2ecf20Sopenharmony_ci .pm = &aoa_fabric_layout_pm_ops, 11648c2ecf20Sopenharmony_ci#endif 11658c2ecf20Sopenharmony_ci } 11668c2ecf20Sopenharmony_ci}; 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_cistatic int __init aoa_fabric_layout_init(void) 11698c2ecf20Sopenharmony_ci{ 11708c2ecf20Sopenharmony_ci return soundbus_register_driver(&aoa_soundbus_driver); 11718c2ecf20Sopenharmony_ci} 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_cistatic void __exit aoa_fabric_layout_exit(void) 11748c2ecf20Sopenharmony_ci{ 11758c2ecf20Sopenharmony_ci soundbus_unregister_driver(&aoa_soundbus_driver); 11768c2ecf20Sopenharmony_ci aoa_fabric_unregister(&layout_fabric); 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cimodule_init(aoa_fabric_layout_init); 11808c2ecf20Sopenharmony_cimodule_exit(aoa_fabric_layout_exit); 1181