cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

layout.c (28104B)


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