[ALSA] hda: 92HD7XXX power management support

Added support for advanced power management support for output ports on
92HD7xxx family of codecs. Inactive output ports are powered down when
the pin sense  doesn't detect a connection, and powered back up when a
connection is sensed.

Signed-off-by: Matthew Ranostay <mranostay@embeddedalley.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
This commit is contained in:
Matthew Ranostay 2008-01-10 16:55:06 +01:00 коммит произвёл Jaroslav Kysela
Родитель 2134ea4f37
Коммит a64135a2d8
1 изменённых файлов: 83 добавлений и 8 удалений

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

@ -34,7 +34,8 @@
#include "hda_local.h"
#define NUM_CONTROL_ALLOC 32
#define STAC_HP_EVENT 0x37
#define STAC_PWR_EVENT 0x20
#define STAC_HP_EVENT 0x30
enum {
STAC_REF,
@ -126,6 +127,10 @@ struct sigmatel_spec {
unsigned char aloopback_mask;
unsigned char aloopback_shift;
/* power management */
unsigned int num_pwrs;
hda_nid_t *pwr_nids;
/* playback */
struct hda_multi_out multiout;
hda_nid_t dac_nids[5];
@ -187,6 +192,11 @@ static hda_nid_t stac9200_dac_nids[1] = {
0x02,
};
static hda_nid_t stac92hd73xx_pwr_nids[8] = {
0x0a, 0x0b, 0x0c, 0xd, 0x0e,
0x0f, 0x10, 0x11
};
static hda_nid_t stac92hd73xx_adc_nids[2] = {
0x1a, 0x1b
};
@ -209,6 +219,10 @@ static hda_nid_t stac92hd73xx_dmux_nids[2] = {
0x20, 0x21,
};
static hda_nid_t stac92hd71bxx_pwr_nids[3] = {
0x0a, 0x0d, 0x0f
};
static hda_nid_t stac92hd71bxx_adc_nids[2] = {
0x12, 0x13,
};
@ -546,7 +560,7 @@ static struct hda_verb stac92hd71bxx_analog_core_init[] = {
/* connect ports 0d and 0f to audio mixer */
{ 0x0d, AC_VERB_SET_CONNECT_SEL, 0x2},
{ 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2},
{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
{ 0x0f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT}, /* Speaker */
/* unmute dac0 input in audio mixer */
{ 0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0x701f},
/* unmute right and left channels for nodes 0x0a, 0xd, 0x0f */
@ -2704,6 +2718,16 @@ static void enable_pin_detect(struct hda_codec *codec, hda_nid_t nid,
(AC_USRSP_EN | event));
}
static int is_nid_hp_pin(struct auto_pin_cfg *cfg, hda_nid_t nid)
{
int i;
for (i = 0; i < cfg->hp_outs; i++)
if (cfg->hp_pins[i] == nid)
return 1; /* nid is a HP-Out */
return 0; /* nid is not a HP-Out */
};
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@ -2739,10 +2763,23 @@ static int stac92xx_init(struct hda_codec *codec)
stac92xx_auto_set_pinctl(codec, nid, pinctl);
}
}
if (spec->num_dmics > 0)
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_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);
/* 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;
enable_pin_detect(codec, spec->pwr_nids[i], event | i);
codec->patch_ops.unsol_event(codec, (event | i) << 26);
}
if (cfg->dig_out_pin)
stac92xx_auto_set_pinctl(codec, cfg->dig_out_pin,
@ -2869,12 +2906,37 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res)
}
}
static void stac92xx_pin_sense(struct hda_codec *codec, int idx)
{
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);
idx = 1 << idx;
if (presence)
val &= ~idx;
else
val |= idx;
/* power down unused output ports */
snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
};
static void stac92xx_unsol_event(struct hda_codec *codec, unsigned int res)
{
switch (res >> 26) {
struct sigmatel_spec *spec = codec->spec;
int idx = res >> 26 & 0x0f;
switch ((res >> 26) & 0x30) {
case STAC_HP_EVENT:
stac92xx_hp_detect(codec, res);
break;
/* fallthru */
case STAC_PWR_EVENT:
if (spec->num_pwrs > 0)
stac92xx_pin_sense(codec, idx);
}
}
@ -2945,6 +3007,7 @@ static int patch_stac9200(struct hda_codec *codec)
spec->num_muxes = 1;
spec->num_dmics = 0;
spec->num_adcs = 1;
spec->num_pwrs = 0;
if (spec->board_config == STAC_9200_GATEWAY)
spec->init = stac9200_eapd_init;
@ -3000,6 +3063,7 @@ static int patch_stac925x(struct hda_codec *codec)
spec->mux_nids = stac925x_mux_nids;
spec->num_muxes = 1;
spec->num_adcs = 1;
spec->num_pwrs = 0;
switch (codec->vendor_id) {
case 0x83847632: /* STAC9202 */
case 0x83847633: /* STAC9202D */
@ -3123,6 +3187,9 @@ again:
spec->gpio_mask = spec->gpio_data = 0x000001;
stac92xx_enable_gpio_mask(codec);
spec->num_pwrs = ARRAY_SIZE(stac92hd73xx_pwr_nids);
spec->pwr_nids = stac92hd73xx_pwr_nids;
err = stac92xx_parse_auto_config(codec, 0x22, 0x24);
if (!err) {
@ -3205,6 +3272,9 @@ again:
spec->num_dmics = STAC92HD71BXX_NUM_DMICS;
spec->num_dmuxes = ARRAY_SIZE(stac92hd71bxx_dmux_nids);
spec->num_pwrs = ARRAY_SIZE(stac92hd71bxx_pwr_nids);
spec->pwr_nids = stac92hd71bxx_pwr_nids;
spec->multiout.num_dacs = 2;
spec->multiout.hp_nid = 0x11;
spec->multiout.dac_nids = stac92hd71bxx_dac_nids;
@ -3299,6 +3369,7 @@ static int patch_stac922x(struct hda_codec *codec)
spec->num_muxes = ARRAY_SIZE(stac922x_mux_nids);
spec->num_adcs = ARRAY_SIZE(stac922x_adc_nids);
spec->num_dmics = 0;
spec->num_pwrs = 0;
spec->init = stac922x_core_init;
spec->mixer = stac922x_mixer;
@ -3405,6 +3476,7 @@ static int patch_stac927x(struct hda_codec *codec)
spec->mixer = stac927x_mixer;
}
spec->num_pwrs = 0;
spec->aloopback_mask = 0x40;
spec->aloopback_shift = 0;
@ -3466,6 +3538,7 @@ static int patch_stac9205(struct hda_codec *codec)
spec->num_dmics = STAC9205_NUM_DMICS;
spec->dmux_nids = stac9205_dmux_nids;
spec->num_dmuxes = ARRAY_SIZE(stac9205_dmux_nids);
spec->num_pwrs = 0;
spec->init = stac9205_core_init;
spec->mixer = stac9205_mixer;
@ -3728,6 +3801,7 @@ static int patch_stac9872(struct hda_codec *codec)
spec->multiout.hp_nid = VAIO_HP_DAC;
spec->num_adcs = ARRAY_SIZE(vaio_adcs);
spec->adc_nids = vaio_adcs;
spec->num_pwrs = 0;
spec->input_mux = &vaio_mux;
spec->mux_nids = vaio_mux_nids;
codec->patch_ops = stac9872_vaio_patch_ops;
@ -3741,6 +3815,7 @@ static int patch_stac9872(struct hda_codec *codec)
spec->multiout.dac_nids = vaio_dacs;
spec->multiout.hp_nid = VAIO_HP_DAC;
spec->num_adcs = ARRAY_SIZE(vaio_adcs);
spec->num_pwrs = 0;
spec->adc_nids = vaio_adcs;
spec->input_mux = &vaio_mux;
spec->mux_nids = vaio_mux_nids;