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

Commit 432c641e authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Fix D3 clock stop check for codecs with own set_power_state op



When a codec provides its own set_power_state op, the D3-clock-stop
isn't checked correctly.  And the recent changes for repeating the
state-setting operation isn't applied to such a codec, too.

This patch fixes these issues by moving the call of codec's own op to
the place where the generic power-set operation is done, and move the
power-state synchronization code out of
snd_hda_set_power_state_to_all() so that it can be called always at
the end of power-up/down sequence, and updates the D3 clock-stop flag
properly.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 68467f51
Loading
Loading
Loading
Loading
+37 −24
Original line number Diff line number Diff line
@@ -3518,20 +3518,6 @@ void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
				    power_state);
	}

	if (power_state == AC_PWRST_D0) {
		unsigned long end_time;
		int state;
		/* wait until the codec reachs to D0 */
		end_time = jiffies + msecs_to_jiffies(500);
		do {
			state = snd_hda_codec_read(codec, fg, 0,
						   AC_VERB_GET_POWER_STATE, 0);
			if (state == power_state)
				break;
			msleep(1);
		} while (time_after_eq(end_time, jiffies));
	}
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);

@@ -3551,6 +3537,32 @@ static bool snd_hda_codec_get_supported_ps(struct hda_codec *codec, hda_nid_t fg
		return false;
}

/*
 * wait until the state is reached, returns the current state
 */
static unsigned int hda_sync_power_state(struct hda_codec *codec,
					 hda_nid_t fg,
					 unsigned int power_state)
{
	unsigned long end_time = jiffies + msecs_to_jiffies(500);
	unsigned int state, actual_state;

	for (;;) {
		state = snd_hda_codec_read(codec, fg, 0,
					   AC_VERB_GET_POWER_STATE, 0);
		if (state & AC_PWRST_ERROR)
			break;
		actual_state = (state >> 4) & 0x0f;
		if (actual_state == power_state)
			break;
		if (time_after_eq(jiffies, end_time))
			break;
		/* wait until the codec reachs to the target state */
		msleep(1);
	}
	return state;
}

/*
 * set power state of the codec
 */
@@ -3564,11 +3576,6 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
	codec->d3_stop_clk_ok = 0;
#endif

	if (codec->patch_ops.set_power_state) {
		codec->patch_ops.set_power_state(codec, fg, power_state);
		return;
	}

	/* this delay seems necessary to avoid click noise at power-down */
	if (power_state == AC_PWRST_D3) {
		/* transition time less than 10ms for power down */
@@ -3577,11 +3584,17 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,

	/* repeat power states setting at most 10 times*/
	for (count = 0; count < 10; count++) {
		snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
		if (codec->patch_ops.set_power_state)
			codec->patch_ops.set_power_state(codec, fg,
							 power_state);
		snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
		state = snd_hda_codec_read(codec, fg, 0,
					   AC_VERB_GET_POWER_STATE, 0);
		else {
			snd_hda_codec_read(codec, fg, 0,
					   AC_VERB_SET_POWER_STATE,
					   power_state);
			snd_hda_codec_set_power_to_all(codec, fg, power_state,
						       true);
		}
		state = hda_sync_power_state(codec, fg, power_state);
		if (!(state & AC_PWRST_ERROR))
			break;
	}