diff options
| -rw-r--r-- | sound/pci/hda/patch_via.c | 159 |
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 | ||
| 112 | enum { | ||
| 113 | STREAM_MULTI_OUT = (1 << 0), | ||
| 114 | STREAM_INDEP_HP = (1 << 1), | ||
| 115 | }; | ||
| 116 | |||
| 112 | struct via_spec { | 117 | struct 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 | ||
| 212 | static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec); | 226 | static 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 */ | ||
| 775 | static 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 | */ | ||
| 792 | static 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 | |||
| 759 | static int via_independent_hp_put(struct snd_kcontrol *kcontrol, | 835 | static 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 | ||
| 992 | static void set_stream_active(struct hda_codec *codec, bool active) | 1070 | static 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 | ||
| 1226 | static const struct hda_pcm_stream via_pcm_analog_playback = { | 1317 | static const struct hda_pcm_stream via_pcm_analog_playback = { |
