Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e09b67e authored by Mark Brown's avatar Mark Brown
Browse files

Merge branch 'dapm' into for-2.6.32

parents fa44c077 291f3bbc
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -369,8 +369,6 @@ struct snd_soc_codec {
	enum snd_soc_bias_level bias_level;
	enum snd_soc_bias_level suspend_bias_level;
	struct delayed_work delayed_work;
	struct list_head up_list;
	struct list_head down_list;

	/* codec DAI's */
	struct snd_soc_dai *dai;
+227 −75
Original line number Diff line number Diff line
@@ -52,19 +52,37 @@

/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
	snd_soc_dapm_pre, snd_soc_dapm_supply, snd_soc_dapm_micbias,
	snd_soc_dapm_mic, snd_soc_dapm_mux, snd_soc_dapm_value_mux,
	snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_mixer_named_ctl,
	snd_soc_dapm_pga, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
	snd_soc_dapm_post
	[snd_soc_dapm_pre] = 0,
	[snd_soc_dapm_supply] = 1,
	[snd_soc_dapm_micbias] = 2,
	[snd_soc_dapm_mic] = 3,
	[snd_soc_dapm_mux] = 4,
	[snd_soc_dapm_value_mux] = 4,
	[snd_soc_dapm_dac] = 5,
	[snd_soc_dapm_mixer] = 6,
	[snd_soc_dapm_mixer_named_ctl] = 6,
	[snd_soc_dapm_pga] = 7,
	[snd_soc_dapm_adc] = 8,
	[snd_soc_dapm_hp] = 9,
	[snd_soc_dapm_spk] = 10,
	[snd_soc_dapm_post] = 11,
};

static int dapm_down_seq[] = {
	snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
	snd_soc_dapm_pga, snd_soc_dapm_mixer_named_ctl, snd_soc_dapm_mixer,
	snd_soc_dapm_dac, snd_soc_dapm_mic, snd_soc_dapm_micbias,
	snd_soc_dapm_mux, snd_soc_dapm_value_mux, snd_soc_dapm_supply,
	snd_soc_dapm_post
	[snd_soc_dapm_pre] = 0,
	[snd_soc_dapm_adc] = 1,
	[snd_soc_dapm_hp] = 2,
	[snd_soc_dapm_spk] = 3,
	[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_value_mux] = 9,
	[snd_soc_dapm_supply] = 10,
	[snd_soc_dapm_post] = 11,
};

static void pop_wait(u32 pop_time)
@@ -689,53 +707,211 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
	return power;
}

/*
 * Scan a single DAPM widget for a complete audio path and update the
 * power status appropriately.
static int dapm_seq_compare(struct snd_soc_dapm_widget *a,
			    struct snd_soc_dapm_widget *b,
			    int sort[])
{
	if (sort[a->id] != sort[b->id])
		return sort[a->id] - sort[b->id];
	if (a->reg != b->reg)
		return a->reg - b->reg;

	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,
			    int sort[])
{
	struct snd_soc_dapm_widget *w;

	list_for_each_entry(w, list, power_list)
		if (dapm_seq_compare(new_widget, w, sort) < 0) {
			list_add_tail(&new_widget->power_list, &w->power_list);
			return;
		}

	list_add_tail(&new_widget->power_list, list);
}

/* Apply the coalesced changes from a DAPM sequence */
static void dapm_seq_run_coalesced(struct snd_soc_codec *codec,
				   struct list_head *pending)
{
	struct snd_soc_dapm_widget *w;
	int reg, power, ret;
	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(codec->pop_time,
			"pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
			w->name, reg, value, mask);

		/* power up pre event */
		if (w->power && w->event &&
		    (w->event_flags & SND_SOC_DAPM_PRE_PMU)) {
			pop_dbg(codec->pop_time, "pop test : %s PRE_PMU\n",
				w->name);
			ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMU);
			if (ret < 0)
				pr_err("%s: pre event failed: %d\n",
				       w->name, ret);
		}

		/* power down pre event */
		if (!w->power && w->event &&
		    (w->event_flags & SND_SOC_DAPM_PRE_PMD)) {
			pop_dbg(codec->pop_time, "pop test : %s PRE_PMD\n",
				w->name);
			ret = w->event(w, NULL, SND_SOC_DAPM_PRE_PMD);
			if (ret < 0)
				pr_err("%s: pre event failed: %d\n",
				       w->name, ret);
		}

		/* Lower PGA volume to reduce pops */
		if (w->id == snd_soc_dapm_pga && !w->power)
			dapm_set_pga(w, w->power);
	}

	if (reg >= 0) {
		pop_dbg(codec->pop_time,
			"pop test : Applying 0x%x/0x%x to %x in %dms\n",
			value, mask, reg, codec->pop_time);
		pop_wait(codec->pop_time);
		snd_soc_update_bits(codec, reg, mask, value);
	}

	list_for_each_entry(w, pending, power_list) {
		/* Raise PGA volume to reduce pops */
		if (w->id == snd_soc_dapm_pga && w->power)
			dapm_set_pga(w, w->power);

		/* power up post event */
		if (w->power && w->event &&
		    (w->event_flags & SND_SOC_DAPM_POST_PMU)) {
			pop_dbg(codec->pop_time, "pop test : %s POST_PMU\n",
				w->name);
			ret = w->event(w,
				       NULL, SND_SOC_DAPM_POST_PMU);
			if (ret < 0)
				pr_err("%s: post event failed: %d\n",
				       w->name, ret);
		}

		/* power down post event */
		if (!w->power && w->event &&
		    (w->event_flags & SND_SOC_DAPM_POST_PMD)) {
			pop_dbg(codec->pop_time, "pop test : %s POST_PMD\n",
				w->name);
			ret = w->event(w, NULL, SND_SOC_DAPM_POST_PMD);
			if (ret < 0)
				pr_err("%s: post event failed: %d\n",
				       w->name, ret);
		}
	}
}

