Commit 45eebda7 authored by Vitaliy Kulikov's avatar Vitaliy Kulikov Committed by Takashi Iwai

ALSA: hda - Add support for vref-out based mute LED control on IDT codecs

This patch also registers all necessary callbacks to support mute LED
only when such control is enabled. And it keeps codec AFG in D0 or D1
state all the time when aggressive power managemnt is enabled for vref-out
control (and mute LED) work correctly.
Signed-off-by: default avatarVitaliy Kulikov <Vitaliy.Kulikov@idt.com>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 636f7858
......@@ -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 @@ static int patch_stac92hd83xxx(struct hda_codec *codec)
#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 @@ static int patch_stac92hd71bxx(struct hda_codec *codec)
#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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment