Annotation of sys/dev/ic/ac97.c, Revision 1.1.1.1
1.1 nbrk 1: /* $OpenBSD: ac97.c,v 1.63 2007/07/27 01:48:04 ian 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: /* External Amp */
270: AudioCoutputs, AudioNextamp, NULL, AUDIO_MIXER_ENUM,
271: WRAP(ac97_on_off),
272: AC97_REG_POWER, 1, 15, 0, 0, 0x0000
273: }
274:
275: /* Missing features: Simulated Stereo, POP, Loopback mode */
276: };
277:
278: #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
279:
280: /*
281: * Check out http://www.intel.com/technology/computing/audio/index.htm
282: * for information on AC-97
283: */
284:
285: struct ac97_softc {
286: struct ac97_codec_if codec_if;
287: struct ac97_host_if *host_if;
288: struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
289: int num_source_info;
290: enum ac97_host_flags host_flags;
291: u_int16_t caps, ext_id;
292: u_int16_t shadow_reg[128];
293: };
294:
295: int ac97_mixer_get_port(struct ac97_codec_if *, mixer_ctrl_t *);
296: int ac97_mixer_set_port(struct ac97_codec_if *, mixer_ctrl_t *);
297: int ac97_query_devinfo(struct ac97_codec_if *, mixer_devinfo_t *);
298: int ac97_get_portnum_by_name(struct ac97_codec_if *, char *, char *,
299: char *);
300: void ac97_restore_shadow(struct ac97_codec_if *);
301:
302: void ac97_ad1886_init(struct ac97_softc *);
303: void ac97_ad198x_init(struct ac97_softc *);
304: void ac97_alc655_init(struct ac97_softc *);
305: void ac97_cx20468_init(struct ac97_softc *);
306:
307: struct ac97_codec_if_vtbl ac97civ = {
308: ac97_mixer_get_port,
309: ac97_mixer_set_port,
310: ac97_query_devinfo,
311: ac97_get_portnum_by_name,
312: ac97_restore_shadow
313: };
314:
315: const struct ac97_codecid {
316: u_int8_t id;
317: u_int8_t mask;
318: u_int8_t rev;
319: u_int8_t shift; /* no use yet */
320: char * const name;
321: void (*init)(struct ac97_softc *);
322: } ac97_ad[] = {
323: { 0x03, 0xff, 0, 0, "AD1819" },
324: { 0x40, 0xff, 0, 0, "AD1881" },
325: { 0x48, 0xff, 0, 0, "AD1881A" },
326: { 0x60, 0xff, 0, 0, "AD1885" },
327: { 0x61, 0xff, 0, 0, "AD1886", ac97_ad1886_init },
328: { 0x63, 0xff, 0, 0, "AD1886A" },
329: { 0x68, 0xff, 0, 0, "AD1888", ac97_ad198x_init },
330: { 0x70, 0xff, 0, 0, "AD1980" },
331: { 0x72, 0xff, 0, 0, "AD1981A" },
332: { 0x74, 0xff, 0, 0, "AD1981B" },
333: { 0x75, 0xff, 0, 0, "AD1985", ac97_ad198x_init },
334: }, ac97_ak[] = {
335: { 0x00, 0xfe, 1, 0, "AK4540" },
336: { 0x01, 0xfe, 1, 0, "AK4540" },
337: { 0x02, 0xff, 0, 0, "AK4543" },
338: { 0x05, 0xff, 0, 0, "AK4544" },
339: { 0x06, 0xff, 0, 0, "AK4544A" },
340: { 0x07, 0xff, 0, 0, "AK4545" },
341: }, ac97_av[] = {
342: { 0x10, 0xff, 0, 0, "ALC200" },
343: { 0x20, 0xff, 0, 0, "ALC650" },
344: { 0x21, 0xff, 0, 0, "ALC650D" },
345: { 0x22, 0xff, 0, 0, "ALC650E" },
346: { 0x23, 0xff, 0, 0, "ALC650F" },
347: { 0x30, 0xff, 0, 0, "ALC101" },
348: { 0x40, 0xff, 0, 0, "ALC202" },
349: { 0x50, 0xff, 0, 0, "ALC250" },
350: { 0x52, 0xff, 0, 0, "ALC250A?" },
351: { 0x60, 0xf0, 0xf, 0, "ALC655", ac97_alc655_init },
352: { 0x70, 0xf0, 0xf, 0, "ALC203" },
353: { 0x80, 0xf0, 0xf, 0, "ALC658", ac97_alc655_init },
354: { 0x90, 0xf0, 0xf, 0, "ALC850" },
355: }, ac97_rl[] = {
356: { 0x00, 0xf0, 0xf, 0, "RL5306" },
357: { 0x10, 0xf0, 0xf, 0, "RL5382" },
358: { 0x20, 0xf0, 0xf, 0, "RL5383" },
359: }, ac97_cm[] = {
360: { 0x41, 0xff, 0, 0, "CMI9738" },
361: { 0x61, 0xff, 0, 0, "CMI9739" },
362: { 0x78, 0xff, 0, 0, "CMI9761A" },
363: { 0x82, 0xff, 0, 0, "CMI9761B" },
364: { 0x83, 0xff, 0, 0, "CMI9761A+" },
365: }, ac97_cr[] = {
366: { 0x84, 0xff, 0, 0, "EV1938" },
367: }, ac97_cs[] = {
368: { 0x00, 0xf8, 7, 0, "CS4297" },
369: { 0x10, 0xf8, 7, 0, "CS4297A" },
370: { 0x20, 0xf8, 7, 0, "CS4298" },
371: { 0x28, 0xf8, 7, 0, "CS4294" },
372: { 0x30, 0xf8, 7, 0, "CS4299" },
373: { 0x48, 0xf8, 7, 0, "CS4201" },
374: { 0x58, 0xf8, 7, 0, "CS4205" },
375: { 0x60, 0xf8, 7, 0, "CS4291" },
376: { 0x70, 0xf8, 7, 0, "CS4202" },
377: }, ac97_cx[] = {
378: { 0x21, 0xff, 0, 0, "HSD11246" },
379: { 0x28, 0xf8, 7, 0, "CX20468", ac97_cx20468_init },
380: { 0x30, 0xff, 0, 0, "CXT48", },
381: { 0x42, 0xff, 0, 0, "CXT66", },
382: }, ac97_dt[] = {
383: { 0x00, 0xff, 0, 0, "DT0398" },
384: }, ac97_em[] = {
385: { 0x23, 0xff, 0, 0, "EM28023" },
386: { 0x28, 0xff, 0, 0, "EM28028" },
387: }, ac97_es[] = {
388: { 0x08, 0xff, 0, 0, "ES1921" },
389: }, ac97_is[] = {
390: { 0x00, 0xff, 0, 0, "HMP9701" },
391: }, ac97_ic[] = {
392: { 0x01, 0xff, 0, 0, "ICE1230" },
393: { 0x11, 0xff, 0, 0, "ICE1232" },
394: { 0x14, 0xff, 0, 0, "ICE1232A" },
395: { 0x51, 0xff, 0, 0, "VIA VT1616" },
396: { 0x52, 0xff, 0, 0, "VIA VT1616i" },
397: }, ac97_it[] = {
398: { 0x20, 0xff, 0, 0, "ITE2226E" },
399: { 0x60, 0xff, 0, 0, "ITE2646E" },
400: }, ac97_ns[] = {
401: { 0x00, 0xff, 0, 0, "LM454[03568]" },
402: { 0x31, 0xff, 0, 0, "LM4549" },
403: { 0x40, 0xff, 0, 0, "LM4540" },
404: { 0x43, 0xff, 0, 0, "LM4543" },
405: { 0x46, 0xff, 0, 0, "LM4546A" },
406: { 0x48, 0xff, 0, 0, "LM4548A" },
407: { 0x49, 0xff, 0, 0, "LM4549A" },
408: { 0x50, 0xff, 0, 0, "LM4550" },
409: }, ac97_ps[] = {
410: { 0x01, 0xff, 0, 0, "UCB1510" },
411: { 0x04, 0xff, 0, 0, "UCB1400" },
412: }, ac97_sl[] = {
413: { 0x20, 0xe0, 0, 0, "Si3036/38" },
414: }, ac97_st[] = {
415: { 0x00, 0xff, 0, 0, "STAC9700" },
416: { 0x04, 0xff, 0, 0, "STAC970[135]" },
417: { 0x05, 0xff, 0, 0, "STAC9704" },
418: { 0x08, 0xff, 0, 0, "STAC9708/11" },
419: { 0x09, 0xff, 0, 0, "STAC9721/23" },
420: { 0x44, 0xff, 0, 0, "STAC9744/45" },
421: { 0x50, 0xff, 0, 0, "STAC9750/51" },
422: { 0x52, 0xff, 0, 0, "STAC9752/53" },
423: { 0x56, 0xff, 0, 0, "STAC9756/57" },
424: { 0x58, 0xff, 0, 0, "STAC9758/59" },
425: { 0x60, 0xff, 0, 0, "STAC9760/61" },
426: { 0x62, 0xff, 0, 0, "STAC9762/63" },
427: { 0x66, 0xff, 0, 0, "STAC9766/67" },
428: { 0x84, 0xff, 0, 0, "STAC9784/85" },
429: }, ac97_vi[] = {
430: { 0x61, 0xff, 0, 0, "VT1612A" },
431: }, ac97_tt[] = {
432: { 0x02, 0xff, 0, 0, "TR28022" },
433: { 0x03, 0xff, 0, 0, "TR28023" },
434: { 0x06, 0xff, 0, 0, "TR28026" },
435: { 0x08, 0xff, 0, 0, "TR28028" },
436: { 0x23, 0xff, 0, 0, "TR28602" },
437: }, ac97_ti[] = {
438: { 0x20, 0xff, 0, 0, "TLC320AD9xC" },
439: }, ac97_wb[] = {
440: { 0x01, 0xff, 0, 0, "W83971D" },
441: }, ac97_wo[] = {
442: { 0x00, 0xff, 0, 0, "WM9701A" },
443: { 0x03, 0xff, 0, 0, "WM9704M/Q-0" }, /* & WM9703 */
444: { 0x04, 0xff, 0, 0, "WM9704M/Q-1" },
445: { 0x05, 0xff, 0, 0, "WM9705/10" },
446: { 0x09, 0xff, 0, 0, "WM9709" },
447: { 0x12, 0xff, 0, 0, "WM9711/12" },
448: }, ac97_ym[] = {
449: { 0x00, 0xff, 0, 0, "YMF743-S" },
450: { 0x02, 0xff, 0, 0, "YMF752-S" },
451: { 0x03, 0xff, 0, 0, "YMF753-S" },
452: };
453:
454: #define cl(n) n, sizeof(n)/sizeof(n[0])
455: const struct ac97_vendorid {
456: u_int32_t id;
457: char * const name;
458: const struct ac97_codecid * const codecs;
459: u_int8_t num;
460: } ac97_vendors[] = {
461: { 0x01408300, "Creative", cl(ac97_cr) },
462: { 0x41445300, "Analog Devices", cl(ac97_ad) },
463: { 0x414b4D00, "Asahi Kasei", cl(ac97_ak) },
464: { 0x414c4300, "Realtek", cl(ac97_rl) },
465: { 0x414c4700, "Avance Logic", cl(ac97_av) },
466: { 0x434d4900, "C-Media Electronics", cl(ac97_cm) },
467: { 0x43525900, "Cirrus Logic", cl(ac97_cs) },
468: { 0x43585400, "Conexant", cl(ac97_cx) },
469: { 0x44543000, "Diamond Technology", cl(ac97_dt) },
470: { 0x454d4300, "eMicro", cl(ac97_em) },
471: { 0x45838300, "ESS Technology", cl(ac97_es) },
472: { 0x48525300, "Intersil", cl(ac97_is) },
473: { 0x49434500, "ICEnsemble", cl(ac97_ic) },
474: { 0x49544500, "ITE, Inc.", cl(ac97_it) },
475: { 0x4e534300, "National Semiconductor", cl(ac97_ns) },
476: { 0x50534300, "Philips Semiconductor", cl(ac97_ps) },
477: { 0x53494c00, "Silicon Laboratory", cl(ac97_sl) },
478: { 0x54524100, "TriTech Microelectronics", cl(ac97_tt) },
479: { 0x54584e00, "Texas Instruments", cl(ac97_ti) },
480: { 0x56494100, "VIA Technologies", cl(ac97_vi) },
481: { 0x57454300, "Winbond", cl(ac97_wb) },
482: { 0x574d4c00, "Wolfson", cl(ac97_wo) },
483: { 0x594d4800, "Yamaha", cl(ac97_ym) },
484: { 0x83847600, "SigmaTel", cl(ac97_st) },
485: };
486: #undef cl
487:
488: const char * const ac97enhancement[] = {
489: "No 3D Stereo",
490: "Analog Devices Phat Stereo",
491: "Creative",
492: "National Semi 3D",
493: "Yamaha Ymersion",
494: "BBE 3D",
495: "Crystal Semi 3D",
496: "Qsound QXpander",
497: "Spatializer 3D",
498: "SRS 3D",
499: "Platform Tech 3D",
500: "AKM 3D",
501: "Aureal",
502: "AZTECH 3D",
503: "Binaura 3D",
504: "ESS Technology",
505: "Harman International VMAx",
506: "Nvidea 3D",
507: "Philips Incredible Sound",
508: "Texas Instruments 3D",
509: "VLSI Technology 3D",
510: "TriTech 3D",
511: "Realtek 3D",
512: "Samsung 3D",
513: "Wolfson Microelectronics 3D",
514: "Delta Integration 3D",
515: "SigmaTel 3D",
516: "KS Waves 3D",
517: "Rockwell 3D",
518: "Unknown 3D",
519: "Unknown 3D",
520: "Unknown 3D"
521: };
522:
523: const char * const ac97feature[] = {
524: "mic channel",
525: "reserved",
526: "tone",
527: "simulated stereo",
528: "headphone",
529: "bass boost",
530: "18 bit DAC",
531: "20 bit DAC",
532: "18 bit ADC",
533: "20 bit ADC"
534: };
535:
536:
537: int ac97_str_equal(const char *, const char *);
538: void ac97_setup_source_info(struct ac97_softc *);
539: void ac97_setup_defaults(struct ac97_softc *);
540: int ac97_read(struct ac97_softc *, u_int8_t, u_int16_t *);
541: int ac97_write(struct ac97_softc *, u_int8_t, u_int16_t);
542:
543:
544: #ifdef AUDIO_DEBUG
545: #define DPRINTF(x) if (ac97debug) printf x
546: #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
547: #ifdef AC97_DEBUG
548: int ac97debug = 1;
549: #else
550: int ac97debug = 0;
551: #endif
552: #else
553: #define DPRINTF(x)
554: #define DPRINTFN(n,x)
555: #endif
556:
557: int
558: ac97_read(struct ac97_softc *as, u_int8_t reg, u_int16_t *val)
559: {
560: int error;
561:
562: if (((as->host_flags & AC97_HOST_DONT_READ) &&
563: (reg != AC97_REG_VENDOR_ID1 && reg != AC97_REG_VENDOR_ID2 &&
564: reg != AC97_REG_RESET)) ||
565: (as->host_flags & AC97_HOST_DONT_READANY)) {
566: *val = as->shadow_reg[reg >> 1];
567: return (0);
568: }
569:
570: if ((error = as->host_if->read(as->host_if->arg, reg, val)))
571: *val = as->shadow_reg[reg >> 1];
572: return (error);
573: }
574:
575: int
576: ac97_write(struct ac97_softc *as, u_int8_t reg, u_int16_t val)
577: {
578: as->shadow_reg[reg >> 1] = val;
579: return (as->host_if->write(as->host_if->arg, reg, val));
580: }
581:
582: void
583: ac97_setup_defaults(struct ac97_softc *as)
584: {
585: int idx;
586:
587: bzero(as->shadow_reg, sizeof(as->shadow_reg));
588:
589: for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
590: const struct ac97_source_info *si = &source_info[idx];
591:
592: ac97_write(as, si->reg, si->default_value);
593: }
594: }
595:
596: void
597: ac97_restore_shadow(struct ac97_codec_if *self)
598: {
599: struct ac97_softc *as = (struct ac97_softc *)self;
600: int idx;
601:
602: for (idx = 0; idx < SOURCE_INFO_SIZE; idx++) {
603: const struct ac97_source_info *si = &source_info[idx];
604:
605: ac97_write(as, si->reg, as->shadow_reg[si->reg >> 1]);
606: }
607: }
608:
609: int
610: ac97_str_equal(const char *a, const char *b)
611: {
612: return ((a == b) || (a && b && (!strcmp(a, b))));
613: }
614:
615: void
616: ac97_setup_source_info(struct ac97_softc *as)
617: {
618: struct ac97_source_info *si, *si2;
619: int idx, ouridx;
620:
621: for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
622: si = &as->source_info[ouridx];
623:
624: bcopy(&source_info[idx], si, sizeof(*si));
625:
626: switch (si->type) {
627: case AUDIO_MIXER_CLASS:
628: si->mixer_class = ouridx;
629: ouridx++;
630: break;
631: case AUDIO_MIXER_VALUE:
632: /* Todo - Test to see if it works */
633: ouridx++;
634:
635: /* Add an entry for mute, if necessary */
636: if (si->mute) {
637: si = &as->source_info[ouridx];
638: bcopy(&source_info[idx], si, sizeof(*si));
639: si->qualifier = AudioNmute;
640: si->type = AUDIO_MIXER_ENUM;
641: si->info = &ac97_on_off;
642: si->info_size = sizeof(ac97_on_off);
643: si->bits = 1;
644: si->ofs = 15;
645: si->mute = 0;
646: si->polarity = 0;
647: ouridx++;
648: }
649: break;
650: case AUDIO_MIXER_ENUM:
651: /* Todo - Test to see if it works */
652: ouridx++;
653: break;
654: default:
655: printf ("ac97: shouldn't get here\n");
656: break;
657: }
658: }
659:
660: as->num_source_info = ouridx;
661:
662: for (idx = 0; idx < as->num_source_info; idx++) {
663: int idx2, previdx;
664:
665: si = &as->source_info[idx];
666:
667: /* Find mixer class */
668: for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
669: si2 = &as->source_info[idx2];
670:
671: if (si2->type == AUDIO_MIXER_CLASS &&
672: ac97_str_equal(si->class, si2->class)) {
673: si->mixer_class = idx2;
674: }
675: }
676:
677:
678: /* Setup prev and next pointers */
679: if (si->prev != 0 || si->qualifier)
680: continue;
681:
682: si->prev = AUDIO_MIXER_LAST;
683: previdx = idx;
684:
685: for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
686: if (idx2 == idx)
687: continue;
688:
689: si2 = &as->source_info[idx2];
690:
691: if (!si2->prev &&
692: ac97_str_equal(si->class, si2->class) &&
693: ac97_str_equal(si->device, si2->device)) {
694: as->source_info[previdx].next = idx2;
695: as->source_info[idx2].prev = previdx;
696:
697: previdx = idx2;
698: }
699: }
700:
701: as->source_info[previdx].next = AUDIO_MIXER_LAST;
702: }
703: }
704:
705: int
706: ac97_attach(struct ac97_host_if *host_if)
707: {
708: struct ac97_softc *as;
709: u_int16_t id1, id2;
710: u_int32_t id;
711: mixer_ctrl_t ctl;
712: int error, i;
713: void (*initfunc)(struct ac97_softc *);
714:
715: initfunc = NULL;
716:
717: if (!(as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_NOWAIT)))
718: return (ENOMEM);
719:
720: bzero(as, sizeof(*as));
721:
722: as->codec_if.vtbl = &ac97civ;
723: as->host_if = host_if;
724:
725: if ((error = host_if->attach(host_if->arg, &as->codec_if))) {
726: free(as, M_DEVBUF);
727: return (error);
728: }
729:
730: host_if->reset(host_if->arg);
731: DELAY(1000);
732:
733: host_if->write(host_if->arg, AC97_REG_POWER, 0);
734: host_if->write(host_if->arg, AC97_REG_RESET, 0);
735: DELAY(10000);
736:
737: if (host_if->flags)
738: as->host_flags = host_if->flags(host_if->arg);
739:
740: ac97_setup_defaults(as);
741: ac97_read(as, AC97_REG_VENDOR_ID1, &id1);
742: ac97_read(as, AC97_REG_VENDOR_ID2, &id2);
743: ac97_read(as, AC97_REG_RESET, &as->caps);
744:
745: id = (id1 << 16) | id2;
746: if (id) {
747: register const struct ac97_vendorid *vendor;
748: register const struct ac97_codecid *codec;
749:
750: printf("ac97: codec id 0x%08x", id);
751: for (vendor = &ac97_vendors[sizeof(ac97_vendors) /
752: sizeof(ac97_vendors[0]) - 1];
753: vendor >= ac97_vendors; vendor--) {
754: if (vendor->id == (id & AC97_VENDOR_ID_MASK)) {
755: printf(" (%s", vendor->name);
756: for (codec = &vendor->codecs[vendor->num-1];
757: codec >= vendor->codecs; codec--) {
758: if (codec->id == (id & codec->mask))
759: break;
760: }
761: if (codec >= vendor->codecs && codec->mask) {
762: printf(" %s", codec->name);
763: initfunc = codec->init;
764: } else
765: printf(" <%02x>", id & 0xff);
766: if (codec >= vendor->codecs && codec->rev)
767: printf(" rev %d", id & codec->rev);
768: printf(")");
769: break;
770: }
771: }
772: printf("\n");
773: } else
774: printf("ac97: codec id not read\n");
775:
776: if (as->caps) {
777: printf("ac97: codec features ");
778: for (i = 0; i < 10; i++) {
779: if (as->caps & (1 << i))
780: printf("%s, ", ac97feature[i]);
781: }
782: printf("%s\n",
783: ac97enhancement[AC97_CAPS_ENHANCEMENT(as->caps)]);
784: }
785:
786: ac97_read(as, AC97_REG_EXT_AUDIO_ID, &as->ext_id);
787: if (as->ext_id)
788: DPRINTF(("ac97: ext id %b\n", as->ext_id,
789: AC97_EXT_AUDIO_BITS));
790: if (as->ext_id & (AC97_EXT_AUDIO_VRA | AC97_EXT_AUDIO_VRM)) {
791: ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id1);
792: if (as->ext_id & AC97_EXT_AUDIO_VRA)
793: id1 |= AC97_EXT_AUDIO_VRA;
794: if (as->ext_id & AC97_EXT_AUDIO_VRM)
795: id1 |= AC97_EXT_AUDIO_VRM;
796: ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id1);
797: }
798:
799: ac97_setup_source_info(as);
800:
801: /* use initfunc for specific device */
802: if (initfunc != NULL)
803: initfunc(as);
804:
805: /* Just enable the DAC and master volumes by default */
806: bzero(&ctl, sizeof(ctl));
807:
808: ctl.type = AUDIO_MIXER_ENUM;
809: ctl.un.ord = 0; /* off */
810: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCoutputs,
811: AudioNmaster, AudioNmute);
812: ac97_mixer_set_port(&as->codec_if, &ctl);
813:
814: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCinputs,
815: AudioNdac, AudioNmute);
816: ac97_mixer_set_port(&as->codec_if, &ctl);
817:
818: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
819: AudioNvolume, AudioNmute);
820: ac97_mixer_set_port(&as->codec_if, &ctl);
821:
822: ctl.type = AUDIO_MIXER_ENUM;
823: ctl.un.ord = 0;
824: ctl.dev = ac97_get_portnum_by_name(&as->codec_if, AudioCrecord,
825: AudioNsource, NULL);
826: ac97_mixer_set_port(&as->codec_if, &ctl);
827:
828: return (0);
829: }
830:
831: int
832: ac97_query_devinfo(struct ac97_codec_if *codec_if, mixer_devinfo_t *dip)
833: {
834: struct ac97_softc *as = (struct ac97_softc *)codec_if;
835:
836: if (dip->index < as->num_source_info) {
837: struct ac97_source_info *si = &as->source_info[dip->index];
838: const char *name;
839:
840: dip->type = si->type;
841: dip->mixer_class = si->mixer_class;
842: dip->prev = si->prev;
843: dip->next = si->next;
844:
845: if (si->qualifier)
846: name = si->qualifier;
847: else if (si->device)
848: name = si->device;
849: else if (si->class)
850: name = si->class;
851:
852: if (name)
853: strlcpy(dip->label.name, name, sizeof dip->label.name);
854:
855: bcopy(si->info, &dip->un, si->info_size);
856:
857: /* Set the delta for volume sources */
858: if (dip->type == AUDIO_MIXER_VALUE)
859: dip->un.v.delta = 1 << (8 - si->bits);
860:
861: return (0);
862: }
863:
864: return (ENXIO);
865: }
866:
867: int
868: ac97_mixer_set_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
869: {
870: struct ac97_softc *as = (struct ac97_softc *)codec_if;
871: struct ac97_source_info *si = &as->source_info[cp->dev];
872: u_int16_t mask;
873: u_int16_t val, newval;
874: int error;
875:
876: if (cp->dev < 0 || cp->dev >= as->num_source_info ||
877: cp->type != si->type)
878: return (EINVAL);
879:
880: ac97_read(as, si->reg, &val);
881:
882: DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
883:
884: mask = (1 << si->bits) - 1;
885:
886: switch (cp->type) {
887: case AUDIO_MIXER_ENUM:
888: if (cp->un.ord > mask || cp->un.ord < 0)
889: return (EINVAL);
890:
891: newval = (cp->un.ord << si->ofs);
892: if (si->reg == AC97_REG_RECORD_SELECT) {
893: newval |= (newval << (8 + si->ofs));
894: mask |= (mask << 8);
895: }
896:
897: if (si->mute) {
898: newval |= newval << 8;
899: mask |= mask << 8;
900: }
901:
902: break;
903: case AUDIO_MIXER_VALUE:
904: {
905: const struct audio_mixer_value *value = si->info;
906: u_int16_t l, r;
907:
908: if (cp->un.value.num_channels <= 0 ||
909: cp->un.value.num_channels > value->num_channels)
910: return (EINVAL);
911:
912: if (cp->un.value.num_channels == 1) {
913: l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
914: } else {
915: if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
916: l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
917: r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
918: } else {
919: r = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
920: l = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
921: }
922: }
923:
924: if (!si->polarity) {
925: l = 255 - l;
926: r = 255 - r;
927: }
928:
929: l >>= 8 - si->bits;
930: r >>= 8 - si->bits;
931:
932: newval = ((l & mask) << si->ofs);
933: if (value->num_channels == 2) {
934: newval |= ((r & mask) << (si->ofs + 8));
935: mask |= (mask << 8);
936: }
937:
938: break;
939: }
940: default:
941: return (EINVAL);
942: }
943:
944: mask = mask << si->ofs;
945: error = ac97_write(as, si->reg, (val & ~mask) | newval);
946: if (error)
947: return (error);
948:
949: return (0);
950: }
951:
952: int
953: ac97_get_portnum_by_name(struct ac97_codec_if *codec_if, char *class,
954: char *device, char *qualifier)
955: {
956: struct ac97_softc *as = (struct ac97_softc *)codec_if;
957: int idx;
958:
959: for (idx = 0; idx < as->num_source_info; idx++) {
960: struct ac97_source_info *si = &as->source_info[idx];
961: if (ac97_str_equal(class, si->class) &&
962: ac97_str_equal(device, si->device) &&
963: ac97_str_equal(qualifier, si->qualifier))
964: return (idx);
965: }
966:
967: return (-1);
968: }
969:
970: int
971: ac97_mixer_get_port(struct ac97_codec_if *codec_if, mixer_ctrl_t *cp)
972: {
973: struct ac97_softc *as = (struct ac97_softc *)codec_if;
974: struct ac97_source_info *si = &as->source_info[cp->dev];
975: u_int16_t mask;
976: u_int16_t val;
977:
978: if (cp->dev < 0 || cp->dev >= as->num_source_info ||
979: cp->type != si->type)
980: return (EINVAL);
981:
982: ac97_read(as, si->reg, &val);
983:
984: DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
985:
986: mask = (1 << si->bits) - 1;
987:
988: switch (cp->type) {
989: case AUDIO_MIXER_ENUM:
990: cp->un.ord = (val >> si->ofs) & mask;
991: DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs,
992: mask, cp->un.ord));
993: break;
994: case AUDIO_MIXER_VALUE:
995: {
996: const struct audio_mixer_value *value = si->info;
997: u_int16_t l, r;
998:
999: if ((cp->un.value.num_channels <= 0) ||
1000: (cp->un.value.num_channels > value->num_channels))
1001: return (EINVAL);
1002:
1003: if (value->num_channels == 1)
1004: l = r = (val >> si->ofs) & mask;
1005: else {
1006: if (!(as->host_flags & AC97_HOST_SWAPPED_CHANNELS)) {
1007: l = (val >> si->ofs) & mask;
1008: r = (val >> (si->ofs + 8)) & mask;
1009: } else {
1010: r = (val >> si->ofs) & mask;
1011: l = (val >> (si->ofs + 8)) & mask;
1012: }
1013: }
1014:
1015: l <<= 8 - si->bits;
1016: r <<= 8 - si->bits;
1017: if (!si->polarity) {
1018: l = 255 - l;
1019: r = 255 - r;
1020: }
1021:
1022: /*
1023: * The EAP driver averages l and r for stereo
1024: * channels that are requested in MONO mode. Does this
1025: * make sense?
1026: */
1027: if (cp->un.value.num_channels == 1) {
1028: cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
1029: } else if (cp->un.value.num_channels == 2) {
1030: cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
1031: cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
1032: }
1033:
1034: break;
1035: }
1036: default:
1037: return (EINVAL);
1038: }
1039:
1040: return (0);
1041: }
1042:
1043: int
1044: ac97_set_rate(struct ac97_codec_if *codec_if, struct audio_params *p,
1045: int mode)
1046: {
1047: struct ac97_softc *as = (struct ac97_softc *)codec_if;
1048: u_int16_t reg, val, regval, id = 0;
1049:
1050: DPRINTFN(5, ("set_rate(%lu) ", p->sample_rate));
1051:
1052: if (!(as->ext_id & AC97_EXT_AUDIO_VRA)) {
1053: p->sample_rate = AC97_SINGLERATE;
1054: return (0);
1055: }
1056:
1057: if (p->sample_rate > 0xffff) {
1058: if (mode != AUMODE_PLAY)
1059: return (EINVAL);
1060: if (!(as->ext_id & AC97_EXT_AUDIO_DRA))
1061: return (EINVAL);
1062: if (ac97_read(as, AC97_REG_EXT_AUDIO_CTRL, &id))
1063: return (EIO);
1064: id |= AC97_EXT_AUDIO_DRA;
1065: if (ac97_write(as, AC97_REG_EXT_AUDIO_CTRL, id))
1066: return (EIO);
1067: p->sample_rate /= 2;
1068: }
1069:
1070: /* i guess it's better w/o clicks and squeecks when changing the rate */
1071: if (ac97_read(as, AC97_REG_POWER, &val) ||
1072: ac97_write(as, AC97_REG_POWER, val |
1073: (mode == AUMODE_PLAY? AC97_POWER_OUT : AC97_POWER_IN)))
1074: return (EIO);
1075:
1076: reg = mode == AUMODE_PLAY ?
1077: AC97_REG_FRONT_DAC_RATE : AC97_REG_PCM_ADC_RATE;
1078:
1079: if (ac97_write(as, reg, (u_int16_t) p->sample_rate) ||
1080: ac97_read(as, reg, ®val))
1081: return (EIO);
1082: p->sample_rate = regval;
1083: if (id & AC97_EXT_AUDIO_DRA)
1084: p->sample_rate *= 2;
1085:
1086: DPRINTFN(5, (" %lu\n", regval));
1087:
1088: if (ac97_write(as, AC97_REG_POWER, val))
1089: return (EIO);
1090:
1091: return (0);
1092: }
1093:
1094: /*
1095: * Codec-dependent initialization
1096: */
1097:
1098: #define AC97_AD1886_JACK_SENSE 0x72
1099:
1100: void
1101: ac97_ad1886_init(struct ac97_softc *as)
1102: {
1103: ac97_write(as, AC97_AD1886_JACK_SENSE, 0x0010);
1104: }
1105:
1106: void
1107: ac97_ad198x_init(struct ac97_softc *as)
1108: {
1109: int i;
1110: u_int16_t misc;
1111:
1112: ac97_read(as, AC97_AD_REG_MISC, &misc);
1113: ac97_write(as, AC97_AD_REG_MISC,
1114: misc|AC97_AD_MISC_HPSEL|AC97_AD_MISC_LOSEL);
1115:
1116: for (i = 0; i < as->num_source_info; i++) {
1117: if (as->source_info[i].reg == AC97_REG_SURROUND_VOLUME)
1118: as->source_info[i].reg = AC97_REG_MASTER_VOLUME;
1119: else if (as->source_info[i].reg == AC97_REG_MASTER_VOLUME) {
1120: as->source_info[i].reg = AC97_REG_SURROUND_VOLUME;
1121: if (as->source_info[i].type == AUDIO_MIXER_ENUM) {
1122: as->source_info[i].mute = 1;
1123: as->source_info[i].ofs = 7;
1124: }
1125: }
1126: }
1127: }
1128:
1129: void
1130: ac97_alc655_init(struct ac97_softc *as)
1131: {
1132: u_int16_t misc;
1133:
1134: ac97_read(as, AC97_AV_REG_MISC, &misc);
1135: if (as->host_flags & AC97_HOST_DONT_ENABLE_SPDIF) {
1136: misc &= ~AC97_AV_MISC_SPDIFEN;
1137: } else {
1138: misc |= AC97_AV_MISC_SPDIFEN;
1139: }
1140: misc &= ~AC97_AV_MISC_VREFDIS;
1141: ac97_write(as, AC97_AV_REG_MISC, misc);
1142:
1143: ac97_write(as, AC97_AV_REG_MULTICH, AC97_AV_MULTICH_MAGIC);
1144: }
1145:
1146: void
1147: ac97_cx20468_init(struct ac97_softc *as)
1148: {
1149: u_int16_t misc;
1150:
1151: ac97_read(as, AC97_CX_REG_MISC, &misc);
1152: ac97_write(as, AC97_CX_REG_MISC, misc &
1153: ~(AC97_CX_SPDIFEN | AC97_CX_COPYRIGHT | AC97_CX_MASK));
1154: }
CVSweb