ALSA: hda - Fix AFG power management on IDT 92HD* codecs

The AFG pin power-mapping isn't properly set for the fixed I/O pins
on IDT 92HD* codecs.  This resulted in the low power mode after the
boot until any jack detection is executed, thus no output from the
speaker.

This patch fixes the power mapping for the fixed pins, and also fixes
the GPIO bits and digital I/O pin settings properly in stac92xx_ini().

Reference: Novell bnc#446025
	https://bugzilla.novell.com/show_bug.cgi?id=446025

Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Iwai 2008-11-25 08:21:51 +01:00
Родитель 9e97697666
Коммит f73d35853e
1 изменённых файлов: 61 добавлений и 33 удалений

Просмотреть файл

@ -3697,10 +3697,14 @@ static void stac92xx_power_down(struct hda_codec *codec)
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
}
static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
int enable);
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int gpio;
int i;
snd_hda_sequence_write(codec, spec->init);
@ -3711,6 +3715,16 @@ static int stac92xx_init(struct hda_codec *codec)
snd_hda_codec_write_cache(codec,
spec->adc_nids[i], 0,
AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
/* set up GPIO */
gpio = spec->gpio_data;
/* turn on EAPD statically when spec->eapd_switch isn't set.
* otherwise, unsol event will turn it on/off dynamically
*/
if (!spec->eapd_switch)
gpio |= spec->eapd_mask;
stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, gpio);
/* set up pins */
if (spec->hp_detect) {
/* Enable unsolicited responses on the HP widget */
@ -3750,39 +3764,43 @@ static int stac92xx_init(struct hda_codec *codec)
for (i = 0; i < spec->num_dmics; i++)
stac92xx_auto_set_pinctl(codec, spec->dmic_nids[i],
AC_PINCTL_IN_EN);
for (i = 0; i < spec->num_pwrs; i++) {
int event = is_nid_hp_pin(cfg, spec->pwr_nids[i])
? STAC_HP_EVENT : STAC_PWR_EVENT;
int pinctl = snd_hda_codec_read(codec, spec->pwr_nids[i],
0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
int def_conf = snd_hda_codec_read(codec, spec->pwr_nids[i],
0, AC_VERB_GET_CONFIG_DEFAULT, 0);
def_conf = get_defcfg_connect(def_conf);
/* outputs are only ports capable of power management
* any attempts on powering down a input port cause the
* referenced VREF to act quirky.
*/
if (pinctl & AC_PINCTL_IN_EN)
continue;
/* skip any ports that don't have jacks since presence
* detection is useless */
if (def_conf && def_conf != AC_JACK_PORT_FIXED)
continue;
enable_pin_detect(codec, spec->pwr_nids[i], event | i);
codec->patch_ops.unsol_event(codec, (event | i) << 26);
}
if (spec->dac_list)
stac92xx_power_down(codec);
if (cfg->dig_out_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
AC_PINCTL_OUT_EN);
if (cfg->dig_in_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_in_pin,
AC_PINCTL_IN_EN);
for (i = 0; i < spec->num_pwrs; i++) {
hda_nid_t nid = spec->pwr_nids[i];
int pinctl, def_conf;
int event = STAC_PWR_EVENT;
stac_gpio_set(codec, spec->gpio_mask,
spec->gpio_dir, spec->gpio_data);
if (is_nid_hp_pin(cfg, nid) && spec->hp_detect)
continue; /* already has an unsol event */
pinctl = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
/* outputs are only ports capable of power management
* any attempts on powering down a input port cause the
* referenced VREF to act quirky.
*/
if (pinctl & AC_PINCTL_IN_EN)
continue;
def_conf = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_CONFIG_DEFAULT, 0);
def_conf = get_defcfg_connect(def_conf);
/* skip any ports that don't have jacks since presence
* detection is useless */
if (def_conf != AC_JACK_PORT_COMPLEX) {
if (def_conf != AC_JACK_PORT_NONE)
stac_toggle_power_map(codec, nid, 1);
continue;
}
enable_pin_detect(codec, spec->pwr_nids[i], event | i);
codec->patch_ops.unsol_event(codec, (event | i) << 26);
}
if (spec->dac_list)
stac92xx_power_down(codec);
return 0;
}
@ -3947,14 +3965,18 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
}
}
static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
int enable)
{
struct sigmatel_spec *spec = codec->spec;
hda_nid_t nid = spec->pwr_nids[idx];
int presence, val;
val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0)
& 0x000000ff;
presence = get_hp_pin_presence(codec, nid);
unsigned int idx, val;
for (idx = 0; idx < spec->num_pwrs; idx++) {
if (spec->pwr_nids[idx] == nid)
break;
}
if (idx >= spec->num_pwrs)
return;
/* several codecs have two power down bits */
if (spec->pwr_mapping)
@ -3962,14 +3984,20 @@ static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
else
idx = 1 << idx;
if (presence)
val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
if (enable)
val &= ~idx;
else
val |= idx;
/* power down unused output ports */
snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
};
}
static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
{
stac_toggle_power_map(codec, nid, get_hp_pin_presence(codec, nid));
}
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
{