|
@@ -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;
|
|
|
}
|