ac97.c revision 1.8 1 1.8 augustss /* $NetBSD: ac97.c,v 1.8 2000/04/28 12:39:59 augustss Exp $ */
2 1.1 augustss /* $OpenBSD: ac97.c,v 1.2 1999/09/21 16:06:27 csapuntz Exp $ */
3 1.1 augustss
4 1.1 augustss /*
5 1.1 augustss * Copyright (c) 1999 Constantine Sapuntzakis
6 1.1 augustss *
7 1.1 augustss * Author: Constantine Sapuntzakis <csapuntz (at) stanford.edu>
8 1.1 augustss *
9 1.1 augustss * Redistribution and use in source and binary forms, with or without
10 1.1 augustss * modification, are permitted provided that the following conditions
11 1.1 augustss * are met:
12 1.1 augustss * 1. Redistributions of source code must retain the above copyright
13 1.1 augustss * notice, this list of conditions and the following disclaimer.
14 1.1 augustss * 2. Redistributions in binary form must reproduce the above copyright
15 1.1 augustss * notice, this list of conditions and the following disclaimer in the
16 1.1 augustss * documentation and/or other materials provided with the distribution.
17 1.1 augustss *
18 1.1 augustss * THIS SOFTWARE IS PROVIDED BY CONSTANTINE SAPUNTZAKIS AND CONTRIBUTORS
19 1.1 augustss * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 1.1 augustss * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 1.1 augustss * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
22 1.1 augustss * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 1.1 augustss * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 1.1 augustss * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 1.1 augustss * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 1.1 augustss * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 1.1 augustss * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 1.1 augustss * POSSIBILITY OF SUCH DAMAGE.
29 1.1 augustss */
30 1.1 augustss
31 1.1 augustss /* Partially inspired by FreeBSD's sys/dev/pcm/ac97.c. It came with
32 1.1 augustss the following copyright */
33 1.1 augustss
34 1.1 augustss /*
35 1.1 augustss * Copyright (c) 1999 Cameron Grant <gandalf (at) vilnya.demon.co.uk>
36 1.1 augustss * All rights reserved.
37 1.1 augustss *
38 1.1 augustss * Redistribution and use in source and binary forms, with or without
39 1.1 augustss * modification, are permitted provided that the following conditions
40 1.1 augustss * are met:
41 1.1 augustss * 1. Redistributions of source code must retain the above copyright
42 1.1 augustss * notice, this list of conditions and the following disclaimer.
43 1.1 augustss * 2. Redistributions in binary form must reproduce the above copyright
44 1.1 augustss * notice, this list of conditions and the following disclaimer in the
45 1.1 augustss * documentation and/or other materials provided with the distribution.
46 1.1 augustss *
47 1.1 augustss * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
48 1.1 augustss * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49 1.1 augustss * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
50 1.1 augustss * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
51 1.1 augustss * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
52 1.1 augustss * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
53 1.1 augustss * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
54 1.1 augustss * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
55 1.1 augustss * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
56 1.1 augustss * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 1.1 augustss * SUCH DAMAGE.
58 1.1 augustss *
59 1.1 augustss * $FreeBSD$
60 1.1 augustss */
61 1.1 augustss
62 1.1 augustss #include <sys/param.h>
63 1.1 augustss #include <sys/systm.h>
64 1.1 augustss #include <sys/kernel.h>
65 1.1 augustss #include <sys/malloc.h>
66 1.1 augustss #include <sys/device.h>
67 1.1 augustss
68 1.1 augustss #include <sys/audioio.h>
69 1.1 augustss #include <dev/audio_if.h>
70 1.1 augustss #include <dev/ic/ac97.h>
71 1.1 augustss
72 1.1 augustss #define AC97_REG_RESET 0x00
73 1.1 augustss #define AC97_SOUND_ENHANCEMENT(reg) (((reg) >> 10) & 0x1f)
74 1.1 augustss #define AC97_REG_MASTER_VOLUME 0x02
75 1.1 augustss #define AC97_REG_HEADPHONE_VOLUME 0x04
76 1.1 augustss #define AC97_REG_MASTER_VOLUME_MONO 0x06
77 1.1 augustss #define AC97_REG_MASTER_TONE 0x08
78 1.1 augustss #define AC97_REG_PCBEEP_VOLUME 0x0a
79 1.1 augustss #define AC97_REG_PHONE_VOLUME 0x0c
80 1.1 augustss #define AC97_REG_MIC_VOLUME 0x0e
81 1.1 augustss #define AC97_REG_LINEIN_VOLUME 0x10
82 1.1 augustss #define AC97_REG_CD_VOLUME 0x12
83 1.1 augustss #define AC97_REG_VIDEO_VOLUME 0x14
84 1.1 augustss #define AC97_REG_AUX_VOLUME 0x16
85 1.1 augustss #define AC97_REG_PCMOUT_VOLUME 0x18
86 1.1 augustss #define AC97_REG_RECORD_SELECT 0x1a
87 1.1 augustss #define AC97_REG_RECORD_GAIN 0x1c
88 1.1 augustss #define AC97_REG_RECORD_GAIN_MIC 0x1e
89 1.1 augustss #define AC97_REG_GP 0x20
90 1.1 augustss #define AC97_REG_3D_CONTROL 0x22
91 1.1 augustss #define AC97_REG_POWER 0x26
92 1.1 augustss #define AC97_REG_VENDOR_ID1 0x7c
93 1.1 augustss #define AC97_REG_VENDOR_ID2 0x7e
94 1.1 augustss
95 1.1 augustss static struct audio_mixer_enum ac97_on_off = { 2,
96 1.1 augustss { { { AudioNoff } , 0 },
97 1.1 augustss { { AudioNon } , 1 } }};
98 1.1 augustss
99 1.1 augustss
100 1.1 augustss static struct audio_mixer_enum ac97_mic_select = { 2,
101 1.1 augustss { { { AudioNmicrophone "0" },
102 1.1 augustss 0 },
103 1.1 augustss { { AudioNmicrophone "1" },
104 1.1 augustss 1 } }};
105 1.1 augustss
106 1.1 augustss static struct audio_mixer_enum ac97_mono_select = { 2,
107 1.1 augustss { { { AudioNmixerout },
108 1.1 augustss 0 },
109 1.1 augustss { { AudioNmicrophone },
110 1.1 augustss 1 } }};
111 1.1 augustss
112 1.1 augustss static struct audio_mixer_enum ac97_source = { 8,
113 1.1 augustss { { { AudioNmicrophone } , 0 },
114 1.1 augustss { { AudioNcd }, 1 },
115 1.1 augustss { { "video" }, 2 },
116 1.1 augustss { { AudioNaux }, 3 },
117 1.1 augustss { { AudioNline }, 4 },
118 1.1 augustss { { AudioNmixerout }, 5 },
119 1.1 augustss { { AudioNmixerout AudioNmono }, 6 },
120 1.1 augustss { { "phone" }, 7 }}};
121 1.1 augustss
122 1.1 augustss static struct audio_mixer_value ac97_volume_stereo = { { AudioNvolume },
123 1.1 augustss 2 };
124 1.1 augustss
125 1.1 augustss
126 1.1 augustss static struct audio_mixer_value ac97_volume_mono = { { AudioNvolume },
127 1.1 augustss 1 };
128 1.1 augustss
129 1.1 augustss #define WRAP(a) &a, sizeof(a)
130 1.1 augustss
131 1.1 augustss struct ac97_source_info {
132 1.1 augustss char *class;
133 1.1 augustss char *device;
134 1.1 augustss char *qualifier;
135 1.1 augustss int type;
136 1.1 augustss
137 1.1 augustss void *info;
138 1.1 augustss int info_size;
139 1.1 augustss
140 1.1 augustss u_int8_t reg;
141 1.1 augustss u_int8_t bits:3;
142 1.1 augustss u_int8_t ofs:4;
143 1.1 augustss u_int8_t mute:1;
144 1.1 augustss u_int8_t polarity:1; /* Does 0 == MAX or MIN */
145 1.1 augustss
146 1.1 augustss int prev;
147 1.1 augustss int next;
148 1.1 augustss int mixer_class;
149 1.1 augustss } source_info[] = {
150 1.1 augustss { AudioCinputs , NULL, NULL, AUDIO_MIXER_CLASS,
151 1.1 augustss },
152 1.1 augustss { AudioCoutputs, NULL, NULL, AUDIO_MIXER_CLASS,
153 1.1 augustss },
154 1.1 augustss { AudioCrecord , NULL, NULL, AUDIO_MIXER_CLASS,
155 1.1 augustss },
156 1.1 augustss /* Stereo master volume*/
157 1.1 augustss { AudioCoutputs, AudioNmaster, NULL, AUDIO_MIXER_VALUE,
158 1.1 augustss WRAP(ac97_volume_stereo),
159 1.1 augustss AC97_REG_MASTER_VOLUME, 5, 0, 1,
160 1.1 augustss },
161 1.1 augustss /* Mono volume */
162 1.1 augustss { AudioCoutputs, AudioNmono, NULL, AUDIO_MIXER_VALUE,
163 1.1 augustss WRAP(ac97_volume_mono),
164 1.1 augustss AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1,
165 1.1 augustss },
166 1.1 augustss { AudioCoutputs, AudioNmono,AudioNsource, AUDIO_MIXER_ENUM,
167 1.1 augustss WRAP(ac97_mono_select),
168 1.1 augustss AC97_REG_GP, 1, 9, 0,
169 1.1 augustss },
170 1.1 augustss /* Headphone volume */
171 1.1 augustss { AudioCoutputs, AudioNheadphone, NULL, AUDIO_MIXER_VALUE,
172 1.1 augustss WRAP(ac97_volume_stereo),
173 1.1 augustss AC97_REG_HEADPHONE_VOLUME, 6, 0, 1,
174 1.1 augustss },
175 1.1 augustss /* Tone */
176 1.1 augustss { AudioCoutputs, "tone", NULL, AUDIO_MIXER_VALUE,
177 1.1 augustss WRAP(ac97_volume_stereo),
178 1.1 augustss AC97_REG_MASTER_TONE, 4, 0, 0,
179 1.1 augustss },
180 1.1 augustss /* PC Beep Volume */
181 1.1 augustss { AudioCinputs, AudioNspeaker, NULL, AUDIO_MIXER_VALUE,
182 1.1 augustss WRAP(ac97_volume_mono),
183 1.1 augustss AC97_REG_PCBEEP_VOLUME, 4, 1, 1,
184 1.1 augustss },
185 1.1 augustss /* Phone */
186 1.1 augustss { AudioCinputs, "phone", NULL, AUDIO_MIXER_VALUE,
187 1.1 augustss WRAP(ac97_volume_mono),
188 1.1 augustss AC97_REG_PHONE_VOLUME, 5, 0, 1,
189 1.1 augustss },
190 1.1 augustss /* Mic Volume */
191 1.1 augustss { AudioCinputs, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
192 1.1 augustss WRAP(ac97_volume_mono),
193 1.1 augustss AC97_REG_MIC_VOLUME, 5, 0, 1,
194 1.1 augustss },
195 1.1 augustss { AudioCinputs, AudioNmicrophone, AudioNpreamp, AUDIO_MIXER_ENUM,
196 1.1 augustss WRAP(ac97_on_off),
197 1.1 augustss AC97_REG_MIC_VOLUME, 1, 6, 0,
198 1.1 augustss },
199 1.1 augustss { AudioCinputs, AudioNmicrophone, AudioNsource, AUDIO_MIXER_ENUM,
200 1.1 augustss WRAP(ac97_mic_select),
201 1.1 augustss AC97_REG_GP, 1, 8, 0,
202 1.1 augustss },
203 1.1 augustss /* Line in Volume */
204 1.1 augustss { AudioCinputs, AudioNline, NULL, AUDIO_MIXER_VALUE,
205 1.1 augustss WRAP(ac97_volume_stereo),
206 1.1 augustss AC97_REG_LINEIN_VOLUME, 5, 0, 1,
207 1.1 augustss },
208 1.1 augustss /* CD Volume */
209 1.1 augustss { AudioCinputs, AudioNcd, NULL, AUDIO_MIXER_VALUE,
210 1.1 augustss WRAP(ac97_volume_stereo),
211 1.1 augustss AC97_REG_CD_VOLUME, 5, 0, 1,
212 1.1 augustss },
213 1.1 augustss /* Video Volume */
214 1.1 augustss { AudioCinputs, "video", NULL, AUDIO_MIXER_VALUE,
215 1.1 augustss WRAP(ac97_volume_stereo),
216 1.1 augustss AC97_REG_VIDEO_VOLUME, 5, 0, 1,
217 1.1 augustss },
218 1.1 augustss /* AUX volume */
219 1.1 augustss { AudioCinputs, AudioNaux, NULL, AUDIO_MIXER_VALUE,
220 1.1 augustss WRAP(ac97_volume_stereo),
221 1.1 augustss AC97_REG_AUX_VOLUME, 5, 0, 1,
222 1.1 augustss },
223 1.1 augustss /* PCM out volume */
224 1.1 augustss { AudioCinputs, AudioNdac, NULL, AUDIO_MIXER_VALUE,
225 1.1 augustss WRAP(ac97_volume_stereo),
226 1.1 augustss AC97_REG_PCMOUT_VOLUME, 5, 0, 1,
227 1.1 augustss },
228 1.1 augustss /* Record Source - some logic for this is hard coded - see below */
229 1.1 augustss { AudioCrecord, AudioNsource, NULL, AUDIO_MIXER_ENUM,
230 1.1 augustss WRAP(ac97_source),
231 1.1 augustss AC97_REG_RECORD_SELECT, 3, 0, 0,
232 1.1 augustss },
233 1.1 augustss /* Record Gain */
234 1.1 augustss { AudioCrecord, AudioNvolume, NULL, AUDIO_MIXER_VALUE,
235 1.1 augustss WRAP(ac97_volume_stereo),
236 1.1 augustss AC97_REG_RECORD_GAIN, 4, 0, 1,
237 1.1 augustss },
238 1.1 augustss /* Record Gain mic */
239 1.1 augustss { AudioCrecord, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
240 1.1 augustss WRAP(ac97_volume_mono),
241 1.1 augustss AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1,
242 1.1 augustss },
243 1.1 augustss /* */
244 1.1 augustss { AudioCoutputs, AudioNloudness, NULL, AUDIO_MIXER_ENUM,
245 1.1 augustss WRAP(ac97_on_off),
246 1.1 augustss AC97_REG_GP, 1, 12, 0,
247 1.1 augustss },
248 1.1 augustss { AudioCoutputs, AudioNspatial, NULL, AUDIO_MIXER_ENUM,
249 1.1 augustss WRAP(ac97_on_off),
250 1.1 augustss AC97_REG_GP, 1, 13, 0,
251 1.1 augustss },
252 1.1 augustss { AudioCoutputs, AudioNspatial, "center", AUDIO_MIXER_VALUE,
253 1.1 augustss WRAP(ac97_volume_mono),
254 1.1 augustss AC97_REG_3D_CONTROL, 4, 8, 0, 1,
255 1.1 augustss },
256 1.1 augustss { AudioCoutputs, AudioNspatial, "depth", AUDIO_MIXER_VALUE,
257 1.1 augustss WRAP(ac97_volume_mono),
258 1.1 augustss AC97_REG_3D_CONTROL, 4, 0, 0, 1,
259 1.1 augustss },
260 1.1 augustss
261 1.1 augustss /* Missing features: Simulated Stereo, POP, Loopback mode */
262 1.1 augustss } ;
263 1.1 augustss
264 1.1 augustss #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
265 1.1 augustss
266 1.1 augustss /*
267 1.1 augustss * Check out http://developer.intel.com/pc-supp/platform/ac97/ for
268 1.1 augustss * information on AC-97
269 1.1 augustss */
270 1.1 augustss
271 1.1 augustss struct ac97_softc {
272 1.1 augustss struct ac97_codec_if codecIf;
273 1.1 augustss
274 1.1 augustss struct ac97_host_if *hostIf;
275 1.1 augustss
276 1.1 augustss struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
277 1.1 augustss int num_source_info;
278 1.1 augustss };
279 1.1 augustss
280 1.1 augustss int ac97_mixer_get_port __P((struct ac97_codec_if *self, mixer_ctrl_t *cp));
281 1.1 augustss int ac97_mixer_set_port __P((struct ac97_codec_if *self, mixer_ctrl_t *));
282 1.1 augustss int ac97_query_devinfo __P((struct ac97_codec_if *self, mixer_devinfo_t *));
283 1.1 augustss int ac97_get_portnum_by_name __P((struct ac97_codec_if *, char *, char *,
284 1.1 augustss char *));
285 1.1 augustss
286 1.1 augustss struct ac97_codec_if_vtbl ac97civ = {
287 1.1 augustss ac97_mixer_get_port,
288 1.1 augustss ac97_mixer_set_port,
289 1.1 augustss ac97_query_devinfo,
290 1.1 augustss ac97_get_portnum_by_name
291 1.1 augustss };
292 1.1 augustss
293 1.7 thorpej #define AC97_CODEC_ID(a0, a1, a2, x) \
294 1.7 thorpej (((a0) << 24) | ((a1) << 16) | ((a2) << 8) | (x))
295 1.7 thorpej
296 1.7 thorpej #define AC97_GET_CODEC_ID(id, cp) \
297 1.7 thorpej do { \
298 1.7 thorpej (cp)[0] = ((id) >> 24) & 0xff; \
299 1.7 thorpej (cp)[1] = ((id) >> 16) & 0xff; \
300 1.7 thorpej (cp)[2] = ((id) >> 8) & 0xff; \
301 1.7 thorpej (cp)[3] = (id) & 0xff; \
302 1.7 thorpej } while (0)
303 1.7 thorpej
304 1.7 thorpej static const struct ac97_codecid {
305 1.1 augustss u_int32_t id;
306 1.7 thorpej const char *name;
307 1.1 augustss } ac97codecid[] = {
308 1.7 thorpej { AC97_CODEC_ID('A', 'K', 'M', 0), "Asahi Kasei AK4540" },
309 1.7 thorpej #if 0
310 1.7 thorpej { AC97_CODEC_ID('C', 'R', 'Y', 0), "Crystal CS4297" },
311 1.7 thorpej #endif
312 1.7 thorpej { AC97_CODEC_ID('C', 'R', 'Y', 3), "Crystal CS4297" },
313 1.7 thorpej { AC97_CODEC_ID('C', 'R', 'Y', 19), "Crystal CS4297A" },
314 1.7 thorpej { 0x83847600, "SigmaTel STAC????" },
315 1.7 thorpej { 0x83847604, "SigmaTel STAC9701/3/4/5" },
316 1.7 thorpej { 0x83847605, "SigmaTel STAC9704" },
317 1.7 thorpej { 0x83847608, "SigmaTel STAC9708" },
318 1.7 thorpej { 0x83847609, "SigmaTel STAC9721" },
319 1.7 thorpej { 0, NULL }
320 1.1 augustss };
321 1.1 augustss
322 1.1 augustss static char *ac97enhancement[] = {
323 1.2 soren "no 3D stereo",
324 1.1 augustss "Analog Devices Phat Stereo",
325 1.1 augustss "Creative"
326 1.1 augustss "National Semi 3D",
327 1.1 augustss "Yamaha Ymersion",
328 1.1 augustss "BBE 3D",
329 1.1 augustss "Crystal Semi 3D"
330 1.1 augustss "Qsound QXpander",
331 1.1 augustss "Spatializer 3D",
332 1.1 augustss "SRS 3D",
333 1.1 augustss "Platform Tech 3D",
334 1.1 augustss "AKM 3D",
335 1.1 augustss "Aureal",
336 1.1 augustss "AZTECH 3D",
337 1.1 augustss "Binaura 3D",
338 1.1 augustss "ESS Technology",
339 1.1 augustss "Harman International VMAx",
340 1.1 augustss "Nvidea 3D",
341 1.1 augustss "Philips Incredible Sound",
342 1.1 augustss "Texas Instruments' 3D",
343 1.1 augustss "VLSI Technology 3D",
344 1.1 augustss "TriTech 3D",
345 1.1 augustss "Realtek 3D",
346 1.1 augustss "Samsung 3D",
347 1.1 augustss "Wolfson Microelectronics 3D",
348 1.1 augustss "Delta Integration 3D",
349 1.1 augustss "SigmaTel 3D",
350 1.1 augustss "Unknown 3D",
351 1.1 augustss "Rockwell 3D",
352 1.1 augustss "Unknown 3D",
353 1.1 augustss "Unknown 3D",
354 1.1 augustss "Unknown 3D",
355 1.1 augustss };
356 1.1 augustss
357 1.1 augustss static char *ac97feature[] = {
358 1.1 augustss "mic channel",
359 1.1 augustss "reserved",
360 1.1 augustss "tone",
361 1.1 augustss "simulated stereo",
362 1.1 augustss "headphone",
363 1.1 augustss "bass boost",
364 1.1 augustss "18 bit DAC",
365 1.1 augustss "20 bit DAC",
366 1.1 augustss "18 bit ADC",
367 1.1 augustss "20 bit ADC"
368 1.1 augustss };
369 1.1 augustss
370 1.1 augustss
371 1.1 augustss int ac97_str_equal __P((char *, char *));
372 1.1 augustss void ac97_setup_source_info __P((struct ac97_softc *));
373 1.1 augustss
374 1.1 augustss /* #define AC97_DEBUG 10 */
375 1.1 augustss
376 1.1 augustss #ifdef AUDIO_DEBUG
377 1.1 augustss #define DPRINTF(x) if (ac97debug) printf x
378 1.1 augustss #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
379 1.1 augustss #ifdef AC97_DEBUG
380 1.1 augustss int ac97debug = AC97_DEBUG;
381 1.1 augustss #else
382 1.1 augustss int ac97debug = 0;
383 1.1 augustss #endif
384 1.1 augustss #else
385 1.1 augustss #define DPRINTF(x)
386 1.1 augustss #define DPRINTFN(n,x)
387 1.1 augustss #endif
388 1.1 augustss
389 1.1 augustss
390 1.1 augustss int
391 1.1 augustss ac97_str_equal(a, b)
392 1.1 augustss char *a, *b;
393 1.1 augustss {
394 1.1 augustss return ((a == b) || (a && b && (!strcmp(a, b))));
395 1.1 augustss }
396 1.1 augustss
397 1.1 augustss void
398 1.1 augustss ac97_setup_source_info(as)
399 1.1 augustss struct ac97_softc *as;
400 1.1 augustss {
401 1.1 augustss int idx, ouridx;
402 1.1 augustss struct ac97_source_info *si, *si2;
403 1.1 augustss
404 1.1 augustss for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
405 1.1 augustss si = &as->source_info[ouridx];
406 1.1 augustss
407 1.1 augustss bcopy(&source_info[idx], si, sizeof(*si));
408 1.1 augustss
409 1.1 augustss switch (si->type) {
410 1.1 augustss case AUDIO_MIXER_CLASS:
411 1.1 augustss si->mixer_class = ouridx;
412 1.1 augustss ouridx++;
413 1.1 augustss break;
414 1.1 augustss case AUDIO_MIXER_VALUE:
415 1.1 augustss /* Todo - Test to see if it works */
416 1.1 augustss ouridx++;
417 1.1 augustss
418 1.1 augustss /* Add an entry for mute, if necessary */
419 1.1 augustss if (si->mute) {
420 1.1 augustss si = &as->source_info[ouridx];
421 1.1 augustss bcopy(&source_info[idx], si, sizeof(*si));
422 1.1 augustss si->qualifier = AudioNmute;
423 1.1 augustss si->type = AUDIO_MIXER_ENUM;
424 1.1 augustss si->info = &ac97_on_off;
425 1.1 augustss si->info_size = sizeof(ac97_on_off);
426 1.1 augustss si->bits = 1;
427 1.1 augustss si->ofs = 15;
428 1.1 augustss si->mute = 0;
429 1.1 augustss si->polarity = 0;
430 1.1 augustss ouridx++;
431 1.1 augustss }
432 1.1 augustss break;
433 1.1 augustss case AUDIO_MIXER_ENUM:
434 1.1 augustss /* Todo - Test to see if it works */
435 1.1 augustss ouridx++;
436 1.1 augustss break;
437 1.1 augustss default:
438 1.1 augustss printf ("ac97: shouldn't get here\n");
439 1.1 augustss break;
440 1.1 augustss }
441 1.1 augustss }
442 1.1 augustss
443 1.1 augustss as->num_source_info = ouridx;
444 1.1 augustss
445 1.1 augustss for (idx = 0; idx < as->num_source_info; idx++) {
446 1.1 augustss int idx2, previdx;
447 1.1 augustss
448 1.1 augustss si = &as->source_info[idx];
449 1.1 augustss
450 1.1 augustss /* Find mixer class */
451 1.1 augustss for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
452 1.1 augustss si2 = &as->source_info[idx2];
453 1.1 augustss
454 1.1 augustss if (si2->type == AUDIO_MIXER_CLASS &&
455 1.1 augustss ac97_str_equal(si->class,
456 1.1 augustss si2->class)) {
457 1.1 augustss si->mixer_class = idx2;
458 1.1 augustss }
459 1.1 augustss }
460 1.1 augustss
461 1.1 augustss
462 1.1 augustss /* Setup prev and next pointers */
463 1.1 augustss if (si->prev != 0)
464 1.1 augustss continue;
465 1.1 augustss
466 1.1 augustss if (si->qualifier)
467 1.1 augustss continue;
468 1.1 augustss
469 1.1 augustss si->prev = AUDIO_MIXER_LAST;
470 1.1 augustss previdx = idx;
471 1.1 augustss
472 1.1 augustss for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
473 1.1 augustss if (idx2 == idx)
474 1.1 augustss continue;
475 1.1 augustss
476 1.1 augustss si2 = &as->source_info[idx2];
477 1.1 augustss
478 1.1 augustss if (!si2->prev &&
479 1.1 augustss ac97_str_equal(si->class, si2->class) &&
480 1.1 augustss ac97_str_equal(si->device, si2->device)) {
481 1.1 augustss as->source_info[previdx].next = idx2;
482 1.1 augustss as->source_info[idx2].prev = previdx;
483 1.1 augustss
484 1.1 augustss previdx = idx2;
485 1.1 augustss }
486 1.1 augustss }
487 1.1 augustss
488 1.1 augustss as->source_info[previdx].next = AUDIO_MIXER_LAST;
489 1.1 augustss }
490 1.1 augustss }
491 1.1 augustss
492 1.1 augustss int
493 1.1 augustss ac97_attach(hostIf)
494 1.1 augustss struct ac97_host_if *hostIf;
495 1.1 augustss {
496 1.1 augustss struct ac97_softc *as;
497 1.2 soren struct device *sc_dev = (struct device *)hostIf->arg;
498 1.1 augustss int error, i, j;
499 1.1 augustss u_int16_t id1, id2, caps;
500 1.1 augustss u_int32_t id;
501 1.8 augustss mixer_ctrl_t ctl;
502 1.1 augustss
503 1.1 augustss as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK);
504 1.1 augustss
505 1.2 soren if (as == NULL)
506 1.2 soren return (ENOMEM);
507 1.1 augustss
508 1.1 augustss as->codecIf.vtbl = &ac97civ;
509 1.1 augustss as->hostIf = hostIf;
510 1.1 augustss
511 1.1 augustss if ((error = hostIf->attach(hostIf->arg, &as->codecIf))) {
512 1.1 augustss free (as, M_DEVBUF);
513 1.1 augustss return (error);
514 1.1 augustss }
515 1.1 augustss
516 1.1 augustss hostIf->reset(hostIf->arg);
517 1.1 augustss
518 1.1 augustss hostIf->write(hostIf->arg, AC97_REG_POWER, 0);
519 1.1 augustss hostIf->write(hostIf->arg, AC97_REG_RESET, 0);
520 1.1 augustss
521 1.1 augustss if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID1, &id1)))
522 1.1 augustss return (error);
523 1.1 augustss
524 1.1 augustss if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID2, &id2)))
525 1.1 augustss return (error);
526 1.1 augustss
527 1.1 augustss if ((error = hostIf->read(hostIf->arg, AC97_REG_RESET, &caps)))
528 1.1 augustss return (error);
529 1.1 augustss
530 1.1 augustss id = (id1 << 16) | id2;
531 1.1 augustss
532 1.2 soren printf("%s: ", sc_dev->dv_xname);
533 1.2 soren
534 1.2 soren for (i = 0; ; i++) {
535 1.2 soren if (ac97codecid[i].id == id) {
536 1.2 soren printf("%s", ac97codecid[i].name);
537 1.2 soren break;
538 1.2 soren }
539 1.2 soren if (ac97codecid[i].id == 0) {
540 1.4 augustss char pnp[4];
541 1.7 thorpej
542 1.7 thorpej AC97_GET_CODEC_ID(id, pnp);
543 1.4 augustss #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
544 1.4 augustss if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
545 1.4 augustss ISASCII(pnp[2]))
546 1.7 thorpej printf("%c%c%c%d", pnp[0], pnp[1], pnp[2],
547 1.7 thorpej pnp[3]);
548 1.4 augustss else
549 1.7 thorpej printf("unknown (0x%08x)", id);
550 1.2 soren break;
551 1.2 soren }
552 1.1 augustss }
553 1.2 soren printf(" codec; ");
554 1.1 augustss for (i = j = 0; i < 10; i++) {
555 1.1 augustss if (caps & (1 << i)) {
556 1.1 augustss printf("%s%s", j? ", " : "", ac97feature[i]);
557 1.1 augustss j++;
558 1.1 augustss }
559 1.1 augustss }
560 1.1 augustss
561 1.1 augustss printf("%s%s\n", j? ", " : "", ac97enhancement[(caps >> 10) & 0x1f]);
562 1.1 augustss
563 1.1 augustss ac97_setup_source_info(as);
564 1.8 augustss
565 1.8 augustss /* Just enable the DAC and master volumes by default */
566 1.8 augustss memset(&ctl, 0, sizeof(ctl));
567 1.8 augustss
568 1.8 augustss ctl.type = AUDIO_MIXER_ENUM;
569 1.8 augustss ctl.un.ord = 0; /* off */
570 1.8 augustss ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCoutputs,
571 1.8 augustss AudioNmaster, AudioNmute);
572 1.8 augustss ac97_mixer_set_port(&as->codecIf, &ctl);
573 1.8 augustss ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCinputs,
574 1.8 augustss AudioNdac, AudioNmute);
575 1.8 augustss
576 1.8 augustss ac97_mixer_set_port(&as->codecIf, &ctl);
577 1.8 augustss ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCrecord,
578 1.8 augustss AudioNvolume, AudioNmute);
579 1.8 augustss ac97_mixer_set_port(&as->codecIf, &ctl);
580 1.8 augustss
581 1.8 augustss ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCrecord,
582 1.8 augustss AudioNsource, NULL);
583 1.8 augustss ctl.type = AUDIO_MIXER_ENUM;
584 1.8 augustss ctl.un.ord = 0;
585 1.8 augustss ac97_mixer_set_port(&as->codecIf, &ctl);
586 1.1 augustss
587 1.1 augustss return (0);
588 1.1 augustss }
589 1.1 augustss
590 1.1 augustss
591 1.1 augustss int
592 1.1 augustss ac97_query_devinfo(codec_if, dip)
593 1.1 augustss struct ac97_codec_if *codec_if;
594 1.1 augustss mixer_devinfo_t *dip;
595 1.1 augustss {
596 1.1 augustss struct ac97_softc *as = (struct ac97_softc *)codec_if;
597 1.1 augustss
598 1.1 augustss if (dip->index < as->num_source_info) {
599 1.1 augustss struct ac97_source_info *si = &as->source_info[dip->index];
600 1.1 augustss char *name;
601 1.1 augustss
602 1.1 augustss dip->type = si->type;
603 1.1 augustss dip->mixer_class = si->mixer_class;
604 1.1 augustss dip->prev = si->prev;
605 1.1 augustss dip->next = si->next;
606 1.1 augustss
607 1.1 augustss if (si->qualifier)
608 1.1 augustss name = si->qualifier;
609 1.1 augustss else if (si->device)
610 1.1 augustss name = si->device;
611 1.1 augustss else if (si->class)
612 1.1 augustss name = si->class;
613 1.6 augustss else
614 1.6 augustss name = 0;
615 1.1 augustss
616 1.1 augustss if (name)
617 1.1 augustss strcpy(dip->label.name, name);
618 1.1 augustss
619 1.1 augustss bcopy(si->info, &dip->un, si->info_size);
620 1.1 augustss return (0);
621 1.1 augustss }
622 1.1 augustss
623 1.1 augustss return (ENXIO);
624 1.1 augustss }
625 1.1 augustss
626 1.1 augustss
627 1.1 augustss
628 1.1 augustss int
629 1.1 augustss ac97_mixer_set_port(codec_if, cp)
630 1.1 augustss struct ac97_codec_if *codec_if;
631 1.1 augustss mixer_ctrl_t *cp;
632 1.1 augustss {
633 1.1 augustss struct ac97_softc *as = (struct ac97_softc *)codec_if;
634 1.1 augustss struct ac97_source_info *si = &as->source_info[cp->dev];
635 1.1 augustss u_int16_t mask;
636 1.1 augustss u_int16_t val, newval;
637 1.1 augustss int error;
638 1.1 augustss
639 1.1 augustss if (cp->dev < 0 || cp->dev >= as->num_source_info)
640 1.1 augustss return (EINVAL);
641 1.1 augustss
642 1.1 augustss if (cp->type != si->type)
643 1.1 augustss return (EINVAL);
644 1.1 augustss
645 1.1 augustss error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
646 1.1 augustss if (error)
647 1.1 augustss return (error);
648 1.1 augustss
649 1.1 augustss DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
650 1.1 augustss
651 1.1 augustss mask = (1 << si->bits) - 1;
652 1.1 augustss
653 1.1 augustss switch (cp->type) {
654 1.1 augustss case AUDIO_MIXER_ENUM:
655 1.1 augustss if (cp->un.ord > mask || cp->un.ord < 0)
656 1.1 augustss return (EINVAL);
657 1.1 augustss
658 1.1 augustss newval = (cp->un.ord << si->ofs);
659 1.1 augustss if (si->reg == AC97_REG_RECORD_SELECT) {
660 1.1 augustss newval |= (newval << (8 + si->ofs));
661 1.1 augustss mask |= (mask << 8);
662 1.1 augustss }
663 1.1 augustss break;
664 1.1 augustss case AUDIO_MIXER_VALUE:
665 1.1 augustss {
666 1.1 augustss struct audio_mixer_value *value = si->info;
667 1.1 augustss u_int16_t l, r;
668 1.1 augustss
669 1.1 augustss if ((cp->un.value.num_channels <= 0) ||
670 1.1 augustss (cp->un.value.num_channels > value->num_channels))
671 1.1 augustss return (EINVAL);
672 1.1 augustss
673 1.1 augustss if (cp->un.value.num_channels == 1) {
674 1.1 augustss l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
675 1.1 augustss } else {
676 1.1 augustss l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
677 1.1 augustss r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
678 1.1 augustss }
679 1.1 augustss
680 1.1 augustss if (!si->polarity) {
681 1.1 augustss l = 255 - l;
682 1.1 augustss r = 255 - r;
683 1.1 augustss }
684 1.1 augustss
685 1.1 augustss l = l >> (8 - si->bits);
686 1.1 augustss r = r >> (8 - si->bits);
687 1.1 augustss
688 1.1 augustss newval = ((l & mask) << si->ofs);
689 1.1 augustss if (value->num_channels == 2) {
690 1.1 augustss newval |= ((r & mask) << (si->ofs + 8));
691 1.1 augustss mask |= (mask << 8);
692 1.1 augustss }
693 1.1 augustss
694 1.1 augustss break;
695 1.1 augustss }
696 1.1 augustss default:
697 1.1 augustss return (EINVAL);
698 1.1 augustss }
699 1.1 augustss
700 1.1 augustss mask = mask << si->ofs;
701 1.1 augustss error = as->hostIf->write(as->hostIf->arg, si->reg, (val & ~mask) | newval);
702 1.1 augustss if (error)
703 1.1 augustss return (error);
704 1.1 augustss
705 1.1 augustss return (0);
706 1.1 augustss }
707 1.1 augustss
708 1.1 augustss int
709 1.1 augustss ac97_get_portnum_by_name(codec_if, class, device, qualifier)
710 1.1 augustss struct ac97_codec_if *codec_if;
711 1.1 augustss char *class, *device, *qualifier;
712 1.1 augustss {
713 1.1 augustss struct ac97_softc *as = (struct ac97_softc *)codec_if;
714 1.1 augustss int idx;
715 1.1 augustss
716 1.1 augustss for (idx = 0; idx < as->num_source_info; idx++) {
717 1.1 augustss struct ac97_source_info *si = &as->source_info[idx];
718 1.1 augustss if (ac97_str_equal(class, si->class) &&
719 1.1 augustss ac97_str_equal(device, si->device) &&
720 1.1 augustss ac97_str_equal(qualifier, si->qualifier))
721 1.1 augustss return (idx);
722 1.1 augustss }
723 1.1 augustss
724 1.1 augustss return (-1);
725 1.1 augustss }
726 1.1 augustss
727 1.1 augustss int
728 1.1 augustss ac97_mixer_get_port(codec_if, cp)
729 1.1 augustss struct ac97_codec_if *codec_if;
730 1.1 augustss mixer_ctrl_t *cp;
731 1.1 augustss {
732 1.1 augustss struct ac97_softc *as = (struct ac97_softc *)codec_if;
733 1.1 augustss struct ac97_source_info *si = &as->source_info[cp->dev];
734 1.1 augustss u_int16_t mask;
735 1.1 augustss u_int16_t val;
736 1.1 augustss int error;
737 1.1 augustss
738 1.1 augustss if (cp->dev < 0 || cp->dev >= as->num_source_info)
739 1.1 augustss return (EINVAL);
740 1.1 augustss
741 1.1 augustss if (cp->type != si->type)
742 1.1 augustss return (EINVAL);
743 1.1 augustss
744 1.1 augustss error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
745 1.1 augustss if (error)
746 1.1 augustss return (error);
747 1.1 augustss
748 1.1 augustss DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
749 1.1 augustss
750 1.1 augustss mask = (1 << si->bits) - 1;
751 1.1 augustss
752 1.1 augustss switch (cp->type) {
753 1.1 augustss case AUDIO_MIXER_ENUM:
754 1.1 augustss cp->un.ord = (val >> si->ofs) & mask;
755 1.1 augustss DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
756 1.1 augustss break;
757 1.1 augustss case AUDIO_MIXER_VALUE:
758 1.1 augustss {
759 1.1 augustss struct audio_mixer_value *value = si->info;
760 1.1 augustss u_int16_t l, r;
761 1.1 augustss
762 1.1 augustss if ((cp->un.value.num_channels <= 0) ||
763 1.1 augustss (cp->un.value.num_channels > value->num_channels))
764 1.1 augustss return (EINVAL);
765 1.1 augustss
766 1.1 augustss if (value->num_channels == 1) {
767 1.1 augustss l = r = (val >> si->ofs) & mask;
768 1.1 augustss } else {
769 1.1 augustss l = (val >> si->ofs) & mask;
770 1.1 augustss r = (val >> (si->ofs + 8)) & mask;
771 1.1 augustss }
772 1.1 augustss
773 1.1 augustss l = (l << (8 - si->bits));
774 1.1 augustss r = (r << (8 - si->bits));
775 1.1 augustss if (!si->polarity) {
776 1.1 augustss l = 255 - l;
777 1.1 augustss r = 255 - r;
778 1.1 augustss }
779 1.1 augustss
780 1.1 augustss /* The EAP driver averages l and r for stereo
781 1.1 augustss channels that are requested in MONO mode. Does this
782 1.1 augustss make sense? */
783 1.1 augustss if (cp->un.value.num_channels == 1) {
784 1.1 augustss cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
785 1.1 augustss } else if (cp->un.value.num_channels == 2) {
786 1.1 augustss cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
787 1.1 augustss cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
788 1.1 augustss }
789 1.1 augustss
790 1.1 augustss break;
791 1.1 augustss }
792 1.1 augustss default:
793 1.1 augustss return (EINVAL);
794 1.1 augustss }
795 1.1 augustss
796 1.1 augustss return (0);
797 1.1 augustss }
798 1.1 augustss
799