cregit-Linux how code gets into the kernel

Release 4.8 sound/aoa/fabrics/layout.c

/*
 * Apple Onboard Audio driver -- layout/machine id fabric
 *
 * Copyright 2006-2008 Johannes Berg <johannes@sipsolutions.net>
 *
 * GPL v2, can be found in COPYING.
 *
 *
 * This fabric module looks for sound codecs based on the
 * layout-id or device-id property in the device tree.
 */
#include <asm/prom.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/slab.h>
#include "../aoa.h"
#include "../soundbus/soundbus.h"

MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Layout-ID fabric for snd-aoa");


#define MAX_CODECS_PER_BUS	2

/* These are the connections the layout fabric
 * knows about. It doesn't really care about the
 * input ones, but I thought I'd separate them
 * to give them proper names. The thing is that
 * Apple usually will distinguish the active output
 * by GPIOs, while the active input is set directly
 * on the codec. Hence we here tell the codec what
 * we think is connected. This information is hard-
 * coded below ... */

#define CC_SPEAKERS	(1<<0)

#define CC_HEADPHONE	(1<<1)

#define CC_LINEOUT	(1<<2)

#define CC_DIGITALOUT	(1<<3)

#define CC_LINEIN	(1<<4)

#define CC_MICROPHONE	(1<<5)

#define CC_DIGITALIN	(1<<6)
/* pretty bogus but users complain...
 * This is a flag saying that the LINEOUT
 * should be renamed to HEADPHONE.
 * be careful with input detection! */

#define CC_LINEOUT_LABELLED_HEADPHONE	(1<<7)


struct codec_connection {
	/* CC_ flags from above */
	
int connected;
	/* codec dependent bit to be set in the aoa_codec.connected field.
         * This intentionally doesn't have any generic flags because the
         * fabric has to know the codec anyway and all codecs might have
         * different connectors */
	
int codec_bit;
};


struct codec_connect_info {
	
char *name;
	
struct codec_connection *connections;
};


#define LAYOUT_FLAG_COMBO_LINEOUT_SPDIF	(1<<0)


struct layout {
	

unsigned int layout_id, device_id;
	
struct codec_connect_info codecs[MAX_CODECS_PER_BUS];
	
int flags;

	/* if busname is not assigned, we use 'Master' below,
         * so that our layout table doesn't need to be filled
         * too much.
         * We only assign these two if we expect to find more
         * than one soundbus, i.e. on those machines with
         * multiple layout-ids */
	
char *busname;
	
int pcmid;
};

MODULE_ALIAS("sound-layout-36");
MODULE_ALIAS("sound-layout-41");
MODULE_ALIAS("sound-layout-45");
MODULE_ALIAS("sound-layout-47");
MODULE_ALIAS("sound-layout-48");
MODULE_ALIAS("sound-layout-49");
MODULE_ALIAS("sound-layout-50");
MODULE_ALIAS("sound-layout-51");
MODULE_ALIAS("sound-layout-56");
MODULE_ALIAS("sound-layout-57");
MODULE_ALIAS("sound-layout-58");
MODULE_ALIAS("sound-layout-60");
MODULE_ALIAS("sound-layout-61");
MODULE_ALIAS("sound-layout-62");
MODULE_ALIAS("sound-layout-64");
MODULE_ALIAS("sound-layout-65");
MODULE_ALIAS("sound-layout-66");
MODULE_ALIAS("sound-layout-67");
MODULE_ALIAS("sound-layout-68");
MODULE_ALIAS("sound-layout-69");
MODULE_ALIAS("sound-layout-70");
MODULE_ALIAS("sound-layout-72");
MODULE_ALIAS("sound-layout-76");
MODULE_ALIAS("sound-layout-80");
MODULE_ALIAS("sound-layout-82");
MODULE_ALIAS("sound-layout-84");
MODULE_ALIAS("sound-layout-86");
MODULE_ALIAS("sound-layout-90");
MODULE_ALIAS("sound-layout-92");
MODULE_ALIAS("sound-layout-94");
MODULE_ALIAS("sound-layout-96");
MODULE_ALIAS("sound-layout-98");
MODULE_ALIAS("sound-layout-100");

