|
@@ -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 };
|
|
@@ -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)
|
|
@@ -3824,23 +3815,21 @@ 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;
|
|
|
}
|
|
|
}
|
|
@@ -3848,85 +3837,183 @@ static bool __is_out_ctl_present(struct hda_codec *codec,
|
|
|
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)
|
|
@@ -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]);
|
|
|
|
|
|
}
|
|
|
}
|
|
@@ -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)
|
|
@@ -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);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -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);
|
|
|
}
|