| /* | 
 |  * soc-dapm.c  --  ALSA SoC Dynamic Audio Power Management | 
 |  * | 
 |  * Copyright 2005 Wolfson Microelectronics PLC. | 
 |  * Author: Liam Girdwood <lrg@slimlogic.co.uk> | 
 |  * | 
 |  *  This program is free software; you can redistribute  it and/or modify it | 
 |  *  under  the terms of  the GNU General  Public License as published by the | 
 |  *  Free Software Foundation;  either version 2 of the  License, or (at your | 
 |  *  option) any later version. | 
 |  * | 
 |  *  Features: | 
 |  *    o Changes power status of internal codec blocks depending on the | 
 |  *      dynamic configuration of codec internal audio paths and active | 
 |  *      DACs/ADCs. | 
 |  *    o Platform power domain - can support external components i.e. amps and | 
 |  *      mic/meadphone insertion events. | 
 |  *    o Automatic Mic Bias support | 
 |  *    o Jack insertion power event initiation - e.g. hp insertion will enable | 
 |  *      sinks, dacs, etc | 
 |  *    o Delayed powerdown of audio susbsystem to reduce pops between a quick | 
 |  *      device reopen. | 
 |  * | 
 |  *  Todo: | 
 |  *    o DAPM power change sequencing - allow for configurable per | 
 |  *      codec sequences. | 
 |  *    o Support for analogue bias optimisation. | 
 |  *    o Support for reduced codec oversampling rates. | 
 |  *    o Support for reduced codec bias currents. | 
 |  */ | 
 |  | 
 | #include <linux/module.h> | 
 | #include <linux/moduleparam.h> | 
 | #include <linux/init.h> | 
 | #include <linux/async.h> | 
 | #include <linux/delay.h> | 
 | #include <linux/pm.h> | 
 | #include <linux/bitops.h> | 
 | #include <linux/platform_device.h> | 
 | #include <linux/jiffies.h> | 
 | #include <linux/debugfs.h> | 
 | #include <linux/slab.h> | 
 | #include <sound/core.h> | 
 | #include <sound/pcm.h> | 
 | #include <sound/pcm_params.h> | 
 | #include <sound/soc.h> | 
 | #include <sound/initval.h> | 
 |  | 
 | #include <trace/events/asoc.h> | 
 |  | 
 | /* dapm power sequences - make this per codec in the future */ | 
 | static int dapm_up_seq[] = { | 
 | 	[snd_soc_dapm_pre] = 0, | 
 | 	[snd_soc_dapm_supply] = 1, | 
 | 	[snd_soc_dapm_micbias] = 2, | 
 | 	[snd_soc_dapm_aif_in] = 3, | 
 | 	[snd_soc_dapm_aif_out] = 3, | 
 | 	[snd_soc_dapm_mic] = 4, | 
 | 	[snd_soc_dapm_mux] = 5, | 
 | 	[snd_soc_dapm_virt_mux] = 5, | 
 | 	[snd_soc_dapm_value_mux] = 5, | 
 | 	[snd_soc_dapm_dac] = 6, | 
 | 	[snd_soc_dapm_mixer] = 7, | 
 | 	[snd_soc_dapm_mixer_named_ctl] = 7, | 
 | 	[snd_soc_dapm_pga] = 8, | 
 | 	[snd_soc_dapm_adc] = 9, | 
 | 	[snd_soc_dapm_out_drv] = 10, | 
 | 	[snd_soc_dapm_hp] = 10, | 
 | 	[snd_soc_dapm_spk] = 10, | 
 | 	[snd_soc_dapm_post] = 11, | 
 | }; | 
 |  | 
 | static int dapm_down_seq[] = { | 
 | 	[snd_soc_dapm_pre] = 0, | 
 | 	[snd_soc_dapm_adc] = 1, | 
 | 	[snd_soc_dapm_hp] = 2, | 
 | 	[snd_soc_dapm_spk] = 2, | 
 | 	[snd_soc_dapm_out_drv] = 2, | 
 | 	[snd_soc_dapm_pga] = 4, | 
 | 	[snd_soc_dapm_mixer_named_ctl] = 5, | 
 | 	[snd_soc_dapm_mixer] = 5, | 
 | 	[snd_soc_dapm_dac] = 6, | 
 | 	[snd_soc_dapm_mic] = 7, | 
 | 	[snd_soc_dapm_micbias] = 8, | 
 | 	[snd_soc_dapm_mux] = 9, | 
 | 	[snd_soc_dapm_virt_mux] = 9, | 
 | 	[snd_soc_dapm_value_mux] = 9, | 
 | 	[snd_soc_dapm_aif_in] = 10, | 
 | 	[snd_soc_dapm_aif_out] = 10, | 
 | 	[snd_soc_dapm_supply] = 11, | 
 | 	[snd_soc_dapm_post] = 12, | 
 | }; | 
 |  | 
 | static void pop_wait(u32 pop_time) | 
 | { | 
 | 	if (pop_time) | 
 | 		schedule_timeout_uninterruptible(msecs_to_jiffies(pop_time)); | 
 | } | 
 |  | 
 | static void pop_dbg(struct device *dev, u32 pop_time, const char *fmt, ...) | 
 | { | 
 | 	va_list args; | 
 | 	char *buf; | 
 |  | 
 | 	if (!pop_time) | 
 | 		return; | 
 |  | 
 | 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | 
 | 	if (buf == NULL) | 
 | 		return; | 
 |  | 
 | 	va_start(args, fmt); | 
 | 	vsnprintf(buf, PAGE_SIZE, fmt, args); | 
 | 	dev_info(dev, "%s", buf); | 
 | 	va_end(args); | 
 |  | 
 | 	kfree(buf); | 
 | } | 
 |  | 
 | /* create a new dapm widget */ | 
 | static inline struct snd_soc_dapm_widget *dapm_cnew_widget( | 
 | 	const struct snd_soc_dapm_widget *_widget) | 
 | { | 
 | 	return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL); | 
 | } | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_set_bias_level - set the bias level for the system | 
 |  * @dapm: DAPM context | 
 |  * @level: level to configure | 
 |  * | 
 |  * Configure the bias (power) levels for the SoC audio device. | 
 |  * | 
 |  * Returns 0 for success else error. | 
 |  */ | 
 | static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm, | 
 | 				       enum snd_soc_bias_level level) | 
 | { | 
 | 	struct snd_soc_card *card = dapm->card; | 
 | 	int ret = 0; | 
 |  | 
 | 	switch (level) { | 
 | 	case SND_SOC_BIAS_ON: | 
 | 		dev_dbg(dapm->dev, "Setting full bias\n"); | 
 | 		break; | 
 | 	case SND_SOC_BIAS_PREPARE: | 
 | 		dev_dbg(dapm->dev, "Setting bias prepare\n"); | 
 | 		break; | 
 | 	case SND_SOC_BIAS_STANDBY: | 
 | 		dev_dbg(dapm->dev, "Setting standby bias\n"); | 
 | 		break; | 
 | 	case SND_SOC_BIAS_OFF: | 
 | 		dev_dbg(dapm->dev, "Setting bias off\n"); | 
 | 		break; | 
 | 	default: | 
 | 		dev_err(dapm->dev, "Setting invalid bias %d\n", level); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	trace_snd_soc_bias_level_start(card, level); | 
 |  | 
 | 	if (card && card->set_bias_level) | 
 | 		ret = card->set_bias_level(card, level); | 
 | 	if (ret == 0) { | 
 | 		if (dapm->codec && dapm->codec->driver->set_bias_level) | 
 | 			ret = dapm->codec->driver->set_bias_level(dapm->codec, level); | 
 | 		else | 
 | 			dapm->bias_level = level; | 
 | 	} | 
 | 	if (ret == 0) { | 
 | 		if (card && card->set_bias_level_post) | 
 | 			ret = card->set_bias_level_post(card, level); | 
 | 	} | 
 |  | 
 | 	trace_snd_soc_bias_level_done(card, level); | 
 |  | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* set up initial codec paths */ | 
 | static void dapm_set_path_status(struct snd_soc_dapm_widget *w, | 
 | 	struct snd_soc_dapm_path *p, int i) | 
 | { | 
 | 	switch (w->id) { | 
 | 	case snd_soc_dapm_switch: | 
 | 	case snd_soc_dapm_mixer: | 
 | 	case snd_soc_dapm_mixer_named_ctl: { | 
 | 		int val; | 
 | 		struct soc_mixer_control *mc = (struct soc_mixer_control *) | 
 | 			w->kcontrol_news[i].private_value; | 
 | 		unsigned int reg = mc->reg; | 
 | 		unsigned int shift = mc->shift; | 
 | 		int max = mc->max; | 
 | 		unsigned int mask = (1 << fls(max)) - 1; | 
 | 		unsigned int invert = mc->invert; | 
 |  | 
 | 		val = snd_soc_read(w->codec, reg); | 
 | 		val = (val >> shift) & mask; | 
 |  | 
 | 		if ((invert && !val) || (!invert && val)) | 
 | 			p->connect = 1; | 
 | 		else | 
 | 			p->connect = 0; | 
 | 	} | 
 | 	break; | 
 | 	case snd_soc_dapm_mux: { | 
 | 		struct soc_enum *e = (struct soc_enum *) | 
 | 			w->kcontrol_news[i].private_value; | 
 | 		int val, item, bitmask; | 
 |  | 
 | 		for (bitmask = 1; bitmask < e->max; bitmask <<= 1) | 
 | 		; | 
 | 		val = snd_soc_read(w->codec, e->reg); | 
 | 		item = (val >> e->shift_l) & (bitmask - 1); | 
 |  | 
 | 		p->connect = 0; | 
 | 		for (i = 0; i < e->max; i++) { | 
 | 			if (!(strcmp(p->name, e->texts[i])) && item == i) | 
 | 				p->connect = 1; | 
 | 		} | 
 | 	} | 
 | 	break; | 
 | 	case snd_soc_dapm_virt_mux: { | 
 | 		struct soc_enum *e = (struct soc_enum *) | 
 | 			w->kcontrol_news[i].private_value; | 
 |  | 
 | 		p->connect = 0; | 
 | 		/* since a virtual mux has no backing registers to | 
 | 		 * decide which path to connect, it will try to match | 
 | 		 * with the first enumeration.  This is to ensure | 
 | 		 * that the default mux choice (the first) will be | 
 | 		 * correctly powered up during initialization. | 
 | 		 */ | 
 | 		if (!strcmp(p->name, e->texts[0])) | 
 | 			p->connect = 1; | 
 | 	} | 
 | 	break; | 
 | 	case snd_soc_dapm_value_mux: { | 
 | 		struct soc_enum *e = (struct soc_enum *) | 
 | 			w->kcontrol_news[i].private_value; | 
 | 		int val, item; | 
 |  | 
 | 		val = snd_soc_read(w->codec, e->reg); | 
 | 		val = (val >> e->shift_l) & e->mask; | 
 | 		for (item = 0; item < e->max; item++) { | 
 | 			if (val == e->values[item]) | 
 | 				break; | 
 | 		} | 
 |  | 
 | 		p->connect = 0; | 
 | 		for (i = 0; i < e->max; i++) { | 
 | 			if (!(strcmp(p->name, e->texts[i])) && item == i) | 
 | 				p->connect = 1; | 
 | 		} | 
 | 	} | 
 | 	break; | 
 | 	/* does not effect routing - always connected */ | 
 | 	case snd_soc_dapm_pga: | 
 | 	case snd_soc_dapm_out_drv: | 
 | 	case snd_soc_dapm_output: | 
 | 	case snd_soc_dapm_adc: | 
 | 	case snd_soc_dapm_input: | 
 | 	case snd_soc_dapm_dac: | 
 | 	case snd_soc_dapm_micbias: | 
 | 	case snd_soc_dapm_vmid: | 
 | 	case snd_soc_dapm_supply: | 
 | 	case snd_soc_dapm_aif_in: | 
 | 	case snd_soc_dapm_aif_out: | 
 | 		p->connect = 1; | 
 | 	break; | 
 | 	/* does effect routing - dynamically connected */ | 
 | 	case snd_soc_dapm_hp: | 
 | 	case snd_soc_dapm_mic: | 
 | 	case snd_soc_dapm_spk: | 
 | 	case snd_soc_dapm_line: | 
 | 	case snd_soc_dapm_pre: | 
 | 	case snd_soc_dapm_post: | 
 | 		p->connect = 0; | 
 | 	break; | 
 | 	} | 
 | } | 
 |  | 
 | /* connect mux widget to its interconnecting audio paths */ | 
 | static int dapm_connect_mux(struct snd_soc_dapm_context *dapm, | 
 | 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 
 | 	struct snd_soc_dapm_path *path, const char *control_name, | 
 | 	const struct snd_kcontrol_new *kcontrol) | 
 | { | 
 | 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 
 | 	int i; | 
 |  | 
 | 	for (i = 0; i < e->max; i++) { | 
 | 		if (!(strcmp(control_name, e->texts[i]))) { | 
 | 			list_add(&path->list, &dapm->card->paths); | 
 | 			list_add(&path->list_sink, &dest->sources); | 
 | 			list_add(&path->list_source, &src->sinks); | 
 | 			path->name = (char*)e->texts[i]; | 
 | 			dapm_set_path_status(dest, path, 0); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return -ENODEV; | 
 | } | 
 |  | 
 | /* connect mixer widget to its interconnecting audio paths */ | 
 | static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm, | 
 | 	struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest, | 
 | 	struct snd_soc_dapm_path *path, const char *control_name) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	/* search for mixer kcontrol */ | 
 | 	for (i = 0; i < dest->num_kcontrols; i++) { | 
 | 		if (!strcmp(control_name, dest->kcontrol_news[i].name)) { | 
 | 			list_add(&path->list, &dapm->card->paths); | 
 | 			list_add(&path->list_sink, &dest->sources); | 
 | 			list_add(&path->list_source, &src->sinks); | 
 | 			path->name = dest->kcontrol_news[i].name; | 
 | 			dapm_set_path_status(dest, path, i); | 
 | 			return 0; | 
 | 		} | 
 | 	} | 
 | 	return -ENODEV; | 
 | } | 
 |  | 
 | static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm, | 
 | 	struct snd_soc_dapm_widget *kcontrolw, | 
 | 	const struct snd_kcontrol_new *kcontrol_new, | 
 | 	struct snd_kcontrol **kcontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	int i; | 
 |  | 
 | 	*kcontrol = NULL; | 
 |  | 
 | 	list_for_each_entry(w, &dapm->card->widgets, list) { | 
 | 		if (w == kcontrolw || w->dapm != kcontrolw->dapm) | 
 | 			continue; | 
 | 		for (i = 0; i < w->num_kcontrols; i++) { | 
 | 			if (&w->kcontrol_news[i] == kcontrol_new) { | 
 | 				if (w->kcontrols) | 
 | 					*kcontrol = w->kcontrols[i]; | 
 | 				return 1; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* create new dapm mixer control */ | 
 | static int dapm_new_mixer(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	struct snd_soc_dapm_context *dapm = w->dapm; | 
 | 	int i, ret = 0; | 
 | 	size_t name_len, prefix_len; | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	struct snd_card *card = dapm->card->snd_card; | 
 | 	const char *prefix; | 
 | 	struct snd_soc_dapm_widget_list *wlist; | 
 | 	size_t wlistsize; | 
 |  | 
 | 	if (dapm->codec) | 
 | 		prefix = dapm->codec->name_prefix; | 
 | 	else | 
 | 		prefix = NULL; | 
 |  | 
 | 	if (prefix) | 
 | 		prefix_len = strlen(prefix) + 1; | 
 | 	else | 
 | 		prefix_len = 0; | 
 |  | 
 | 	/* add kcontrol */ | 
 | 	for (i = 0; i < w->num_kcontrols; i++) { | 
 |  | 
 | 		/* match name */ | 
 | 		list_for_each_entry(path, &w->sources, list_sink) { | 
 |  | 
 | 			/* mixer/mux paths name must match control name */ | 
 | 			if (path->name != (char *)w->kcontrol_news[i].name) | 
 | 				continue; | 
 |  | 
 | 			wlistsize = sizeof(struct snd_soc_dapm_widget_list) + | 
 | 				    sizeof(struct snd_soc_dapm_widget *), | 
 | 			wlist = kzalloc(wlistsize, GFP_KERNEL); | 
 | 			if (wlist == NULL) { | 
 | 				dev_err(dapm->dev, | 
 | 					"asoc: can't allocate widget list for %s\n", | 
 | 					w->name); | 
 | 				return -ENOMEM; | 
 | 			} | 
 | 			wlist->num_widgets = 1; | 
 | 			wlist->widgets[0] = w; | 
 |  | 
 | 			/* add dapm control with long name. | 
 | 			 * for dapm_mixer this is the concatenation of the | 
 | 			 * mixer and kcontrol name. | 
 | 			 * for dapm_mixer_named_ctl this is simply the | 
 | 			 * kcontrol name. | 
 | 			 */ | 
 | 			name_len = strlen(w->kcontrol_news[i].name) + 1; | 
 | 			if (w->id != snd_soc_dapm_mixer_named_ctl) | 
 | 				name_len += 1 + strlen(w->name); | 
 |  | 
 | 			path->long_name = kmalloc(name_len, GFP_KERNEL); | 
 |  | 
 | 			if (path->long_name == NULL) { | 
 | 				kfree(wlist); | 
 | 				return -ENOMEM; | 
 | 			} | 
 |  | 
 | 			switch (w->id) { | 
 | 			default: | 
 | 				/* The control will get a prefix from | 
 | 				 * the control creation process but | 
 | 				 * we're also using the same prefix | 
 | 				 * for widgets so cut the prefix off | 
 | 				 * the front of the widget name. | 
 | 				 */ | 
 | 				snprintf(path->long_name, name_len, "%s %s", | 
 | 					 w->name + prefix_len, | 
 | 					 w->kcontrol_news[i].name); | 
 | 				break; | 
 | 			case snd_soc_dapm_mixer_named_ctl: | 
 | 				snprintf(path->long_name, name_len, "%s", | 
 | 					 w->kcontrol_news[i].name); | 
 | 				break; | 
 | 			} | 
 |  | 
 | 			path->long_name[name_len - 1] = '\0'; | 
 |  | 
 | 			path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i], | 
 | 						      wlist, path->long_name, | 
 | 						      prefix); | 
 | 			ret = snd_ctl_add(card, path->kcontrol); | 
 | 			if (ret < 0) { | 
 | 				dev_err(dapm->dev, | 
 | 					"asoc: failed to add dapm kcontrol %s: %d\n", | 
 | 					path->long_name, ret); | 
 | 				kfree(wlist); | 
 | 				kfree(path->long_name); | 
 | 				path->long_name = NULL; | 
 | 				return ret; | 
 | 			} | 
 | 			w->kcontrols[i] = path->kcontrol; | 
 | 		} | 
 | 	} | 
 | 	return ret; | 
 | } | 
 |  | 
 | /* create new dapm mux control */ | 
 | static int dapm_new_mux(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	struct snd_soc_dapm_context *dapm = w->dapm; | 
 | 	struct snd_soc_dapm_path *path = NULL; | 
 | 	struct snd_kcontrol *kcontrol; | 
 | 	struct snd_card *card = dapm->card->snd_card; | 
 | 	const char *prefix; | 
 | 	size_t prefix_len; | 
 | 	int ret; | 
 | 	struct snd_soc_dapm_widget_list *wlist; | 
 | 	int shared, wlistentries; | 
 | 	size_t wlistsize; | 
 | 	char *name; | 
 |  | 
 | 	if (w->num_kcontrols != 1) { | 
 | 		dev_err(dapm->dev, | 
 | 			"asoc: mux %s has incorrect number of controls\n", | 
 | 			w->name); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[0], | 
 | 					 &kcontrol); | 
 | 	if (kcontrol) { | 
 | 		wlist = kcontrol->private_data; | 
 | 		wlistentries = wlist->num_widgets + 1; | 
 | 	} else { | 
 | 		wlist = NULL; | 
 | 		wlistentries = 1; | 
 | 	} | 
 | 	wlistsize = sizeof(struct snd_soc_dapm_widget_list) + | 
 | 		wlistentries * sizeof(struct snd_soc_dapm_widget *), | 
 | 	wlist = krealloc(wlist, wlistsize, GFP_KERNEL); | 
 | 	if (wlist == NULL) { | 
 | 		dev_err(dapm->dev, | 
 | 			"asoc: can't allocate widget list for %s\n", w->name); | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	wlist->num_widgets = wlistentries; | 
 | 	wlist->widgets[wlistentries - 1] = w; | 
 |  | 
 | 	if (!kcontrol) { | 
 | 		if (dapm->codec) | 
 | 			prefix = dapm->codec->name_prefix; | 
 | 		else | 
 | 			prefix = NULL; | 
 |  | 
 | 		if (shared) { | 
 | 			name = w->kcontrol_news[0].name; | 
 | 			prefix_len = 0; | 
 | 		} else { | 
 | 			name = w->name; | 
 | 			if (prefix) | 
 | 				prefix_len = strlen(prefix) + 1; | 
 | 			else | 
 | 				prefix_len = 0; | 
 | 		} | 
 |  | 
 | 		/* | 
 | 		 * The control will get a prefix from the control creation | 
 | 		 * process but we're also using the same prefix for widgets so | 
 | 		 * cut the prefix off the front of the widget name. | 
 | 		 */ | 
 | 		kcontrol = snd_soc_cnew(&w->kcontrol_news[0], wlist, | 
 | 					name + prefix_len, prefix); | 
 | 		ret = snd_ctl_add(card, kcontrol); | 
 | 		if (ret < 0) { | 
 | 			dev_err(dapm->dev, | 
 | 				"asoc: failed to add kcontrol %s\n", w->name); | 
 | 			kfree(wlist); | 
 | 			return ret; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	kcontrol->private_data = wlist; | 
 |  | 
 | 	w->kcontrols[0] = kcontrol; | 
 |  | 
 | 	list_for_each_entry(path, &w->sources, list_sink) | 
 | 		path->kcontrol = kcontrol; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* create new dapm volume control */ | 
 | static int dapm_new_pga(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	if (w->num_kcontrols) | 
 | 		dev_err(w->dapm->dev, | 
 | 			"asoc: PGA controls not supported: '%s'\n", w->name); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* reset 'walked' bit for each dapm path */ | 
 | static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	struct snd_soc_dapm_path *p; | 
 |  | 
 | 	list_for_each_entry(p, &dapm->card->paths, list) | 
 | 		p->walked = 0; | 
 | } | 
 |  | 
 | /* We implement power down on suspend by checking the power state of | 
 |  * the ALSA card - when we are suspending the ALSA state for the card | 
 |  * is set to D3. | 
 |  */ | 
 | static int snd_soc_dapm_suspend_check(struct snd_soc_dapm_widget *widget) | 
 | { | 
 | 	int level = snd_power_get_state(widget->dapm->card->snd_card); | 
 |  | 
 | 	switch (level) { | 
 | 	case SNDRV_CTL_POWER_D3hot: | 
 | 	case SNDRV_CTL_POWER_D3cold: | 
 | 		if (widget->ignore_suspend) | 
 | 			dev_dbg(widget->dapm->dev, "%s ignoring suspend\n", | 
 | 				widget->name); | 
 | 		return widget->ignore_suspend; | 
 | 	default: | 
 | 		return 1; | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * Recursively check for a completed path to an active or physically connected | 
 |  * output widget. Returns number of complete paths. | 
 |  */ | 
 | static int is_connected_output_ep(struct snd_soc_dapm_widget *widget) | 
 | { | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	int con = 0; | 
 |  | 
 | 	if (widget->id == snd_soc_dapm_supply) | 
 | 		return 0; | 
 |  | 
 | 	switch (widget->id) { | 
 | 	case snd_soc_dapm_adc: | 
 | 	case snd_soc_dapm_aif_out: | 
 | 		if (widget->active) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (widget->connected) { | 
 | 		/* connected pin ? */ | 
 | 		if (widget->id == snd_soc_dapm_output && !widget->ext) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 |  | 
 | 		/* connected jack or spk ? */ | 
 | 		if (widget->id == snd_soc_dapm_hp || widget->id == snd_soc_dapm_spk || | 
 | 		    (widget->id == snd_soc_dapm_line && !list_empty(&widget->sources))) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 | 	} | 
 |  | 
 | 	list_for_each_entry(path, &widget->sinks, list_source) { | 
 | 		if (path->walked) | 
 | 			continue; | 
 |  | 
 | 		if (path->sink && path->connect) { | 
 | 			path->walked = 1; | 
 | 			con += is_connected_output_ep(path->sink); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return con; | 
 | } | 
 |  | 
 | /* | 
 |  * Recursively check for a completed path to an active or physically connected | 
 |  * input widget. Returns number of complete paths. | 
 |  */ | 
 | static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) | 
 | { | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	int con = 0; | 
 |  | 
 | 	if (widget->id == snd_soc_dapm_supply) | 
 | 		return 0; | 
 |  | 
 | 	/* active stream ? */ | 
 | 	switch (widget->id) { | 
 | 	case snd_soc_dapm_dac: | 
 | 	case snd_soc_dapm_aif_in: | 
 | 		if (widget->active) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 | 	default: | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (widget->connected) { | 
 | 		/* connected pin ? */ | 
 | 		if (widget->id == snd_soc_dapm_input && !widget->ext) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 |  | 
 | 		/* connected VMID/Bias for lower pops */ | 
 | 		if (widget->id == snd_soc_dapm_vmid) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 |  | 
 | 		/* connected jack ? */ | 
 | 		if (widget->id == snd_soc_dapm_mic || | 
 | 		    (widget->id == snd_soc_dapm_line && !list_empty(&widget->sinks))) | 
 | 			return snd_soc_dapm_suspend_check(widget); | 
 | 	} | 
 |  | 
 | 	list_for_each_entry(path, &widget->sources, list_sink) { | 
 | 		if (path->walked) | 
 | 			continue; | 
 |  | 
 | 		if (path->source && path->connect) { | 
 | 			path->walked = 1; | 
 | 			con += is_connected_input_ep(path->source); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return con; | 
 | } | 
 |  | 
 | /* | 
 |  * Handler for generic register modifier widget. | 
 |  */ | 
 | int dapm_reg_event(struct snd_soc_dapm_widget *w, | 
 | 		   struct snd_kcontrol *kcontrol, int event) | 
 | { | 
 | 	unsigned int val; | 
 |  | 
 | 	if (SND_SOC_DAPM_EVENT_ON(event)) | 
 | 		val = w->on_val; | 
 | 	else | 
 | 		val = w->off_val; | 
 |  | 
 | 	snd_soc_update_bits(w->codec, -(w->reg + 1), | 
 | 			    w->mask << w->shift, val << w->shift); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(dapm_reg_event); | 
 |  | 
 | /* Generic check to see if a widget should be powered. | 
 |  */ | 
 | static int dapm_generic_check_power(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	int in, out; | 
 |  | 
 | 	in = is_connected_input_ep(w); | 
 | 	dapm_clear_walk(w->dapm); | 
 | 	out = is_connected_output_ep(w); | 
 | 	dapm_clear_walk(w->dapm); | 
 | 	return out != 0 && in != 0; | 
 | } | 
 |  | 
 | /* Check to see if an ADC has power */ | 
 | static int dapm_adc_check_power(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	int in; | 
 |  | 
 | 	if (w->active) { | 
 | 		in = is_connected_input_ep(w); | 
 | 		dapm_clear_walk(w->dapm); | 
 | 		return in != 0; | 
 | 	} else { | 
 | 		return dapm_generic_check_power(w); | 
 | 	} | 
 | } | 
 |  | 
 | /* Check to see if a DAC has power */ | 
 | static int dapm_dac_check_power(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	int out; | 
 |  | 
 | 	if (w->active) { | 
 | 		out = is_connected_output_ep(w); | 
 | 		dapm_clear_walk(w->dapm); | 
 | 		return out != 0; | 
 | 	} else { | 
 | 		return dapm_generic_check_power(w); | 
 | 	} | 
 | } | 
 |  | 
 | /* Check to see if a power supply is needed */ | 
 | static int dapm_supply_check_power(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	int power = 0; | 
 |  | 
 | 	/* Check if one of our outputs is connected */ | 
 | 	list_for_each_entry(path, &w->sinks, list_source) { | 
 | 		if (path->connected && | 
 | 		    !path->connected(path->source, path->sink)) | 
 | 			continue; | 
 |  | 
 | 		if (!path->sink) | 
 | 			continue; | 
 |  | 
 | 		if (path->sink->force) { | 
 | 			power = 1; | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		if (path->sink->power_check && | 
 | 		    path->sink->power_check(path->sink)) { | 
 | 			power = 1; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	dapm_clear_walk(w->dapm); | 
 |  | 
 | 	return power; | 
 | } | 
 |  | 
 | static int dapm_seq_compare(struct snd_soc_dapm_widget *a, | 
 | 			    struct snd_soc_dapm_widget *b, | 
 | 			    bool power_up) | 
 | { | 
 | 	int *sort; | 
 |  | 
 | 	if (power_up) | 
 | 		sort = dapm_up_seq; | 
 | 	else | 
 | 		sort = dapm_down_seq; | 
 |  | 
 | 	if (sort[a->id] != sort[b->id]) | 
 | 		return sort[a->id] - sort[b->id]; | 
 | 	if (a->subseq != b->subseq) { | 
 | 		if (power_up) | 
 | 			return a->subseq - b->subseq; | 
 | 		else | 
 | 			return b->subseq - a->subseq; | 
 | 	} | 
 | 	if (a->reg != b->reg) | 
 | 		return a->reg - b->reg; | 
 | 	if (a->dapm != b->dapm) | 
 | 		return (unsigned long)a->dapm - (unsigned long)b->dapm; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* Insert a widget in order into a DAPM power sequence. */ | 
 | static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget, | 
 | 			    struct list_head *list, | 
 | 			    bool power_up) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 |  | 
 | 	list_for_each_entry(w, list, power_list) | 
 | 		if (dapm_seq_compare(new_widget, w, power_up) < 0) { | 
 | 			list_add_tail(&new_widget->power_list, &w->power_list); | 
 | 			return; | 
 | 		} | 
 |  | 
 | 	list_add_tail(&new_widget->power_list, list); | 
 | } | 
 |  | 
 | static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm, | 
 | 				 struct snd_soc_dapm_widget *w, int event) | 
 | { | 
 | 	struct snd_soc_card *card = dapm->card; | 
 | 	const char *ev_name; | 
 | 	int power, ret; | 
 |  | 
 | 	switch (event) { | 
 | 	case SND_SOC_DAPM_PRE_PMU: | 
 | 		ev_name = "PRE_PMU"; | 
 | 		power = 1; | 
 | 		break; | 
 | 	case SND_SOC_DAPM_POST_PMU: | 
 | 		ev_name = "POST_PMU"; | 
 | 		power = 1; | 
 | 		break; | 
 | 	case SND_SOC_DAPM_PRE_PMD: | 
 | 		ev_name = "PRE_PMD"; | 
 | 		power = 0; | 
 | 		break; | 
 | 	case SND_SOC_DAPM_POST_PMD: | 
 | 		ev_name = "POST_PMD"; | 
 | 		power = 0; | 
 | 		break; | 
 | 	default: | 
 | 		BUG(); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	if (w->power != power) | 
 | 		return; | 
 |  | 
 | 	if (w->event && (w->event_flags & event)) { | 
 | 		pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n", | 
 | 			w->name, ev_name); | 
 | 		trace_snd_soc_dapm_widget_event_start(w, event); | 
 | 		ret = w->event(w, NULL, event); | 
 | 		trace_snd_soc_dapm_widget_event_done(w, event); | 
 | 		if (ret < 0) | 
 | 			pr_err("%s: %s event failed: %d\n", | 
 | 			       ev_name, w->name, ret); | 
 | 	} | 
 | } | 
 |  | 
 | /* Apply the coalesced changes from a DAPM sequence */ | 
 | static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm, | 
 | 				   struct list_head *pending) | 
 | { | 
 | 	struct snd_soc_card *card = dapm->card; | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	int reg, power; | 
 | 	unsigned int value = 0; | 
 | 	unsigned int mask = 0; | 
 | 	unsigned int cur_mask; | 
 |  | 
 | 	reg = list_first_entry(pending, struct snd_soc_dapm_widget, | 
 | 			       power_list)->reg; | 
 |  | 
 | 	list_for_each_entry(w, pending, power_list) { | 
 | 		cur_mask = 1 << w->shift; | 
 | 		BUG_ON(reg != w->reg); | 
 |  | 
 | 		if (w->invert) | 
 | 			power = !w->power; | 
 | 		else | 
 | 			power = w->power; | 
 |  | 
 | 		mask |= cur_mask; | 
 | 		if (power) | 
 | 			value |= cur_mask; | 
 |  | 
 | 		pop_dbg(dapm->dev, card->pop_time, | 
 | 			"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n", | 
 | 			w->name, reg, value, mask); | 
 |  | 
 | 		/* Check for events */ | 
 | 		dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU); | 
 | 		dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD); | 
 | 	} | 
 |  | 
 | 	if (reg >= 0) { | 
 | 		pop_dbg(dapm->dev, card->pop_time, | 
 | 			"pop test : Applying 0x%x/0x%x to %x in %dms\n", | 
 | 			value, mask, reg, card->pop_time); | 
 | 		pop_wait(card->pop_time); | 
 | 		snd_soc_update_bits(dapm->codec, reg, mask, value); | 
 | 	} | 
 |  | 
 | 	list_for_each_entry(w, pending, power_list) { | 
 | 		dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU); | 
 | 		dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD); | 
 | 	} | 
 | } | 
 |  | 
 | /* Apply a DAPM power sequence. | 
 |  * | 
 |  * We walk over a pre-sorted list of widgets to apply power to.  In | 
 |  * order to minimise the number of writes to the device required | 
 |  * multiple widgets will be updated in a single write where possible. | 
 |  * Currently anything that requires more than a single write is not | 
 |  * handled. | 
 |  */ | 
 | static void dapm_seq_run(struct snd_soc_dapm_context *dapm, | 
 | 			 struct list_head *list, int event, bool power_up) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w, *n; | 
 | 	LIST_HEAD(pending); | 
 | 	int cur_sort = -1; | 
 | 	int cur_subseq = -1; | 
 | 	int cur_reg = SND_SOC_NOPM; | 
 | 	struct snd_soc_dapm_context *cur_dapm = NULL; | 
 | 	int ret, i; | 
 | 	int *sort; | 
 |  | 
 | 	if (power_up) | 
 | 		sort = dapm_up_seq; | 
 | 	else | 
 | 		sort = dapm_down_seq; | 
 |  | 
 | 	list_for_each_entry_safe(w, n, list, power_list) { | 
 | 		ret = 0; | 
 |  | 
 | 		/* Do we need to apply any queued changes? */ | 
 | 		if (sort[w->id] != cur_sort || w->reg != cur_reg || | 
 | 		    w->dapm != cur_dapm || w->subseq != cur_subseq) { | 
 | 			if (!list_empty(&pending)) | 
 | 				dapm_seq_run_coalesced(cur_dapm, &pending); | 
 |  | 
 | 			if (cur_dapm && cur_dapm->seq_notifier) { | 
 | 				for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) | 
 | 					if (sort[i] == cur_sort) | 
 | 						cur_dapm->seq_notifier(cur_dapm, | 
 | 								       i, | 
 | 								       cur_subseq); | 
 | 			} | 
 |  | 
 | 			INIT_LIST_HEAD(&pending); | 
 | 			cur_sort = -1; | 
 | 			cur_subseq = -1; | 
 | 			cur_reg = SND_SOC_NOPM; | 
 | 			cur_dapm = NULL; | 
 | 		} | 
 |  | 
 | 		switch (w->id) { | 
 | 		case snd_soc_dapm_pre: | 
 | 			if (!w->event) | 
 | 				list_for_each_entry_safe_continue(w, n, list, | 
 | 								  power_list); | 
 |  | 
 | 			if (event == SND_SOC_DAPM_STREAM_START) | 
 | 				ret = w->event(w, | 
 | 					       NULL, SND_SOC_DAPM_PRE_PMU); | 
 | 			else if (event == SND_SOC_DAPM_STREAM_STOP) | 
 | 				ret = w->event(w, | 
 | 					       NULL, SND_SOC_DAPM_PRE_PMD); | 
 | 			break; | 
 |  | 
 | 		case snd_soc_dapm_post: | 
 | 			if (!w->event) | 
 | 				list_for_each_entry_safe_continue(w, n, list, | 
 | 								  power_list); | 
 |  | 
 | 			if (event == SND_SOC_DAPM_STREAM_START) | 
 | 				ret = w->event(w, | 
 | 					       NULL, SND_SOC_DAPM_POST_PMU); | 
 | 			else if (event == SND_SOC_DAPM_STREAM_STOP) | 
 | 				ret = w->event(w, | 
 | 					       NULL, SND_SOC_DAPM_POST_PMD); | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			/* Queue it up for application */ | 
 | 			cur_sort = sort[w->id]; | 
 | 			cur_subseq = w->subseq; | 
 | 			cur_reg = w->reg; | 
 | 			cur_dapm = w->dapm; | 
 | 			list_move(&w->power_list, &pending); | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		if (ret < 0) | 
 | 			dev_err(w->dapm->dev, | 
 | 				"Failed to apply widget power: %d\n", ret); | 
 | 	} | 
 |  | 
 | 	if (!list_empty(&pending)) | 
 | 		dapm_seq_run_coalesced(cur_dapm, &pending); | 
 |  | 
 | 	if (cur_dapm && cur_dapm->seq_notifier) { | 
 | 		for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) | 
 | 			if (sort[i] == cur_sort) | 
 | 				cur_dapm->seq_notifier(cur_dapm, | 
 | 						       i, cur_subseq); | 
 | 	} | 
 | } | 
 |  | 
 | static void dapm_widget_update(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	struct snd_soc_dapm_update *update = dapm->update; | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	int ret; | 
 |  | 
 | 	if (!update) | 
 | 		return; | 
 |  | 
 | 	w = update->widget; | 
 |  | 
 | 	if (w->event && | 
 | 	    (w->event_flags & SND_SOC_DAPM_PRE_REG)) { | 
 | 		ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG); | 
 | 		if (ret != 0) | 
 | 			pr_err("%s DAPM pre-event failed: %d\n", | 
 | 			       w->name, ret); | 
 | 	} | 
 |  | 
 | 	ret = snd_soc_update_bits(w->codec, update->reg, update->mask, | 
 | 				  update->val); | 
 | 	if (ret < 0) | 
 | 		pr_err("%s DAPM update failed: %d\n", w->name, ret); | 
 |  | 
 | 	if (w->event && | 
 | 	    (w->event_flags & SND_SOC_DAPM_POST_REG)) { | 
 | 		ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG); | 
 | 		if (ret != 0) | 
 | 			pr_err("%s DAPM post-event failed: %d\n", | 
 | 			       w->name, ret); | 
 | 	} | 
 | } | 
 |  | 
 | /* Async callback run prior to DAPM sequences - brings to _PREPARE if | 
 |  * they're changing state. | 
 |  */ | 
 | static void dapm_pre_sequence_async(void *data, async_cookie_t cookie) | 
 | { | 
 | 	struct snd_soc_dapm_context *d = data; | 
 | 	int ret; | 
 |  | 
 | 	if (d->dev_power && d->bias_level == SND_SOC_BIAS_OFF) { | 
 | 		ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); | 
 | 		if (ret != 0) | 
 | 			dev_err(d->dev, | 
 | 				"Failed to turn on bias: %d\n", ret); | 
 | 	} | 
 |  | 
 | 	/* If we're changing to all on or all off then prepare */ | 
 | 	if ((d->dev_power && d->bias_level == SND_SOC_BIAS_STANDBY) || | 
 | 	    (!d->dev_power && d->bias_level == SND_SOC_BIAS_ON)) { | 
 | 		ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE); | 
 | 		if (ret != 0) | 
 | 			dev_err(d->dev, | 
 | 				"Failed to prepare bias: %d\n", ret); | 
 | 	} | 
 | } | 
 |  | 
 | /* Async callback run prior to DAPM sequences - brings to their final | 
 |  * state. | 
 |  */ | 
 | static void dapm_post_sequence_async(void *data, async_cookie_t cookie) | 
 | { | 
 | 	struct snd_soc_dapm_context *d = data; | 
 | 	int ret; | 
 |  | 
 | 	/* If we just powered the last thing off drop to standby bias */ | 
 | 	if (d->bias_level == SND_SOC_BIAS_PREPARE && !d->dev_power) { | 
 | 		ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY); | 
 | 		if (ret != 0) | 
 | 			dev_err(d->dev, "Failed to apply standby bias: %d\n", | 
 | 				ret); | 
 | 	} | 
 |  | 
 | 	/* If we're in standby and can support bias off then do that */ | 
 | 	if (d->bias_level == SND_SOC_BIAS_STANDBY && d->idle_bias_off) { | 
 | 		ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF); | 
 | 		if (ret != 0) | 
 | 			dev_err(d->dev, "Failed to turn off bias: %d\n", ret); | 
 | 	} | 
 |  | 
 | 	/* If we just powered up then move to active bias */ | 
 | 	if (d->bias_level == SND_SOC_BIAS_PREPARE && d->dev_power) { | 
 | 		ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON); | 
 | 		if (ret != 0) | 
 | 			dev_err(d->dev, "Failed to apply active bias: %d\n", | 
 | 				ret); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * Scan each dapm widget for complete audio path. | 
 |  * A complete path is a route that has valid endpoints i.e.:- | 
 |  * | 
 |  *  o DAC to output pin. | 
 |  *  o Input Pin to ADC. | 
 |  *  o Input pin to Output pin (bypass, sidetone) | 
 |  *  o DAC to ADC (loopback). | 
 |  */ | 
 | static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event) | 
 | { | 
 | 	struct snd_soc_card *card = dapm->card; | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	struct snd_soc_dapm_context *d; | 
 | 	LIST_HEAD(up_list); | 
 | 	LIST_HEAD(down_list); | 
 | 	LIST_HEAD(async_domain); | 
 | 	int power; | 
 |  | 
 | 	trace_snd_soc_dapm_start(card); | 
 |  | 
 | 	list_for_each_entry(d, &card->dapm_list, list) | 
 | 		if (d->n_widgets || d->codec == NULL) | 
 | 			d->dev_power = 0; | 
 |  | 
 | 	/* Check which widgets we need to power and store them in | 
 | 	 * lists indicating if they should be powered up or down. | 
 | 	 */ | 
 | 	list_for_each_entry(w, &card->widgets, list) { | 
 | 		switch (w->id) { | 
 | 		case snd_soc_dapm_pre: | 
 | 			dapm_seq_insert(w, &down_list, false); | 
 | 			break; | 
 | 		case snd_soc_dapm_post: | 
 | 			dapm_seq_insert(w, &up_list, true); | 
 | 			break; | 
 |  | 
 | 		default: | 
 | 			if (!w->power_check) | 
 | 				continue; | 
 |  | 
 | 			if (!w->force) | 
 | 				power = w->power_check(w); | 
 | 			else | 
 | 				power = 1; | 
 | 			if (power) | 
 | 				w->dapm->dev_power = 1; | 
 |  | 
 | 			if (w->power == power) | 
 | 				continue; | 
 |  | 
 | 			trace_snd_soc_dapm_widget_power(w, power); | 
 |  | 
 | 			if (power) | 
 | 				dapm_seq_insert(w, &up_list, true); | 
 | 			else | 
 | 				dapm_seq_insert(w, &down_list, false); | 
 |  | 
 | 			w->power = power; | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* If there are no DAPM widgets then try to figure out power from the | 
 | 	 * event type. | 
 | 	 */ | 
 | 	if (!dapm->n_widgets) { | 
 | 		switch (event) { | 
 | 		case SND_SOC_DAPM_STREAM_START: | 
 | 		case SND_SOC_DAPM_STREAM_RESUME: | 
 | 			dapm->dev_power = 1; | 
 | 			break; | 
 | 		case SND_SOC_DAPM_STREAM_STOP: | 
 | 			dapm->dev_power = !!dapm->codec->active; | 
 | 			break; | 
 | 		case SND_SOC_DAPM_STREAM_SUSPEND: | 
 | 			dapm->dev_power = 0; | 
 | 			break; | 
 | 		case SND_SOC_DAPM_STREAM_NOP: | 
 | 			switch (dapm->bias_level) { | 
 | 				case SND_SOC_BIAS_STANDBY: | 
 | 				case SND_SOC_BIAS_OFF: | 
 | 					dapm->dev_power = 0; | 
 | 					break; | 
 | 				default: | 
 | 					dapm->dev_power = 1; | 
 | 					break; | 
 | 			} | 
 | 			break; | 
 | 		default: | 
 | 			break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Force all contexts in the card to the same bias state */ | 
 | 	power = 0; | 
 | 	list_for_each_entry(d, &card->dapm_list, list) | 
 | 		if (d->dev_power) | 
 | 			power = 1; | 
 | 	list_for_each_entry(d, &card->dapm_list, list) | 
 | 		d->dev_power = power; | 
 |  | 
 |  | 
 | 	/* Run all the bias changes in parallel */ | 
 | 	list_for_each_entry(d, &dapm->card->dapm_list, list) | 
 | 		async_schedule_domain(dapm_pre_sequence_async, d, | 
 | 					&async_domain); | 
 | 	async_synchronize_full_domain(&async_domain); | 
 |  | 
 | 	/* Power down widgets first; try to avoid amplifying pops. */ | 
 | 	dapm_seq_run(dapm, &down_list, event, false); | 
 |  | 
 | 	dapm_widget_update(dapm); | 
 |  | 
 | 	/* Now power up. */ | 
 | 	dapm_seq_run(dapm, &up_list, event, true); | 
 |  | 
 | 	/* Run all the bias changes in parallel */ | 
 | 	list_for_each_entry(d, &dapm->card->dapm_list, list) | 
 | 		async_schedule_domain(dapm_post_sequence_async, d, | 
 | 					&async_domain); | 
 | 	async_synchronize_full_domain(&async_domain); | 
 |  | 
 | 	pop_dbg(dapm->dev, card->pop_time, | 
 | 		"DAPM sequencing finished, waiting %dms\n", card->pop_time); | 
 | 	pop_wait(card->pop_time); | 
 |  | 
 | 	trace_snd_soc_dapm_done(card); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #ifdef CONFIG_DEBUG_FS | 
 | static int dapm_widget_power_open_file(struct inode *inode, struct file *file) | 
 | { | 
 | 	file->private_data = inode->i_private; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static ssize_t dapm_widget_power_read_file(struct file *file, | 
 | 					   char __user *user_buf, | 
 | 					   size_t count, loff_t *ppos) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w = file->private_data; | 
 | 	char *buf; | 
 | 	int in, out; | 
 | 	ssize_t ret; | 
 | 	struct snd_soc_dapm_path *p = NULL; | 
 |  | 
 | 	buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | 
 | 	if (!buf) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	in = is_connected_input_ep(w); | 
 | 	dapm_clear_walk(w->dapm); | 
 | 	out = is_connected_output_ep(w); | 
 | 	dapm_clear_walk(w->dapm); | 
 |  | 
 | 	ret = snprintf(buf, PAGE_SIZE, "%s: %s  in %d out %d", | 
 | 		       w->name, w->power ? "On" : "Off", in, out); | 
 |  | 
 | 	if (w->reg >= 0) | 
 | 		ret += snprintf(buf + ret, PAGE_SIZE - ret, | 
 | 				" - R%d(0x%x) bit %d", | 
 | 				w->reg, w->reg, w->shift); | 
 |  | 
 | 	ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); | 
 |  | 
 | 	if (w->sname) | 
 | 		ret += snprintf(buf + ret, PAGE_SIZE - ret, " stream %s %s\n", | 
 | 				w->sname, | 
 | 				w->active ? "active" : "inactive"); | 
 |  | 
 | 	list_for_each_entry(p, &w->sources, list_sink) { | 
 | 		if (p->connected && !p->connected(w, p->sink)) | 
 | 			continue; | 
 |  | 
 | 		if (p->connect) | 
 | 			ret += snprintf(buf + ret, PAGE_SIZE - ret, | 
 | 					" in  \"%s\" \"%s\"\n", | 
 | 					p->name ? p->name : "static", | 
 | 					p->source->name); | 
 | 	} | 
 | 	list_for_each_entry(p, &w->sinks, list_source) { | 
 | 		if (p->connected && !p->connected(w, p->sink)) | 
 | 			continue; | 
 |  | 
 | 		if (p->connect) | 
 | 			ret += snprintf(buf + ret, PAGE_SIZE - ret, | 
 | 					" out \"%s\" \"%s\"\n", | 
 | 					p->name ? p->name : "static", | 
 | 					p->sink->name); | 
 | 	} | 
 |  | 
 | 	ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); | 
 |  | 
 | 	kfree(buf); | 
 | 	return ret; | 
 | } | 
 |  | 
 | static const struct file_operations dapm_widget_power_fops = { | 
 | 	.open = dapm_widget_power_open_file, | 
 | 	.read = dapm_widget_power_read_file, | 
 | 	.llseek = default_llseek, | 
 | }; | 
 |  | 
 | static int dapm_bias_open_file(struct inode *inode, struct file *file) | 
 | { | 
 | 	file->private_data = inode->i_private; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static ssize_t dapm_bias_read_file(struct file *file, char __user *user_buf, | 
 | 				   size_t count, loff_t *ppos) | 
 | { | 
 | 	struct snd_soc_dapm_context *dapm = file->private_data; | 
 | 	char *level; | 
 |  | 
 | 	switch (dapm->bias_level) { | 
 | 	case SND_SOC_BIAS_ON: | 
 | 		level = "On\n"; | 
 | 		break; | 
 | 	case SND_SOC_BIAS_PREPARE: | 
 | 		level = "Prepare\n"; | 
 | 		break; | 
 | 	case SND_SOC_BIAS_STANDBY: | 
 | 		level = "Standby\n"; | 
 | 		break; | 
 | 	case SND_SOC_BIAS_OFF: | 
 | 		level = "Off\n"; | 
 | 		break; | 
 | 	default: | 
 | 		BUG(); | 
 | 		level = "Unknown\n"; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	return simple_read_from_buffer(user_buf, count, ppos, level, | 
 | 				       strlen(level)); | 
 | } | 
 |  | 
 | static const struct file_operations dapm_bias_fops = { | 
 | 	.open = dapm_bias_open_file, | 
 | 	.read = dapm_bias_read_file, | 
 | 	.llseek = default_llseek, | 
 | }; | 
 |  | 
 | void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, | 
 | 	struct dentry *parent) | 
 | { | 
 | 	struct dentry *d; | 
 |  | 
 | 	dapm->debugfs_dapm = debugfs_create_dir("dapm", parent); | 
 |  | 
 | 	if (!dapm->debugfs_dapm) { | 
 | 		printk(KERN_WARNING | 
 | 		       "Failed to create DAPM debugfs directory\n"); | 
 | 		return; | 
 | 	} | 
 |  | 
 | 	d = debugfs_create_file("bias_level", 0444, | 
 | 				dapm->debugfs_dapm, dapm, | 
 | 				&dapm_bias_fops); | 
 | 	if (!d) | 
 | 		dev_warn(dapm->dev, | 
 | 			 "ASoC: Failed to create bias level debugfs file\n"); | 
 | } | 
 |  | 
 | static void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) | 
 | { | 
 | 	struct snd_soc_dapm_context *dapm = w->dapm; | 
 | 	struct dentry *d; | 
 |  | 
 | 	if (!dapm->debugfs_dapm || !w->name) | 
 | 		return; | 
 |  | 
 | 	d = debugfs_create_file(w->name, 0444, | 
 | 				dapm->debugfs_dapm, w, | 
 | 				&dapm_widget_power_fops); | 
 | 	if (!d) | 
 | 		dev_warn(w->dapm->dev, | 
 | 			"ASoC: Failed to create %s debugfs file\n", | 
 | 			w->name); | 
 | } | 
 |  | 
 | static void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	debugfs_remove_recursive(dapm->debugfs_dapm); | 
 | } | 
 |  | 
 | #else | 
 | void snd_soc_dapm_debugfs_init(struct snd_soc_dapm_context *dapm, | 
 | 	struct dentry *parent) | 
 | { | 
 | } | 
 |  | 
 | static inline void dapm_debugfs_add_widget(struct snd_soc_dapm_widget *w) | 
 | { | 
 | } | 
 |  | 
 | static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | /* test and update the power status of a mux widget */ | 
 | static int dapm_mux_update_power(struct snd_soc_dapm_widget *widget, | 
 | 				 struct snd_kcontrol *kcontrol, int change, | 
 | 				 int mux, struct soc_enum *e) | 
 | { | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	int found = 0; | 
 |  | 
 | 	if (widget->id != snd_soc_dapm_mux && | 
 | 	    widget->id != snd_soc_dapm_virt_mux && | 
 | 	    widget->id != snd_soc_dapm_value_mux) | 
 | 		return -ENODEV; | 
 |  | 
 | 	if (!change) | 
 | 		return 0; | 
 |  | 
 | 	/* find dapm widget path assoc with kcontrol */ | 
 | 	list_for_each_entry(path, &widget->dapm->card->paths, list) { | 
 | 		if (path->kcontrol != kcontrol) | 
 | 			continue; | 
 |  | 
 | 		if (!path->name || !e->texts[mux]) | 
 | 			continue; | 
 |  | 
 | 		found = 1; | 
 | 		/* we now need to match the string in the enum to the path */ | 
 | 		if (!(strcmp(path->name, e->texts[mux]))) | 
 | 			path->connect = 1; /* new connection */ | 
 | 		else | 
 | 			path->connect = 0; /* old connection must be powered down */ | 
 | 	} | 
 |  | 
 | 	if (found) | 
 | 		dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* test and update the power status of a mixer or switch widget */ | 
 | static int dapm_mixer_update_power(struct snd_soc_dapm_widget *widget, | 
 | 				   struct snd_kcontrol *kcontrol, int connect) | 
 | { | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	int found = 0; | 
 |  | 
 | 	if (widget->id != snd_soc_dapm_mixer && | 
 | 	    widget->id != snd_soc_dapm_mixer_named_ctl && | 
 | 	    widget->id != snd_soc_dapm_switch) | 
 | 		return -ENODEV; | 
 |  | 
 | 	/* find dapm widget path assoc with kcontrol */ | 
 | 	list_for_each_entry(path, &widget->dapm->card->paths, list) { | 
 | 		if (path->kcontrol != kcontrol) | 
 | 			continue; | 
 |  | 
 | 		/* found, now check type */ | 
 | 		found = 1; | 
 | 		path->connect = connect; | 
 | 		break; | 
 | 	} | 
 |  | 
 | 	if (found) | 
 | 		dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /* show dapm widget status in sys fs */ | 
 | static ssize_t dapm_widget_show(struct device *dev, | 
 | 	struct device_attribute *attr, char *buf) | 
 | { | 
 | 	struct snd_soc_pcm_runtime *rtd = | 
 | 			container_of(dev, struct snd_soc_pcm_runtime, dev); | 
 | 	struct snd_soc_codec *codec =rtd->codec; | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	int count = 0; | 
 | 	char *state = "not set"; | 
 |  | 
 | 	list_for_each_entry(w, &codec->card->widgets, list) { | 
 | 		if (w->dapm != &codec->dapm) | 
 | 			continue; | 
 |  | 
 | 		/* only display widgets that burnm power */ | 
 | 		switch (w->id) { | 
 | 		case snd_soc_dapm_hp: | 
 | 		case snd_soc_dapm_mic: | 
 | 		case snd_soc_dapm_spk: | 
 | 		case snd_soc_dapm_line: | 
 | 		case snd_soc_dapm_micbias: | 
 | 		case snd_soc_dapm_dac: | 
 | 		case snd_soc_dapm_adc: | 
 | 		case snd_soc_dapm_pga: | 
 | 		case snd_soc_dapm_out_drv: | 
 | 		case snd_soc_dapm_mixer: | 
 | 		case snd_soc_dapm_mixer_named_ctl: | 
 | 		case snd_soc_dapm_supply: | 
 | 			if (w->name) | 
 | 				count += sprintf(buf + count, "%s: %s\n", | 
 | 					w->name, w->power ? "On":"Off"); | 
 | 		break; | 
 | 		default: | 
 | 		break; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	switch (codec->dapm.bias_level) { | 
 | 	case SND_SOC_BIAS_ON: | 
 | 		state = "On"; | 
 | 		break; | 
 | 	case SND_SOC_BIAS_PREPARE: | 
 | 		state = "Prepare"; | 
 | 		break; | 
 | 	case SND_SOC_BIAS_STANDBY: | 
 | 		state = "Standby"; | 
 | 		break; | 
 | 	case SND_SOC_BIAS_OFF: | 
 | 		state = "Off"; | 
 | 		break; | 
 | 	} | 
 | 	count += sprintf(buf + count, "PM State: %s\n", state); | 
 |  | 
 | 	return count; | 
 | } | 
 |  | 
 | static DEVICE_ATTR(dapm_widget, 0444, dapm_widget_show, NULL); | 
 |  | 
 | int snd_soc_dapm_sys_add(struct device *dev) | 
 | { | 
 | 	return device_create_file(dev, &dev_attr_dapm_widget); | 
 | } | 
 |  | 
 | static void snd_soc_dapm_sys_remove(struct device *dev) | 
 | { | 
 | 	device_remove_file(dev, &dev_attr_dapm_widget); | 
 | } | 
 |  | 
 | /* free all dapm widgets and resources */ | 
 | static void dapm_free_widgets(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w, *next_w; | 
 | 	struct snd_soc_dapm_path *p, *next_p; | 
 |  | 
 | 	list_for_each_entry_safe(w, next_w, &dapm->card->widgets, list) { | 
 | 		if (w->dapm != dapm) | 
 | 			continue; | 
 | 		list_del(&w->list); | 
 | 		/* | 
 | 		 * remove source and sink paths associated to this widget. | 
 | 		 * While removing the path, remove reference to it from both | 
 | 		 * source and sink widgets so that path is removed only once. | 
 | 		 */ | 
 | 		list_for_each_entry_safe(p, next_p, &w->sources, list_sink) { | 
 | 			list_del(&p->list_sink); | 
 | 			list_del(&p->list_source); | 
 | 			list_del(&p->list); | 
 | 			kfree(p->long_name); | 
 | 			kfree(p); | 
 | 		} | 
 | 		list_for_each_entry_safe(p, next_p, &w->sinks, list_source) { | 
 | 			list_del(&p->list_sink); | 
 | 			list_del(&p->list_source); | 
 | 			list_del(&p->list); | 
 | 			kfree(p->long_name); | 
 | 			kfree(p); | 
 | 		} | 
 | 		kfree(w->kcontrols); | 
 | 		kfree(w->name); | 
 | 		kfree(w); | 
 | 	} | 
 | } | 
 |  | 
 | static struct snd_soc_dapm_widget *dapm_find_widget( | 
 | 			struct snd_soc_dapm_context *dapm, const char *pin, | 
 | 			bool search_other_contexts) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	struct snd_soc_dapm_widget *fallback = NULL; | 
 |  | 
 | 	list_for_each_entry(w, &dapm->card->widgets, list) { | 
 | 		if (!strcmp(w->name, pin)) { | 
 | 			if (w->dapm == dapm) | 
 | 				return w; | 
 | 			else | 
 | 				fallback = w; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (search_other_contexts) | 
 | 		return fallback; | 
 |  | 
 | 	return NULL; | 
 | } | 
 |  | 
 | static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm, | 
 | 				const char *pin, int status) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); | 
 |  | 
 | 	if (!w) { | 
 | 		dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	w->connected = status; | 
 | 	if (status == 0) | 
 | 		w->force = 0; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_sync - scan and power dapm paths | 
 |  * @dapm: DAPM context | 
 |  * | 
 |  * Walks all dapm audio paths and powers widgets according to their | 
 |  * stream or path usage. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	return dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); | 
 |  | 
 | static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm, | 
 | 				  const struct snd_soc_dapm_route *route) | 
 | { | 
 | 	struct snd_soc_dapm_path *path; | 
 | 	struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; | 
 | 	struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL; | 
 | 	const char *sink; | 
 | 	const char *control = route->control; | 
 | 	const char *source; | 
 | 	char prefixed_sink[80]; | 
 | 	char prefixed_source[80]; | 
 | 	int ret = 0; | 
 |  | 
 | 	if (dapm->codec && dapm->codec->name_prefix) { | 
 | 		snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s", | 
 | 			 dapm->codec->name_prefix, route->sink); | 
 | 		sink = prefixed_sink; | 
 | 		snprintf(prefixed_source, sizeof(prefixed_source), "%s %s", | 
 | 			 dapm->codec->name_prefix, route->source); | 
 | 		source = prefixed_source; | 
 | 	} else { | 
 | 		sink = route->sink; | 
 | 		source = route->source; | 
 | 	} | 
 |  | 
 | 	/* | 
 | 	 * find src and dest widgets over all widgets but favor a widget from | 
 | 	 * current DAPM context | 
 | 	 */ | 
 | 	list_for_each_entry(w, &dapm->card->widgets, list) { | 
 | 		if (!wsink && !(strcmp(w->name, sink))) { | 
 | 			wtsink = w; | 
 | 			if (w->dapm == dapm) | 
 | 				wsink = w; | 
 | 			continue; | 
 | 		} | 
 | 		if (!wsource && !(strcmp(w->name, source))) { | 
 | 			wtsource = w; | 
 | 			if (w->dapm == dapm) | 
 | 				wsource = w; | 
 | 		} | 
 | 	} | 
 | 	/* use widget from another DAPM context if not found from this */ | 
 | 	if (!wsink) | 
 | 		wsink = wtsink; | 
 | 	if (!wsource) | 
 | 		wsource = wtsource; | 
 |  | 
 | 	if (wsource == NULL || wsink == NULL) | 
 | 		return -ENODEV; | 
 |  | 
 | 	path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL); | 
 | 	if (!path) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	path->source = wsource; | 
 | 	path->sink = wsink; | 
 | 	path->connected = route->connected; | 
 | 	INIT_LIST_HEAD(&path->list); | 
 | 	INIT_LIST_HEAD(&path->list_source); | 
 | 	INIT_LIST_HEAD(&path->list_sink); | 
 |  | 
 | 	/* check for external widgets */ | 
 | 	if (wsink->id == snd_soc_dapm_input) { | 
 | 		if (wsource->id == snd_soc_dapm_micbias || | 
 | 			wsource->id == snd_soc_dapm_mic || | 
 | 			wsource->id == snd_soc_dapm_line || | 
 | 			wsource->id == snd_soc_dapm_output) | 
 | 			wsink->ext = 1; | 
 | 	} | 
 | 	if (wsource->id == snd_soc_dapm_output) { | 
 | 		if (wsink->id == snd_soc_dapm_spk || | 
 | 			wsink->id == snd_soc_dapm_hp || | 
 | 			wsink->id == snd_soc_dapm_line || | 
 | 			wsink->id == snd_soc_dapm_input) | 
 | 			wsource->ext = 1; | 
 | 	} | 
 |  | 
 | 	/* connect static paths */ | 
 | 	if (control == NULL) { | 
 | 		list_add(&path->list, &dapm->card->paths); | 
 | 		list_add(&path->list_sink, &wsink->sources); | 
 | 		list_add(&path->list_source, &wsource->sinks); | 
 | 		path->connect = 1; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	/* connect dynamic paths */ | 
 | 	switch (wsink->id) { | 
 | 	case snd_soc_dapm_adc: | 
 | 	case snd_soc_dapm_dac: | 
 | 	case snd_soc_dapm_pga: | 
 | 	case snd_soc_dapm_out_drv: | 
 | 	case snd_soc_dapm_input: | 
 | 	case snd_soc_dapm_output: | 
 | 	case snd_soc_dapm_micbias: | 
 | 	case snd_soc_dapm_vmid: | 
 | 	case snd_soc_dapm_pre: | 
 | 	case snd_soc_dapm_post: | 
 | 	case snd_soc_dapm_supply: | 
 | 	case snd_soc_dapm_aif_in: | 
 | 	case snd_soc_dapm_aif_out: | 
 | 		list_add(&path->list, &dapm->card->paths); | 
 | 		list_add(&path->list_sink, &wsink->sources); | 
 | 		list_add(&path->list_source, &wsource->sinks); | 
 | 		path->connect = 1; | 
 | 		return 0; | 
 | 	case snd_soc_dapm_mux: | 
 | 	case snd_soc_dapm_virt_mux: | 
 | 	case snd_soc_dapm_value_mux: | 
 | 		ret = dapm_connect_mux(dapm, wsource, wsink, path, control, | 
 | 			&wsink->kcontrol_news[0]); | 
 | 		if (ret != 0) | 
 | 			goto err; | 
 | 		break; | 
 | 	case snd_soc_dapm_switch: | 
 | 	case snd_soc_dapm_mixer: | 
 | 	case snd_soc_dapm_mixer_named_ctl: | 
 | 		ret = dapm_connect_mixer(dapm, wsource, wsink, path, control); | 
 | 		if (ret != 0) | 
 | 			goto err; | 
 | 		break; | 
 | 	case snd_soc_dapm_hp: | 
 | 	case snd_soc_dapm_mic: | 
 | 	case snd_soc_dapm_line: | 
 | 	case snd_soc_dapm_spk: | 
 | 		list_add(&path->list, &dapm->card->paths); | 
 | 		list_add(&path->list_sink, &wsink->sources); | 
 | 		list_add(&path->list_source, &wsource->sinks); | 
 | 		path->connect = 0; | 
 | 		return 0; | 
 | 	} | 
 | 	return 0; | 
 |  | 
 | err: | 
 | 	dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s\n", | 
 | 		 source, control, sink); | 
 | 	kfree(path); | 
 | 	return ret; | 
 | } | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_add_routes - Add routes between DAPM widgets | 
 |  * @dapm: DAPM context | 
 |  * @route: audio routes | 
 |  * @num: number of routes | 
 |  * | 
 |  * Connects 2 dapm widgets together via a named audio path. The sink is | 
 |  * the widget receiving the audio signal, whilst the source is the sender | 
 |  * of the audio signal. | 
 |  * | 
 |  * Returns 0 for success else error. On error all resources can be freed | 
 |  * with a call to snd_soc_card_free(). | 
 |  */ | 
 | int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, | 
 | 			    const struct snd_soc_dapm_route *route, int num) | 
 | { | 
 | 	int i, ret; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		ret = snd_soc_dapm_add_route(dapm, route); | 
 | 		if (ret < 0) { | 
 | 			dev_err(dapm->dev, "Failed to add route %s->%s\n", | 
 | 				route->source, route->sink); | 
 | 			return ret; | 
 | 		} | 
 | 		route++; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_new_widgets - add new dapm widgets | 
 |  * @dapm: DAPM context | 
 |  * | 
 |  * Checks the codec for any new dapm widgets and creates them if found. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	unsigned int val; | 
 |  | 
 | 	list_for_each_entry(w, &dapm->card->widgets, list) | 
 | 	{ | 
 | 		if (w->new) | 
 | 			continue; | 
 |  | 
 | 		if (w->num_kcontrols) { | 
 | 			w->kcontrols = kzalloc(w->num_kcontrols * | 
 | 						sizeof(struct snd_kcontrol *), | 
 | 						GFP_KERNEL); | 
 | 			if (!w->kcontrols) | 
 | 				return -ENOMEM; | 
 | 		} | 
 |  | 
 | 		switch(w->id) { | 
 | 		case snd_soc_dapm_switch: | 
 | 		case snd_soc_dapm_mixer: | 
 | 		case snd_soc_dapm_mixer_named_ctl: | 
 | 			w->power_check = dapm_generic_check_power; | 
 | 			dapm_new_mixer(w); | 
 | 			break; | 
 | 		case snd_soc_dapm_mux: | 
 | 		case snd_soc_dapm_virt_mux: | 
 | 		case snd_soc_dapm_value_mux: | 
 | 			w->power_check = dapm_generic_check_power; | 
 | 			dapm_new_mux(w); | 
 | 			break; | 
 | 		case snd_soc_dapm_adc: | 
 | 		case snd_soc_dapm_aif_out: | 
 | 			w->power_check = dapm_adc_check_power; | 
 | 			break; | 
 | 		case snd_soc_dapm_dac: | 
 | 		case snd_soc_dapm_aif_in: | 
 | 			w->power_check = dapm_dac_check_power; | 
 | 			break; | 
 | 		case snd_soc_dapm_pga: | 
 | 		case snd_soc_dapm_out_drv: | 
 | 			w->power_check = dapm_generic_check_power; | 
 | 			dapm_new_pga(w); | 
 | 			break; | 
 | 		case snd_soc_dapm_input: | 
 | 		case snd_soc_dapm_output: | 
 | 		case snd_soc_dapm_micbias: | 
 | 		case snd_soc_dapm_spk: | 
 | 		case snd_soc_dapm_hp: | 
 | 		case snd_soc_dapm_mic: | 
 | 		case snd_soc_dapm_line: | 
 | 			w->power_check = dapm_generic_check_power; | 
 | 			break; | 
 | 		case snd_soc_dapm_supply: | 
 | 			w->power_check = dapm_supply_check_power; | 
 | 		case snd_soc_dapm_vmid: | 
 | 		case snd_soc_dapm_pre: | 
 | 		case snd_soc_dapm_post: | 
 | 			break; | 
 | 		} | 
 |  | 
 | 		/* Read the initial power state from the device */ | 
 | 		if (w->reg >= 0) { | 
 | 			val = snd_soc_read(w->codec, w->reg); | 
 | 			val &= 1 << w->shift; | 
 | 			if (w->invert) | 
 | 				val = !val; | 
 |  | 
 | 			if (val) | 
 | 				w->power = 1; | 
 | 		} | 
 |  | 
 | 		w->new = 1; | 
 |  | 
 | 		dapm_debugfs_add_widget(w); | 
 | 	} | 
 |  | 
 | 	dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP); | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_get_volsw - dapm mixer get callback | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Callback to get the value of a dapm mixer control. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct soc_mixer_control *mc = | 
 | 		(struct soc_mixer_control *)kcontrol->private_value; | 
 | 	unsigned int reg = mc->reg; | 
 | 	unsigned int shift = mc->shift; | 
 | 	unsigned int rshift = mc->rshift; | 
 | 	int max = mc->max; | 
 | 	unsigned int invert = mc->invert; | 
 | 	unsigned int mask = (1 << fls(max)) - 1; | 
 |  | 
 | 	ucontrol->value.integer.value[0] = | 
 | 		(snd_soc_read(widget->codec, reg) >> shift) & mask; | 
 | 	if (shift != rshift) | 
 | 		ucontrol->value.integer.value[1] = | 
 | 			(snd_soc_read(widget->codec, reg) >> rshift) & mask; | 
 | 	if (invert) { | 
 | 		ucontrol->value.integer.value[0] = | 
 | 			max - ucontrol->value.integer.value[0]; | 
 | 		if (shift != rshift) | 
 | 			ucontrol->value.integer.value[1] = | 
 | 				max - ucontrol->value.integer.value[1]; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_put_volsw - dapm mixer set callback | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Callback to set the value of a dapm mixer control. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct snd_soc_codec *codec = widget->codec; | 
 | 	struct soc_mixer_control *mc = | 
 | 		(struct soc_mixer_control *)kcontrol->private_value; | 
 | 	unsigned int reg = mc->reg; | 
 | 	unsigned int shift = mc->shift; | 
 | 	int max = mc->max; | 
 | 	unsigned int mask = (1 << fls(max)) - 1; | 
 | 	unsigned int invert = mc->invert; | 
 | 	unsigned int val; | 
 | 	int connect, change; | 
 | 	struct snd_soc_dapm_update update; | 
 | 	int wi; | 
 |  | 
 | 	val = (ucontrol->value.integer.value[0] & mask); | 
 |  | 
 | 	if (invert) | 
 | 		val = max - val; | 
 | 	mask = mask << shift; | 
 | 	val = val << shift; | 
 |  | 
 | 	if (val) | 
 | 		/* new connection */ | 
 | 		connect = invert ? 0 : 1; | 
 | 	else | 
 | 		/* old connection must be powered down */ | 
 | 		connect = invert ? 1 : 0; | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 |  | 
 | 	change = snd_soc_test_bits(widget->codec, reg, mask, val); | 
 | 	if (change) { | 
 | 		for (wi = 0; wi < wlist->num_widgets; wi++) { | 
 | 			widget = wlist->widgets[wi]; | 
 |  | 
 | 			widget->value = val; | 
 |  | 
 | 			update.kcontrol = kcontrol; | 
 | 			update.widget = widget; | 
 | 			update.reg = reg; | 
 | 			update.mask = mask; | 
 | 			update.val = val; | 
 | 			widget->dapm->update = &update; | 
 |  | 
 | 			dapm_mixer_update_power(widget, kcontrol, connect); | 
 |  | 
 | 			widget->dapm->update = NULL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mutex_unlock(&codec->mutex); | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_get_enum_double - dapm enumerated double mixer get callback | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Callback to get the value of a dapm enumerated double mixer control. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 
 | 	unsigned int val, bitmask; | 
 |  | 
 | 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1) | 
 | 		; | 
 | 	val = snd_soc_read(widget->codec, e->reg); | 
 | 	ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & (bitmask - 1); | 
 | 	if (e->shift_l != e->shift_r) | 
 | 		ucontrol->value.enumerated.item[1] = | 
 | 			(val >> e->shift_r) & (bitmask - 1); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_put_enum_double - dapm enumerated double mixer set callback | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Callback to set the value of a dapm enumerated double mixer control. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct snd_soc_codec *codec = widget->codec; | 
 | 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 
 | 	unsigned int val, mux, change; | 
 | 	unsigned int mask, bitmask; | 
 | 	struct snd_soc_dapm_update update; | 
 | 	int wi; | 
 |  | 
 | 	for (bitmask = 1; bitmask < e->max; bitmask <<= 1) | 
 | 		; | 
 | 	if (ucontrol->value.enumerated.item[0] > e->max - 1) | 
 | 		return -EINVAL; | 
 | 	mux = ucontrol->value.enumerated.item[0]; | 
 | 	val = mux << e->shift_l; | 
 | 	mask = (bitmask - 1) << e->shift_l; | 
 | 	if (e->shift_l != e->shift_r) { | 
 | 		if (ucontrol->value.enumerated.item[1] > e->max - 1) | 
 | 			return -EINVAL; | 
 | 		val |= ucontrol->value.enumerated.item[1] << e->shift_r; | 
 | 		mask |= (bitmask - 1) << e->shift_r; | 
 | 	} | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 |  | 
 | 	change = snd_soc_test_bits(widget->codec, e->reg, mask, val); | 
 | 	if (change) { | 
 | 		for (wi = 0; wi < wlist->num_widgets; wi++) { | 
 | 			widget = wlist->widgets[wi]; | 
 |  | 
 | 			widget->value = val; | 
 |  | 
 | 			update.kcontrol = kcontrol; | 
 | 			update.widget = widget; | 
 | 			update.reg = e->reg; | 
 | 			update.mask = mask; | 
 | 			update.val = val; | 
 | 			widget->dapm->update = &update; | 
 |  | 
 | 			dapm_mux_update_power(widget, kcontrol, change, mux, e); | 
 |  | 
 | 			widget->dapm->update = NULL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mutex_unlock(&codec->mutex); | 
 | 	return change; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_get_enum_virt - Get virtual DAPM mux | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol, | 
 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 |  | 
 | 	ucontrol->value.enumerated.item[0] = widget->value; | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_put_enum_virt - Set virtual DAPM mux | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol, | 
 | 			       struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct snd_soc_codec *codec = widget->codec; | 
 | 	struct soc_enum *e = | 
 | 		(struct soc_enum *)kcontrol->private_value; | 
 | 	int change; | 
 | 	int ret = 0; | 
 | 	int wi; | 
 |  | 
 | 	if (ucontrol->value.enumerated.item[0] >= e->max) | 
 | 		return -EINVAL; | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 |  | 
 | 	change = widget->value != ucontrol->value.enumerated.item[0]; | 
 | 	if (change) { | 
 | 		for (wi = 0; wi < wlist->num_widgets; wi++) { | 
 | 			widget = wlist->widgets[wi]; | 
 |  | 
 | 			widget->value = ucontrol->value.enumerated.item[0]; | 
 |  | 
 | 			dapm_mux_update_power(widget, kcontrol, change, | 
 | 					      widget->value, e); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mutex_unlock(&codec->mutex); | 
 | 	return ret; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_get_value_enum_double - dapm semi enumerated double mixer get | 
 |  *					callback | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Callback to get the value of a dapm semi enumerated double mixer control. | 
 |  * | 
 |  * Semi enumerated mixer: the enumerated items are referred as values. Can be | 
 |  * used for handling bitfield coded enumeration for example. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 
 | 	unsigned int reg_val, val, mux; | 
 |  | 
 | 	reg_val = snd_soc_read(widget->codec, e->reg); | 
 | 	val = (reg_val >> e->shift_l) & e->mask; | 
 | 	for (mux = 0; mux < e->max; mux++) { | 
 | 		if (val == e->values[mux]) | 
 | 			break; | 
 | 	} | 
 | 	ucontrol->value.enumerated.item[0] = mux; | 
 | 	if (e->shift_l != e->shift_r) { | 
 | 		val = (reg_val >> e->shift_r) & e->mask; | 
 | 		for (mux = 0; mux < e->max; mux++) { | 
 | 			if (val == e->values[mux]) | 
 | 				break; | 
 | 		} | 
 | 		ucontrol->value.enumerated.item[1] = mux; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_put_value_enum_double - dapm semi enumerated double mixer set | 
 |  *					callback | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: control element information | 
 |  * | 
 |  * Callback to set the value of a dapm semi enumerated double mixer control. | 
 |  * | 
 |  * Semi enumerated mixer: the enumerated items are referred as values. Can be | 
 |  * used for handling bitfield coded enumeration for example. | 
 |  * | 
 |  * Returns 0 for success. | 
 |  */ | 
 | int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol, | 
 | 	struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); | 
 | 	struct snd_soc_dapm_widget *widget = wlist->widgets[0]; | 
 | 	struct snd_soc_codec *codec = widget->codec; | 
 | 	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; | 
 | 	unsigned int val, mux, change; | 
 | 	unsigned int mask; | 
 | 	struct snd_soc_dapm_update update; | 
 | 	int wi; | 
 |  | 
 | 	if (ucontrol->value.enumerated.item[0] > e->max - 1) | 
 | 		return -EINVAL; | 
 | 	mux = ucontrol->value.enumerated.item[0]; | 
 | 	val = e->values[ucontrol->value.enumerated.item[0]] << e->shift_l; | 
 | 	mask = e->mask << e->shift_l; | 
 | 	if (e->shift_l != e->shift_r) { | 
 | 		if (ucontrol->value.enumerated.item[1] > e->max - 1) | 
 | 			return -EINVAL; | 
 | 		val |= e->values[ucontrol->value.enumerated.item[1]] << e->shift_r; | 
 | 		mask |= e->mask << e->shift_r; | 
 | 	} | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 |  | 
 | 	change = snd_soc_test_bits(widget->codec, e->reg, mask, val); | 
 | 	if (change) { | 
 | 		for (wi = 0; wi < wlist->num_widgets; wi++) { | 
 | 			widget = wlist->widgets[wi]; | 
 |  | 
 | 			widget->value = val; | 
 |  | 
 | 			update.kcontrol = kcontrol; | 
 | 			update.widget = widget; | 
 | 			update.reg = e->reg; | 
 | 			update.mask = mask; | 
 | 			update.val = val; | 
 | 			widget->dapm->update = &update; | 
 |  | 
 | 			dapm_mux_update_power(widget, kcontrol, change, mux, e); | 
 |  | 
 | 			widget->dapm->update = NULL; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	mutex_unlock(&codec->mutex); | 
 | 	return change; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_value_enum_double); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_info_pin_switch - Info for a pin switch | 
 |  * | 
 |  * @kcontrol: mixer control | 
 |  * @uinfo: control element information | 
 |  * | 
 |  * Callback to provide information about a pin switch control. | 
 |  */ | 
 | int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol, | 
 | 				 struct snd_ctl_elem_info *uinfo) | 
 | { | 
 | 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | 
 | 	uinfo->count = 1; | 
 | 	uinfo->value.integer.min = 0; | 
 | 	uinfo->value.integer.max = 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_get_pin_switch - Get information for a pin switch | 
 |  * | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: Value | 
 |  */ | 
 | int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol, | 
 | 				struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 	const char *pin = (const char *)kcontrol->private_value; | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 |  | 
 | 	ucontrol->value.integer.value[0] = | 
 | 		snd_soc_dapm_get_pin_status(&codec->dapm, pin); | 
 |  | 
 | 	mutex_unlock(&codec->mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_put_pin_switch - Set information for a pin switch | 
 |  * | 
 |  * @kcontrol: mixer control | 
 |  * @ucontrol: Value | 
 |  */ | 
 | int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol, | 
 | 				struct snd_ctl_elem_value *ucontrol) | 
 | { | 
 | 	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
 | 	const char *pin = (const char *)kcontrol->private_value; | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 |  | 
 | 	if (ucontrol->value.integer.value[0]) | 
 | 		snd_soc_dapm_enable_pin(&codec->dapm, pin); | 
 | 	else | 
 | 		snd_soc_dapm_disable_pin(&codec->dapm, pin); | 
 |  | 
 | 	snd_soc_dapm_sync(&codec->dapm); | 
 |  | 
 | 	mutex_unlock(&codec->mutex); | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_new_control - create new dapm control | 
 |  * @dapm: DAPM context | 
 |  * @widget: widget template | 
 |  * | 
 |  * Creates a new dapm control based upon the template. | 
 |  * | 
 |  * Returns 0 for success else error. | 
 |  */ | 
 | int snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm, | 
 | 	const struct snd_soc_dapm_widget *widget) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	size_t name_len; | 
 |  | 
 | 	if ((w = dapm_cnew_widget(widget)) == NULL) | 
 | 		return -ENOMEM; | 
 |  | 
 | 	name_len = strlen(widget->name) + 1; | 
 | 	if (dapm->codec && dapm->codec->name_prefix) | 
 | 		name_len += 1 + strlen(dapm->codec->name_prefix); | 
 | 	w->name = kmalloc(name_len, GFP_KERNEL); | 
 | 	if (w->name == NULL) { | 
 | 		kfree(w); | 
 | 		return -ENOMEM; | 
 | 	} | 
 | 	if (dapm->codec && dapm->codec->name_prefix) | 
 | 		snprintf(w->name, name_len, "%s %s", | 
 | 			dapm->codec->name_prefix, widget->name); | 
 | 	else | 
 | 		snprintf(w->name, name_len, "%s", widget->name); | 
 |  | 
 | 	dapm->n_widgets++; | 
 | 	w->dapm = dapm; | 
 | 	w->codec = dapm->codec; | 
 | 	INIT_LIST_HEAD(&w->sources); | 
 | 	INIT_LIST_HEAD(&w->sinks); | 
 | 	INIT_LIST_HEAD(&w->list); | 
 | 	list_add(&w->list, &dapm->card->widgets); | 
 |  | 
 | 	/* machine layer set ups unconnected pins and insertions */ | 
 | 	w->connected = 1; | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_new_controls - create new dapm controls | 
 |  * @dapm: DAPM context | 
 |  * @widget: widget array | 
 |  * @num: number of widgets | 
 |  * | 
 |  * Creates new DAPM controls based upon the templates. | 
 |  * | 
 |  * Returns 0 for success else error. | 
 |  */ | 
 | int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm, | 
 | 	const struct snd_soc_dapm_widget *widget, | 
 | 	int num) | 
 | { | 
 | 	int i, ret; | 
 |  | 
 | 	for (i = 0; i < num; i++) { | 
 | 		ret = snd_soc_dapm_new_control(dapm, widget); | 
 | 		if (ret < 0) { | 
 | 			dev_err(dapm->dev, | 
 | 				"ASoC: Failed to create DAPM control %s: %d\n", | 
 | 				widget->name, ret); | 
 | 			return ret; | 
 | 		} | 
 | 		widget++; | 
 | 	} | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); | 
 |  | 
 | static void soc_dapm_stream_event(struct snd_soc_dapm_context *dapm, | 
 | 	const char *stream, int event) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 |  | 
 | 	list_for_each_entry(w, &dapm->card->widgets, list) | 
 | 	{ | 
 | 		if (!w->sname || w->dapm != dapm) | 
 | 			continue; | 
 | 		dev_dbg(w->dapm->dev, "widget %s\n %s stream %s event %d\n", | 
 | 			w->name, w->sname, stream, event); | 
 | 		if (strstr(w->sname, stream)) { | 
 | 			switch(event) { | 
 | 			case SND_SOC_DAPM_STREAM_START: | 
 | 				w->active = 1; | 
 | 				break; | 
 | 			case SND_SOC_DAPM_STREAM_STOP: | 
 | 				w->active = 0; | 
 | 				break; | 
 | 			case SND_SOC_DAPM_STREAM_SUSPEND: | 
 | 			case SND_SOC_DAPM_STREAM_RESUME: | 
 | 			case SND_SOC_DAPM_STREAM_PAUSE_PUSH: | 
 | 			case SND_SOC_DAPM_STREAM_PAUSE_RELEASE: | 
 | 				break; | 
 | 			} | 
 | 		} | 
 | 	} | 
 |  | 
 | 	dapm_power_widgets(dapm, event); | 
 | } | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_stream_event - send a stream event to the dapm core | 
 |  * @rtd: PCM runtime data | 
 |  * @stream: stream name | 
 |  * @event: stream event | 
 |  * | 
 |  * Sends a stream event to the dapm core. The core then makes any | 
 |  * necessary widget power changes. | 
 |  * | 
 |  * Returns 0 for success else error. | 
 |  */ | 
 | int snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, | 
 | 	const char *stream, int event) | 
 | { | 
 | 	struct snd_soc_codec *codec = rtd->codec; | 
 |  | 
 | 	if (stream == NULL) | 
 | 		return 0; | 
 |  | 
 | 	mutex_lock(&codec->mutex); | 
 | 	soc_dapm_stream_event(&codec->dapm, stream, event); | 
 | 	mutex_unlock(&codec->mutex); | 
 | 	return 0; | 
 | } | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_enable_pin - enable pin. | 
 |  * @dapm: DAPM context | 
 |  * @pin: pin name | 
 |  * | 
 |  * Enables input/output pin and its parents or children widgets iff there is | 
 |  * a valid audio route and active audio stream. | 
 |  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | 
 |  * do any widget power switching. | 
 |  */ | 
 | int snd_soc_dapm_enable_pin(struct snd_soc_dapm_context *dapm, const char *pin) | 
 | { | 
 | 	return snd_soc_dapm_set_pin(dapm, pin, 1); | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_force_enable_pin - force a pin to be enabled | 
 |  * @dapm: DAPM context | 
 |  * @pin: pin name | 
 |  * | 
 |  * Enables input/output pin regardless of any other state.  This is | 
 |  * intended for use with microphone bias supplies used in microphone | 
 |  * jack detection. | 
 |  * | 
 |  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | 
 |  * do any widget power switching. | 
 |  */ | 
 | int snd_soc_dapm_force_enable_pin(struct snd_soc_dapm_context *dapm, | 
 | 				  const char *pin) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); | 
 |  | 
 | 	if (!w) { | 
 | 		dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	dev_dbg(w->dapm->dev, "dapm: force enable pin %s\n", pin); | 
 | 	w->connected = 1; | 
 | 	w->force = 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_force_enable_pin); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_disable_pin - disable pin. | 
 |  * @dapm: DAPM context | 
 |  * @pin: pin name | 
 |  * | 
 |  * Disables input/output pin and its parents or children widgets. | 
 |  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | 
 |  * do any widget power switching. | 
 |  */ | 
 | int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm, | 
 | 			     const char *pin) | 
 | { | 
 | 	return snd_soc_dapm_set_pin(dapm, pin, 0); | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_nc_pin - permanently disable pin. | 
 |  * @dapm: DAPM context | 
 |  * @pin: pin name | 
 |  * | 
 |  * Marks the specified pin as being not connected, disabling it along | 
 |  * any parent or child widgets.  At present this is identical to | 
 |  * snd_soc_dapm_disable_pin() but in future it will be extended to do | 
 |  * additional things such as disabling controls which only affect | 
 |  * paths through the pin. | 
 |  * | 
 |  * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to | 
 |  * do any widget power switching. | 
 |  */ | 
 | int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin) | 
 | { | 
 | 	return snd_soc_dapm_set_pin(dapm, pin, 0); | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_get_pin_status - get audio pin status | 
 |  * @dapm: DAPM context | 
 |  * @pin: audio signal pin endpoint (or start point) | 
 |  * | 
 |  * Get audio pin status - connected or disconnected. | 
 |  * | 
 |  * Returns 1 for connected otherwise 0. | 
 |  */ | 
 | int snd_soc_dapm_get_pin_status(struct snd_soc_dapm_context *dapm, | 
 | 				const char *pin) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, true); | 
 |  | 
 | 	if (w) | 
 | 		return w->connected; | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_ignore_suspend - ignore suspend status for DAPM endpoint | 
 |  * @dapm: DAPM context | 
 |  * @pin: audio signal pin endpoint (or start point) | 
 |  * | 
 |  * Mark the given endpoint or pin as ignoring suspend.  When the | 
 |  * system is disabled a path between two endpoints flagged as ignoring | 
 |  * suspend will not be disabled.  The path must already be enabled via | 
 |  * normal means at suspend time, it will not be turned on if it was not | 
 |  * already enabled. | 
 |  */ | 
 | int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm, | 
 | 				const char *pin) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w = dapm_find_widget(dapm, pin, false); | 
 |  | 
 | 	if (!w) { | 
 | 		dev_err(dapm->dev, "dapm: unknown pin %s\n", pin); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	w->ignore_suspend = 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend); | 
 |  | 
 | /** | 
 |  * snd_soc_dapm_free - free dapm resources | 
 |  * @card: SoC device | 
 |  * | 
 |  * Free all dapm widgets and resources. | 
 |  */ | 
 | void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	snd_soc_dapm_sys_remove(dapm->dev); | 
 | 	dapm_debugfs_cleanup(dapm); | 
 | 	dapm_free_widgets(dapm); | 
 | 	list_del(&dapm->list); | 
 | } | 
 | EXPORT_SYMBOL_GPL(snd_soc_dapm_free); | 
 |  | 
 | static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm) | 
 | { | 
 | 	struct snd_soc_dapm_widget *w; | 
 | 	LIST_HEAD(down_list); | 
 | 	int powerdown = 0; | 
 |  | 
 | 	list_for_each_entry(w, &dapm->card->widgets, list) { | 
 | 		if (w->dapm != dapm) | 
 | 			continue; | 
 | 		if (w->power) { | 
 | 			dapm_seq_insert(w, &down_list, false); | 
 | 			w->power = 0; | 
 | 			powerdown = 1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* If there were no widgets to power down we're already in | 
 | 	 * standby. | 
 | 	 */ | 
 | 	if (powerdown) { | 
 | 		snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE); | 
 | 		dapm_seq_run(dapm, &down_list, 0, false); | 
 | 		snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY); | 
 | 	} | 
 | } | 
 |  | 
 | /* | 
 |  * snd_soc_dapm_shutdown - callback for system shutdown | 
 |  */ | 
 | void snd_soc_dapm_shutdown(struct snd_soc_card *card) | 
 | { | 
 | 	struct snd_soc_codec *codec; | 
 |  | 
 | 	list_for_each_entry(codec, &card->codec_dev_list, list) { | 
 | 		soc_dapm_shutdown_codec(&codec->dapm); | 
 | 		snd_soc_dapm_set_bias_level(&codec->dapm, SND_SOC_BIAS_OFF); | 
 | 	} | 
 | } | 
 |  | 
 | /* Module information */ | 
 | MODULE_AUTHOR("Liam Girdwood, lrg@slimlogic.co.uk"); | 
 | MODULE_DESCRIPTION("Dynamic Audio Power Management core for ALSA SoC"); | 
 | MODULE_LICENSE("GPL"); |