MODULE_ALIAS("aoa-device-id-14");
MODULE_ALIAS("aoa-device-id-22");
MODULE_ALIAS("aoa-device-id-35");
MODULE_ALIAS("aoa-device-id-44");

/* onyx with all but microphone connected */

static struct codec_connection onyx_connections_nomic[] = {
	{
		.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
		.codec_bit = 0,
        },
	{
		.connected = CC_DIGITALOUT,
		.codec_bit = 1,
        },
	{
		.connected = CC_LINEIN,
		.codec_bit = 2,
        },
	{} /* terminate array by .connected == 0 */
};

/* onyx on machines without headphone */

static struct codec_connection onyx_connections_noheadphones[] = {
	{
		.connected = CC_SPEAKERS | CC_LINEOUT |
			     CC_LINEOUT_LABELLED_HEADPHONE,
		.codec_bit = 0,
        },
	{
		.connected = CC_DIGITALOUT,
		.codec_bit = 1,
        },
	/* FIXME: are these correct? probably not for all the machines
         * below ... If not this will need separating. */
	{
		.connected = CC_LINEIN,
		.codec_bit = 2,
        },
	{
		.connected = CC_MICROPHONE,
		.codec_bit = 3,
        },
	{} /* terminate array by .connected == 0 */
};

/* onyx on machines with real line-out */

static struct codec_connection onyx_connections_reallineout[] = {
	{
		.connected = CC_SPEAKERS | CC_LINEOUT | CC_HEADPHONE,
		.codec_bit = 0,
        },
	{
		.connected = CC_DIGITALOUT,
		.codec_bit = 1,
        },
	{
		.connected = CC_LINEIN,
		.codec_bit = 2,
        },
	{} /* terminate array by .connected == 0 */
};

/* tas on machines without line out */

static struct codec_connection tas_connections_nolineout[] = {
	{
		.connected = CC_SPEAKERS | CC_HEADPHONE,
		.codec_bit = 0,
        },
	{
		.connected = CC_LINEIN,
		.codec_bit = 2,
        },
	{
		.connected = CC_MICROPHONE,
		.codec_bit = 3,
        },
	{} /* terminate array by .connected == 0 */
};

/* tas on machines with neither line out nor line in */

static struct codec_connection tas_connections_noline[] = {
	{
		.connected = CC_SPEAKERS | CC_HEADPHONE,
		.codec_bit = 0,
        },
	{
		.connected = CC_MICROPHONE,
		.codec_bit = 3,
        },
	{} /* terminate array by .connected == 0 */
};

/* tas on machines without microphone */

static struct codec_connection tas_connections_nomic[] = {
	{
		.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
		.codec_bit = 0,
        },
	{
		.connected = CC_LINEIN,
		.codec_bit = 2,
        },
	{} /* terminate array by .connected == 0 */
};

/* tas on machines with everything connected */

static struct codec_connection tas_connections_all[] = {
	{
		.connected = CC_SPEAKERS | CC_HEADPHONE | CC_LINEOUT,
		.codec_bit = 0,
        },
	{
		.connected = CC_LINEIN,
		.codec_bit = 2,
        },
	{
		.connected = CC_MICROPHONE,
		.codec_bit = 3,
        },
	{} /* terminate array by .connected == 0 */
};


static struct codec_connection toonie_connections[] = {
	{
		.connected = CC_SPEAKERS | CC_HEADPHONE,
		.codec_bit = 0,
        },
	{} /* terminate array by .connected == 0 */
};


static struct codec_connection topaz_input[] = {
	{
		.connected = CC_DIGITALIN,
		.codec_bit = 0,
        },
	{} /* terminate array by .connected == 0 */
};


