brintos

brintos / linux-shallow public Read only

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