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

Commit 4d7fbdbc authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: hda - Allow codec-specific set_power_state ops



The procedure for codec D-state change may have exceptional cases
depending on the codec chip, such as a longer delay or suppressing D3.

This patch adds a new codec ops, set_power_state() to override the system
default function.  For ease of porting, snd_hda_codec_set_power_to_all()
helper function is extracted from the default set_power_state() function.

As an example, the Conexant codec-specific delay is removed from the
default routine but moved to patch_conexant.c.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent e581f3db
Loading
Loading
Loading
Loading
+37 −41
Original line number Original line Diff line number Diff line
@@ -3203,52 +3203,31 @@ void snd_hda_sequence_write_cache(struct hda_codec *codec,
EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
EXPORT_SYMBOL_HDA(snd_hda_sequence_write_cache);
#endif /* CONFIG_PM */
#endif /* CONFIG_PM */


/*
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
 * set power state of the codec
				    unsigned int power_state,
 */
				    bool eapd_workaround)
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
				unsigned int power_state)
{
{
	hda_nid_t nid;
	hda_nid_t nid = codec->start_nid;
	int i;
	int i;


	/* this delay seems necessary to avoid click noise at power-down */
	if (power_state == AC_PWRST_D3)
		msleep(100);
	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
			    power_state);
	/* partial workaround for "azx_get_response timeout" */
	if (power_state == AC_PWRST_D0 &&
	    (codec->vendor_id & 0xffff0000) == 0x14f10000)
		msleep(10);

	nid = codec->start_nid;
	for (i = 0; i < codec->num_nodes; i++, nid++) {
	for (i = 0; i < codec->num_nodes; i++, nid++) {
		unsigned int wcaps = get_wcaps(codec, nid);
		unsigned int wcaps = get_wcaps(codec, nid);
		if (wcaps & AC_WCAP_POWER) {
		if (!(wcaps & AC_WCAP_POWER))
			unsigned int wid_type = get_wcaps_type(wcaps);
			continue;
			if (power_state == AC_PWRST_D3 &&
		/* don't power down the widget if it controls eapd and
			    wid_type == AC_WID_PIN) {
		 * EAPD_BTLENABLE is set.
				unsigned int pincap;
				/*
				 * don't power down the widget if it controls
				 * eapd and EAPD_BTLENABLE is set.
		 */
		 */
				pincap = snd_hda_query_pin_caps(codec, nid);
		if (eapd_workaround && power_state == AC_PWRST_D3 &&
				if (pincap & AC_PINCAP_EAPD) {
		    get_wcaps_type(wcaps) == AC_WID_PIN &&
					int eapd = snd_hda_codec_read(codec,
		    (snd_hda_query_pin_caps(codec, nid) & AC_PINCAP_EAPD)) {
						nid, 0,
			int eapd = snd_hda_codec_read(codec, nid, 0,
						AC_VERB_GET_EAPD_BTLENABLE, 0);
						AC_VERB_GET_EAPD_BTLENABLE, 0);
					eapd &= 0x02;
			if (eapd & 0x02)
					if (eapd)
				continue;
				continue;
		}
		}
			}
		snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_POWER_STATE,
			snd_hda_codec_write(codec, nid, 0,
					    AC_VERB_SET_POWER_STATE,
				    power_state);
				    power_state);
	}
	}
	}


	if (power_state == AC_PWRST_D0) {
	if (power_state == AC_PWRST_D0) {
		unsigned long end_time;
		unsigned long end_time;
@@ -3264,6 +3243,26 @@ static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
		} while (time_after_eq(end_time, jiffies));
		} while (time_after_eq(end_time, jiffies));
	}
	}
}
}
EXPORT_SYMBOL_HDA(snd_hda_codec_set_power_to_all);

/*
 * set power state of the codec
 */
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
				unsigned int power_state)
{
	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)
		msleep(100);
	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);
}


#ifdef CONFIG_SND_HDA_HWDEP
#ifdef CONFIG_SND_HDA_HWDEP
/* execute additional init verbs */
/* execute additional init verbs */
@@ -4073,9 +4072,6 @@ int snd_hda_add_new_ctls(struct hda_codec *codec,
EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);
EXPORT_SYMBOL_HDA(snd_hda_add_new_ctls);


#ifdef CONFIG_SND_HDA_POWER_SAVE
#ifdef CONFIG_SND_HDA_POWER_SAVE
static void hda_set_power_state(struct hda_codec *codec, hda_nid_t fg,
				unsigned int power_state);

static void hda_power_work(struct work_struct *work)
static void hda_power_work(struct work_struct *work)
{
{
	struct hda_codec *codec =
	struct hda_codec *codec =
+5 −0
Original line number Original line Diff line number Diff line
@@ -700,6 +700,8 @@ struct hda_codec_ops {
	int (*init)(struct hda_codec *codec);
	int (*init)(struct hda_codec *codec);
	void (*free)(struct hda_codec *codec);
	void (*free)(struct hda_codec *codec);
	void (*unsol_event)(struct hda_codec *codec, unsigned int res);
	void (*unsol_event)(struct hda_codec *codec, unsigned int res);
	void (*set_power_state)(struct hda_codec *codec, hda_nid_t fg,
				unsigned int power_state);
#ifdef CONFIG_PM
#ifdef CONFIG_PM
	int (*suspend)(struct hda_codec *codec, pm_message_t state);
	int (*suspend)(struct hda_codec *codec, pm_message_t state);
	int (*post_suspend)(struct hda_codec *codec);
	int (*post_suspend)(struct hda_codec *codec);
@@ -1006,6 +1008,9 @@ int snd_hda_is_supported_format(struct hda_codec *codec, hda_nid_t nid,
 */
 */
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_get_codec_name(struct hda_codec *codec, char *name, int namelen);
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
void snd_hda_bus_reboot_notify(struct hda_bus *bus);
void snd_hda_codec_set_power_to_all(struct hda_codec *codec, hda_nid_t fg,
				    unsigned int power_state,
				    bool eapd_workaround);


/*
/*
 * power management
 * power management
+14 −0
Original line number Original line Diff line number Diff line
@@ -446,6 +446,19 @@ static int conexant_init_jacks(struct hda_codec *codec)
	return 0;
	return 0;
}
}


static void conexant_set_power(struct hda_codec *codec, hda_nid_t fg,
			       unsigned int power_state)
{
	if (power_state == AC_PWRST_D3)
		msleep(100);
	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE,
			    power_state);
	/* partial workaround for "azx_get_response timeout" */
	if (power_state == AC_PWRST_D0)
		msleep(10);
	snd_hda_codec_set_power_to_all(codec, fg, power_state, true);
}

static int conexant_init(struct hda_codec *codec)
static int conexant_init(struct hda_codec *codec)
{
{
	struct conexant_spec *spec = codec->spec;
	struct conexant_spec *spec = codec->spec;
@@ -588,6 +601,7 @@ static const struct hda_codec_ops conexant_patch_ops = {
	.build_pcms = conexant_build_pcms,
	.build_pcms = conexant_build_pcms,
	.init = conexant_init,
	.init = conexant_init,
	.free = conexant_free,
	.free = conexant_free,
	.set_power_state = conexant_set_power,
#ifdef CONFIG_SND_HDA_POWER_SAVE
#ifdef CONFIG_SND_HDA_POWER_SAVE
	.suspend = conexant_suspend,
	.suspend = conexant_suspend,
#endif
#endif