static struct codec_connection topaz_output[] = {
	{
		.connected = CC_DIGITALOUT,
		.codec_bit = 1,
        },
	{} /* terminate array by .connected == 0 */
};


static struct codec_connection topaz_inout[] = {
	{
		.connected = CC_DIGITALIN,
		.codec_bit = 0,
        },
	{
		.connected = CC_DIGITALOUT,
		.codec_bit = 1,
        },
	{} /* terminate array by .connected == 0 */
};


static struct layout layouts[] = {
	/* last PowerBooks (15" Oct 2005) */
	{ .layout_id = 82,
	  .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	/* PowerMac9,1 */
	{ .layout_id = 60,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_reallineout,
          },
        },
	/* PowerMac9,1 */
	{ .layout_id = 61,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	/* PowerBook5,7 */
	{ .layout_id = 64,
	  .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
        },
	/* PowerBook5,7 */
	{ .layout_id = 65,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	/* PowerBook5,9 [17" Oct 2005] */
	{ .layout_id = 84,
	  .flags = LAYOUT_FLAG_COMBO_LINEOUT_SPDIF,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	/* PowerMac8,1 */
	{ .layout_id = 45,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	/* Quad PowerMac (analog in, analog/digital out) */
	{ .layout_id = 68,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_nomic,
          },
        },
	/* Quad PowerMac (digital in) */
	{ .layout_id = 69,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
	  .busname = "digital in", .pcmid = 1 },
	/* Early 2005 PowerBook (PowerBook 5,6) */
	{ .layout_id = 70,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_nolineout,
          },
        },
	/* PowerBook 5,4 */
	{ .layout_id = 51,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_nolineout,
          },
        },
	/* PowerBook6,5 */
	{ .device_id = 44,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_all,
          },
        },
	/* PowerBook6,7 */
	{ .layout_id = 80,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_noline,
          },
        },
	/* PowerBook6,8 */
	{ .layout_id = 72,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_nolineout,
          },
        },
	/* PowerMac8,2 */
	{ .layout_id = 86,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_nomic,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	/* PowerBook6,7 */
	{ .layout_id = 92,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_nolineout,
          },
        },
	/* PowerMac10,1 (Mac Mini) */
	{ .layout_id = 58,
	  .codecs[0] = {
		.name = "toonie",
		.connections = toonie_connections,
          },
        },
	{
	  .layout_id = 96,
	  .codecs[0] = {
	  	.name = "onyx",
	  	.connections = onyx_connections_noheadphones,
          },
        },
	/* unknown, untested, but this comes from Apple */
	{ .layout_id = 41,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_all,
          },
        },
	{ .layout_id = 36,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_nomic,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_inout,
          },
        },
	{ .layout_id = 47,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
        },
	{ .layout_id = 48,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	{ .layout_id = 49,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_nomic,
          },
        },
	{ .layout_id = 50,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	{ .layout_id = 56,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
        },
	{ .layout_id = 57,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	{ .layout_id = 62,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_output,
          },
        },
	{ .layout_id = 66,
	  .codecs[0] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
        },
	{ .layout_id = 67,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
        },
	{ .layout_id = 76,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_nomic,
          },
	  .codecs[1] = {
		.name = "topaz",
		.connections = topaz_inout,
          },
        },
	{ .layout_id = 90,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_noline,
          },
        },
	{ .layout_id = 94,
	  .codecs[0] = {
		.name = "onyx",
		/* but it has an external mic?? how to select? */
		.connections = onyx_connections_noheadphones,
          },
        },
	{ .layout_id = 98,
	  .codecs[0] = {
		.name = "toonie",
		.connections = toonie_connections,
          },
        },
	{ .layout_id = 100,
	  .codecs[0] = {
		.name = "topaz",
		.connections = topaz_input,
          },
	  .codecs[1] = {
		.name = "onyx",
		.connections = onyx_connections_noheadphones,
          },
        },
	/* PowerMac3,4 */
	{ .device_id = 14,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_noline,
          },
        },
	/* PowerMac3,6 */
	{ .device_id = 22,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_all,
          },
        },
	/* PowerBook5,2 */
	{ .device_id = 35,
	  .codecs[0] = {
		.name = "tas",
		.connections = tas_connections_all,
          },
        },
	{}
};


