Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 353012
b: refs/heads/master
c: 130e5f0
h: refs/heads/master
v: v3
  • Loading branch information
Takashi Iwai committed Jan 12, 2013
1 parent 91a9bfc commit 55e91ea
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 89 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b8a47c79b28c34652acf9594ef48b0c9fc875401
refs/heads/master: 130e5f0642de99a61f46c4f0468bfc5db6030967
3 changes: 2 additions & 1 deletion trunk/sound/pci/hda/hda_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,8 @@ int snd_hda_check_amp_list_power(struct hda_codec *codec,
#define get_amp_channels(kc) (((kc)->private_value >> 16) & 0x3)
#define get_amp_direction_(pv) (((pv) >> 18) & 0x1)
#define get_amp_direction(kc) get_amp_direction_((kc)->private_value)
#define get_amp_index(kc) (((kc)->private_value >> 19) & 0xf)
#define get_amp_index_(pv) (((pv) >> 19) & 0xf)
#define get_amp_index(kc) get_amp_index_((kc)->private_value)
#define get_amp_offset(kc) (((kc)->private_value >> 23) & 0x3f)
#define get_amp_min_mute(kc) (((kc)->private_value >> 29) & 0x1)

Expand Down
261 changes: 174 additions & 87 deletions trunk/sound/pci/hda/patch_realtek.c
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ struct nid_path {
unsigned char idx[MAX_NID_PATH_DEPTH];
unsigned char multi[MAX_NID_PATH_DEPTH];
unsigned int ctls[2]; /* 0 = volume, 1 = mute */
bool active;
};

enum { NID_PATH_VOL_CTL = 0, NID_PATH_MUTE_CTL = 1 };
Expand Down Expand Up @@ -2853,16 +2854,6 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
return 0;
}

static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
unsigned int pin_type)
{
snd_hda_set_pin_ctl(codec, nid, pin_type);
/* unmute pin */
if (nid_has_mute(codec, nid, HDA_OUTPUT))
snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
}

static int get_pin_type(int line_out_type)
{
if (line_out_type == AUTO_PIN_HP_OUT)
Expand Down Expand Up @@ -3824,109 +3815,205 @@ static int alc_auto_create_speaker_out(struct hda_codec *codec)
"Speaker");
}

/* is a volume or mute control already present? */
static bool __is_out_ctl_present(struct hda_codec *codec,
struct nid_path *exclude_path,
hda_nid_t nid, int dir, int types)
static bool is_ctl_associated_in_list(struct snd_array *array, hda_nid_t nid,
int dir, int idx, int types)
{
struct alc_spec *spec = codec->spec;
int i, type;

for (i = 0; i < spec->out_path.used; i++) {
struct nid_path *p = snd_array_elem(&spec->out_path, i);
if (p == exclude_path || p->depth <= 0)
for (i = 0; i < array->used; i++) {
struct nid_path *p = snd_array_elem(array, i);
if (p->depth <= 0)
continue;
for (type = 0; type < 2; type++) {
if (types & (1 << type)) {
unsigned int val = p->ctls[type];
if (get_amp_nid_(val) == nid &&
get_amp_direction_(val) == dir)
get_amp_direction_(val) == dir &&
get_amp_index_(val) == idx)
return true;
}
}
}
return false;
}

#define is_out_ctl_present(codec, path, nid, dir) \
__is_out_ctl_present(codec, path, nid, dir, 3) /* check both types */
#define is_out_vol_ctl_present(codec, nid, dir) \
__is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_VOL_CTL)
#define is_out_mute_ctl_present(codec, nid, dir) \
__is_out_ctl_present(codec, NULL, nid, dir, 1 << NID_PATH_MUTE_CTL)
/* check whether a control with the given (nid, dir, idx) was assigned */
static bool is_ctl_associated(struct hda_codec *codec, hda_nid_t nid,
int dir, int idx)
{
struct alc_spec *spec = codec->spec;
return is_ctl_associated_in_list(&spec->out_path, nid, dir, idx, 3) ||
is_ctl_associated_in_list(&spec->in_path, nid, dir, idx, 3) ||
is_ctl_associated_in_list(&spec->loopback_path, nid, dir, idx, 3);
}

