diff options
Diffstat (limited to 'sound/soc/soc-dapm.c')
| -rw-r--r-- | sound/soc/soc-dapm.c | 318 | 
1 files changed, 170 insertions, 148 deletions
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index d6d9ba2e691..21779a6a781 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -504,17 +504,27 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,  	return 0;  } -/* create new dapm mixer control */ -static int dapm_new_mixer(struct snd_soc_dapm_widget *w) +/* + * Determine if a kcontrol is shared. If it is, look it up. If it isn't, + * create it. Either way, add the widget into the control's widget list + */ +static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w, +	int kci, struct snd_soc_dapm_path *path)  {  	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; +	size_t prefix_len; +	int shared; +	struct snd_kcontrol *kcontrol;  	struct snd_soc_dapm_widget_list *wlist; +	int wlistentries;  	size_t wlistsize; +	bool wname_in_long_name, kcname_in_long_name; +	size_t name_len; +	char *long_name; +	const char *name; +	int ret;  	if (dapm->codec)  		prefix = dapm->codec->name_prefix; @@ -526,103 +536,141 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)  	else  		prefix_len = 0; -	/* add kcontrol */ -	for (i = 0; i < w->num_kcontrols; i++) { +	shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci], +					 &kcontrol); -		/* match name */ -		list_for_each_entry(path, &w->sources, list_sink) { +	if (kcontrol) { +		wlist = kcontrol->private_data; +		wlistentries = wlist->num_widgets + 1; +	} else { +		wlist = NULL; +		wlistentries = 1; +	} -			/* 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) + +			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 (w->kcontrols[i]) { -				path->kcontrol = w->kcontrols[i]; -				continue; +	if (!kcontrol) { +		if (shared) { +			wname_in_long_name = false; +			kcname_in_long_name = true; +		} else { +			switch (w->id) { +			case snd_soc_dapm_switch: +			case snd_soc_dapm_mixer: +				wname_in_long_name = true; +				kcname_in_long_name = true; +				break; +			case snd_soc_dapm_mixer_named_ctl: +				wname_in_long_name = false; +				kcname_in_long_name = true; +				break; +			case snd_soc_dapm_mux: +			case snd_soc_dapm_virt_mux: +			case snd_soc_dapm_value_mux: +				wname_in_long_name = true; +				kcname_in_long_name = false; +				break; +			default: +				kfree(wlist); +				return -EINVAL;  			} +		} + +		if (wname_in_long_name && kcname_in_long_name) { +			name_len = strlen(w->name) - prefix_len + 1 + +				   strlen(w->kcontrol_news[kci].name) + 1; -			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); +			long_name = kmalloc(name_len, GFP_KERNEL); +			if (long_name == NULL) { +				kfree(wlist);  				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. +			/* +			 * 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.  			 */ -			name_len = strlen(w->kcontrol_news[i].name) + 1; -			if (w->id != snd_soc_dapm_mixer_named_ctl) -				name_len += 1 + strlen(w->name); +			snprintf(long_name, name_len, "%s %s", +				 w->name + prefix_len, +				 w->kcontrol_news[kci].name); +			long_name[name_len - 1] = '\0'; -			path->long_name = kmalloc(name_len, GFP_KERNEL); +			name = long_name; +		} else if (wname_in_long_name) { +			long_name = NULL; +			name = w->name + prefix_len; +		} else { +			long_name = NULL; +			name = w->kcontrol_news[kci].name; +		} -			if (path->long_name == NULL) { -				kfree(wlist); -				return -ENOMEM; -			} +		kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name, +					prefix); +		ret = snd_ctl_add(card, kcontrol); +		if (ret < 0) { +			dev_err(dapm->dev, +				"ASoC: failed to add widget %s dapm kcontrol %s: %d\n", +				w->name, name, ret); +			kfree(wlist); +			kfree(long_name); +			return ret; +		} -			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((char *)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((char *)path->long_name, name_len, -					 "%s", w->kcontrol_news[i].name); -				break; -			} +		path->long_name = long_name; +	} -			((char *)path->long_name)[name_len - 1] = '\0'; +	kcontrol->private_data = wlist; +	w->kcontrols[kci] = kcontrol; +	path->kcontrol = kcontrol; -			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 widget" -					" %s dapm kcontrol %s: %d\n", -					w->name, path->long_name, ret); -				kfree(wlist); -				kfree(path->long_name); -				path->long_name = NULL; -				return ret; +	return 0; +} + +/* create new dapm mixer control */ +static int dapm_new_mixer(struct snd_soc_dapm_widget *w) +{ +	int i, ret; +	struct snd_soc_dapm_path *path; + +	/* 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; + +			if (w->kcontrols[i]) { +				path->kcontrol = w->kcontrols[i]; +				continue;  			} -			w->kcontrols[i] = path->kcontrol; + +			ret = dapm_create_or_share_mixmux_kcontrol(w, i, path); +			if (ret < 0) +				return ret;  		}  	} -	return ret; + +	return 0;  }  /* 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; +	struct snd_soc_dapm_path *path;  	int ret; -	struct snd_soc_dapm_widget_list *wlist; -	int shared, wlistentries; -	size_t wlistsize; -	const char *name;  	if (w->num_kcontrols != 1) {  		dev_err(dapm->dev, @@ -631,65 +679,19 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)  		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: %d\n", -				w->name, ret); -			kfree(wlist); -			return ret; -		} +	path = list_first_entry(&w->sources, struct snd_soc_dapm_path, +				list_sink); +	if (!path) { +		dev_err(dapm->dev, "ASoC: mux %s has no paths\n", w->name); +		return -EINVAL;  	} -	kcontrol->private_data = wlist; - -	w->kcontrols[0] = kcontrol; +	ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path); +	if (ret < 0) +		return ret;  	list_for_each_entry(path, &w->sources, list_sink) -		path->kcontrol = kcontrol; +		path->kcontrol = w->kcontrols[0];  	return 0;  } @@ -705,14 +707,33 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)  }  /* reset 'walked' bit for each dapm path */ -static inline void dapm_clear_walk(struct snd_soc_dapm_context *dapm) +static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm, +				   struct list_head *sink)  {  	struct snd_soc_dapm_path *p; -	list_for_each_entry(p, &dapm->card->paths, list) -		p->walked = 0; +	list_for_each_entry(p, sink, list_source) { +		if (p->walked) { +			p->walked = 0; +			dapm_clear_walk_output(dapm, &p->sink->sinks); +		} +	}  } +static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm, +				  struct list_head *source) +{ +	struct snd_soc_dapm_path *p; + +	list_for_each_entry(p, source, list_sink) { +		if (p->walked) { +			p->walked = 0; +			dapm_clear_walk_input(dapm, &p->source->sources); +		} +	} +} + +  /* 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. @@ -995,13 +1016,17 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,  	mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);  	dapm_reset(card); -	if (stream == SNDRV_PCM_STREAM_PLAYBACK) +	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {  		paths = is_connected_output_ep(dai->playback_widget, list); -	else +		dapm_clear_walk_output(&card->dapm, +				       &dai->playback_widget->sinks); +	} else {  		paths = is_connected_input_ep(dai->capture_widget, list); +		dapm_clear_walk_input(&card->dapm, +				      &dai->capture_widget->sources); +	}  	trace_snd_soc_dapm_connected(paths, stream); -	dapm_clear_walk(&card->dapm);  	mutex_unlock(&card->dapm_mutex);  	return paths; @@ -1104,9 +1129,9 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)  	DAPM_UPDATE_STAT(w, power_checks);  	in = is_connected_input_ep(w, NULL); -	dapm_clear_walk(w->dapm); +	dapm_clear_walk_input(w->dapm, &w->sources);  	out = is_connected_output_ep(w, NULL); -	dapm_clear_walk(w->dapm); +	dapm_clear_walk_output(w->dapm, &w->sinks);  	return out != 0 && in != 0;  } @@ -1129,7 +1154,7 @@ static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)  	if (w->active) {  		in = is_connected_input_ep(w, NULL); -		dapm_clear_walk(w->dapm); +		dapm_clear_walk_input(w->dapm, &w->sources);  		return in != 0;  	} else {  		return dapm_generic_check_power(w); @@ -1145,7 +1170,7 @@ static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)  	if (w->active) {  		out = is_connected_output_ep(w, NULL); -		dapm_clear_walk(w->dapm); +		dapm_clear_walk_output(w->dapm, &w->sinks);  		return out != 0;  	} else {  		return dapm_generic_check_power(w); @@ -1177,8 +1202,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)  			return 1;  	} -	dapm_clear_walk(w->dapm); -  	return 0;  } @@ -1759,9 +1782,9 @@ static ssize_t dapm_widget_power_read_file(struct file *file,  		return -ENOMEM;  	in = is_connected_input_ep(w, NULL); -	dapm_clear_walk(w->dapm); +	dapm_clear_walk_input(w->dapm, &w->sources);  	out = is_connected_output_ep(w, NULL); -	dapm_clear_walk(w->dapm); +	dapm_clear_walk_output(w->dapm, &w->sinks);  	ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",  		       w->name, w->power ? "On" : "Off", @@ -3137,7 +3160,6 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,  		break;  	} -	dapm->n_widgets++;  	w->dapm = dapm;  	w->codec = dapm->codec;  	w->platform = dapm->platform;  |