static struct layout *find_layout_by_id(unsigned int id) { struct layout *l; l = layouts; while (l->codecs[0].name) { if (l->layout_id == id) return l; l++; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg51100.00%2100.00%
Total51100.00%2100.00%


static struct layout *find_layout_by_device(unsigned int id) { struct layout *l; l = layouts; while (l->codecs[0].name) { if (l->device_id == id) return l; l++; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg51100.00%1100.00%
Total51100.00%1100.00%


static void use_layout(struct layout *l) { int i; for (i=0; i<MAX_CODECS_PER_BUS; i++) { if (l->codecs[i].name) { request_module("snd-aoa-codec-%s", l->codecs[i].name); } } /* now we wait for the codecs to call us back */ }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg57100.00%1100.00%
Total57100.00%1100.00%

struct layout_dev; struct layout_dev_ptr { struct layout_dev *ptr; }; struct layout_dev { struct list_head list; struct soundbus_dev *sdev; struct device_node *sound; struct aoa_codec *codecs[MAX_CODECS_PER_BUS]; struct layout *layout; struct gpio_runtime gpio; /* we need these for headphone/lineout detection */ struct snd_kcontrol *headphone_ctrl; struct snd_kcontrol *lineout_ctrl; struct snd_kcontrol *speaker_ctrl; struct snd_kcontrol *master_ctrl; struct snd_kcontrol *headphone_detected_ctrl; struct snd_kcontrol *lineout_detected_ctrl; struct layout_dev_ptr selfptr_headphone; struct layout_dev_ptr selfptr_lineout; u32 have_lineout_detect:1, have_headphone_detect:1, switch_on_headphone:1, switch_on_lineout:1; }; static LIST_HEAD(layouts_list); static int layouts_list_items; /* this can go away but only if we allow multiple cards, * make the fabric handle all the card stuff, etc... */ static struct layout_dev *layout_device; #define control_info snd_ctl_boolean_mono_info #define AMP_CONTROL(n, description) \ static int n##_control_get(struct snd_kcontrol *kcontrol, \ struct snd_ctl_elem_value *ucontrol) \ { \ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ if (gpio->methods && gpio->methods->get_##n) \ ucontrol->value.integer.value[0] = \ gpio->methods->get_##n(gpio); \ return 0; \ } \ static int n##_control_put(struct snd_kcontrol *kcontrol, \ struct snd_ctl_elem_value *ucontrol) \ { \ struct gpio_runtime *gpio = snd_kcontrol_chip(kcontrol); \ if (gpio->methods && gpio->methods->set_##n) \ gpio->methods->set_##n(gpio, \ !!ucontrol->value.integer.value[0]); \ return 1; \ } \ static struct snd_kcontrol_new n##_ctl = { \ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ .name = description, \ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \ .info = control_info, \ .get = n##_control_get, \ .put = n##_control_put, \ } AMP_CONTROL(headphone, "Headphone Switch"); AMP_CONTROL(speakers, "Speakers Switch"); AMP_CONTROL(lineout, "Line-Out Switch"); AMP_CONTROL(master, "Master Switch");
static int detect_choice_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); switch (kcontrol->private_value) { case 0: ucontrol->value.integer.value[0] = ldev->switch_on_headphone; break; case 1: ucontrol->value.integer.value[0] = ldev->switch_on_lineout; break; default: return -ENODEV; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg80100.00%1100.00%
Total80100.00%1100.00%


static int detect_choice_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); switch (kcontrol->private_value) { case 0: ldev->switch_on_headphone = !!ucontrol->value.integer.value[0]; break; case 1: ldev->switch_on_lineout = !!ucontrol->value.integer.value[0]; break; default: return -ENODEV; } return 1; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg84100.00%1100.00%
Total84100.00%1100.00%

static struct snd_kcontrol_new headphone_detect_choice = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Detect Autoswitch", .info = control_info, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .get = detect_choice_get, .put = detect_choice_put, .private_value = 0, }; static struct snd_kcontrol_new lineout_detect_choice = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line-Out Detect Autoswitch", .info = control_info, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .get = detect_choice_get, .put = detect_choice_put, .private_value = 1, };
static int detected_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct layout_dev *ldev = snd_kcontrol_chip(kcontrol); int v; switch (kcontrol->private_value) { case 0: v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE); break; case 1: v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT); break; default: return -ENODEV; } ucontrol->value.integer.value[0] = v; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg102100.00%1100.00%
Total102100.00%1100.00%