static int get_default_amp_val(struct hda_codec *codec, hda_nid_t nid, int dir)
/* can have the amp-in capability? */
static bool has_amp_in(struct hda_codec *codec, struct nid_path *path, int idx)
{
unsigned int caps, offset;
hda_nid_t nid = path->path[idx];
unsigned int caps = get_wcaps(codec, nid);
unsigned int type = get_wcaps_type(caps);

if (!(caps & AC_WCAP_IN_AMP))
return false;
if (type == AC_WID_PIN && idx > 0) /* only for input pins */
return false;
return true;
}

/* can have the amp-out capability? */
static bool has_amp_out(struct hda_codec *codec, struct nid_path *path, int idx)
{
hda_nid_t nid = path->path[idx];
unsigned int caps = get_wcaps(codec, nid);
unsigned int type = get_wcaps_type(caps);

if (!(caps & AC_WCAP_OUT_AMP))
return false;
if (type == AC_WID_PIN && !idx) /* only for output pins */
return false;
return true;
}

static bool is_active_in_list(struct hda_codec *codec, struct snd_array *array,
hda_nid_t nid, int dir, int idx)
{
int i, n;

for (n = 0; n < array->used; n++) {
struct nid_path *path = snd_array_elem(array, n);
if (!path->active)
continue;
for (i = 0; i < path->depth; i++) {
if (path->path[i] == nid) {
if (dir == HDA_OUTPUT || path->idx[i] == idx)
return true;
break;
}
}
}
return false;
}

/* check whether the given (nid,dir,idx) is active */
static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid,
unsigned int idx, unsigned int dir)
{
struct alc_spec *spec = codec->spec;
return is_active_in_list(codec, &spec->out_path, nid, idx, dir) ||
is_active_in_list(codec, &spec->in_path, nid, idx, dir) ||
is_active_in_list(codec, &spec->loopback_path, nid, idx, dir);
}

/* get the default amp value for the target state */
static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid,
int dir, bool enable)
{
unsigned int caps;
unsigned int val = 0;

caps = query_amp_caps(codec, nid, dir);
if (caps & AC_AMPCAP_NUM_STEPS) {
offset = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
/* if a volume control is assigned, set the lowest level
* as default; otherwise set to 0dB
*/
if (is_out_vol_ctl_present(codec, nid, dir))
val = 0;
else
val = offset;
/* set to 0dB */
if (enable)
val = (caps & AC_AMPCAP_OFFSET) >> AC_AMPCAP_OFFSET_SHIFT;
}
if (caps & AC_AMPCAP_MUTE) {
/* if a mute control is assigned, mute as default */
if (is_out_mute_ctl_present(codec, nid, dir))
if (!enable)
val |= HDA_AMP_MUTE;
}
return val;
}

/* configure the path from the given dac to the pin as the proper output */
static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t pin, int pin_type,
hda_nid_t dac, bool force)
/* initialize the amp value (only at the first time) */
static void init_amp(struct hda_codec *codec, hda_nid_t nid, int dir, int idx)
{
int val = get_amp_val_to_activate(codec, nid, dir, false);
snd_hda_codec_amp_init_stereo(codec, nid, dir, idx, 0xff, val);
}

static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
int idx, bool enable)
{
int val;
if (is_ctl_associated(codec, nid, dir, idx) ||
is_active_nid(codec, nid, dir, idx))
return;
val = get_amp_val_to_activate(codec, nid, dir, enable);
snd_hda_codec_amp_stereo(codec, nid, dir, idx, 0xff, val);
}

static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
int i, bool enable)
{
hda_nid_t nid = path->path[i];
init_amp(codec, nid, HDA_OUTPUT, 0);
activate_amp(codec, nid, HDA_OUTPUT, 0, enable);
}

static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
int i, bool enable)
{
struct alc_spec *spec = codec->spec;
int i, val;
struct nid_path *path;
hda_nid_t conn[16];
int n, nums;
hda_nid_t nid = path->path[i];

alc_set_pin_output(codec, pin, pin_type);
path = get_out_path(codec, pin, dac);
if (!path)
nums = snd_hda_get_connections(codec, nid, conn, ARRAY_SIZE(conn));
for (n = 0; n < nums; n++)
init_amp(codec, nid, HDA_INPUT, n);

if (is_ctl_associated(codec, nid, HDA_INPUT, path->idx[i]))
return;

/* here is a little bit tricky in comparison with activate_amp_out();
* when aa-mixer is available, we need to enable the path as well
*/
for (n = 0; n < nums; n++) {
if (n != path->idx[i] && conn[n] != spec->mixer_nid)
continue;
activate_amp(codec, nid, HDA_INPUT, n, enable);
}
}

