diff options
| -rw-r--r-- | sound/pci/hda/patch_sigmatel.c | 199 | 
1 files changed, 156 insertions, 43 deletions
| diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index fcf4c7142103..aa376b59c006 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -213,6 +213,7 @@ struct sigmatel_spec {  	unsigned int gpio_mute;  	unsigned int gpio_led;  	unsigned int gpio_led_polarity; +	unsigned int vref_led;  	/* stream */  	unsigned int stream_delay; @@ -672,6 +673,30 @@ static int stac92xx_smux_enum_put(struct snd_kcontrol *kcontrol,  	return 0;  } +static int stac_vrefout_set(struct hda_codec *codec, +					hda_nid_t nid, unsigned int new_vref) +{ +	int error, pinctl; + +	snd_printdd("%s, nid %x ctl %x\n", __func__, nid, new_vref); +	pinctl = snd_hda_codec_read(codec, nid, 0, +				AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + +	if (pinctl < 0) +		return pinctl; + +	pinctl &= 0xff; +	pinctl &= ~AC_PINCTL_VREFEN; +	pinctl |= (new_vref & AC_PINCTL_VREFEN); + +	error = snd_hda_codec_write_cache(codec, nid, 0, +					AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl); +	if (error < 0) +		return error; + +	return 1; +} +  static unsigned int stac92xx_vref_set(struct hda_codec *codec,  					hda_nid_t nid, unsigned int new_vref)  { @@ -4069,6 +4094,8 @@ static void stac_gpio_set(struct hda_codec *codec, unsigned int mask,  {  	unsigned int gpiostate, gpiomask, gpiodir; +	snd_printdd("%s msk %x dir %x gpio %x\n", __func__, mask, dir_mask, data); +  	gpiostate = snd_hda_codec_read(codec, codec->afg, 0,  				       AC_VERB_GET_GPIO_DATA, 0);  	gpiostate = (gpiostate & ~dir_mask) | (data & dir_mask); @@ -4258,10 +4285,12 @@ static void stac_store_hints(struct hda_codec *codec)  		spec->eapd_switch = val;  	get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);  	if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) { -		spec->gpio_mask |= spec->gpio_led; -		spec->gpio_dir |= spec->gpio_led; -		if (spec->gpio_led_polarity) -			spec->gpio_data |= spec->gpio_led; +		if (spec->gpio_led <= 8) { +			spec->gpio_mask |= spec->gpio_led; +			spec->gpio_dir |= spec->gpio_led; +			if (spec->gpio_led_polarity) +				spec->gpio_data |= spec->gpio_led; +		}  	}  } @@ -4431,11 +4460,26 @@ static void stac92xx_free_kctls(struct hda_codec *codec)  	snd_array_free(&spec->kctls);  } +static void stac92xx_shutup_pins(struct hda_codec *codec) +{ +	unsigned int i, def_conf; + +	if (codec->bus->shutdown) +		return; +	for (i = 0; i < codec->init_pins.used; i++) { +		struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i); +		def_conf = snd_hda_codec_get_pincfg(codec, pin->nid); +		if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE) +			snd_hda_codec_write(codec, pin->nid, 0, +				    AC_VERB_SET_PIN_WIDGET_CONTROL, 0); +	} +} +  static void stac92xx_shutup(struct hda_codec *codec)  {  	struct sigmatel_spec *spec = codec->spec; -	snd_hda_shutup_pins(codec); +	stac92xx_shutup_pins(codec);  	if (spec->eapd_mask)  		stac_gpio_set(codec, spec->gpio_mask, @@ -4833,10 +4877,11 @@ static int find_mute_led_gpio(struct hda_codec *codec, int default_polarity)  	if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {  		while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,  								NULL, dev))) { -			if (sscanf(dev->name, "HP_Mute_LED_%d_%d", +			if (sscanf(dev->name, "HP_Mute_LED_%d_%x",  				  &spec->gpio_led_polarity,  				  &spec->gpio_led) == 2) { -				spec->gpio_led = 1 << spec->gpio_led; +				if (spec->gpio_led < 4) +					spec->gpio_led = 1 << spec->gpio_led;  				return 1;  			}  			if (sscanf(dev->name, "HP_Mute_LED_%d", @@ -4935,17 +4980,6 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,  #endif  #ifdef CONFIG_PM -static int stac92xx_pre_resume(struct hda_codec *codec) -{ -	struct sigmatel_spec *spec = codec->spec; - -	/* sync mute LED */ -	if (spec->gpio_led) -		stac_gpio_set(codec, spec->gpio_mask, -				spec->gpio_dir, spec->gpio_data); -	return 0; -} -  static int stac92xx_resume(struct hda_codec *codec)  {  	struct sigmatel_spec *spec = codec->spec; @@ -4964,7 +4998,65 @@ static int stac92xx_resume(struct hda_codec *codec)  	return 0;  } +static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) +{ +	stac92xx_shutup(codec); +	return 0; +} +  #ifdef CONFIG_SND_HDA_POWER_SAVE +static int stac92xx_pre_resume(struct hda_codec *codec) +{ +	struct sigmatel_spec *spec = codec->spec; + +	/* sync mute LED */ +	if (spec->gpio_led) { +		if (spec->gpio_led <= 8) { +			stac_gpio_set(codec, spec->gpio_mask, +					spec->gpio_dir, spec->gpio_data); +		} else { +			stac_vrefout_set(codec, +					spec->gpio_led, spec->vref_led); +		} +	} +	return 0; +} + +static int stac92xx_post_suspend(struct hda_codec *codec) +{ +	struct sigmatel_spec *spec = codec->spec; +	if (spec->gpio_led > 8) { +		/* with vref-out pin used for mute led control +		 * codec AFG is prevented from D3 state, but on +		 * system suspend it can (and should) be used +		 */ +		snd_hda_codec_read(codec, codec->afg, 0, +				AC_VERB_SET_POWER_STATE, AC_PWRST_D3); +	} +	return 0; +} + +static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg, +				unsigned int power_state) +{ +	unsigned int afg_power_state = power_state; +	struct sigmatel_spec *spec = codec->spec; + +	if (power_state == AC_PWRST_D3) { +		if (spec->gpio_led > 8) { +			/* with vref-out pin used for mute led control +			 * codec AFG is prevented from D3 state +			 */ +			afg_power_state = AC_PWRST_D1; +		} +		/* this delay seems necessary to avoid click noise at power-down */ +		msleep(100); +	} +	snd_hda_codec_read(codec, fg, 0, AC_VERB_SET_POWER_STATE, +			afg_power_state); +	snd_hda_codec_set_power_to_all(codec, fg, power_state, true); +} +  /*   * For this feature CONFIG_SND_HDA_POWER_SAVE is needed   * as mute LED state is updated in check_power_status hook @@ -4973,8 +5065,12 @@ static int stac92xx_update_led_status(struct hda_codec *codec)  {  	struct sigmatel_spec *spec = codec->spec;  	int i, num_ext_dacs, muted = 1; +	unsigned int muted_lvl, notmtd_lvl;  	hda_nid_t nid; +	if (!spec->gpio_led) +		return 0; +  	for (i = 0; i < spec->multiout.num_dacs; i++) {  		nid = spec->multiout.dac_nids[i];  		if (!(snd_hda_codec_amp_read(codec, nid, 0, HDA_OUTPUT, 0) & @@ -4999,17 +5095,27 @@ static int stac92xx_update_led_status(struct hda_codec *codec)  			muted = 0; /* extra output is not muted */  		}  	} -	if (muted) -		spec->gpio_data &= ~spec->gpio_led; /* orange */ -	else -		spec->gpio_data |= spec->gpio_led; /* white */ +	/*polarity defines *not* muted state level*/ +	if (spec->gpio_led <= 8) { +		if (muted) +			spec->gpio_data &= ~spec->gpio_led; /* orange */ +		else +			spec->gpio_data |= spec->gpio_led; /* white */ -	if (!spec->gpio_led_polarity) { -		/* LED state is inverted on these systems */ -		spec->gpio_data ^= spec->gpio_led; +		if (!spec->gpio_led_polarity) { +			/* LED state is inverted on these systems */ +			spec->gpio_data ^= spec->gpio_led; +		} +		stac_gpio_set(codec, spec->gpio_mask, +				spec->gpio_dir, spec->gpio_data); +	} else { +		notmtd_lvl = spec->gpio_led_polarity ? +				AC_PINCTL_VREF_HIZ : AC_PINCTL_VREF_GRD; +		muted_lvl = spec->gpio_led_polarity ? +				AC_PINCTL_VREF_GRD : AC_PINCTL_VREF_HIZ; +		spec->vref_led = muted ? muted_lvl : notmtd_lvl; +		stac_vrefout_set(codec,	spec->gpio_led, spec->vref_led);  	} - -	stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);  	return 0;  } @@ -5023,13 +5129,7 @@ static int stac92xx_check_power_status(struct hda_codec *codec,  	return 0;  } -#endif - -static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state) -{ -	stac92xx_shutup(codec); -	return 0; -} +#endif /* CONFIG_SND_HDA_POWER_SAVE */  #endif /* CONFIG_PM */  static const struct hda_codec_ops stac92xx_patch_ops = { @@ -5041,7 +5141,6 @@ static const struct hda_codec_ops stac92xx_patch_ops = {  #ifdef CONFIG_PM  	.suspend = stac92xx_suspend,  	.resume = stac92xx_resume, -	.pre_resume = stac92xx_pre_resume,  #endif  	.reboot_notify = stac92xx_shutup,  }; @@ -5555,10 +5654,17 @@ again:  #ifdef CONFIG_SND_HDA_POWER_SAVE  	if (spec->gpio_led) { -		spec->gpio_mask |= spec->gpio_led; -		spec->gpio_dir |= spec->gpio_led; -		spec->gpio_data |= spec->gpio_led; -		/* register check_power_status callback. */ +		if (spec->gpio_led <= 8) { +			spec->gpio_mask |= spec->gpio_led; +			spec->gpio_dir |= spec->gpio_led; +			spec->gpio_data |= spec->gpio_led; +		} else { +			codec->patch_ops.set_power_state = +					stac92xx_set_power_state; +			codec->patch_ops.post_suspend = +					stac92xx_post_suspend; +		} +		codec->patch_ops.pre_resume = stac92xx_pre_resume;  		codec->patch_ops.check_power_status =  			stac92xx_check_power_status;  	} @@ -5883,10 +5989,17 @@ again:  #ifdef CONFIG_SND_HDA_POWER_SAVE  	if (spec->gpio_led) { -		spec->gpio_mask |= spec->gpio_led; -		spec->gpio_dir |= spec->gpio_led; -		spec->gpio_data |= spec->gpio_led; -		/* register check_power_status callback. */ +		if (spec->gpio_led <= 8) { +			spec->gpio_mask |= spec->gpio_led; +			spec->gpio_dir |= spec->gpio_led; +			spec->gpio_data |= spec->gpio_led; +		} else { +			codec->patch_ops.set_power_state = +					stac92xx_set_power_state; +			codec->patch_ops.post_suspend = +					stac92xx_post_suspend; +		} +		codec->patch_ops.pre_resume = stac92xx_pre_resume;  		codec->patch_ops.check_power_status =  			stac92xx_check_power_status;  	} | 