static struct snd_kcontrol_new headphone_detected = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Headphone Detected", .info = control_info, .access = SNDRV_CTL_ELEM_ACCESS_READ, .get = detected_get, .private_value = 0, }; static struct snd_kcontrol_new lineout_detected = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line-Out Detected", .info = control_info, .access = SNDRV_CTL_ELEM_ACCESS_READ, .get = detected_get, .private_value = 1, };
static int check_codec(struct aoa_codec *codec, struct layout_dev *ldev, struct codec_connect_info *cci) { const u32 *ref; char propname[32]; struct codec_connection *cc; /* if the codec has a 'codec' node, we require a reference */ if (codec->node && (strcmp(codec->node->name, "codec") == 0)) { snprintf(propname, sizeof(propname), "platform-%s-codec-ref", codec->name); ref = of_get_property(ldev->sound, propname, NULL); if (!ref) { printk(KERN_INFO "snd-aoa-fabric-layout: " "required property %s not present\n", propname); return -ENODEV; } if (*ref != codec->node->phandle) { printk(KERN_INFO "snd-aoa-fabric-layout: " "%s doesn't match!\n", propname); return -ENODEV; } } else { if (layouts_list_items != 1) { printk(KERN_INFO "snd-aoa-fabric-layout: " "more than one soundbus, but no references.\n"); return -ENODEV; } } codec->soundbus_dev = ldev->sdev; codec->gpio = &ldev->gpio; cc = cci->connections; if (!cc) return -EINVAL; printk(KERN_INFO "snd-aoa-fabric-layout: can use this codec\n"); codec->connected = 0; codec->fabric_data = cc; while (cc->connected) { codec->connected |= 1<<cc->codec_bit; cc++; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg22998.71%125.00%
stephen rothwellstephen rothwell20.86%250.00%
grant likelygrant likely10.43%125.00%
Total232100.00%4100.00%


static int layout_found_codec(struct aoa_codec *codec) { struct layout_dev *ldev; int i; list_for_each_entry(ldev, &layouts_list, list) { for (i=0; i<MAX_CODECS_PER_BUS; i++) { if (!ldev->layout->codecs[i].name) continue; if (strcmp(ldev->layout->codecs[i].name, codec->name) == 0) { if (check_codec(codec, ldev, &ldev->layout->codecs[i]) == 0) return 0; } } } return -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg111100.00%1100.00%
Total111100.00%1100.00%


static void layout_remove_codec(struct aoa_codec *codec) { int i; /* here remove the codec from the layout dev's * codec reference */ codec->soundbus_dev = NULL; codec->gpio = NULL; for (i=0; i<MAX_CODECS_PER_BUS; i++) { } }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg41100.00%1100.00%
Total41100.00%1100.00%


static void layout_notify(void *data) { struct layout_dev_ptr *dptr = data; struct layout_dev *ldev; int v, update; struct snd_kcontrol *detected, *c; struct snd_card *card = aoa_get_card(); ldev = dptr->ptr; if (data == &ldev->selfptr_headphone) { v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_HEADPHONE); detected = ldev->headphone_detected_ctrl; update = ldev->switch_on_headphone; if (update) { ldev->gpio.methods->set_speakers(&ldev->gpio, !v); ldev->gpio.methods->set_headphone(&ldev->gpio, v); ldev->gpio.methods->set_lineout(&ldev->gpio, 0); } } else if (data == &ldev->selfptr_lineout) { v = ldev->gpio.methods->get_detect(&ldev->gpio, AOA_NOTIFY_LINE_OUT); detected = ldev->lineout_detected_ctrl; update = ldev->switch_on_lineout; if (update) { ldev->gpio.methods->set_speakers(&ldev->gpio, !v); ldev->gpio.methods->set_headphone(&ldev->gpio, 0); ldev->gpio.methods->set_lineout(&ldev->gpio, v); } } else return; if (detected) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &detected->id); if (update) { c = ldev->headphone_ctrl; if (c) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); c = ldev->speaker_ctrl; if (c) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); c = ldev->lineout_ctrl; if (c) snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &c->id); } }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg332100.00%1100.00%
Total332100.00%1100.00%


