Skip to content

Commit

Permalink
ALSA: hda - Add support for vref-out based mute LED control on IDT co…
Browse files Browse the repository at this point in the history
…decs

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: Vitaliy Kulikov <Vitaliy.Kulikov@idt.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Vitaliy Kulikov authored and Takashi Iwai committed Jul 27, 2011
1 parent 636f785 commit 45eebda
Showing 1 changed file with 156 additions and 43 deletions.
199 changes: 156 additions & 43 deletions sound/pci/hda/patch_sigmatel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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) &
Expand All @@ -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;
}

Expand All @@ -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 = {
Expand All @@ -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,
};
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down

0 comments on commit 45eebda

Please sign in to comment.