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