static void layout_attached_codec(struct aoa_codec *codec) { struct codec_connection *cc; struct snd_kcontrol *ctl; int headphones, lineout; struct layout_dev *ldev = layout_device; /* need to add this codec to our codec array! */ cc = codec->fabric_data; headphones = codec->gpio->methods->get_detect(codec->gpio, AOA_NOTIFY_HEADPHONE); lineout = codec->gpio->methods->get_detect(codec->gpio, AOA_NOTIFY_LINE_OUT); if (codec->gpio->methods->set_master) { ctl = snd_ctl_new1(&master_ctl, codec->gpio); ldev->master_ctrl = ctl; aoa_snd_ctl_add(ctl); } while (cc->connected) { if (cc->connected & CC_SPEAKERS) { if (headphones <= 0 && lineout <= 0) ldev->gpio.methods->set_speakers(codec->gpio, 1); ctl = snd_ctl_new1(&speakers_ctl, codec->gpio); ldev->speaker_ctrl = ctl; aoa_snd_ctl_add(ctl); } if (cc->connected & CC_HEADPHONE) { if (headphones == 1) ldev->gpio.methods->set_headphone(codec->gpio, 1); ctl = snd_ctl_new1(&headphone_ctl, codec->gpio); ldev->headphone_ctrl = ctl; aoa_snd_ctl_add(ctl); ldev->have_headphone_detect = !ldev->gpio.methods ->set_notify(&ldev->gpio, AOA_NOTIFY_HEADPHONE, layout_notify, &ldev->selfptr_headphone); if (ldev->have_headphone_detect) { ctl = snd_ctl_new1(&headphone_detect_choice, ldev); aoa_snd_ctl_add(ctl); ctl = snd_ctl_new1(&headphone_detected, ldev); ldev->headphone_detected_ctrl = ctl; aoa_snd_ctl_add(ctl); } } if (cc->connected & CC_LINEOUT) { if (lineout == 1) ldev->gpio.methods->set_lineout(codec->gpio, 1); ctl = snd_ctl_new1(&lineout_ctl, codec->gpio); if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strlcpy(ctl->id.name, "Headphone Switch", sizeof(ctl->id.name)); ldev->lineout_ctrl = ctl; aoa_snd_ctl_add(ctl); ldev->have_lineout_detect = !ldev->gpio.methods ->set_notify(&ldev->gpio, AOA_NOTIFY_LINE_OUT, layout_notify, &ldev->selfptr_lineout); if (ldev->have_lineout_detect) { ctl = snd_ctl_new1(&lineout_detect_choice, ldev); if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strlcpy(ctl->id.name, "Headphone Detect Autoswitch", sizeof(ctl->id.name)); aoa_snd_ctl_add(ctl); ctl = snd_ctl_new1(&lineout_detected, ldev); if (cc->connected & CC_LINEOUT_LABELLED_HEADPHONE) strlcpy(ctl->id.name, "Headphone Detected", sizeof(ctl->id.name)); ldev->lineout_detected_ctrl = ctl; aoa_snd_ctl_add(ctl); } } cc++; } /* now update initial state */ if (ldev->have_headphone_detect) layout_notify(&ldev->selfptr_headphone); if (ldev->have_lineout_detect) layout_notify(&ldev->selfptr_lineout); }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg543100.00%2100.00%
Total543100.00%2100.00%

