File Index
Files and directories
Sign in
FILE

layout.c

File preview with syntax highlighting.

linux-6.19.14/sound/aoa/fabrics/layout.c C
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Apple Onboard Audio driver -- layout/machine id fabric
   4 *
   5 * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
   6 *
   7 * This fabric module looks for sound codecs based on the
   8 * layout-id or device-id property in the device tree.
   9 */
  10#include <linux/list.h>
  11#include <linux/module.h>
  12#include <linux/of.h>
  13#include <linux/platform_device.h>
  14#include <linux/slab.h>
  15#include "../aoa.h"
  16#include "../soundbus/soundbus.h"
  17
  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	2
  23
  24/* These are the connections the layout fabric
  25 * knows about. It doesn't really care about the
  26 * input ones, but I thought I'd separate them
  27 * to give them proper names. The thing is that
  28 * Apple usually will distinguish the active output
  29 * by GPIOs, while the active input is set directly
  30 * on the codec. Hence we here tell the codec what
  31 * we think is connected. This information is hard-
  32 * coded below ... */
  33#define CC_SPEAKERS	(1<<0)
  34#define CC_HEADPHONE	(1<<1)
  35#define CC_LINEOUT	(1<<2)
  36#define CC_DIGITALOUT	(1<<3)
  37#define CC_LINEIN	(1<<4)
  38#define CC_MICROPHONE	(1<<5)
  39#define CC_DIGITALIN	(1<<6)
  40/* pretty bogus but users complain...
  41 * This is a flag saying that the LINEOUT
  42 * should be renamed to HEADPHONE.
  43 * be careful with input detection! */
  44#define CC_LINEOUT_LABELLED_HEADPHONE	(1<<7)
  45
  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 the
  51	 * fabric has to know the codec anyway and all codecs might have
  52	 * 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 filled
  70	 * too much.
  71	 * We only assign these two if we expect to find more
  72	 * than one soundbus, i.e. on those machines with
  73	 * multiple layout-ids */
  74	char *busname;
  75	int pcmid;
  76};
  77
  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 machines
 147	 * below ... If not this will need separating. */
 148	{
 149		.connected = CC_LINEIN,
 150		.codec_bit = 2,
 151	},
 152	{
 153		.connected = CC_MICROPHONE,
 154		.codec_bit = 3,
 155	},
 156	{} /* terminate array by .connected == 0 */
 157};
 158
 159/* onyx on machines with real line-out */
 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_info
 639
 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's
 843	 * codec reference */
 844
 845	codec->soundbus_dev = NULL;
 846	codec->gpio = NULL;
 847	for (i=0; i<MAX_CODECS_PER_BUS; i++) {
 848	}
 849}
 850
 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	} else
 879		return;
 880
 881	if (detected)
 882		snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id);
 883	if (update) {
 884		c = ldev->headphone_ctrl;
 885		if (c)
 886			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
 887		c = ldev->speaker_ctrl;
 888		if (c)
 889			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
 890		c = ldev->lineout_ctrl;
 891		if (c)
 892			snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id);
 893	}
 894}
 895
 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.methods
 933					->set_notify(&ldev->gpio,
 934						     AOA_NOTIFY_HEADPHONE,
 935						     layout_notify,
 936						     &ldev->selfptr_headphone);
 937			if (ldev->have_headphone_detect) {
 938				ctl = snd_ctl_new1(&headphone_detect_choice,
 939						   ldev);
 940				aoa_snd_ctl_add(ctl);
 941				ctl = snd_ctl_new1(&headphone_detected,
 942						   ldev);
 943				ldev->headphone_detected_ctrl = ctl;
 944				aoa_snd_ctl_add(ctl);
 945			}
 946		}
 947		if (cc->connected & CC_LINEOUT) {
 948			if (lineout == 1)
 949				ldev->gpio.methods->set_lineout(codec->gpio, 1);
 950			ctl = snd_ctl_new1(&lineout_ctl, codec->gpio);
 951			if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
 952				strscpy(ctl->id.name, "Headphone Switch");
 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				aoa_snd_ctl_add(ctl);
 968				ctl = snd_ctl_new1(&lineout_detected,
 969						   ldev);
 970				if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE)
 971					strscpy(ctl->id.name,
 972						"Headphone Detected");
 973				ldev->lineout_detected_ctrl = ctl;
 974				aoa_snd_ctl_add(ctl);
 975			}
 976		}
 977		cc++;
 978	}
 979	/* now update initial state */
 980	if (ldev->have_headphone_detect)
 981		layout_notify(&ldev->selfptr_headphone);
 982	if (ldev->have_lineout_detect)
 983		layout_notify(&ldev->selfptr_lineout);
 984}
 985
 986static struct aoa_fabric layout_fabric = {
 987	.name = "SoundByLayout",
 988	.owner = THIS_MODULE,
 989	.found_codec = layout_found_codec,
 990	.remove_codec = layout_remove_codec,
 991	.attached_codec = layout_attached_codec,
 992};
 993
 994static int aoa_fabric_layout_probe(struct soundbus_dev *sdev)
 995{
 996	struct device_node *sound = NULL;
 997	const unsigned int *id;
 998	struct layout *layout = NULL;
 999	struct layout_dev *ldev = NULL;
1000	int err;
1001
1002	/* hm, currently we can only have one ... */
1003	if (layout_device)
1004		return -ENODEV;
1005
1006	/* by breaking out we keep a reference */
1007	for_each_child_of_node(sdev->ofdev.dev.of_node, sound) {
1008		if (of_node_is_type(sound, "soundchip"))
1009			break;
1010	}
1011	if (!sound)
1012		return -ENODEV;
1013
1014	id = of_get_property(sound, "layout-id", NULL);
1015	if (id) {
1016		layout = find_layout_by_id(*id);
1017	} else {
1018		id = of_get_property(sound, "device-id", NULL);
1019		if (id)
1020			layout = find_layout_by_device(*id);
1021	}
1022
1023	if (!layout) {
1024		printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n");
1025		goto outnodev;
1026	}
1027
1028	ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL);
1029	if (!ldev)
1030		goto outnodev;
1031
1032	layout_device = ldev;
1033	ldev->sdev = sdev;
1034	ldev->sound = sound;
1035	ldev->layout = layout;
1036	ldev->gpio.node = sound->parent;
1037	switch (layout->layout_id) {
1038	case 0:  /* anything with device_id, not layout_id */
1039	case 41: /* that unknown machine no one seems to have */
1040	case 51: /* PowerBook5,4 */
1041	case 58: /* Mac Mini */
1042		ldev->gpio.methods = ftr_gpio_methods;
1043		printk(KERN_DEBUG
1044		       "snd-aoa-fabric-layout: Using direct GPIOs\n");
1045		break;
1046	default:
1047		ldev->gpio.methods = pmf_gpio_methods;
1048		printk(KERN_DEBUG
1049		       "snd-aoa-fabric-layout: Using PMF GPIOs\n");
1050	}
1051	ldev->selfptr_headphone.ptr = ldev;
1052	ldev->selfptr_lineout.ptr = ldev;
1053	dev_set_drvdata(&sdev->ofdev.dev, ldev);
1054	list_add(&ldev->list, &layouts_list);
1055	layouts_list_items++;
1056
1057	/* assign these before registering ourselves, so
1058	 * callbacks that are done during registration
1059	 * already have the values */
1060	sdev->pcmid = ldev->layout->pcmid;
1061	if (ldev->layout->busname) {
1062		sdev->pcmname = ldev->layout->busname;
1063	} else {
1064		sdev->pcmname = "Master";
1065	}
1066
1067	ldev->gpio.methods->init(&ldev->gpio);
1068
1069	err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev);
1070	if (err && err != -EALREADY) {
1071		printk(KERN_INFO "snd-aoa-fabric-layout: can't use,"
1072				 " another fabric is active!\n");
1073		goto outlistdel;
1074	}
1075
1076	use_layout(layout);
1077	ldev->switch_on_headphone = 1;
1078	ldev->switch_on_lineout = 1;
1079	return 0;
1080 outlistdel:
1081	/* we won't be using these then... */
1082	ldev->gpio.methods->exit(&ldev->gpio);
1083	/* reset if we didn't use it */
1084	sdev->pcmname = NULL;
1085	sdev->pcmid = -1;
1086	list_del(&ldev->list);
1087	layouts_list_items--;
1088	kfree(ldev);
1089 outnodev:
1090 	of_node_put(sound);
1091 	layout_device = NULL;
1092	return -ENODEV;
1093}
1094
1095static void aoa_fabric_layout_remove(struct soundbus_dev *sdev)
1096{
1097	struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev);
1098	int i;
1099
1100	for (i=0; i<MAX_CODECS_PER_BUS; i++) {
1101		if (ldev->codecs[i]) {
1102			aoa_fabric_unlink_codec(ldev->codecs[i]);
1103		}
1104		ldev->codecs[i] = NULL;
1105	}
1106	list_del(&ldev->list);
1107	layouts_list_items--;
1108	of_node_put(ldev->sound);
1109
1110	ldev->gpio.methods->set_notify(&ldev->gpio,
1111				       AOA_NOTIFY_HEADPHONE,
1112				       NULL,
1113				       NULL);
1114	ldev->gpio.methods->set_notify(&ldev->gpio,
1115				       AOA_NOTIFY_LINE_OUT,
1116				       NULL,
1117				       NULL);
1118
1119	ldev->gpio.methods->exit(&ldev->gpio);
1120	layout_device = NULL;
1121	kfree(ldev);
1122	sdev->pcmid = -1;
1123	sdev->pcmname = NULL;
1124}
1125
1126static int aoa_fabric_layout_suspend(struct device *dev)
1127{
1128	struct layout_dev *ldev = dev_get_drvdata(dev);
1129
1130	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off)
1131		ldev->gpio.methods->all_amps_off(&ldev->gpio);
1132
1133	return 0;
1134}
1135
1136static int aoa_fabric_layout_resume(struct device *dev)
1137{
1138	struct layout_dev *ldev = dev_get_drvdata(dev);
1139
1140	if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore)
1141		ldev->gpio.methods->all_amps_restore(&ldev->gpio);
1142
1143	return 0;
1144}
1145
1146static DEFINE_SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops,
1147	aoa_fabric_layout_suspend, aoa_fabric_layout_resume);
1148
1149static struct soundbus_driver aoa_soundbus_driver = {
1150	.name = "snd_aoa_soundbus_drv",
1151	.owner = THIS_MODULE,
1152	.probe = aoa_fabric_layout_probe,
1153	.remove = aoa_fabric_layout_remove,
1154	.driver = {
1155		.owner = THIS_MODULE,
1156		.pm = &aoa_fabric_layout_pm_ops,
1157	}
1158};
1159
1160static int __init aoa_fabric_layout_init(void)
1161{
1162	return soundbus_register_driver(&aoa_soundbus_driver);
1163}
1164
1165static void __exit aoa_fabric_layout_exit(void)
1166{
1167	soundbus_unregister_driver(&aoa_soundbus_driver);
1168	aoa_fabric_unregister(&layout_fabric);
1169}
1170
1171module_init(aoa_fabric_layout_init);
1172module_exit(aoa_fabric_layout_exit);
~/ Read-only file preview.