1 /* $OpenBSD: ac97.c,v 1.56 2005/06/18 21:23:59 canacar Exp $ */
2
3 /*
4 * Copyright (c) 1999, 2000 Constantine Sapuntzakis
5 *
6 * Author: Constantine Sapuntzakis <csapuntz@stanford.edu>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote
17 * products derived from this software without specific prior written
18 * permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS
20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
25 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
26 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
29 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30 * DAMAGE. */
31
32 /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
33 the following copyright */
34
35 /*
36 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
37 * All rights reserved.
38 *
39 * Redistribution and use in source and binary forms, with or without
40 * modification, are permitted provided that the following conditions
41 * are met:
42 * 1. Redistributions of source code must retain the above copyright
43 * notice, this list of conditions and the following disclaimer.
44 * 2. Redistributions in binary form must reproduce the above copyright
45 * notice, this list of conditions and the following disclaimer in the
46 * documentation and/or other materials provided with the distribution.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
49 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
52 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
53 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
54 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
56 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
57 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
58 * SUCH DAMAGE.
59 *
60 * $FreeBSD$
61 */
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/kernel.h>
66 #include <sys/malloc.h>
67 #include <sys/device.h>
68
69 #include <sys/audioio.h>
70 #include <dev/audio_if.h>
71 #include <dev/ic/ac97.h>
72
73 const struct audio_mixer_enum ac97_on_off = {
74 2,
75 { { { AudioNoff } , 0 },
76 { { AudioNon } , 1 } }
77 };
78
79 const struct audio_mixer_enum ac97_mic_select = {
80 2,
81 { { { AudioNmicrophone "0" }, 0 },
82 { { AudioNmicrophone "1" }, 1 } }
83 };
84
85 const struct audio_mixer_enum ac97_mono_select = {
86 2,
87 { { { AudioNmixerout }, 0 },
88 { { AudioNmicrophone }, 1 } }
89 };
90
91 const struct audio_mixer_enum ac97_source = {
92 8,
93 { { { AudioNmicrophone } , 0 },
94 { { AudioNcd }, 1 },
95 { { "video" }, 2 },
96 { { AudioNaux }, 3 },
97 { { AudioNline }, 4 },
98 { { AudioNmixerout }, 5 },
99 { { AudioNmixerout AudioNmono }, 6 },
100 { { "phone" }, 7 }}
101 };
102
103 /*
104 * Due to different values for each source that uses these structures,
105 * the ac97_query_devinfo function sets delta in mixer_devinfo_t using
106 * ac97_source_info.bits.
107 */
108 const struct audio_mixer_value ac97_volume_stereo = {
109 { AudioNvolume },
110 2
111 };
112
113 const struct audio_mixer_value ac97_volume_mono = {
114 { AudioNvolume },
115 1
116 };
117
118 #define WRAP(a) &a, sizeof(a)
119
120 const struct ac97_source_info {
121 char *class;
122 char *device;
123 char *qualifier;
124 int type;
125
126 const void *info;
127 int16_t info_size;
128
129 u_int8_t reg;
130 u_int8_t bits:3;
131 u_int8_t ofs:4;
132 u_int8_t mute:1;
133 u_int8_t polarity:1; /* Does 0 == MAX or MIN */
134 u_int16_t default_value;
135
136 int16_t prev;
137 int16_t next;
138 int16_t mixer_class;
139 } source_info[] = {
140 {
141 AudioCinputs, NULL, NULL, AUDIO_MIXER_CLASS,
142 }, {
143 AudioCoutputs, NULL, NULL, AUDIO_MIXER_CLASS,
144 }, {
145 AudioCrecord, NULL, NULL, AUDIO_MIXER_CLASS,
146 }, {
147 /* Stereo master volume*/
148 AudioCoutputs, AudioNmaster, NULL, AUDIO_MIXER_VALUE,
149 WRAP(ac97_volume_stereo),
150 AC97_REG_MASTER_VOLUME, 5, 0, 1, 0, 0x8000
151 }, {
152 /* Mono volume */
153 AudioCoutputs, AudioNmono, NULL, AUDIO_MIXER_VALUE,
154 WRAP(ac97_volume_mono),
155 AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1, 0, 0x8000
156 }, {
157 AudioCoutputs, AudioNmono, AudioNsource, AUDIO_MIXER_ENUM,
158 WRAP(ac97_mono_select),
159 AC97_REG_GP, 1, 9, 0, 0, 0x0000
160 }, {
161 /* Headphone volume */
162 AudioCoutputs, AudioNheadphone, NULL, AUDIO_MIXER_VALUE,
163 WRAP(ac97_volume_stereo),
164 AC97_REG_HEADPHONE_VOLUME, 6, 0, 1, 0, 0x8000
165 }, {
166 AudioCoutputs, AudioNbass, NULL, AUDIO_MIXER_VALUE,
167 WRAP(ac97_volume_mono),
168 AC97_REG_MASTER_TONE, 4, 8, 0, 0, 0x0f0f
169 }, {
170 AudioCoutputs, AudioNtreble, NULL, AUDIO_MIXER_VALUE,
171 WRAP(ac97_volume_mono),
172 AC97_REG_MASTER_TONE, 4, 0, 0, 0, 0x0f0f
173 }, {
174 /* PC Beep Volume */
175 AudioCinputs, AudioNspeaker, NULL, AUDIO_MIXER_VALUE,
176 WRAP(ac97_volume_mono),
177 AC97_REG_PCBEEP_VOLUME, 4, 1, 1, 0, 0x0000
178 }, {
179 /* Phone */
180 AudioCinputs, "phone", NULL, AUDIO_MIXER_VALUE,
181 WRAP(ac97_volume_mono),
182 AC97_REG_PHONE_VOLUME, 5, 0, 1, 0, 0x8008
183 }, {
184 /* Mic Volume */
185 AudioCinputs, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
186 WRAP(ac97_volume_mono),
187 AC97_REG_MIC_VOLUME, 5, 0, 1, 0, 0x8008
188 }, {
189 AudioCinputs, AudioNmicrophone, AudioNpreamp, AUDIO_MIXER_ENUM,
190 WRAP(ac97_on_off),
191 AC97_REG_MIC_VOLUME, 1, 6, 0, 0, 0x8008
192 }, {
193 AudioCinputs, AudioNmicrophone, AudioNsource, AUDIO_MIXER_ENUM,
194 WRAP(ac97_mic_select),
195 AC97_REG_GP, 1, 8, 0, 0x0000
196 }, {
197 /* Line in Volume */
198 AudioCinputs, AudioNline, NULL, AUDIO_MIXER_VALUE,
199 WRAP(ac97_volume_stereo),
200 AC97_REG_LINEIN_VOLUME, 5, 0, 1, 0, 0x8808
201 }, {
202 /* CD Volume */
203 AudioCinputs, AudioNcd, NULL, AUDIO_MIXER_VALUE,
204 WRAP(ac97_volume_stereo),
205 AC97_REG_CD_VOLUME, 5, 0, 1, 0, 0x8808
206 }, {
207 /* Video Volume */
208 AudioCinputs, "video", NULL, AUDIO_MIXER_VALUE,
209 WRAP(ac97_volume_stereo),
210 AC97_REG_VIDEO_VOLUME, 5, 0, 1, 0, 0x8808
211 }, {
212 /* AUX volume */
213 AudioCinputs, AudioNaux, NULL, AUDIO_MIXER_VALUE,
214 WRAP(ac97_volume_stereo),
215 AC97_REG_AUX_VOLUME, 5, 0, 1, 0, 0x8808
216 }, {
217 /* PCM out volume */
218 AudioCinputs, AudioNdac, NULL, AUDIO_MIXER_VALUE,
219 WRAP(ac97_volume_stereo),
220 AC97_REG_PCMOUT_VOLUME, 5, 0, 1, 0, 0x8808
221 }, {
222 /* Record Source - some logic for this is hard coded - see below */
223 AudioCrecord, AudioNsource, NULL, AUDIO_MIXER_ENUM,
224 WRAP(ac97_source),
225 AC97_REG_RECORD_SELECT, 3, 0, 0, 0, 0x0000
226 }, {
227 /* Record Gain */
228 AudioCrecord, AudioNvolume, NULL, AUDIO_MIXER_VALUE,
229 WRAP(ac97_volume_stereo),
230 AC97_REG_RECORD_GAIN, 4, 0, 1, 0, 0x8000
231 }, {
232 /* Record Gain mic */
233 AudioCrecord, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
234 WRAP(ac97_volume_mono),
235 AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1, 0x8000
236 }, {
237 /* */
238 AudioCoutputs, AudioNloudness, NULL, AUDIO_MIXER_ENUM,
239 WRAP(ac97_on_off),
240 AC97_REG_GP, 1, 12, 0, 0, 0x0000
241 }, {
242 AudioCoutputs, AudioNspatial, NULL, AUDIO_MIXER_ENUM,
243 WRAP(ac97_on_off),
244 AC97_REG_GP, 1, 13, 0, 0, 0x0000
245 }, {
246 AudioCoutputs, AudioNspatial, AudioNcenter,AUDIO_MIXER_VALUE,
247 WRAP(ac97_volume_mono),
248 AC97_REG_3D_CONTROL, 4, 8, 0, 1, 0x0000
249 }, {
250 AudioCoutputs, AudioNspatial, AudioNdepth, AUDIO_MIXER_VALUE,
251 WRAP(ac97_volume_mono),
252 AC97_REG_3D_CONTROL, 4, 0, 0, 1, 0x0000
253 }, {
254 /* Surround volume */
255 AudioCoutputs, AudioNsurround, NULL, AUDIO_MIXER_VALUE,
256 WRAP(ac97_volume_stereo),
257 AC97_REG_SURROUND_VOLUME, 6, 0, 1, 0, 0x8080
258 }, {
259 /* Center volume */
260 AudioCoutputs, AudioNcenter, NULL, AUDIO_MIXER_VALUE,
261 WRAP(ac97_volume_mono),
262 AC97_REG_CENTER_LFE_VOLUME, 6, 0, 1, 0, 0x8080
263 }, {
264 /* LFE volume */
265 AudioCoutputs, AudioNlfe, NULL, AUDIO_MIXER_VALUE,
266 WRAP(ac97_volume_mono),
267 AC97_REG_CENTER_LFE_VOLUME, 6, 8, 1, 0, 0x8080
268 }
269
270 /* Missing features: Simulated Stereo, POP, Loopback mode */
271 } ;
272
273 #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
274
275 /*
276 * Check out http://www.intel.com/technology/computing/audio/index.htm
277 * for information on AC-97
278 */
279
280 struct ac97_softc {
281 struct ac97_codec_if codec_if;
282 struct ac97_host_if *host_if;
283 struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
284 int num_source_info;
285 enum ac97_host_flags host_flags;
286 u_int16_t caps, ext_id;
287 u_int16_t shadow_reg[128];
288 };
289
290 int ac97_mixer_get_port(struct ac97_codec_if *self, mixer_ctrl_t *cp);
291 int ac97_mixer_set_port(struct ac97_codec_if *self, mixer_ctrl_t *);
292 int ac97_query_devinfo(struct ac97_codec_if *self, mixer_devinfo_t *);
293 int ac97_get_portnum_by_name(struct ac97_codec_if *, char *, char *,
294 char *);
295 void ac97_restore_shadow(struct ac97_codec_if *self);
296
297 void ac97_ad1886_init(struct ac97_softc *);
298 void ac97_ad198x_init(struct ac97_softc *);
299 void ac97_alc655_init(struct ac97_softc *);
300 void ac97_cx20468_init(struct ac97_softc *);
301 void ac97_cx_init(struct ac97_softc *);
302
303 struct ac97_codec_if_vtbl ac97civ = {
304 ac97_mixer_get_port,
305 ac97_mixer_set_port,
306 ac97_query_devinfo,
307 ac97_get_portnum_by_name,
308 ac97_restore_shadow
309 };
310
311 const struct ac97_codecid {
312 u_int8_t id;
313 u_int8_t mask;
314 u_int8_t rev;
315 u_int8_t shift; /* no use yet */
316 char * const name;
317 void (*init)(struct ac97_softc *);
318 } ac97_ad[] = {
319 { 0x03, 0xff, 0, 0, "AD1819" },
320 { 0x40, 0xff, 0, 0, "AD1881" },
321 { 0x48, 0xff, 0, 0, "AD1881A" },
322 { 0x60, 0xff, 0, 0, "AD1885" },
323 { 0x61, 0xff, 0, 0, "AD1886", ac97_ad1886_init },
324 { 0x63, 0xff, 0, 0, "AD1886A" },
325 { 0x68, 0xff, 0, 0, "AD1888", ac97_ad198x_init },
326 { 0x70, 0xff, 0, 0, "AD1980" },
327 { 0x72, 0xff, 0, 0, "AD1981A" },
328 { 0x74, 0xff, 0, 0, "AD1981B" },
329 { 0x75, 0xff, 0, 0, "AD1985", ac97_ad198x_init },
330 }, ac97_ak[] = {
331 { 0x00, 0xfe, 1, 0, "AK4540" },
332 { 0x01, 0xfe, 1, 0, "AK4540" },
333 { 0x02, 0xff, 0, 0, "AK4543" },
334 { 0x05, 0xff, 0, 0, "AK4544" },
335 { 0x06, 0xff, 0, 0, "AK4544A" },
336 { 0x07, 0xff, 0, 0, "AK4545" },
337 }, ac97_av[] = {
338 { 0x10, 0xff, 0, 0, "ALC200" },
339 { 0x20, 0xff, 0, 0, "ALC650" },
340 { 0x21, 0xff, 0, 0, "ALC650D" },
341 { 0x22, 0xff, 0, 0, "ALC650E" },
342 { 0x23, 0xff, 0, 0, "ALC650F" },
343 { 0x30, 0xff, 0, 0, "ALC101" },
344 { 0x40, 0xff, 0, 0, "ALC202" },
345 { 0x50, 0xff, 0, 0, "ALC250" },
346 { 0x52, 0xff, 0, 0, "ALC250A?" },
347 { 0x60, 0xff, 0, 0, "ALC655", ac97_alc655_init },
348 { 0x70, 0xff, 0, 0, "ALC203" },
349 { 0x80, 0xff, 0, 0, "ALC658", ac97_alc655_init },
350 { 0x90, 0xff, 0, 0, "ALC850" },
351 }, ac97_rl[] = {
352 { 0x00, 0xf0, 0xf, 0, "RL5306" },
353 { 0x10, 0xf0, 0xf, 0, "RL5382" },
354 { 0x20, 0xf0, 0xf, 0, "RL5383" },
355 }, ac97_cm[] = {
356 { 0x41, 0xff, 0, 0, "CMI9738" },
357 { 0x61, 0xff, 0, 0, "CMI9739" },
358 { 0x78, 0xff, 0, 0, "CMI9761A" },
359 { 0x82, 0xff, 0, 0, "CMI9761B" },
360 { 0x83, 0xff, 0, 0, "CMI9761A+" },
361 }, ac97_cr[] = {
362 { 0x84, 0xff, 0, 0, "EV1938" },
363 }, ac97_cs[] = {
364 { 0x00, 0xf8, 7, 0, "CS4297" },
365 { 0x10, 0xf8, 7, 0, "CS4297A" },
366 { 0x20, 0xf8, 7, 0, "CS4298" },
367 { 0x28, 0xf8, 7, 0, "CS4294" },
368 { 0x30, 0xf8, 7, 0, "CS4299" },
369 { 0x48, 0xf8, 7, 0, "CS4201" },
370 { 0x58, 0xf8, 7, 0, "CS4205" },
371 { 0x60, 0xf8, 7, 0, "CS4291" },
372 { 0x70, 0xf8, 7, 0, "CS4202" },
373 }, ac97_cx[] = {
374 { 0x21, 0xff, 0, 0, "HSD11246" },
375 { 0x28, 0xf8, 7, 0, "CX20468", ac97_cx20468_init },
376 { 0x30, 0xff, 0, 0, "CX?????", ac97_cx_init },
377 }, ac97_dt[] = {
378 { 0x00, 0xff, 0, 0, "DT0398" },
379 }, ac97_em[] = {
380 { 0x23, 0xff, 0, 0, "EM28023" },
381 { 0x28, 0xff, 0, 0, "EM28028" },
382 }, ac97_es[] = {
383 { 0x08, 0xff, 0, 0, "ES1921" },
384 }, ac97_is[] = {
385 { 0x00, 0xff, 0, 0, "HMP9701" },
386 }, ac97_ic[] = {
387 { 0x01, 0xff, 0, 0, "ICE1230" },
388 { 0x11, 0xff, 0, 0, "ICE1232" },
389 { 0x14, 0xff, 0, 0, "ICE1232A" },
390 { 0x51, 0xff, 0, 0, "VIA VT1616" },
391 { 0x52, 0xff, 0, 0, "VIA VT1616i" },
392 }, ac97_it[] = {
393 { 0x20, 0xff, 0, 0, "ITE2226E" },
394 { 0x60, 0xff, 0, 0, "ITE2646E" },
395 }, ac97_ns[] = {
396 { 0x00, 0xff, 0, 0, "LM454[03568]" },
397 { 0x31, 0xff, 0, 0, "LM4549" },
398 { 0x40, 0xff, 0, 0, "LM4540" },
399 { 0x43, 0xff, 0, 0, "LM4543" },
400 { 0x46, 0xff, 0, 0, "LM4546A" },
401 { 0x48, 0xff, 0, 0, "LM4548A" },
402 { 0x49, 0xff, 0, 0, "LM4549A" },
403 { 0x50, 0xff, 0, 0, "LM4550" },
404 }, ac97_ps[] = {
405 { 0x01, 0xff, 0, 0, "UCB1510" },
406 { 0x04, 0xff, 0, 0, "UCB1400" },
407 }, ac97_sl[] = {
408 { 0x20, 0xe0, 0, 0, "Si3036/38" },
409 }, ac97_st[] = {
410 { 0x00, 0xff, 0, 0, "STAC9700" },
411 { 0x04, 0xff, 0, 0, "STAC970[135]" },
412 { 0x05, 0xff, 0, 0, "STAC9704" },
413 { 0x08, 0xff, 0, 0, "STAC9708/11" },
414 { 0x09, 0xff, 0, 0, "STAC9721/23" },
415 { 0x44, 0xff, 0, 0, "STAC9744/45" },
416 { 0x50, 0xff, 0, 0, "STAC9750/51" },
417 { 0x52, 0xff, 0, 0, "STAC9752/53" },
418 { 0x56, 0xff, 0, 0, "STAC9756/57" },
419 { 0x58, 0xff, 0, 0, "STAC9758/59" },
420 { 0x60, 0xff, 0, 0, "STAC9760/61" },
421 { 0x62, 0xff, 0, 0, "STAC9762/63" },
422 { 0x66, 0xff, 0, 0, "STAC9766/67" },
423 { 0x84, 0xff, 0, 0, "STAC9784/85" },
424 }, ac97_vi[] = {
425 { 0x61, 0xff, 0, 0, "VT1612A" },
426 }, ac97_tt[] = {
427 { 0x02, 0xff, 0, 0, "TR28022" },
428 { 0x03, 0xff, 0, 0, "TR28023" },
429 { 0x06, 0xff, 0, 0, "TR28026" },
430 { 0x08, 0xff, 0, 0, "TR28028" },
431 { 0x23, 0xff, 0, 0, "TR28602" },
432 }, ac97_ti[] = {
433 { 0x20, 0xff, 0, 0, "TLC320AD9xC" },
434 }, ac97_wb[] = {
435 { 0x01, 0xff, 0, 0, "W83971D" },
436 }, ac97_wo[] = {
437 { 0x00, 0xff, 0, 0, "WM9701A" },
438 { 0x03, 0xff, 0, 0, "WM9704M/Q-0" }, /* & WM9703 */
439 { 0x04, 0xff, 0, 0, "WM9704M/Q-1" },
440 { 0x05, 0xff, 0, 0, "WM9705/10" },
441 { 0x09, 0xff, 0, 0, "WM9709" },
442 { 0x12, 0xff, 0, 0, "WM9711/12" },
443 }, ac97_ym[] = {
444 { 0x00, 0xff, 0, 0, "YMF743-S" },
445 { 0x02, 0xff, 0, 0, "YMF752-S" },
446 { 0x03, 0xff, 0, 0, "YMF753-S" },
447 };
448
449 #define cl(n) n, sizeof(n)/sizeof(n[0])
450 const struct ac97_vendorid {
451 u_int32_t id;
452 char * const name;
453 const struct ac97_codecid * const codecs;
454 u_int8_t num;
455 } ac97_vendors[] = {
456 { 0x01408300, "Creative", cl(ac97_cr) },
457 { 0x41445300, "Analog Devices", cl(ac97_ad) },
458 { 0x414b4D00, "Asahi Kasei", cl(ac97_ak) },
459 { 0x414c4300, "Realtek", cl(ac97_rl) },
460 { 0x414c4700, "Avance Logic", cl(ac97_av) },
461 { 0x434d4900, "C-Media Electronics", cl(ac97_cm) },
462 { 0x43525900, "Cirrus Logic", cl(ac97_cs) },
463 { 0x43585400, "Conexant", cl(ac97_cx) },
464 { 0x44543000, "Diamond Technology", cl(ac97_dt) },
465 { 0x454d4300, "eMicro", cl(ac97_em) },
466 { 0x45838300, "ESS Technology", cl(ac97_es) },
467 { 0x48525300, "Intersil", cl(ac97_is) },
468 { 0x49434500, "ICEnsemble", cl(ac97_ic) },
469 { 0x49544500, "ITE, Inc.", cl(ac97_it) },
470 { 0x4e534300, "National Semiconductor", cl(ac97_ns) },
471 { 0x50534300, "Philips Semiconductor", cl(ac97_ps) },
472 { 0x53494c00, "Silicon Laboratory", cl(ac97_sl) },
473 { 0x54524100, "TriTech Microelectronics", cl(ac97_tt) },
474 { 0x54584e00, "Texas Instruments", cl(ac97_ti) },
475 { 0x56494100, "VIA Technologies", cl(ac97_vi) },
476 { 0x57454300, "Winbond", cl(ac97_wb) },
477 { 0x574d4c00, "Wolfson", cl(ac97_wo) },
478 { 0x594d4800, "Yamaha", cl(ac97_ym) },
479 { 0x83847600, "SigmaTel", cl(ac97_st) },
480 };
481 #undef cl
482
483 const char * const ac97enhancement[] = {
484 "No 3D Stereo",
485 "Analog Devices Phat Stereo",
486 "Creative",
487 "National Semi 3D",
488 "Yamaha Ymersion",
489 "BBE 3D",
490 "Crystal Semi 3D",
491 "Qsound QXpander",
492 "Spatializer 3D",
493 "SRS 3D",
494 "Platform Tech 3D",
495 "AKM 3D",
496 "Aureal",
497 "AZTECH 3D",
498 "Binaura 3D",
499 "ESS Technology",
500 "Harman International VMAx",
501 "Nvidea 3D",
502 "Philips Incredible Sound",
503 "Texas Instruments 3D",
504 "VLSI Technology 3D",
505 "TriTech 3D",
506 "Realtek 3D",
507 "Samsung 3D",
508 "Wolfson Microelectronics 3D",
509 "Delta Integration 3D",
510 "SigmaTel 3D",
511 "KS Waves 3D",
512 "Rockwell 3D",
513 "Unknown 3D",
514 "Unknown 3D",
515 "Unknown 3D"
516 };
517
518 const char * const ac97feature[] = {
519 "mic channel",
520 "reserved",
521 "tone",
522 "simulated stereo",
523 "headphone",
524 "bass boost",
525 "18 bit DAC",
526 "20 bit DAC",
527 "18 bit ADC",
528 "20 bit ADC"
529 };
530
531
532 int ac97_str_equal(const char *, const char *);
533 void ac97_setup_source_info(struct ac97_softc *);
534 void ac97_setup_defaults(struct ac97_softc *);
535 int ac97_read(struct ac97_softc *, u_int8_t, u_int16_t *);
536 int ac97_write(struct ac97_softc *, u_int8_t, u_int16_t);
537
538 #define AC97_DEBUG 10
539
540 #ifdef AUDIO_DEBUG
541 #define DPRINTF(x) if (ac97debug) printf x
542 #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
543 #ifdef AC97_DEBUG
544 int ac97debug = AC97_DEBUG;
545 #else
546 int ac97debug = 0;
547 #endif
548 #else
549 #define DPRINTF(x)
550 #define DPRINTFN(n,x)
551 #endif
552
553 int
ac97_read(as,reg,val)554 ac97_read(as, reg, val)
555 struct ac97_softc *as;
556 u_int8_t reg;
557 u_int16_t *val;
558 {
559 int error;
560
561 if (((as->host_flags & AC97_HOST_DONT_READ) &&
562 (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
563 reg != AC97_REG_RESET)) ||
564 (as->host_flags & AC97_HOST_DONT_READANY)) {
565 *val = as->shadow_reg[reg >> 1];
566 return (0);
567 }
568
569 if ((error = as->host_if->read(as->host_if->arg, reg, val)))
570 *val = as->shadow_reg[reg >> 1];
571 return (error);
572 }
573
574 int
ac97_write(as,reg,val)575 ac97_write(as, reg, val)
576 struct ac97_softc *as;
577 u_int8_t reg;
578 u_int16_t val;
579 {
580 as->shadow_reg[reg >> 1] = val;
581 return (as->host_if->write(as->host_if->arg, reg, val));
582 }
583
584 void
ac97_setup_defaults(as)585 ac97_setup_defaults(as)
586 struct ac97_softc *as;
587 {
588 int idx;
589
590 bzero(as->shadow_reg, sizeof(as->shadow_reg));
591
592 for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
593 const struct ac97_source_info *si = &source_info[idx];
594
595 ac97_write(as, si->reg, si->default_value);
596 }
597 }
598
599 void
ac97_restore_shadow(self)600 ac97_restore_shadow(self)
601 struct ac97_codec_if *self;
602 {
603 struct ac97_softc *as = (struct ac97_softc *)self;
604 int idx;
605
606 for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
607 const struct ac97_source_info *si = &source_info[idx];
608
609 ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
610 }
611 }
612
613 int
ac97_str_equal(a,b)614 ac97_str_equal(a, b)
615 const char *a, *b;
616 {
617 return ((a == b) || (a && b && (!strcmp(a, b))));
618 }
619
620 void
ac97_setup_source_info(as)621 ac97_setup_source_info(as)
622 struct ac97_softc *as;
623 {
624 struct ac97_source_info *si, *si2;
625 int idx, ouridx;
626
627 for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
628 si = &as->source_info[ouridx];
629
630 bcopy(&source_info[idx], si, sizeof(*si));
631
632 switch (si->type) {
633 case AUDIO_MIXER_CLASS:
634 si->mixer_class = ouridx;
635 ouridx++;
636 break;
637 case AUDIO_MIXER_VALUE:
638 /* Todo - Test to see if it works */
639 ouridx++;
640
641 /* Add an entry for mute, if necessary */
642 if (si->mute) {
643 si = &as->source_info[ouridx];
644 bcopy(&source_info[idx], si, sizeof(*si));
645 si->qualifier = AudioNmute;
646 si->type = AUDIO_MIXER_ENUM;
647 si->info = &ac97_on_off;
648 si->info_size = sizeof(ac97_on_off);
649 si->bits = 1;
650 si->ofs = 15;
651 si->mute = 0;
652 si->polarity = 0;
653 ouridx++;
654 }
655 break;
656 case AUDIO_MIXER_ENUM:
657 /* Todo - Test to see if it works */
658 ouridx++;
659 break;
660 default:
661 printf ("ac97: shouldn't get here\n");
662 break;
663 }
664 }
665
666 as->num_source_info = ouridx;
667
668 for (idx = 0; idx < as->num_source_info; idx++) {
669 int idx2, previdx;
670
671 si = &as->source_info[idx];
672
673 /* Find mixer class */
674 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
675 si2 = &as->source_info[idx2];
676
677 if (si2->type == AUDIO_MIXER_CLASS &&
678 ac97_str_equal(si->class, si2->class)) {
679 si->mixer_class = idx2;
680 }
681 }
682
683
684 /* Setup prev and next pointers */
685 if (si->prev != 0 || si->qualifier)
686 continue;
687
688 si->prev = AUDIO_MIXER_LAST;
689 previdx = idx;
690
691 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
692 if (idx2 == idx)
693 continue;
694
695 si2 = &as->source_info[idx2];
696
697 if (!si2->prev &&
698 ac97_str_equal(si->class, si2->class) &&
699 ac97_str_equal(si->device, si2->device)) {
700 as->source_info[previdx].next = idx2;
701 as->source_info[idx2].prev = previdx;
702
703 previdx = idx2;
704 }
705 }
706
707 as->source_info[previdx].next = AUDIO_MIXER_LAST;
708 }
709 }
710
711 int
ac97_attach(host_if)712 ac97_attach(host_if)
713 struct ac97_host_if *host_if;
714 {
715 struct ac97_softc *as;
716 u_int16_t id1, id2;
717 u_int32_t id;
718 mixer_ctrl_t ctl;
719 int error, i;
720 void (*initfunc)(struct ac97_softc *);
721
722 initfunc = NULL;
723
724 if (!(as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_NOWAIT)))
725 return (ENOMEM);
726
727 bzero(as, sizeof(*as));
728
729 as->codec_if.vtbl = &ac97civ;
730 as->host_if = host_if;
731
732 if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
733 free(as, M_DEVBUF);
734 return (error);
735 }
736
737 host_if->reset(host_if->arg);
738 DELAY(1000);
739
740 host_if->write(host_if->arg, AC97_REG_POWER, 0);
741 host_if->write(host_if->arg, AC97_REG_RESET, 0);
742 DELAY(10000);
743
744 if (host_if->flags)
745 as->host_flags = host_if->flags(host_if->arg);
746
747 ac97_setup_defaults(as);
748 ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
749 ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
750 ac97_read(as, AC97_REG_RESET, &as->caps);
751
752 id = (id1 << 16) | id2;
753 if (id) {
754 register const struct ac97_vendorid *vendor;
755 register const struct ac97_codecid *codec;
756
757 printf("ac97: codec id 0x%08x", id);
758 for (vendor = &ac97_vendors[sizeof(ac97_vendors) /
759 sizeof(ac97_vendors[0]) - 1];
760 vendor >= ac97_vendors; vendor--) {
761 if (vendor->id == (id & AC97_VENDOR_ID_MASK)) {
762 printf(" (%s", vendor->name);
763 for (codec = &vendor->codecs[vendor->num-1];
764 codec >= vendor->codecs; codec--) {
765 if (codec->id == (id & codec->mask))
766 break;
767 }
768 if (codec >= vendor->codecs && codec->mask) {
769 printf(" %s", codec->name);
770 initfunc = codec->init;
771 } else
772 printf(" <%02x>", id & 0xff);
773 if (codec >= vendor->codecs && codec->rev)
774 printf(" rev %d", id & codec->rev);
775 printf(")");
776 break;
777 }
778 }
779 printf("\n");
780 } else
781 printf("ac97: codec id not read\n");
782
783 if (as->caps) {
784 printf("ac97: codec features ");
785 for (i = 0; i < 10; i++) {
786 if (as->caps & (1 << i))
787 printf("%s, ", ac97feature[i]);
788 }
789 printf("%s\n",
790 ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
791 }
792
793 ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
794 if (as->ext_id)
795 DPRINTF(("ac97: ext id %b\n", as->ext_id,
796 AC97_EXT_AUDIO_BITS));
797 if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_VRM)) {
798 ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id1);
799 if (as->ext_id & AC97_EXT_AUDIO_VRA)
800 id1 |= AC97_EXT_AUDIO_VRA;
801 if (as->ext_id & AC97_EXT_AUDIO_VRM)
802 id1 |= AC97_EXT_AUDIO_VRM;
803 ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id1);
804 }
805
806 ac97_setup_source_info(as);
807
808 /* use initfunc for specific device */
809 if (initfunc != NULL)
810 initfunc(as);
811
812 /* Just enable the DAC and master volumes by default */
813 bzero(&ctl, sizeof(ctl));
814
815 ctl.type = AUDIO_MIXER_ENUM;
816 ctl.un.ord = 0; /* off */
817 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
818 AudioNmaster, AudioNmute);
819 ac97_mixer_set_port(&as->codec_if, &ctl);
820
821 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
822 AudioNdac, AudioNmute);
823 ac97_mixer_set_port(&as->codec_if, &ctl);
824
825 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
826 AudioNvolume, AudioNmute);
827 ac97_mixer_set_port(&as->codec_if, &ctl);
828
829 ctl.type = AUDIO_MIXER_ENUM;
830 ctl.un.ord = 0;
831 ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
832 AudioNsource, NULL);
833 ac97_mixer_set_port(&as->codec_if, &ctl);
834
835 return (0);
836 }
837
838 int
ac97_query_devinfo(codec_if,dip)839 ac97_query_devinfo(codec_if, dip)
840 struct ac97_codec_if *codec_if;
841 mixer_devinfo_t *dip;
842 {
843 struct ac97_softc *as = (struct ac97_softc *)codec_if;
844
845 if (dip->index < as->num_source_info) {
846 struct ac97_source_info *si = &as->source_info[dip->index];
847 const char *name;
848
849 dip->type = si->type;
850 dip->mixer_class = si->mixer_class;
851 dip->prev = si->prev;
852 dip->next = si->next;
853
854 if (si->qualifier)
855 name = si->qualifier;
856 else if (si->device)
857 name = si->device;
858 else if (si->class)
859 name = si->class;
860
861 if (name)
862 strlcpy(dip->label.name, name, sizeof dip->label.name);
863
864 bcopy(si->info, &dip->un, si->info_size);
865
866 /* Set the delta for volume sources */
867 if (dip->type == AUDIO_MIXER_VALUE)
868 dip->un.v.delta = 1 << (8 - si->bits);
869
870 return (0);
871 }
872
873 return (ENXIO);
874 }
875
876 int
ac97_mixer_set_port(codec_if,cp)877 ac97_mixer_set_port(codec_if, cp)
878 struct ac97_codec_if *codec_if;
879 mixer_ctrl_t *cp;
880 {
881 struct ac97_softc *as = (struct ac97_softc *)codec_if;
882 struct ac97_source_info *si = &as->source_info[cp->dev];
883 u_int16_t mask;
884 u_int16_t val, newval;
885 int error;
886
887 if (cp->dev < 0 || cp->dev >= as->num_source_info ||
888 cp->type != si->type)
889 return (EINVAL);
890
891 ac97_read(as, si->reg, &val);
892
893 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
894
895 mask = (1 << si->bits) - 1;
896
897 switch (cp->type) {
898 case AUDIO_MIXER_ENUM:
899 if (cp->un.ord > mask || cp->un.ord < 0)
900 return (EINVAL);
901
902 newval = (cp->un.ord << si->ofs);
903 if (si->reg == AC97_REG_RECORD_SELECT) {
904 newval |= (newval << (8 + si->ofs));
905 mask |= (mask << 8);
906 }
907
908 if (si->mute) {
909 newval |= newval << 8;
910 mask |= mask << 8;
911 }
912
913 break;
914 case AUDIO_MIXER_VALUE:
915 {
916 const struct audio_mixer_value *value = si->info;
917 u_int16_t l, r;
918
919 if (cp->un.value.num_channels <= 0 ||
920 cp->un.value.num_channels > value->num_channels)
921 return (EINVAL);
922
923 if (cp->un.value.num_channels == 1) {
924 l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
925 } else {
926 if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
927 l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
928 r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
929 } else {
930 r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
931 l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
932 }
933 }
934
935 if (!si->polarity) {
936 l = 255 - l;
937 r = 255 - r;
938 }
939
940 l >>= 8 - si->bits;
941 r >>= 8 - si->bits;
942
943 newval = ((l & mask) << si->ofs);
944 if (value->num_channels == 2) {
945 newval |= ((r & mask) << (si->ofs + 8));
946 mask |= (mask << 8);
947 }
948
949 break;
950 }
951 default:
952 return (EINVAL);
953 }
954
955 mask = mask << si->ofs;
956 error = ac97_write(as, si->reg, (val & ~mask) | newval);
957 if (error)
958 return (error);
959
960 return (0);
961 }
962
963 int
ac97_get_portnum_by_name(codec_if,class,device,qualifier)964 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
965 struct ac97_codec_if *codec_if;
966 char *class, *device, *qualifier;
967 {
968 struct ac97_softc *as = (struct ac97_softc *)codec_if;
969 int idx;
970
971 for (idx = 0; idx < as->num_source_info; idx++) {
972 struct ac97_source_info *si = &as->source_info[idx];
973 if (ac97_str_equal(class, si->class) &&
974 ac97_str_equal(device, si->device) &&
975 ac97_str_equal(qualifier, si->qualifier))
976 return (idx);
977 }
978
979 return (-1);
980 }
981
982 int
ac97_mixer_get_port(codec_if,cp)983 ac97_mixer_get_port(codec_if, cp)
984 struct ac97_codec_if *codec_if;
985 mixer_ctrl_t *cp;
986 {
987 struct ac97_softc *as = (struct ac97_softc *)codec_if;
988 struct ac97_source_info *si = &as->source_info[cp->dev];
989 u_int16_t mask;
990 u_int16_t val;
991
992 if (cp->dev < 0 || cp->dev >= as->num_source_info ||
993 cp->type != si->type)
994 return (EINVAL);
995
996 ac97_read(as, si->reg, &val);
997
998 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
999
1000 mask = (1 << si->bits) - 1;
1001
1002 switch (cp->type) {
1003 case AUDIO_MIXER_ENUM:
1004 cp->un.ord = (val >> si->ofs) & mask;
1005 DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs,
1006 mask, cp->un.ord));
1007 break;
1008 case AUDIO_MIXER_VALUE:
1009 {
1010 const struct audio_mixer_value *value = si->info;
1011 u_int16_t l, r;
1012
1013 if ((cp->un.value.num_channels <= 0) ||
1014 (cp->un.value.num_channels > value->num_channels))
1015 return (EINVAL);
1016
1017 if (value->num_channels == 1)
1018 l = r = (val >> si->ofs) & mask;
1019 else {
1020 if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
1021 l = (val >> si->ofs) & mask;
1022 r = (val >> (si->ofs + 8)) & mask;
1023 } else {
1024 r = (val >> si->ofs) & mask;
1025 l = (val >> (si->ofs + 8)) & mask;
1026 }
1027 }
1028
1029 l <<= 8 - si->bits;
1030 r <<= 8 - si->bits;
1031 if (!si->polarity) {
1032 l = 255 - l;
1033 r = 255 - r;
1034 }
1035
1036 /*
1037 * The EAP driver averages l and r for stereo
1038 * channels that are requested in MONO mode. Does this
1039 * make sense?
1040 */
1041 if (cp->un.value.num_channels == 1) {
1042 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
1043 } else if (cp->un.value.num_channels == 2) {
1044 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
1045 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
1046 }
1047
1048 break;
1049 }
1050 default:
1051 return (EINVAL);
1052 }
1053
1054 return (0);
1055 }
1056
1057 int
ac97_set_rate(codec_if,p,mode)1058 ac97_set_rate(codec_if, p, mode)
1059 struct ac97_codec_if *codec_if;
1060 struct audio_params *p;
1061 int mode;
1062 {
1063 struct ac97_softc *as = (struct ac97_softc *)codec_if;
1064 u_int16_t reg, val, regval, id = 0;
1065
1066 DPRINTFN(5, ("set_rate(%lu) ", p->sample_rate));
1067
1068 if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1069 p->sample_rate = AC97_SINGLERATE;
1070 return (0);
1071 }
1072
1073 if (p->sample_rate > 0xffff) {
1074 if (mode != AUMODE_PLAY)
1075 return (EINVAL);
1076 if (!(as->ext_id & AC97_EXT_AUDIO_DRA))
1077 return (EINVAL);
1078 if (ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id))
1079 return (EIO);
1080 id |= AC97_EXT_AUDIO_DRA;
1081 if (ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id))
1082 return (EIO);
1083 p->sample_rate /= 2;
1084 }
1085
1086 /* i guess it's better w/o clicks and squeecks when changing the rate */
1087 if (ac97_read(as, AC97_REG_POWER, &val) ||
1088 ac97_write(as, AC97_REG_POWER, val |
1089 (mode == AUMODE_PLAY? AC97_POWER_OUT : AC97_POWER_IN)))
1090 return (EIO);
1091
1092 reg = mode == AUMODE_PLAY ?
1093 AC97_REG_FRONT_DAC_RATE : AC97_REG_PCM_ADC_RATE;
1094
1095 if (ac97_write(as, reg, (u_int16_t) p->sample_rate) ||
1096 ac97_read(as, reg, ®val))
1097 return (EIO);
1098 p->sample_rate = regval;
1099 if (id & AC97_EXT_AUDIO_DRA)
1100 p->sample_rate *= 2;
1101
1102 DPRINTFN(5, (" %lu\n", regval));
1103
1104 if (ac97_write(as, AC97_REG_POWER, val))
1105 return (EIO);
1106
1107 return (0);
1108 }
1109
1110 /*
1111 * Codec-dependent initialization
1112 */
1113
1114 #define AC97_AD1886_JACK_SENSE 0x72
1115
1116 void
ac97_ad1886_init(struct ac97_softc * as)1117 ac97_ad1886_init(struct ac97_softc *as)
1118 {
1119 ac97_write(as, AC97_AD1886_JACK_SENSE, 0x0010);
1120 }
1121
1122 void
ac97_ad198x_init(struct ac97_softc * as)1123 ac97_ad198x_init(struct ac97_softc *as)
1124 {
1125 int i;
1126 u_int16_t misc;
1127
1128 ac97_read(as, AC97_AD_REG_MISC, &misc);
1129 ac97_write(as, AC97_AD_REG_MISC,
1130 misc|AC97_AD_MISC_HPSEL|AC97_AD_MISC_LOSEL);
1131
1132 for (i = 0; i < as->num_source_info; i++) {
1133 if (as->source_info[i].reg == AC97_REG_SURROUND_VOLUME)
1134 as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
1135 else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME) {
1136 as->source_info[i].reg = AC97_REG_SURROUND_VOLUME;
1137 if (as->source_info[i].type == AUDIO_MIXER_ENUM) {
1138 as->source_info[i].mute = 1;
1139 as->source_info[i].ofs = 7;
1140 }
1141 }
1142 }
1143 }
1144
1145 void
ac97_alc655_init(struct ac97_softc * as)1146 ac97_alc655_init(struct ac97_softc *as)
1147 {
1148 u_int16_t misc;
1149
1150 ac97_read(as, AC97_AV_REG_MISC, &misc);
1151 misc |= AC97_AV_MISC_SPDIFEN;
1152 misc &= ~AC97_AV_MISC_VREFDIS;
1153 ac97_write(as, AC97_AV_REG_MISC, misc);
1154
1155 ac97_write(as, AC97_AV_REG_MULTICH, AC97_AV_MULTICH_MAGIC);
1156 }
1157
1158 void
ac97_cx20468_init(struct ac97_softc * as)1159 ac97_cx20468_init(struct ac97_softc *as)
1160 {
1161 u_int16_t misc;
1162
1163 ac97_read(as, AC97_CX_REG_MISC, &misc);
1164 ac97_write(as, AC97_CX_REG_MISC,
1165 AC97_CX_SPDIFEN | AC97_CX_COPYRIGHT | AC97_CX_MASK);
1166 }
1167
1168 void
ac97_cx_init(struct ac97_softc * as)1169 ac97_cx_init(struct ac97_softc *as)
1170 {
1171 u_int16_t misc, new;
1172
1173 ac97_read(as, AC97_CX_REG_MISC, &misc);
1174 ac97_write(as, AC97_CX_REG_MISC,
1175 AC97_CX_SPDIFEN | AC97_CX_COPYRIGHT | AC97_CX_MASK);
1176
1177 ac97_read(as, AC97_REG_POWER, &misc);
1178 new = misc | AC97_POWER_EAMP;
1179 if (new != misc) {
1180 ac97_write(as, AC97_REG_POWER, new);
1181 }
1182 }
1183