static struct aoa_fabric layout_fabric = { .name = "SoundByLayout", .owner = THIS_MODULE, .found_codec = layout_found_codec, .remove_codec = layout_remove_codec, .attached_codec = layout_attached_codec, };
static int aoa_fabric_layout_probe(struct soundbus_dev *sdev) { struct device_node *sound = NULL; const unsigned int *id; struct layout *layout = NULL; struct layout_dev *ldev = NULL; int err; /* hm, currently we can only have one ... */ if (layout_device) return -ENODEV; /* by breaking out we keep a reference */ while ((sound = of_get_next_child(sdev->ofdev.dev.of_node, sound))) { if (sound->type && strcasecmp(sound->type, "soundchip") == 0) break; } if (!sound) return -ENODEV; id = of_get_property(sound, "layout-id", NULL); if (id) { layout = find_layout_by_id(*id); } else { id = of_get_property(sound, "device-id", NULL); if (id) layout = find_layout_by_device(*id); } if (!layout) { printk(KERN_ERR "snd-aoa-fabric-layout: unknown layout\n"); goto outnodev; } ldev = kzalloc(sizeof(struct layout_dev), GFP_KERNEL); if (!ldev) goto outnodev; layout_device = ldev; ldev->sdev = sdev; ldev->sound = sound; ldev->layout = layout; ldev->gpio.node = sound->parent; switch (layout->layout_id) { case 0: /* anything with device_id, not layout_id */ case 41: /* that unknown machine no one seems to have */ case 51: /* PowerBook5,4 */ case 58: /* Mac Mini */ ldev->gpio.methods = ftr_gpio_methods; printk(KERN_DEBUG "snd-aoa-fabric-layout: Using direct GPIOs\n"); break; default: ldev->gpio.methods = pmf_gpio_methods; printk(KERN_DEBUG "snd-aoa-fabric-layout: Using PMF GPIOs\n"); } ldev->selfptr_headphone.ptr = ldev; ldev->selfptr_lineout.ptr = ldev; dev_set_drvdata(&sdev->ofdev.dev, ldev); list_add(&ldev->list, &layouts_list); layouts_list_items++; /* assign these before registering ourselves, so * callbacks that are done during registration * already have the values */ sdev->pcmid = ldev->layout->pcmid; if (ldev->layout->busname) { sdev->pcmname = ldev->layout->busname; } else { sdev->pcmname = "Master"; } ldev->gpio.methods->init(&ldev->gpio); err = aoa_fabric_register(&layout_fabric, &sdev->ofdev.dev); if (err && err != -EALREADY) { printk(KERN_INFO "snd-aoa-fabric-layout: can't use," " another fabric is active!\n"); goto outlistdel; } use_layout(layout); ldev->switch_on_headphone = 1; ldev->switch_on_lineout = 1; return 0; outlistdel: /* we won't be using these then... */ ldev->gpio.methods->exit(&ldev->gpio); /* reset if we didn't use it */ sdev->pcmname = NULL; sdev->pcmid = -1; list_del(&ldev->list); layouts_list_items--; kfree(ldev); outnodev: of_node_put(sound); layout_device = NULL; return -ENODEV; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg45192.61%225.00%
benjamin herrenschmidtbenjamin herrenschmidt142.87%112.50%
olaf heringolaf hering71.44%112.50%
greg kroah-hartmangreg kroah-hartman51.03%112.50%
julia lawalljulia lawall51.03%112.50%
grant likelygrant likely30.62%112.50%
stephen rothwellstephen rothwell20.41%112.50%
Total487100.00%8100.00%


static int aoa_fabric_layout_remove(struct soundbus_dev *sdev) { struct layout_dev *ldev = dev_get_drvdata(&sdev->ofdev.dev); int i; for (i=0; i<MAX_CODECS_PER_BUS; i++) { if (ldev->codecs[i]) { aoa_fabric_unlink_codec(ldev->codecs[i]); } ldev->codecs[i] = NULL; } list_del(&ldev->list); layouts_list_items--; of_node_put(ldev->sound); ldev->gpio.methods->set_notify(&ldev->gpio, AOA_NOTIFY_HEADPHONE, NULL, NULL); ldev->gpio.methods->set_notify(&ldev->gpio, AOA_NOTIFY_LINE_OUT, NULL, NULL); ldev->gpio.methods->exit(&ldev->gpio); layout_device = NULL; kfree(ldev); sdev->pcmid = -1; sdev->pcmname = NULL; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg16797.66%150.00%
greg kroah-hartmangreg kroah-hartman42.34%150.00%
Total171100.00%2100.00%

#ifdef CONFIG_PM_SLEEP
static int aoa_fabric_layout_suspend(struct device *dev) { struct layout_dev *ldev = dev_get_drvdata(dev); if (ldev->gpio.methods && ldev->gpio.methods->all_amps_off) ldev->gpio.methods->all_amps_off(&ldev->gpio); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg4990.74%133.33%
greg kroah-hartmangreg kroah-hartman35.56%133.33%
lars-peter clausenlars-peter clausen23.70%133.33%
Total54100.00%3100.00%


static int aoa_fabric_layout_resume(struct device *dev) { struct layout_dev *ldev = dev_get_drvdata(dev); if (ldev->gpio.methods && ldev->gpio.methods->all_amps_restore) ldev->gpio.methods->all_amps_restore(&ldev->gpio); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg4888.89%125.00%
greg kroah-hartmangreg kroah-hartman35.56%125.00%
lars-peter clausenlars-peter clausen23.70%125.00%
dan carpenterdan carpenter11.85%125.00%
Total54100.00%4100.00%

static SIMPLE_DEV_PM_OPS(aoa_fabric_layout_pm_ops, aoa_fabric_layout_suspend, aoa_fabric_layout_resume); #endif static struct soundbus_driver aoa_soundbus_driver = { .name = "snd_aoa_soundbus_drv", .owner = THIS_MODULE, .probe = aoa_fabric_layout_probe, .remove = aoa_fabric_layout_remove, .driver = { .owner = THIS_MODULE, #ifdef CONFIG_PM_SLEEP .pm = &aoa_fabric_layout_pm_ops, #endif } };
static int __init aoa_fabric_layout_init(void) { int err; err = soundbus_register_driver(&aoa_soundbus_driver); if (err) return err; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg30100.00%1100.00%
Total30100.00%1100.00%


static void __exit aoa_fabric_layout_exit(void) { soundbus_unregister_driver(&aoa_soundbus_driver); aoa_fabric_unregister(&layout_fabric); }

Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg21100.00%1100.00%
Total21100.00%1100.00%

module_init(aoa_fabric_layout_init); module_exit(aoa_fabric_layout_exit);

Overall Contributors

PersonTokensPropCommitsCommitProp
johannes bergjohannes berg470397.43%526.32%
michael ellermanmichael ellerman310.64%15.26%
lars-peter clausenlars-peter clausen280.58%15.26%
greg kroah-hartmangreg kroah-hartman150.31%15.26%
olaf heringolaf hering150.31%210.53%
benjamin herrenschmidtbenjamin herrenschmidt140.29%15.26%
julia lawalljulia lawall50.10%15.26%
stephen rothwellstephen rothwell40.08%210.53%
grant likelygrant likely40.08%210.53%
tejun heotejun heo30.06%15.26%
takashi iwaitakashi iwai30.06%15.26%
dan carpenterdan carpenter20.04%15.26%
Total4827100.00%19100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.