aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2011-07-18 10:54:40 -0400
committerTakashi Iwai <tiwai@suse.de>2011-07-18 10:54:40 -0400
commit3b607e3d3a2538e06686c8c26057f95471ac1f9c (patch)
tree8c795f35ffa819a1edb571251a08770dc93b612a
parent3214b9665c06f684011f169428963b20f8ac554b (diff)
ALSA: hda - Switch HP DAC dynamically with indep-HP switch for VIA
This patch changes the behavior of independent-HP enum switch. Now instead of returning a busy error, the driver switches dynamically the stream of the HP (and shared) DACs according to the current mode. The logic is similar like the dual-mic ADC switch, but a bit more complicated because of the presence of shared DAC. Together with the change, a mutex is introduced to protect against the possible races for the indep-HP mode setting. Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--sound/pci/hda/patch_via.c159
1 files changed, 125 insertions, 34 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 76c688409cd8..5b0342635ebe 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -109,6 +109,11 @@ struct via_input {
109 109
110#define VIA_MAX_ADCS 3 110#define VIA_MAX_ADCS 3
111 111
112enum {
113 STREAM_MULTI_OUT = (1 << 0),
114 STREAM_INDEP_HP = (1 << 1),
115};
116
112struct via_spec { 117struct via_spec {
113 /* codec parameterization */ 118 /* codec parameterization */
114 const struct snd_kcontrol_new *mixers[6]; 119 const struct snd_kcontrol_new *mixers[6];
@@ -132,7 +137,8 @@ struct via_spec {
132 hda_nid_t hp_dac_nid; 137 hda_nid_t hp_dac_nid;
133 hda_nid_t speaker_dac_nid; 138 hda_nid_t speaker_dac_nid;
134 int hp_indep_shared; /* indep HP-DAC is shared with side ch */ 139 int hp_indep_shared; /* indep HP-DAC is shared with side ch */
135 int num_active_streams; 140 int opened_streams; /* STREAM_* bits */
141 int active_streams; /* STREAM_* bits */
136 int aamix_mode; /* loopback is enabled for output-path? */ 142 int aamix_mode; /* loopback is enabled for output-path? */
137 143
138 /* Output-paths: 144 /* Output-paths:
@@ -166,6 +172,12 @@ struct via_spec {
166 struct via_input inputs[AUTO_CFG_MAX_INS + 1]; 172 struct via_input inputs[AUTO_CFG_MAX_INS + 1];
167 unsigned int cur_mux[VIA_MAX_ADCS]; 173 unsigned int cur_mux[VIA_MAX_ADCS];
168 174
175 /* dynamic DAC switching */
176 unsigned int cur_dac_stream_tag;
177 unsigned int cur_dac_format;
178 unsigned int cur_hp_stream_tag;
179 unsigned int cur_hp_format;
180
169 /* dynamic ADC switching */ 181 /* dynamic ADC switching */
170 hda_nid_t cur_adc; 182 hda_nid_t cur_adc;
171 unsigned int cur_adc_stream_tag; 183 unsigned int cur_adc_stream_tag;
@@ -207,6 +219,8 @@ struct via_spec {
207 /* bind capture-volume */ 219 /* bind capture-volume */
208 struct hda_bind_ctls *bind_cap_vol; 220 struct hda_bind_ctls *bind_cap_vol;
209 struct hda_bind_ctls *bind_cap_sw; 221 struct hda_bind_ctls *bind_cap_sw;
222
223 struct mutex config_mutex;
210}; 224};
211 225
212static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); 226static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec);
@@ -218,6 +232,7 @@ static struct via_spec * via_new_spec(struct hda_codec *codec)
218 if (spec == NULL) 232 if (spec == NULL)
219 return NULL; 233 return NULL;
220 234
235 mutex_init(&spec->config_mutex);
221 codec->spec = spec; 236 codec->spec = spec;
222 spec->codec = codec; 237 spec->codec = codec;
223 spec->codec_type = get_codec_type(codec); 238 spec->codec_type = get_codec_type(codec);
@@ -756,6 +771,67 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
756 return 0; 771 return 0;
757} 772}
758 773
774/* adjust spec->multiout setup according to the current flags */
775static void setup_playback_multi_pcm(struct via_spec *spec)
776{
777 const struct auto_pin_cfg *cfg = &spec->autocfg;
778 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
779 spec->multiout.hp_nid = 0;
780 if (!spec->hp_independent_mode) {
781 if (!spec->hp_indep_shared)
782 spec->multiout.hp_nid = spec->hp_dac_nid;
783 } else {
784 if (spec->hp_indep_shared)
785 spec->multiout.num_dacs = cfg->line_outs - 1;
786 }
787}
788
789/* update DAC setups according to indep-HP switch;
790 * this function is called only when indep-HP is modified
791 */
792static void switch_indep_hp_dacs(struct hda_codec *codec)
793{
794 struct via_spec *spec = codec->spec;
795 int shared = spec->hp_indep_shared;
796 hda_nid_t shared_dac, hp_dac;
797
798 if (!spec->opened_streams)
799 return;
800
801 shared_dac = shared ? spec->multiout.dac_nids[shared] : 0;
802 hp_dac = spec->hp_dac_nid;
803 if (spec->hp_independent_mode) {
804 /* switch to indep-HP mode */
805 if (spec->active_streams & STREAM_MULTI_OUT) {
806 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
807 __snd_hda_codec_cleanup_stream(codec, shared_dac, 1);
808 }
809 if (spec->active_streams & STREAM_INDEP_HP)
810 snd_hda_codec_setup_stream(codec, hp_dac,
811 spec->cur_hp_stream_tag, 0,
812 spec->cur_hp_format);
813 } else {
814 /* back to HP or shared-DAC */
815 if (spec->active_streams & STREAM_INDEP_HP)
816 __snd_hda_codec_cleanup_stream(codec, hp_dac, 1);
817 if (spec->active_streams & STREAM_MULTI_OUT) {
818 hda_nid_t dac;
819 int ch;
820 if (shared_dac) { /* reset mutli-ch DAC */
821 dac = shared_dac;
822 ch = shared * 2;
823 } else { /* reset HP DAC */
824 dac = hp_dac;
825 ch = 0;
826 }
827 snd_hda_codec_setup_stream(codec, dac,
828 spec->cur_dac_stream_tag, ch,
829 spec->cur_dac_format);
830 }
831 }
832 setup_playback_multi_pcm(spec);
833}
834
759static int via_independent_hp_put(struct snd_kcontrol *kcontrol, 835static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
760 struct snd_ctl_elem_value *ucontrol) 836 struct snd_ctl_elem_value *ucontrol)
761{ 837{
@@ -763,13 +839,12 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
763 struct via_spec *spec = codec->spec; 839 struct via_spec *spec = codec->spec;
764 int cur, shared; 840 int cur, shared;
765 841
766 /* no independent-hp status change during PCM playback is running */ 842 mutex_lock(&spec->config_mutex);
767 if (spec->num_active_streams)
768 return -EBUSY;
769
770 cur = !!ucontrol->value.enumerated.item[0]; 843 cur = !!ucontrol->value.enumerated.item[0];
771 if (spec->hp_independent_mode == cur) 844 if (spec->hp_independent_mode == cur) {
845 mutex_unlock(&spec->config_mutex);
772 return 0; 846 return 0;
847 }
773 spec->hp_independent_mode = cur; 848 spec->hp_independent_mode = cur;
774 shared = spec->hp_indep_shared; 849 shared = spec->hp_indep_shared;
775 if (cur) { 850 if (cur) {
@@ -786,6 +861,9 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
786 activate_output_path(codec, &spec->hp_mix_path, true, false); 861 activate_output_path(codec, &spec->hp_mix_path, true, false);
787 } 862 }
788 863
864 switch_indep_hp_dacs(codec);
865 mutex_unlock(&spec->config_mutex);
866
789 /* update jack power state */ 867 /* update jack power state */
790 set_widgets_power_state(codec); 868 set_widgets_power_state(codec);
791 via_hp_automute(codec); 869 via_hp_automute(codec);
@@ -948,7 +1026,7 @@ static void analog_low_current_mode(struct hda_codec *codec)
948 bool enable; 1026 bool enable;
949 unsigned int verb, parm; 1027 unsigned int verb, parm;
950 1028
951 enable = is_aa_path_mute(codec) && (spec->num_active_streams > 0); 1029 enable = is_aa_path_mute(codec) && (spec->opened_streams != 0);
952 1030
953 /* decide low current mode's verb & parameter */ 1031 /* decide low current mode's verb & parameter */
954 switch (spec->codec_type) { 1032 switch (spec->codec_type) {
@@ -989,14 +1067,14 @@ static const struct hda_verb vt1708_init_verbs[] = {
989 { } 1067 { }
990}; 1068};
991 1069
992static void set_stream_active(struct hda_codec *codec, bool active) 1070static void set_stream_open(struct hda_codec *codec, int bit, bool active)
993{ 1071{
994 struct via_spec *spec = codec->spec; 1072 struct via_spec *spec = codec->spec;
995 1073
996 if (active) 1074 if (active)
997 spec->num_active_streams++; 1075 spec->opened_streams |= bit;
998 else 1076 else
999 spec->num_active_streams--; 1077 spec->opened_streams &= ~bit;
1000 analog_low_current_mode(codec); 1078 analog_low_current_mode(codec);
1001} 1079}
1002 1080
@@ -1008,22 +1086,13 @@ static int via_playback_multi_pcm_open(struct hda_pcm_stream *hinfo,
1008 const struct auto_pin_cfg *cfg = &spec->autocfg; 1086 const struct auto_pin_cfg *cfg = &spec->autocfg;
1009 int err; 1087 int err;
1010 1088
1011 spec->multiout.hp_nid = 0;
1012 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums; 1089 spec->multiout.num_dacs = cfg->line_outs + spec->smart51_nums;
1013 if (!spec->hp_independent_mode) {
1014 if (!spec->hp_indep_shared)
1015 spec->multiout.hp_nid = spec->hp_dac_nid;
1016 } else {
1017 if (spec->hp_indep_shared)
1018 spec->multiout.num_dacs = cfg->line_outs - 1;
1019 }
1020 spec->multiout.max_channels = spec->multiout.num_dacs * 2; 1090 spec->multiout.max_channels = spec->multiout.num_dacs * 2;
1021 set_stream_active(codec, true); 1091 set_stream_open(codec, STREAM_MULTI_OUT, true);
1022 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, 1092 err = snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
1023 hinfo); 1093 hinfo);
1024 if (err < 0) { 1094 if (err < 0) {
1025 spec->multiout.hp_nid = 0; 1095 set_stream_open(codec, STREAM_MULTI_OUT, false);
1026 set_stream_active(codec, false);
1027 return err; 1096 return err;
1028 } 1097 }
1029 return 0; 1098 return 0;
@@ -1033,10 +1102,7 @@ static int via_playback_multi_pcm_close(struct hda_pcm_stream *hinfo,
1033 struct hda_codec *codec, 1102 struct hda_codec *codec,
1034 struct snd_pcm_substream *substream) 1103 struct snd_pcm_substream *substream)
1035{ 1104{
1036 struct via_spec *spec = codec->spec; 1105 set_stream_open(codec, STREAM_MULTI_OUT, false);
1037
1038 spec->multiout.hp_nid = 0;
1039 set_stream_active(codec, false);
1040 return 0; 1106 return 0;
1041} 1107}
1042 1108
@@ -1048,9 +1114,7 @@ static int via_playback_hp_pcm_open(struct hda_pcm_stream *hinfo,
1048 1114
1049 if (snd_BUG_ON(!spec->hp_dac_nid)) 1115 if (snd_BUG_ON(!spec->hp_dac_nid))
1050 return -EINVAL; 1116 return -EINVAL;
1051 if (!spec->hp_independent_mode || spec->multiout.hp_nid) 1117 set_stream_open(codec, STREAM_INDEP_HP, true);
1052 return -EBUSY;
1053 set_stream_active(codec, true);
1054 return 0; 1118 return 0;
1055} 1119}
1056 1120
@@ -1058,7 +1122,7 @@ static int via_playback_hp_pcm_close(struct hda_pcm_stream *hinfo,
1058 struct hda_codec *codec, 1122 struct hda_codec *codec,
1059 struct snd_pcm_substream *substream) 1123 struct snd_pcm_substream *substream)
1060{ 1124{
1061 set_stream_active(codec, false); 1125 set_stream_open(codec, STREAM_INDEP_HP, false);
1062 return 0; 1126 return 0;
1063} 1127}
1064 1128
@@ -1070,8 +1134,15 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo,
1070{ 1134{
1071 struct via_spec *spec = codec->spec; 1135 struct via_spec *spec = codec->spec;
1072 1136
1137 mutex_lock(&spec->config_mutex);
1138 setup_playback_multi_pcm(spec);
1073 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag, 1139 snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
1074 format, substream); 1140 format, substream);
1141 /* remember for dynamic DAC switch with indep-HP */
1142 spec->active_streams |= STREAM_MULTI_OUT;
1143 spec->cur_dac_stream_tag = stream_tag;
1144 spec->cur_dac_format = format;
1145 mutex_unlock(&spec->config_mutex);
1075 vt1708_start_hp_work(spec); 1146 vt1708_start_hp_work(spec);
1076 return 0; 1147 return 0;
1077} 1148}
@@ -1084,8 +1155,14 @@ static int via_playback_hp_pcm_prepare(struct hda_pcm_stream *hinfo,
1084{ 1155{
1085 struct via_spec *spec = codec->spec; 1156 struct via_spec *spec = codec->spec;
1086 1157
1087 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 1158 mutex_lock(&spec->config_mutex);
1088 stream_tag, 0, format); 1159 if (spec->hp_independent_mode)
1160 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid,
1161 stream_tag, 0, format);
1162 spec->active_streams |= STREAM_INDEP_HP;
1163 spec->cur_hp_stream_tag = stream_tag;
1164 spec->cur_hp_format = format;
1165 mutex_unlock(&spec->config_mutex);
1089 vt1708_start_hp_work(spec); 1166 vt1708_start_hp_work(spec);
1090 return 0; 1167 return 0;
1091} 1168}
@@ -1096,7 +1173,10 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo,
1096{ 1173{
1097 struct via_spec *spec = codec->spec; 1174 struct via_spec *spec = codec->spec;
1098 1175
1176 mutex_lock(&spec->config_mutex);
1099 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); 1177 snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
1178 spec->active_streams &= ~STREAM_MULTI_OUT;
1179 mutex_unlock(&spec->config_mutex);
1100 vt1708_stop_hp_work(spec); 1180 vt1708_stop_hp_work(spec);
1101 return 0; 1181 return 0;
1102} 1182}
@@ -1107,7 +1187,11 @@ static int via_playback_hp_pcm_cleanup(struct hda_pcm_stream *hinfo,
1107{ 1187{
1108 struct via_spec *spec = codec->spec; 1188 struct via_spec *spec = codec->spec;
1109 1189
1110 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0); 1190 mutex_lock(&spec->config_mutex);
1191 if (spec->hp_independent_mode)
1192 snd_hda_codec_setup_stream(codec, spec->hp_dac_nid, 0, 0, 0);
1193 spec->active_streams &= ~STREAM_INDEP_HP;
1194 mutex_unlock(&spec->config_mutex);
1111 vt1708_stop_hp_work(spec); 1195 vt1708_stop_hp_work(spec);
1112 return 0; 1196 return 0;
1113} 1197}
@@ -1186,10 +1270,12 @@ static int via_dyn_adc_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
1186 struct via_spec *spec = codec->spec; 1270 struct via_spec *spec = codec->spec;
1187 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx; 1271 int adc_idx = spec->inputs[spec->cur_mux[0]].adc_idx;
1188 1272
1273 mutex_lock(&spec->config_mutex);
1189 spec->cur_adc = spec->adc_nids[adc_idx]; 1274 spec->cur_adc = spec->adc_nids[adc_idx];
1190 spec->cur_adc_stream_tag = stream_tag; 1275 spec->cur_adc_stream_tag = stream_tag;
1191 spec->cur_adc_format = format; 1276 spec->cur_adc_format = format;
1192 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format); 1277 snd_hda_codec_setup_stream(codec, spec->cur_adc, stream_tag, 0, format);
1278 mutex_unlock(&spec->config_mutex);
1193 return 0; 1279 return 0;
1194} 1280}
1195 1281
@@ -1199,8 +1285,10 @@ static int via_dyn_adc_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
1199{ 1285{
1200 struct via_spec *spec = codec->spec; 1286 struct via_spec *spec = codec->spec;
1201 1287
1288 mutex_lock(&spec->config_mutex);
1202 snd_hda_codec_cleanup_stream(codec, spec->cur_adc); 1289 snd_hda_codec_cleanup_stream(codec, spec->cur_adc);
1203 spec->cur_adc = 0; 1290 spec->cur_adc = 0;
1291 mutex_unlock(&spec->config_mutex);
1204 return 0; 1292 return 0;
1205} 1293}
1206 1294
@@ -1210,7 +1298,9 @@ static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1210 struct via_spec *spec = codec->spec; 1298 struct via_spec *spec = codec->spec;
1211 int adc_idx = spec->inputs[cur].adc_idx; 1299 int adc_idx = spec->inputs[cur].adc_idx;
1212 hda_nid_t adc = spec->adc_nids[adc_idx]; 1300 hda_nid_t adc = spec->adc_nids[adc_idx];
1301 bool ret = false;
1213 1302
1303 mutex_lock(&spec->config_mutex);
1214 if (spec->cur_adc && spec->cur_adc != adc) { 1304 if (spec->cur_adc && spec->cur_adc != adc) {
1215 /* stream is running, let's swap the current ADC */ 1305 /* stream is running, let's swap the current ADC */
1216 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1); 1306 __snd_hda_codec_cleanup_stream(codec, spec->cur_adc, 1);
@@ -1218,9 +1308,10 @@ static bool via_dyn_adc_pcm_resetup(struct hda_codec *codec, int cur)
1218 snd_hda_codec_setup_stream(codec, adc, 1308 snd_hda_codec_setup_stream(codec, adc,
1219 spec->cur_adc_stream_tag, 0, 1309 spec->cur_adc_stream_tag, 0,
1220 spec->cur_adc_format); 1310 spec->cur_adc_format);
1221 return true; 1311 ret = true;
1222 } 1312 }
1223 return false; 1313 mutex_unlock(&spec->config_mutex);
1314 return ret;
1224} 1315}
1225 1316
1226static const struct hda_pcm_stream via_pcm_analog_playback = { 1317static const struct hda_pcm_stream via_pcm_analog_playback = {