/* 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 int dapm_power_widget(struct snd_soc_codec *codec, int event,
			     struct snd_soc_dapm_widget *w)
static void dapm_seq_run(struct snd_soc_codec *codec, struct list_head *list,
			 int event, int sort[])
{
	struct snd_soc_dapm_widget *w, *n;
	LIST_HEAD(pending);
	int cur_sort = -1;
	int cur_reg = SND_SOC_NOPM;
	int ret;

	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) {
			if (!list_empty(&pending))
				dapm_seq_run_coalesced(codec, &pending);

			INIT_LIST_HEAD(&pending);
			cur_sort = -1;
			cur_reg = SND_SOC_NOPM;
		}

		switch (w->id) {
		case snd_soc_dapm_pre:
			if (!w->event)
			return 0;
				list_for_each_entry_safe_continue(w, n, list,
								  power_list);

		if (event == SND_SOC_DAPM_STREAM_START) {
			if (event == SND_SOC_DAPM_STREAM_START)
				ret = w->event(w,
					       NULL, SND_SOC_DAPM_PRE_PMU);
			if (ret < 0)
				return ret;
		} else if (event == SND_SOC_DAPM_STREAM_STOP) {
			else if (event == SND_SOC_DAPM_STREAM_STOP)
				ret = w->event(w,
					       NULL, SND_SOC_DAPM_PRE_PMD);
			if (ret < 0)
				return ret;
		}
		return 0;
			break;

		case snd_soc_dapm_post:
			if (!w->event)
			return 0;
				list_for_each_entry_safe_continue(w, n, list,
								  power_list);

		if (event == SND_SOC_DAPM_STREAM_START) {
			if (event == SND_SOC_DAPM_STREAM_START)
				ret = w->event(w,
					       NULL, SND_SOC_DAPM_POST_PMU);
			if (ret < 0)
				return ret;
		} else if (event == SND_SOC_DAPM_STREAM_STOP) {
			else if (event == SND_SOC_DAPM_STREAM_STOP)
				ret = w->event(w,
					       NULL, SND_SOC_DAPM_POST_PMD);
			if (ret < 0)
				return ret;
		}
		return 0;
			break;

		case snd_soc_dapm_input:
		case snd_soc_dapm_output:
		case snd_soc_dapm_hp:
		case snd_soc_dapm_mic:
		case snd_soc_dapm_line:
		case snd_soc_dapm_spk:
			/* No register support currently */
			ret = dapm_generic_apply_power(w);
			break;

		default:
		return dapm_generic_apply_power(w);
			/* Queue it up for application */
			cur_sort = sort[w->id];
			cur_reg = w->reg;
			list_move(&w->power_list, &pending);
			break;
		}

		if (ret < 0)
			pr_err("Failed to apply widget power: %d\n",
			       ret);
	}

	if (!list_empty(&pending))
		dapm_seq_run_coalesced(codec, &pending);
}

/*
@@ -751,23 +927,22 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
{
	struct snd_soc_device *socdev = codec->socdev;
	struct snd_soc_dapm_widget *w;
	LIST_HEAD(up_list);
	LIST_HEAD(down_list);
	int ret = 0;
	int i, power;
	int power;
	int sys_power = 0;

	INIT_LIST_HEAD(&codec->up_list);
	INIT_LIST_HEAD(&codec->down_list);

	/* 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, &codec->dapm_widgets, list) {
		switch (w->id) {
		case snd_soc_dapm_pre:
			list_add_tail(&codec->down_list, &w->power_list);
			dapm_seq_insert(w, &down_list, dapm_down_seq);
			break;
		case snd_soc_dapm_post:
			list_add_tail(&codec->up_list, &w->power_list);
			dapm_seq_insert(w, &up_list, dapm_up_seq);
			break;

		default:
@@ -782,10 +957,9 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
				continue;

			if (power)
				list_add_tail(&w->power_list, &codec->up_list);
				dapm_seq_insert(w, &up_list, dapm_up_seq);
			else
				list_add_tail(&w->power_list,
					      &codec->down_list);
				dapm_seq_insert(w, &down_list, dapm_down_seq);

			w->power = power;
			break;
@@ -802,32 +976,10 @@ static int dapm_power_widgets(struct snd_soc_codec *codec, int event)
	}

	/* Power down widgets first; try to avoid amplifying pops. */
	for (i = 0; i < ARRAY_SIZE(dapm_down_seq); i++) {
		list_for_each_entry(w, &codec->down_list, power_list) {
			/* is widget in stream order */
			if (w->id != dapm_down_seq[i])
				continue;

			ret = dapm_power_widget(codec, event, w);
			if (ret != 0)
				pr_err("Failed to power down %s: %d\n",
				       w->name, ret);
		}
	}
	dapm_seq_run(codec, &down_list, event, dapm_down_seq);

	/* Now power up. */
	for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++) {
		list_for_each_entry(w, &codec->up_list, power_list) {
			/* is widget in stream order */
			if (w->id != dapm_up_seq[i])
				continue;

			ret = dapm_power_widget(codec, event, w);
			if (ret != 0)
				pr_err("Failed to power up %s: %d\n",
				       w->name, ret);
		}
	}
	dapm_seq_run(codec, &up_list, event, dapm_up_seq);

	/* If we just powered the last thing off drop to standby bias */
	if (codec->bias_level == SND_SOC_BIAS_PREPARE && !sys_power) {