static void activate_path(struct hda_codec *codec, struct nid_path *path,
bool enable)
{
int i;

if (path->active == enable)
return;

if (!enable)
path->active = false;

for (i = path->depth - 1; i >= 0; i--) {
hda_nid_t nid = path->path[i];
if (path->multi[i])
snd_hda_codec_write(codec, nid, 0,
snd_hda_codec_write_cache(codec, path->path[i], 0,
AC_VERB_SET_CONNECT_SEL,
path->idx[i]);

if (i != 0 && i != path->depth - 1 &&
(get_wcaps(codec, nid) & AC_WCAP_IN_AMP) &&
(force || !is_out_ctl_present(codec, path, nid,
HDA_INPUT))) {
hda_nid_t conn[16];
int n, nums;
nums = snd_hda_get_connections(codec, nid, conn,
ARRAY_SIZE(conn));
val = get_default_amp_val(codec, nid, HDA_INPUT);
for (n = 0; n < nums; n++) {
if (n != path->idx[i] &&
conn[n] != spec->mixer_nid)
continue;
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(n) | val);
}
}
if ((get_wcaps(codec, nid) & AC_WCAP_OUT_AMP) &&
(force || !is_out_ctl_present(codec, path, nid,
HDA_OUTPUT))) {
val = get_default_amp_val(codec, nid, HDA_OUTPUT);
snd_hda_codec_write(codec, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE | val);
}
if (has_amp_in(codec, path, i))
activate_amp_in(codec, path, i, enable);
if (has_amp_out(codec, path, i))
activate_amp_out(codec, path, i, enable);
}

if (enable)
path->active = true;
}

/* configure the path from the given dac to the pin as the proper output */
static void alc_auto_set_output_and_unmute(struct hda_codec *codec,
hda_nid_t pin, int pin_type,
hda_nid_t dac)
{
struct nid_path *path;

snd_hda_set_pin_ctl_cache(codec, pin, pin_type);
path = get_out_path(codec, pin, dac);
if (!path)
return;
activate_path(codec, path, true);
}

static void alc_auto_init_multi_out(struct hda_codec *codec)
Expand All @@ -3939,7 +4026,7 @@ static void alc_auto_init_multi_out(struct hda_codec *codec)
hda_nid_t nid = spec->autocfg.line_out_pins[i];
if (nid)
alc_auto_set_output_and_unmute(codec, nid, pin_type,
spec->multiout.dac_nids[i], true);
spec->multiout.dac_nids[i]);

}
}
Expand All @@ -3963,7 +4050,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec)
else
dac = spec->multiout.dac_nids[0];
}
alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac, true);
alc_auto_set_output_and_unmute(codec, pin, PIN_HP, dac);
}
for (i = 0; i < spec->autocfg.speaker_outs; i++) {
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
Expand All @@ -3978,7 +4065,7 @@ static void alc_auto_init_extra_out(struct hda_codec *codec)
else
dac = spec->multiout.dac_nids[0];
}
alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac, true);
alc_auto_set_output_and_unmute(codec, pin, PIN_OUT, dac);
}
}

Expand Down Expand Up @@ -4129,22 +4216,22 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
{
struct alc_spec *spec = codec->spec;
hda_nid_t nid = spec->multi_io[idx].pin;
struct nid_path *path;

path = get_out_path(codec, nid, spec->multi_io[idx].dac);
if (!path)
return -EINVAL;

if (!spec->multi_io[idx].ctl_in)
spec->multi_io[idx].ctl_in =
snd_hda_codec_read(codec, nid, 0,
snd_hda_codec_update_cache(codec, nid, 0,
AC_VERB_GET_PIN_WIDGET_CONTROL, 0);

if (output) {
snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
HDA_AMP_MUTE, 0);
alc_auto_set_output_and_unmute(codec, nid, PIN_OUT,
spec->multi_io[idx].dac, false);
activate_path(codec, path, true);
} else {
if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
HDA_AMP_MUTE, HDA_AMP_MUTE);
activate_path(codec, path, false);
snd_hda_set_pin_ctl_cache(codec, nid,
spec->multi_io[idx].ctl_in);
}
Expand Down

0 comments on commit 55e91ea

Please sign in to comment.