aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-01-04 10:42:48 -0500
committerTakashi Iwai <tiwai@suse.de>2013-01-12 02:43:42 -0500
commitc30aa7b24282c6c544f25f360131fceb646927e4 (patch)
treeb1da236fb6df50dd8be84af255956289e0a781d8
parent117688a9c1023af9241810544b35c7104fbbcfc2 (diff)
ALSA: hda - Add Loopback Mixing control
For codecs that have individual routes going through a loopback mixer (like VIA codecs), we need to provide an explicit switch to choose whether the output goes through mixer or directly from DAC. This patch adds the check for such paths and creates "Loopback Mixing" enum control when available. It won't influence on codecs like Realtek or others where the loopback mixer is connected independently from the primary output routes. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/pci/hda/hda_generic.c106
-rw-r--r--sound/pci/hda/hda_generic.h4
2 files changed, 110 insertions, 0 deletions
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index e7574a863d21..a34c581b6082 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -1115,6 +1115,24 @@ static bool map_singles(struct hda_codec *codec, int outs,
1115 return found; 1115 return found;
1116} 1116}
1117 1117
1118/* create a new path including aamix if available, and return its index */
1119static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
1120{
1121 struct nid_path *path;
1122
1123 path = snd_hda_get_path_from_idx(codec, path_idx);
1124 if (!path || !path->depth || path->with_aa_mix)
1125 return 0;
1126 path = snd_hda_add_new_path(codec, path->path[0],
1127 path->path[path->depth - 1],
1128 HDA_PARSE_ONLY_AAMIX);
1129 if (!path)
1130 return 0;
1131 print_nid_path("output-aamix", path);
1132 path->active = false; /* unused as default */
1133 return snd_hda_get_path_idx(codec, path);
1134}
1135
1118/* fill in the dac_nids table from the parsed pin configuration */ 1136/* fill in the dac_nids table from the parsed pin configuration */
1119static int fill_and_eval_dacs(struct hda_codec *codec, 1137static int fill_and_eval_dacs(struct hda_codec *codec,
1120 bool fill_hardwired, 1138 bool fill_hardwired,
@@ -1211,6 +1229,17 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
1211 badness += err; 1229 badness += err;
1212 } 1230 }
1213 1231
1232 if (spec->mixer_nid) {
1233 spec->aamix_out_paths[0] =
1234 check_aamix_out_path(codec, spec->out_paths[0]);
1235 if (cfg->line_out_type != AUTO_PIN_HP_OUT)
1236 spec->aamix_out_paths[1] =
1237 check_aamix_out_path(codec, spec->hp_paths[0]);
1238 if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
1239 spec->aamix_out_paths[2] =
1240 check_aamix_out_path(codec, spec->speaker_paths[0]);
1241 }
1242
1214 if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) 1243 if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
1215 if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) 1244 if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
1216 spec->multi_ios = 1; /* give badness */ 1245 spec->multi_ios = 1; /* give badness */
@@ -1730,6 +1759,80 @@ static int create_multi_channel_mode(struct hda_codec *codec)
1730} 1759}
1731 1760
1732/* 1761/*
1762 * aamix loopback enable/disable switch
1763 */
1764
1765#define loopback_mixing_info indep_hp_info
1766
1767static int loopback_mixing_get(struct snd_kcontrol *kcontrol,
1768 struct snd_ctl_elem_value *ucontrol)
1769{
1770 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1771 struct hda_gen_spec *spec = codec->spec;
1772 ucontrol->value.enumerated.item[0] = spec->aamix_mode;
1773 return 0;
1774}
1775
1776static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
1777 int nomix_path_idx, int mix_path_idx)
1778{
1779 struct nid_path *nomix_path, *mix_path;
1780
1781 nomix_path = snd_hda_get_path_from_idx(codec, nomix_path_idx);
1782 mix_path = snd_hda_get_path_from_idx(codec, mix_path_idx);
1783 if (!nomix_path || !mix_path)
1784 return;
1785 if (do_mix) {
1786 snd_hda_activate_path(codec, nomix_path, false, true);
1787 snd_hda_activate_path(codec, mix_path, true, true);
1788 } else {
1789 snd_hda_activate_path(codec, mix_path, false, true);
1790 snd_hda_activate_path(codec, nomix_path, true, true);
1791 }
1792}
1793
1794static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
1795 struct snd_ctl_elem_value *ucontrol)
1796{
1797 struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
1798 struct hda_gen_spec *spec = codec->spec;
1799 unsigned int val = ucontrol->value.enumerated.item[0];
1800
1801 if (val == spec->aamix_mode)
1802 return 0;
1803 spec->aamix_mode = val;
1804 update_aamix_paths(codec, val, spec->out_paths[0],
1805 spec->aamix_out_paths[0]);
1806 update_aamix_paths(codec, val, spec->hp_paths[0],
1807 spec->aamix_out_paths[1]);
1808 update_aamix_paths(codec, val, spec->speaker_paths[0],
1809 spec->aamix_out_paths[2]);
1810 return 1;
1811}
1812
1813static const struct snd_kcontrol_new loopback_mixing_enum = {
1814 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
1815 .name = "Loopback Mixing",
1816 .info = loopback_mixing_info,
1817 .get = loopback_mixing_get,
1818 .put = loopback_mixing_put,
1819};
1820
1821static int create_loopback_mixing_ctl(struct hda_codec *codec)
1822{
1823 struct hda_gen_spec *spec = codec->spec;
1824
1825 if (!spec->mixer_nid)
1826 return 0;
1827 if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
1828 spec->aamix_out_paths[2]))
1829 return 0;
1830 if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
1831 return -ENOMEM;
1832 return 0;
1833}
1834
1835/*
1733 * shared headphone/mic handling 1836 * shared headphone/mic handling
1734 */ 1837 */
1735 1838
@@ -3067,6 +3170,9 @@ int snd_hda_gen_parse_auto_config(struct hda_codec *codec,
3067 err = create_indep_hp_ctls(codec); 3170 err = create_indep_hp_ctls(codec);
3068 if (err < 0) 3171 if (err < 0)
3069 return err; 3172 return err;
3173 err = create_loopback_mixing_ctl(codec);
3174 if (err < 0)
3175 return err;
3070 err = create_shared_input(codec); 3176 err = create_shared_input(codec);
3071 if (err < 0) 3177 if (err < 0)
3072 return err; 3178 return err;
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index ba8de12b7125..d4a8f6c4e7a9 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -134,6 +134,7 @@ struct hda_gen_spec {
134 int out_paths[AUTO_CFG_MAX_OUTS]; 134 int out_paths[AUTO_CFG_MAX_OUTS];
135 int hp_paths[AUTO_CFG_MAX_OUTS]; 135 int hp_paths[AUTO_CFG_MAX_OUTS];
136 int speaker_paths[AUTO_CFG_MAX_OUTS]; 136 int speaker_paths[AUTO_CFG_MAX_OUTS];
137 int aamix_out_paths[3];
137 int digout_paths[AUTO_CFG_MAX_OUTS]; 138 int digout_paths[AUTO_CFG_MAX_OUTS];
138 int loopback_paths[HDA_MAX_NUM_INPUTS]; 139 int loopback_paths[HDA_MAX_NUM_INPUTS];
139 int digin_path; 140 int digin_path;
@@ -169,6 +170,9 @@ struct hda_gen_spec {
169 unsigned int indep_hp:1; /* independent HP supported */ 170 unsigned int indep_hp:1; /* independent HP supported */
170 unsigned int indep_hp_enabled:1; /* independent HP enabled */ 171 unsigned int indep_hp_enabled:1; /* independent HP enabled */
171 172
173 /* loopback mixing mode */
174 bool aamix_mode;
175
172 /* for virtual master */ 176 /* for virtual master */
173 hda_nid_t vmaster_nid; 177 hda_nid_t vmaster_nid;
174 struct hda_vmaster_mute_hook vmaster_mute; 178 struct hda_vmaster_mute_hook vmaster_mute;