summaryrefslogtreecommitdiff
path: root/sound/pci/hda/patch_via.c
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2011-06-21 17:57:44 +0400
committerTakashi Iwai <tiwai@suse.de>2011-06-21 18:02:32 +0400
commit09a9ad69a5467fbda3fd358d2be155c22aa416e4 (patch)
tree336c02b4a218897b40413a92cc09c26ac2599de7 /sound/pci/hda/patch_via.c
parenta934d5a983528543850c90b29bedbdfd71f7097b (diff)
downloadlinux-09a9ad69a5467fbda3fd358d2be155c22aa416e4.tar.xz
ALSA: hda - VT1708 independent HP routing fix
The codecs like VT1708 needs more complicated routing using the mixer widget rather than the simple selector widgets. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda/patch_via.c')
-rw-r--r--sound/pci/hda/patch_via.c222
1 files changed, 122 insertions, 100 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 5b907b356951..bceb6b2364fe 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -83,10 +83,20 @@ enum VIA_HDA_CODEC {
#define MAX_NID_PATH_DEPTH 5
+/* output-path: DAC -> ... -> pin
+ * idx[] contains the source index number of the next widget;
+ * e.g. idx[0] is the index of the DAC selected by path[1] widget
+ * multi[] indicates whether it's a selector widget with multi-connectors
+ * (i.e. the connection selection is mandatory)
+ * vol_ctl and mute_ctl contains the NIDs for the assigned mixers
+ */
struct nid_path {
int depth;
hda_nid_t path[MAX_NID_PATH_DEPTH];
- short idx[MAX_NID_PATH_DEPTH];
+ unsigned char idx[MAX_NID_PATH_DEPTH];
+ unsigned char multi[MAX_NID_PATH_DEPTH];
+ unsigned int vol_ctl;
+ unsigned int mute_ctl;
};
struct via_spec {
@@ -422,43 +432,39 @@ static bool check_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
return false;
}
-#define have_vol_or_mute(codec, nid, dir) \
- check_amp_caps(codec, nid, dir, AC_AMPCAP_NUM_STEPS | AC_AMPCAP_MUTE)
+#define have_mute(codec, nid, dir) \
+ check_amp_caps(codec, nid, dir, AC_AMPCAP_MUTE)
-/* unmute input amp and select the specificed source */
-static void unmute_and_select(struct hda_codec *codec, hda_nid_t nid,
- hda_nid_t src, hda_nid_t mix)
+/* enable/disable the output-route */
+static void activate_output_path(struct hda_codec *codec, struct nid_path *path,
+ bool enable, bool force)
{
- int idx, num_conns;
-
- idx = __get_connection_index(codec, nid, src, &num_conns);
- if (idx < 0)
- return;
-
- /* select the route explicitly when multiple connections exist */
- if (num_conns > 1 &&
- get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
-
- /* unmute if the input amp is present */
- if (have_vol_or_mute(codec, nid, HDA_INPUT))
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(idx));
-
- /* unmute the src output */
- if (have_vol_or_mute(codec, src, HDA_OUTPUT))
- snd_hda_codec_write(codec, src, 0, AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_OUT_UNMUTE);
-
- /* unmute AA-path if present */
- if (!mix || mix == src)
- return;
- idx = __get_connection_index(codec, nid, mix, NULL);
- if (idx >= 0 && have_vol_or_mute(codec, nid, HDA_INPUT))
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_AMP_GAIN_MUTE,
- AMP_IN_UNMUTE(idx));
+ int i;
+ for (i = 0; i < path->depth; i++) {
+ hda_nid_t src, dst;
+ int idx = path->idx[i];
+ src = path->path[i];
+ if (i < path->depth - 1)
+ dst = path->path[i + 1];
+ else
+ dst = 0;
+ if (enable && path->multi[i])
+ snd_hda_codec_write(codec, dst, 0,
+ AC_VERB_SET_CONNECT_SEL, idx);
+ if (have_mute(codec, dst, HDA_INPUT)) {
+ int val = enable ? AMP_IN_UNMUTE(idx) :
+ AMP_IN_MUTE(idx);
+ snd_hda_codec_write(codec, dst, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+ if (!force && (src == path->vol_ctl || src == path->mute_ctl))
+ continue;
+ if (have_mute(codec, src, HDA_OUTPUT)) {
+ int val = enable ? AMP_OUT_UNMUTE : AMP_OUT_MUTE;
+ snd_hda_codec_write(codec, src, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE, val);
+ }
+ }
}
/* set the given pin as output */
@@ -474,16 +480,18 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
AC_VERB_SET_EAPD_BTLENABLE, 0x02);
}
-static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
- int pin_type, struct nid_path *path)
+static void via_auto_init_output(struct hda_codec *codec,
+ struct nid_path *path, int pin_type,
+ bool force)
{
struct via_spec *spec = codec->spec;
unsigned int caps;
- hda_nid_t nid;
- int i;
+ hda_nid_t pin, nid;
+ int i, idx;
- if (!pin)
+ if (!path->depth)
return;
+ pin = path->path[path->depth - 1];
init_output_pin(codec, pin, pin_type);
caps = query_amp_caps(codec, pin, HDA_OUTPUT);
@@ -494,34 +502,48 @@ static void via_auto_init_output(struct hda_codec *codec, hda_nid_t pin,
AMP_OUT_MUTE | val);
}
- /* initialize the output path */
+ activate_output_path(codec, path, true, force);
+
+ /* initialize the AA-path */
+ if (!spec->aa_mix_nid)
+ return;
for (i = path->depth - 1; i > 0; i--) {
- nid = path->path[i - 1];
- unmute_and_select(codec, path->path[i], nid, spec->aa_mix_nid);
+ nid = path->path[i];
+ idx = get_connection_index(codec, nid, spec->aa_mix_nid);
+ if (idx >= 0) {
+ if (have_mute(codec, nid, HDA_INPUT))
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_AMP_GAIN_MUTE,
+ AMP_IN_UNMUTE(idx));
+ break;
+ }
}
}
-
static void via_auto_init_multi_out(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
int i;
for (i = 0; i < spec->autocfg.line_outs + spec->smart51_nums; i++)
- via_auto_init_output(codec, spec->autocfg.line_out_pins[i],
- PIN_OUT, &spec->out_path[i]);
+ via_auto_init_output(codec, &spec->out_path[i], PIN_OUT, true);
}
static void via_auto_init_hp_out(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- if (spec->hp_dac_nid)
- via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
- &spec->hp_path);
- else
- via_auto_init_output(codec, spec->autocfg.hp_pins[0], PIN_HP,
- &spec->hp_dep_path);
+ if (!spec->hp_dac_nid) {
+ via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
+ return;
+ }
+ if (spec->hp_independent_mode) {
+ activate_output_path(codec, &spec->hp_dep_path, false, false);
+ via_auto_init_output(codec, &spec->hp_path, PIN_HP, true);
+ } else {
+ activate_output_path(codec, &spec->hp_path, false, false);
+ via_auto_init_output(codec, &spec->hp_dep_path, PIN_HP, true);
+ }
}
static void via_auto_init_speaker_out(struct hda_codec *codec)
@@ -529,8 +551,7 @@ static void via_auto_init_speaker_out(struct hda_codec *codec)
struct via_spec *spec = codec->spec;
if (spec->autocfg.speaker_outs)
- via_auto_init_output(codec, spec->autocfg.speaker_pins[0],
- PIN_OUT, &spec->speaker_path);
+ via_auto_init_output(codec, &spec->speaker_path, PIN_OUT, true);
}
static bool is_smart51_pins(struct hda_codec *codec, hda_nid_t pin);
@@ -738,27 +759,14 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct via_spec *spec = codec->spec;
- hda_nid_t nid, src;
- int i, idx, num_conns;
- struct nid_path *path;
spec->hp_independent_mode = !!ucontrol->value.enumerated.item[0];
- if (spec->hp_independent_mode)
- path = &spec->hp_path;
- else
- path = &spec->hp_dep_path;
-
- /* re-route the output path */
- for (i = path->depth - 1; i > 0; i--) {
- nid = path->path[i];
- src = path->path[i - 1];
- idx = __get_connection_index(codec, nid, src, &num_conns);
- if (idx < 0)
- continue;
- if (num_conns > 1 &&
- get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CONNECT_SEL, idx);
+ if (spec->hp_independent_mode) {
+ activate_output_path(codec, &spec->hp_dep_path, false, false);
+ activate_output_path(codec, &spec->hp_path, true, false);
+ } else {
+ activate_output_path(codec, &spec->hp_path, false, false);
+ activate_output_path(codec, &spec->hp_dep_path, true, false);
}
/* update jack power state */
@@ -1577,12 +1585,8 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
for (i = 0; i < nums; i++) {
if (get_wcaps_type(get_wcaps(codec, conn[i])) != AC_WID_AUD_OUT)
continue;
- if (conn[i] == target_dac || is_empty_dac(codec, conn[i])) {
- path->path[0] = conn[i];
- path->idx[0] = i;
- path->depth = 1;
- return true;
- }
+ if (conn[i] == target_dac || is_empty_dac(codec, conn[i]))
+ goto found;
}
if (depth >= MAX_NID_PATH_DEPTH)
return false;
@@ -1593,14 +1597,18 @@ static bool __parse_output_path(struct hda_codec *codec, hda_nid_t nid,
(wid_type != -1 && type != wid_type))
continue;
if (__parse_output_path(codec, conn[i], target_dac,
- path, depth + 1, AC_WID_AUD_SEL)) {
- path->path[path->depth] = conn[i];
- path->idx[path->depth] = i;
- path->depth++;
- return true;
- }
+ path, depth + 1, AC_WID_AUD_SEL))
+ goto found;
}
return false;
+
+ found:
+ path->path[path->depth] = conn[i];
+ path->idx[path->depth] = i;
+ if (nums > 1 && get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_AUD_MIX)
+ path->multi[path->depth] = 1;
+ path->depth++;
+ return true;
}
static bool parse_output_path(struct hda_codec *codec, hda_nid_t nid,
@@ -1634,18 +1642,16 @@ static int via_auto_fill_dac_nids(struct hda_codec *codec)
}
static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
- hda_nid_t pin, hda_nid_t dac, int chs)
+ int chs, bool check_dac, struct nid_path *path)
{
struct via_spec *spec = codec->spec;
char name[32];
- hda_nid_t nid, sel, conn[8];
- int nums, err;
+ hda_nid_t dac, pin, sel, nid;
+ int err;
- /* check selector widget connected to the pin */
- sel = 0;
- nums = snd_hda_get_connections(codec, pin, conn, ARRAY_SIZE(conn));
- if (nums == 1 && conn[0] != pin)
- sel = conn[0];
+ dac = check_dac ? path->path[0] : 0;
+ pin = path->path[path->depth - 1];
+ sel = path->depth > 1 ? path->path[1] : 0;
if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_NUM_STEPS))
nid = dac;
@@ -1661,6 +1667,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
if (err < 0)
return err;
+ path->vol_ctl = nid;
}
if (dac && check_amp_caps(codec, dac, HDA_OUTPUT, AC_AMPCAP_MUTE))
@@ -1677,6 +1684,7 @@ static int create_ch_ctls(struct hda_codec *codec, const char *pfx,
HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT));
if (err < 0)
return err;
+ path->mute_ctl = nid;
}
return 0;
}
@@ -1747,10 +1755,12 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
if (!pin || !dac)
continue;
if (i == HDA_CLFE) {
- err = create_ch_ctls(codec, "Center", pin, dac, 1);
+ err = create_ch_ctls(codec, "Center", 1, true,
+ &spec->out_path[i]);
if (err < 0)
return err;
- err = create_ch_ctls(codec, "LFE", pin, dac, 2);
+ err = create_ch_ctls(codec, "LFE", 2, true,
+ &spec->out_path[i]);
if (err < 0)
return err;
} else {
@@ -1758,7 +1768,8 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
if (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT &&
cfg->line_outs == 1)
pfx = "Speaker";
- err = create_ch_ctls(codec, pfx, pin, dac, 3);
+ err = create_ch_ctls(codec, pfx, 3, true,
+ &spec->out_path[i]);
if (err < 0)
return err;
}
@@ -1790,6 +1801,7 @@ static int via_auto_create_multi_out_ctls(struct hda_codec *codec)
static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
{
struct via_spec *spec = codec->spec;
+ struct nid_path *path;
int err;
if (!pin)
@@ -1803,9 +1815,17 @@ static int via_auto_create_hp_ctls(struct hda_codec *codec, hda_nid_t pin)
!spec->hp_dac_nid)
return 0;
- err = create_ch_ctls(codec, "Headphone", pin, spec->hp_dac_nid, 3);
+ if (spec->hp_dac_nid)
+ path = &spec->hp_path;
+ else
+ path = &spec->hp_dep_path;
+ err = create_ch_ctls(codec, "Headphone", 3, false, path);
if (err < 0)
return err;
+ if (spec->hp_dac_nid) {
+ spec->hp_dep_path.vol_ctl = spec->hp_path.vol_ctl;
+ spec->hp_dep_path.mute_ctl = spec->hp_path.mute_ctl;
+ }
return 0;
}
@@ -1822,11 +1842,13 @@ static int via_auto_create_speaker_ctls(struct hda_codec *codec)
if (parse_output_path(codec, pin, 0, &spec->speaker_path)) {
dac = spec->speaker_path.path[0];
spec->multiout.extra_out_nid[0] = dac;
- return create_ch_ctls(codec, "Speaker", pin, dac, 3);
+ return create_ch_ctls(codec, "Speaker", 3, true,
+ &spec->speaker_path);
}
if (parse_output_path(codec, pin, spec->multiout.dac_nids[HDA_FRONT],
&spec->speaker_path))
- return create_ch_ctls(codec, "Speaker", pin, 0, 3);
+ return create_ch_ctls(codec, "Speaker", 3, false,
+ &spec->speaker_path);
return 0;
}