Commit 7b451962 authored by Mark Brown's avatar Mark Brown
Browse files

Merge remote-tracking branch 'asoc/topic/dapm' into asoc-next

parents 69976189 1059ecfa
Loading
Loading
Loading
Loading
+171 −148
Original line number Diff line number Diff line
@@ -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++) {

		/* 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;
	shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],
					 &kcontrol);

			if (w->kcontrols[i]) {
				path->kcontrol = w->kcontrols[i];
				continue;
	if (kcontrol) {
		wlist = kcontrol->private_data;
		wlistentries = wlist->num_widgets + 1;
	} else {
		wlist = NULL;
		wlistentries = 1;
	}

	wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
				    sizeof(struct snd_soc_dapm_widget *),
			wlist = kzalloc(wlistsize, GFP_KERNEL);
			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",
		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;
	wlist->num_widgets = wlistentries;
	wlist->widgets[wlistentries - 1] = 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);
	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;
			}
		}

			path->long_name = kmalloc(name_len, GFP_KERNEL);
		if (wname_in_long_name && kcname_in_long_name) {
			name_len = strlen(w->name) - prefix_len + 1 +
				   strlen(w->kcontrol_news[kci].name) + 1;

			if (path->long_name == NULL) {
			long_name = kmalloc(name_len, GFP_KERNEL);
			if (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.
			/*
			 * 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;
			snprintf(long_name, name_len, "%s %s",
				 w->name + prefix_len,
				 w->kcontrol_news[kci].name);
			long_name[name_len - 1] = '\0';

			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;
		}

			((char *)path->long_name)[name_len - 1] = '\0';

			path->kcontrol = snd_soc_cnew(&w->kcontrol_news[i],
						      wlist, path->long_name,
		kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name,
					prefix);
			ret = snd_ctl_add(card, path->kcontrol);
		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, path->long_name, ret);
			dev_err(dapm->dev,
				"ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
				w->name, name, ret);
			kfree(wlist);
				kfree(path->long_name);
				path->long_name = NULL;
			kfree(long_name);
			return ret;
		}
			w->kcontrols[i] = path->kcontrol;

		path->long_name = long_name;
	}

	kcontrol->private_data = wlist;
	w->kcontrols[kci] = kcontrol;
	path->kcontrol = kcontrol;

	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;
			}

			ret = dapm_create_or_share_mixmux_kcontrol(w, i, path);
			if (ret < 0)
				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;
	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;
	}

		/*
		 * 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);
	ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path);
	if (ret < 0)
		return ret;
		}
	}

	kcontrol->private_data = wlist;

	w->kcontrols[0] = kcontrol;

	list_for_each_entry(path, &w->sources, list_sink)
		path->kcontrol = kcontrol;
		path->kcontrol = w->kcontrols[0];

	return 0;
}
@@ -705,13 +707,32 @@ 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, 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, &dapm->card->paths, list)
	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
@@ -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",