diff options
author | Lydia Wang <lydiawang@viatech.com.cn> | 2009-10-10 07:08:17 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2009-10-11 11:57:25 -0400 |
commit | 1f2e99febd5dd0c91f0d0752674029a4376649e5 (patch) | |
tree | 3ea14111718b1779c62ef80587b89c331a35d283 /sound/pci/hda/patch_via.c | |
parent | dcf34c8cc685781cebbe1f4c75272a3269eba3a1 (diff) |
ALSA: HDA VIA: Add Jack detect feature for VT1708.
VT1708 does not support unsolicited response, but we need hp detect to
automute speaker. Implemented in workqueue.
Signed-off-by: Lydia Wang <lydiawang@viatech.com.cn>
Signed-off-by: Logan Li <loganli@viatech.com.cn>
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.c | 230 |
1 files changed, 173 insertions, 57 deletions
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c index c1f4307feaae..38418a53acd7 100644 --- a/sound/pci/hda/patch_via.c +++ b/sound/pci/hda/patch_via.c | |||
@@ -89,6 +89,64 @@ enum VIA_HDA_CODEC { | |||
89 | CODEC_TYPES, | 89 | CODEC_TYPES, |
90 | }; | 90 | }; |
91 | 91 | ||
92 | struct via_spec { | ||
93 | /* codec parameterization */ | ||
94 | struct snd_kcontrol_new *mixers[4]; | ||
95 | unsigned int num_mixers; | ||
96 | |||
97 | struct hda_verb *init_verbs[5]; | ||
98 | unsigned int num_iverbs; | ||
99 | |||
100 | char *stream_name_analog; | ||
101 | struct hda_pcm_stream *stream_analog_playback; | ||
102 | struct hda_pcm_stream *stream_analog_capture; | ||
103 | |||
104 | char *stream_name_digital; | ||
105 | struct hda_pcm_stream *stream_digital_playback; | ||
106 | struct hda_pcm_stream *stream_digital_capture; | ||
107 | |||
108 | /* playback */ | ||
109 | struct hda_multi_out multiout; | ||
110 | hda_nid_t slave_dig_outs[2]; | ||
111 | |||
112 | /* capture */ | ||
113 | unsigned int num_adc_nids; | ||
114 | hda_nid_t *adc_nids; | ||
115 | hda_nid_t mux_nids[3]; | ||
116 | hda_nid_t dig_in_nid; | ||
117 | hda_nid_t dig_in_pin; | ||
118 | |||
119 | /* capture source */ | ||
120 | const struct hda_input_mux *input_mux; | ||
121 | unsigned int cur_mux[3]; | ||
122 | |||
123 | /* PCM information */ | ||
124 | struct hda_pcm pcm_rec[3]; | ||
125 | |||
126 | /* dynamic controls, init_verbs and input_mux */ | ||
127 | struct auto_pin_cfg autocfg; | ||
128 | struct snd_array kctls; | ||
129 | struct hda_input_mux private_imux[2]; | ||
130 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | ||
131 | |||
132 | /* HP mode source */ | ||
133 | const struct hda_input_mux *hp_mux; | ||
134 | unsigned int hp_independent_mode; | ||
135 | unsigned int hp_independent_mode_index; | ||
136 | unsigned int smart51_enabled; | ||
137 | |||
138 | enum VIA_HDA_CODEC codec_type; | ||
139 | |||
140 | /* work to check hp jack state */ | ||
141 | struct hda_codec *codec; | ||
142 | struct delayed_work vt1708_hp_work; | ||
143 | int vt1708_jack_detectect; | ||
144 | int vt1708_hp_present; | ||
145 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
146 | struct hda_loopback_check loopback; | ||
147 | #endif | ||
148 | }; | ||
149 | |||
92 | static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) | 150 | static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec) |
93 | { | 151 | { |
94 | u32 vendor_id = codec->vendor_id; | 152 | u32 vendor_id = codec->vendor_id; |
@@ -181,6 +239,31 @@ static int mic_boost_volume_info(struct snd_kcontrol *kcontrol, | |||
181 | 239 | ||
182 | static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); | 240 | static void analog_low_current_mode(struct hda_codec *codec, int stream_idle); |
183 | static void set_jack_power_state(struct hda_codec *codec); | 241 | static void set_jack_power_state(struct hda_codec *codec); |
242 | static int is_aa_path_mute(struct hda_codec *codec); | ||
243 | |||
244 | static void vt1708_start_hp_work(struct via_spec *spec) | ||
245 | { | ||
246 | if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) | ||
247 | return; | ||
248 | snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, | ||
249 | !spec->vt1708_jack_detectect); | ||
250 | if (!delayed_work_pending(&spec->vt1708_hp_work)) | ||
251 | schedule_delayed_work(&spec->vt1708_hp_work, | ||
252 | msecs_to_jiffies(100)); | ||
253 | } | ||
254 | |||
255 | static void vt1708_stop_hp_work(struct via_spec *spec) | ||
256 | { | ||
257 | if (spec->codec_type != VT1708 || spec->autocfg.hp_pins[0] == 0) | ||
258 | return; | ||
259 | if (snd_hda_get_bool_hint(spec->codec, "analog_loopback_hp_detect") == 1 | ||
260 | && !is_aa_path_mute(spec->codec)) | ||
261 | return; | ||
262 | snd_hda_codec_write(spec->codec, 0x1, 0, 0xf81, | ||
263 | !spec->vt1708_jack_detectect); | ||
264 | cancel_delayed_work(&spec->vt1708_hp_work); | ||
265 | flush_scheduled_work(); | ||
266 | } | ||
184 | 267 | ||
185 | static int analog_input_switch_put(struct snd_kcontrol *kcontrol, | 268 | static int analog_input_switch_put(struct snd_kcontrol *kcontrol, |
186 | struct snd_ctl_elem_value *ucontrol) | 269 | struct snd_ctl_elem_value *ucontrol) |
@@ -190,6 +273,12 @@ static int analog_input_switch_put(struct snd_kcontrol *kcontrol, | |||
190 | 273 | ||
191 | set_jack_power_state(codec); | 274 | set_jack_power_state(codec); |
192 | analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); | 275 | analog_low_current_mode(snd_kcontrol_chip(kcontrol), -1); |
276 | if (snd_hda_get_bool_hint(codec, "analog_loopback_hp_detect") == 1) { | ||
277 | if (is_aa_path_mute(codec)) | ||
278 | vt1708_start_hp_work(codec->spec); | ||
279 | else | ||
280 | vt1708_stop_hp_work(codec->spec); | ||
281 | } | ||
193 | return change; | 282 | return change; |
194 | } | 283 | } |
195 | 284 | ||
@@ -210,59 +299,6 @@ static struct snd_kcontrol_new vt1708_control_templates[] = { | |||
210 | }; | 299 | }; |
211 | 300 | ||
212 | 301 | ||
213 | struct via_spec { | ||
214 | /* codec parameterization */ | ||
215 | struct snd_kcontrol_new *mixers[4]; | ||
216 | unsigned int num_mixers; | ||
217 | |||
218 | struct hda_verb *init_verbs[5]; | ||
219 | unsigned int num_iverbs; | ||
220 | |||
221 | char *stream_name_analog; | ||
222 | struct hda_pcm_stream *stream_analog_playback; | ||
223 | struct hda_pcm_stream *stream_analog_capture; | ||
224 | |||
225 | char *stream_name_digital; | ||
226 | struct hda_pcm_stream *stream_digital_playback; | ||
227 | struct hda_pcm_stream *stream_digital_capture; | ||
228 | |||
229 | /* playback */ | ||
230 | struct hda_multi_out multiout; | ||
231 | hda_nid_t slave_dig_outs[2]; | ||
232 | |||
233 | /* capture */ | ||
234 | unsigned int num_adc_nids; | ||
235 | hda_nid_t *adc_nids; | ||
236 | hda_nid_t mux_nids[3]; | ||
237 | hda_nid_t dig_in_nid; | ||
238 | hda_nid_t dig_in_pin; | ||
239 | |||
240 | /* capture source */ | ||
241 | const struct hda_input_mux *input_mux; | ||
242 | unsigned int cur_mux[3]; | ||
243 | |||
244 | /* PCM information */ | ||
245 | struct hda_pcm pcm_rec[3]; | ||
246 | |||
247 | /* dynamic controls, init_verbs and input_mux */ | ||
248 | struct auto_pin_cfg autocfg; | ||
249 | struct snd_array kctls; | ||
250 | struct hda_input_mux private_imux[2]; | ||
251 | hda_nid_t private_dac_nids[AUTO_CFG_MAX_OUTS]; | ||
252 | |||
253 | /* HP mode source */ | ||
254 | const struct hda_input_mux *hp_mux; | ||
255 | unsigned int hp_independent_mode; | ||
256 | unsigned int hp_independent_mode_index; | ||
257 | unsigned int smart51_enabled; | ||
258 | |||
259 | enum VIA_HDA_CODEC codec_type; | ||
260 | |||
261 | #ifdef CONFIG_SND_HDA_POWER_SAVE | ||
262 | struct hda_loopback_check loopback; | ||
263 | #endif | ||
264 | }; | ||
265 | |||
266 | static hda_nid_t vt1708_adc_nids[2] = { | 302 | static hda_nid_t vt1708_adc_nids[2] = { |
267 | /* ADC1-2 */ | 303 | /* ADC1-2 */ |
268 | 0x15, 0x27 | 304 | 0x15, 0x27 |
@@ -981,7 +1017,6 @@ static int via_playback_pcm_open(struct hda_pcm_stream *hinfo, | |||
981 | struct via_spec *spec = codec->spec; | 1017 | struct via_spec *spec = codec->spec; |
982 | int idle = substream->pstr->substream_opened == 1 | 1018 | int idle = substream->pstr->substream_opened == 1 |
983 | && substream->ref_count == 0; | 1019 | && substream->ref_count == 0; |
984 | |||
985 | analog_low_current_mode(codec, idle); | 1020 | analog_low_current_mode(codec, idle); |
986 | return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, | 1021 | return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream, |
987 | hinfo); | 1022 | hinfo); |
@@ -994,6 +1029,7 @@ static int via_playback_pcm_prepare(struct hda_pcm_stream *hinfo, | |||
994 | struct snd_pcm_substream *substream) | 1029 | struct snd_pcm_substream *substream) |
995 | { | 1030 | { |
996 | struct via_spec *spec = codec->spec; | 1031 | struct via_spec *spec = codec->spec; |
1032 | vt1708_start_hp_work(spec); | ||
997 | return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, | 1033 | return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, |
998 | stream_tag, format, substream); | 1034 | stream_tag, format, substream); |
999 | } | 1035 | } |
@@ -1003,6 +1039,7 @@ static int via_playback_pcm_cleanup(struct hda_pcm_stream *hinfo, | |||
1003 | struct snd_pcm_substream *substream) | 1039 | struct snd_pcm_substream *substream) |
1004 | { | 1040 | { |
1005 | struct via_spec *spec = codec->spec; | 1041 | struct via_spec *spec = codec->spec; |
1042 | vt1708_stop_hp_work(spec); | ||
1006 | return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); | 1043 | return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout); |
1007 | } | 1044 | } |
1008 | 1045 | ||
@@ -1094,7 +1131,7 @@ static int via_playback_multi_pcm_prepare(struct hda_pcm_stream *hinfo, | |||
1094 | snd_hda_codec_setup_stream(codec, mout->hp_nid, | 1131 | snd_hda_codec_setup_stream(codec, mout->hp_nid, |
1095 | stream_tag, 0, format); | 1132 | stream_tag, 0, format); |
1096 | } | 1133 | } |
1097 | 1134 | vt1708_start_hp_work(spec); | |
1098 | return 0; | 1135 | return 0; |
1099 | } | 1136 | } |
1100 | 1137 | ||
@@ -1134,7 +1171,7 @@ static int via_playback_multi_pcm_cleanup(struct hda_pcm_stream *hinfo, | |||
1134 | snd_hda_codec_setup_stream(codec, mout->hp_nid, | 1171 | snd_hda_codec_setup_stream(codec, mout->hp_nid, |
1135 | 0, 0, 0); | 1172 | 0, 0, 0); |
1136 | } | 1173 | } |
1137 | 1174 | vt1708_stop_hp_work(spec); | |
1138 | return 0; | 1175 | return 0; |
1139 | } | 1176 | } |
1140 | 1177 | ||
@@ -1345,6 +1382,7 @@ static void via_free(struct hda_codec *codec) | |||
1345 | return; | 1382 | return; |
1346 | 1383 | ||
1347 | via_free_kctls(codec); | 1384 | via_free_kctls(codec); |
1385 | vt1708_stop_hp_work(spec); | ||
1348 | kfree(codec->spec); | 1386 | kfree(codec->spec); |
1349 | } | 1387 | } |
1350 | 1388 | ||
@@ -1464,6 +1502,15 @@ static int via_init(struct hda_codec *codec) | |||
1464 | return 0; | 1502 | return 0; |
1465 | } | 1503 | } |
1466 | 1504 | ||
1505 | #ifdef SND_HDA_NEEDS_RESUME | ||
1506 | static int via_suspend(struct hda_codec *codec, pm_message_t state) | ||
1507 | { | ||
1508 | struct via_spec *spec = codec->spec; | ||
1509 | vt1708_stop_hp_work(spec); | ||
1510 | return 0; | ||
1511 | } | ||
1512 | #endif | ||
1513 | |||
1467 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1514 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1468 | static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) | 1515 | static int via_check_power_status(struct hda_codec *codec, hda_nid_t nid) |
1469 | { | 1516 | { |
@@ -1479,6 +1526,9 @@ static struct hda_codec_ops via_patch_ops = { | |||
1479 | .build_pcms = via_build_pcms, | 1526 | .build_pcms = via_build_pcms, |
1480 | .init = via_init, | 1527 | .init = via_init, |
1481 | .free = via_free, | 1528 | .free = via_free, |
1529 | #ifdef SND_HDA_NEEDS_RESUME | ||
1530 | .suspend = via_suspend, | ||
1531 | #endif | ||
1482 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1532 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1483 | .check_power_status = via_check_power_status, | 1533 | .check_power_status = via_check_power_status, |
1484 | #endif | 1534 | #endif |
@@ -1728,6 +1778,51 @@ static void vt1708_set_pinconfig_connect(struct hda_codec *codec, hda_nid_t nid) | |||
1728 | return; | 1778 | return; |
1729 | } | 1779 | } |
1730 | 1780 | ||
1781 | static int vt1708_jack_detectect_get(struct snd_kcontrol *kcontrol, | ||
1782 | struct snd_ctl_elem_value *ucontrol) | ||
1783 | { | ||
1784 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1785 | struct via_spec *spec = codec->spec; | ||
1786 | |||
1787 | if (spec->codec_type != VT1708) | ||
1788 | return 0; | ||
1789 | spec->vt1708_jack_detectect = | ||
1790 | !((snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8) & 0x1); | ||
1791 | ucontrol->value.integer.value[0] = spec->vt1708_jack_detectect; | ||
1792 | return 0; | ||
1793 | } | ||
1794 | |||
1795 | static int vt1708_jack_detectect_put(struct snd_kcontrol *kcontrol, | ||
1796 | struct snd_ctl_elem_value *ucontrol) | ||
1797 | { | ||
1798 | struct hda_codec *codec = snd_kcontrol_chip(kcontrol); | ||
1799 | struct via_spec *spec = codec->spec; | ||
1800 | int change; | ||
1801 | |||
1802 | if (spec->codec_type != VT1708) | ||
1803 | return 0; | ||
1804 | spec->vt1708_jack_detectect = ucontrol->value.integer.value[0]; | ||
1805 | change = (0x1 & (snd_hda_codec_read(codec, 0x1, 0, 0xf84, 0) >> 8)) | ||
1806 | == !spec->vt1708_jack_detectect; | ||
1807 | if (spec->vt1708_jack_detectect) { | ||
1808 | mute_aa_path(codec, 1); | ||
1809 | notify_aa_path_ctls(codec); | ||
1810 | } | ||
1811 | return change; | ||
1812 | } | ||
1813 | |||
1814 | static struct snd_kcontrol_new vt1708_jack_detectect[] = { | ||
1815 | { | ||
1816 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
1817 | .name = "Jack Detect", | ||
1818 | .count = 1, | ||
1819 | .info = snd_ctl_boolean_mono_info, | ||
1820 | .get = vt1708_jack_detectect_get, | ||
1821 | .put = vt1708_jack_detectect_put, | ||
1822 | }, | ||
1823 | {} /* end */ | ||
1824 | }; | ||
1825 | |||
1731 | static int vt1708_parse_auto_config(struct hda_codec *codec) | 1826 | static int vt1708_parse_auto_config(struct hda_codec *codec) |
1732 | { | 1827 | { |
1733 | struct via_spec *spec = codec->spec; | 1828 | struct via_spec *spec = codec->spec; |
@@ -1755,6 +1850,10 @@ static int vt1708_parse_auto_config(struct hda_codec *codec) | |||
1755 | err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); | 1850 | err = vt1708_auto_create_analog_input_ctls(spec, &spec->autocfg); |
1756 | if (err < 0) | 1851 | if (err < 0) |
1757 | return err; | 1852 | return err; |
1853 | /* add jack detect on/off control */ | ||
1854 | err = snd_hda_add_new_ctls(codec, vt1708_jack_detectect); | ||
1855 | if (err < 0) | ||
1856 | return err; | ||
1758 | 1857 | ||
1759 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; | 1858 | spec->multiout.max_channels = spec->multiout.num_dacs * 2; |
1760 | 1859 | ||
@@ -1788,6 +1887,22 @@ static int via_auto_init(struct hda_codec *codec) | |||
1788 | return 0; | 1887 | return 0; |
1789 | } | 1888 | } |
1790 | 1889 | ||
1890 | static void vt1708_update_hp_jack_state(struct work_struct *work) | ||
1891 | { | ||
1892 | struct via_spec *spec = container_of(work, struct via_spec, | ||
1893 | vt1708_hp_work.work); | ||
1894 | if (spec->codec_type != VT1708) | ||
1895 | return; | ||
1896 | /* if jack state toggled */ | ||
1897 | if (spec->vt1708_hp_present | ||
1898 | != (snd_hda_codec_read(spec->codec, spec->autocfg.hp_pins[0], 0, | ||
1899 | AC_VERB_GET_PIN_SENSE, 0) >> 31)) { | ||
1900 | spec->vt1708_hp_present ^= 1; | ||
1901 | via_hp_automute(spec->codec); | ||
1902 | } | ||
1903 | vt1708_start_hp_work(spec); | ||
1904 | } | ||
1905 | |||
1791 | static int get_mux_nids(struct hda_codec *codec) | 1906 | static int get_mux_nids(struct hda_codec *codec) |
1792 | { | 1907 | { |
1793 | struct via_spec *spec = codec->spec; | 1908 | struct via_spec *spec = codec->spec; |
@@ -1864,7 +1979,8 @@ static int patch_vt1708(struct hda_codec *codec) | |||
1864 | #ifdef CONFIG_SND_HDA_POWER_SAVE | 1979 | #ifdef CONFIG_SND_HDA_POWER_SAVE |
1865 | spec->loopback.amplist = vt1708_loopbacks; | 1980 | spec->loopback.amplist = vt1708_loopbacks; |
1866 | #endif | 1981 | #endif |
1867 | 1982 | spec->codec = codec; | |
1983 | INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state); | ||
1868 | return 0; | 1984 | return 0; |
1869 | } | 1985 | } |
1870 | 1986 | ||