ac97.c revision 1.11 1 /* $NetBSD: ac97.c,v 1.11 2000/06/06 17:25:52 soren 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', 'K', 'M', 0), "Asahi Kasei AK4540" },
277 #if 0
278 { AC97_CODEC_ID('C', 'R', 'Y', 0), "Crystal CS4297" },
279 #endif
280 { AC97_CODEC_ID('C', 'R', 'Y', 3), "Crystal CS4297" },
281 { AC97_CODEC_ID('C', 'R', 'Y', 19), "Crystal CS4297A" },
282 { 0x83847600, "SigmaTel STAC????" },
283 { 0x83847604, "SigmaTel STAC9701/3/4/5" },
284 { 0x83847605, "SigmaTel STAC9704" },
285 { 0x83847608, "SigmaTel STAC9708" },
286 { 0x83847609, "SigmaTel STAC9721" },
287 { 0, NULL }
288 };
289
290 static char *ac97enhancement[] = {
291 "no 3D stereo",
292 "Analog Devices Phat Stereo",
293 "Creative"
294 "National Semi 3D",
295 "Yamaha Ymersion",
296 "BBE 3D",
297 "Crystal Semi 3D"
298 "Qsound QXpander",
299 "Spatializer 3D",
300 "SRS 3D",
301 "Platform Tech 3D",
302 "AKM 3D",
303 "Aureal",
304 "AZTECH 3D",
305 "Binaura 3D",
306 "ESS Technology",
307 "Harman International VMAx",
308 "Nvidea 3D",
309 "Philips Incredible Sound",
310 "Texas Instruments' 3D",
311 "VLSI Technology 3D",
312 "TriTech 3D",
313 "Realtek 3D",
314 "Samsung 3D",
315 "Wolfson Microelectronics 3D",
316 "Delta Integration 3D",
317 "SigmaTel 3D",
318 "Unknown 3D",
319 "Rockwell 3D",
320 "Unknown 3D",
321 "Unknown 3D",
322 "Unknown 3D",
323 };
324
325 static char *ac97feature[] = {
326 "mic channel",
327 "reserved",
328 "tone",
329 "simulated stereo",
330 "headphone",
331 "bass boost",
332 "18 bit DAC",
333 "20 bit DAC",
334 "18 bit ADC",
335 "20 bit ADC"
336 };
337
338
339 int ac97_str_equal __P((char *, char *));
340 void ac97_setup_source_info __P((struct ac97_softc *));
341
342 /* #define AC97_DEBUG 10 */
343
344 #ifdef AUDIO_DEBUG
345 #define DPRINTF(x) if (ac97debug) printf x
346 #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
347 #ifdef AC97_DEBUG
348 int ac97debug = AC97_DEBUG;
349 #else
350 int ac97debug = 0;
351 #endif
352 #else
353 #define DPRINTF(x)
354 #define DPRINTFN(n,x)
355 #endif
356
357
358 int
359 ac97_str_equal(a, b)
360 char *a, *b;
361 {
362 return ((a == b) || (a && b && (!strcmp(a, b))));
363 }
364
365 void
366 ac97_setup_source_info(as)
367 struct ac97_softc *as;
368 {
369 int idx, ouridx;
370 struct ac97_source_info *si, *si2;
371
372 for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
373 si = &as->source_info[ouridx];
374
375 bcopy(&source_info[idx], si, sizeof(*si));
376
377 switch (si->type) {
378 case AUDIO_MIXER_CLASS:
379 si->mixer_class = ouridx;
380 ouridx++;
381 break;
382 case AUDIO_MIXER_VALUE:
383 /* Todo - Test to see if it works */
384 ouridx++;
385
386 /* Add an entry for mute, if necessary */
387 if (si->mute) {
388 si = &as->source_info[ouridx];
389 bcopy(&source_info[idx], si, sizeof(*si));
390 si->qualifier = AudioNmute;
391 si->type = AUDIO_MIXER_ENUM;
392 si->info = &ac97_on_off;
393 si->info_size = sizeof(ac97_on_off);
394 si->bits = 1;
395 si->ofs = 15;
396 si->mute = 0;
397 si->polarity = 0;
398 ouridx++;
399 }
400 break;
401 case AUDIO_MIXER_ENUM:
402 /* Todo - Test to see if it works */
403 ouridx++;
404 break;
405 default:
406 printf ("ac97: shouldn't get here\n");
407 break;
408 }
409 }
410
411 as->num_source_info = ouridx;
412
413 for (idx = 0; idx < as->num_source_info; idx++) {
414 int idx2, previdx;
415
416 si = &as->source_info[idx];
417
418 /* Find mixer class */
419 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
420 si2 = &as->source_info[idx2];
421
422 if (si2->type == AUDIO_MIXER_CLASS &&
423 ac97_str_equal(si->class,
424 si2->class)) {
425 si->mixer_class = idx2;
426 }
427 }
428
429
430 /* Setup prev and next pointers */
431 if (si->prev != 0)
432 continue;
433
434 if (si->qualifier)
435 continue;
436
437 si->prev = AUDIO_MIXER_LAST;
438 previdx = idx;
439
440 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
441 if (idx2 == idx)
442 continue;
443
444 si2 = &as->source_info[idx2];
445
446 if (!si2->prev &&
447 ac97_str_equal(si->class, si2->class) &&
448 ac97_str_equal(si->device, si2->device)) {
449 as->source_info[previdx].next = idx2;
450 as->source_info[idx2].prev = previdx;
451
452 previdx = idx2;
453 }
454 }
455
456 as->source_info[previdx].next = AUDIO_MIXER_LAST;
457 }
458 }
459
460 int
461 ac97_attach(hostIf)
462 struct ac97_host_if *hostIf;
463 {
464 struct ac97_softc *as;
465 struct device *sc_dev = (struct device *)hostIf->arg;
466 int error, i, j;
467 u_int16_t id1, id2, caps;
468 u_int32_t id;
469 mixer_ctrl_t ctl;
470
471 as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK);
472
473 if (as == NULL)
474 return (ENOMEM);
475
476 as->codecIf.vtbl = &ac97civ;
477 as->hostIf = hostIf;
478
479 if ((error = hostIf->attach(hostIf->arg, &as->codecIf))) {
480 free (as, M_DEVBUF);
481 return (error);
482 }
483
484 hostIf->reset(hostIf->arg);
485
486 hostIf->write(hostIf->arg, AC97_REG_POWER, 0);
487 hostIf->write(hostIf->arg, AC97_REG_RESET, 0);
488
489 if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID1, &id1)))
490 return (error);
491
492 if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID2, &id2)))
493 return (error);
494
495 if ((error = hostIf->read(hostIf->arg, AC97_REG_RESET, &caps)))
496 return (error);
497
498 id = (id1 << 16) | id2;
499
500 printf("%s: ", sc_dev->dv_xname);
501
502 for (i = 0; ; i++) {
503 if (ac97codecid[i].id == 0) {
504 char pnp[4];
505
506 AC97_GET_CODEC_ID(id, pnp);
507 #define ISASCII(c) ((c) >= ' ' && (c) < 0x7f)
508 if (ISASCII(pnp[0]) && ISASCII(pnp[1]) &&
509 ISASCII(pnp[2]))
510 printf("%c%c%c%d", pnp[0], pnp[1], pnp[2],
511 pnp[3]);
512 else
513 printf("unknown (0x%8x)", id);
514 break;
515 }
516 if (ac97codecid[i].id == id) {
517 printf("%s", ac97codecid[i].name);
518 break;
519 }
520 }
521 printf(" codec; ");
522 for (i = j = 0; i < 10; i++) {
523 if (caps & (1 << i)) {
524 printf("%s%s", j? ", " : "", ac97feature[i]);
525 j++;
526 }
527 }
528
529 printf("%s%s\n", j? ", " : "", ac97enhancement[(caps >> 10) & 0x1f]);
530
531 ac97_setup_source_info(as);
532
533 /* Just enable the DAC and master volumes by default */
534 memset(&ctl, 0, sizeof(ctl));
535
536 ctl.type = AUDIO_MIXER_ENUM;
537 ctl.un.ord = 0; /* off */
538 ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCoutputs,
539 AudioNmaster, AudioNmute);
540 ac97_mixer_set_port(&as->codecIf, &ctl);
541 ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCinputs,
542 AudioNdac, AudioNmute);
543
544 ac97_mixer_set_port(&as->codecIf, &ctl);
545 ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCrecord,
546 AudioNvolume, AudioNmute);
547 ac97_mixer_set_port(&as->codecIf, &ctl);
548
549 ctl.dev = ac97_get_portnum_by_name(&as->codecIf, AudioCrecord,
550 AudioNsource, NULL);
551 ctl.type = AUDIO_MIXER_ENUM;
552 ctl.un.ord = 0;
553 ac97_mixer_set_port(&as->codecIf, &ctl);
554
555 return (0);
556 }
557
558
559 int
560 ac97_query_devinfo(codec_if, dip)
561 struct ac97_codec_if *codec_if;
562 mixer_devinfo_t *dip;
563 {
564 struct ac97_softc *as = (struct ac97_softc *)codec_if;
565
566 if (dip->index < as->num_source_info) {
567 struct ac97_source_info *si = &as->source_info[dip->index];
568 char *name;
569
570 dip->type = si->type;
571 dip->mixer_class = si->mixer_class;
572 dip->prev = si->prev;
573 dip->next = si->next;
574
575 if (si->qualifier)
576 name = si->qualifier;
577 else if (si->device)
578 name = si->device;
579 else if (si->class)
580 name = si->class;
581 else
582 name = 0;
583
584 if (name)
585 strcpy(dip->label.name, name);
586
587 bcopy(si->info, &dip->un, si->info_size);
588 return (0);
589 }
590
591 return (ENXIO);
592 }
593
594
595
596 int
597 ac97_mixer_set_port(codec_if, cp)
598 struct ac97_codec_if *codec_if;
599 mixer_ctrl_t *cp;
600 {
601 struct ac97_softc *as = (struct ac97_softc *)codec_if;
602 struct ac97_source_info *si = &as->source_info[cp->dev];
603 u_int16_t mask;
604 u_int16_t val, newval;
605 int error;
606
607 if (cp->dev < 0 || cp->dev >= as->num_source_info)
608 return (EINVAL);
609
610 if (cp->type != si->type)
611 return (EINVAL);
612
613 error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
614 if (error)
615 return (error);
616
617 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
618
619 mask = (1 << si->bits) - 1;
620
621 switch (cp->type) {
622 case AUDIO_MIXER_ENUM:
623 if (cp->un.ord > mask || cp->un.ord < 0)
624 return (EINVAL);
625
626 newval = (cp->un.ord << si->ofs);
627 if (si->reg == AC97_REG_RECORD_SELECT) {
628 newval |= (newval << (8 + si->ofs));
629 mask |= (mask << 8);
630 }
631 break;
632 case AUDIO_MIXER_VALUE:
633 {
634 struct audio_mixer_value *value = si->info;
635 u_int16_t l, r;
636
637 if ((cp->un.value.num_channels <= 0) ||
638 (cp->un.value.num_channels > value->num_channels))
639 return (EINVAL);
640
641 if (cp->un.value.num_channels == 1) {
642 l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
643 } else {
644 l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
645 r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
646 }
647
648 if (!si->polarity) {
649 l = 255 - l;
650 r = 255 - r;
651 }
652
653 l = l >> (8 - si->bits);
654 r = r >> (8 - si->bits);
655
656 newval = ((l & mask) << si->ofs);
657 if (value->num_channels == 2) {
658 newval |= ((r & mask) << (si->ofs + 8));
659 mask |= (mask << 8);
660 }
661
662 break;
663 }
664 default:
665 return (EINVAL);
666 }
667
668 mask = mask << si->ofs;
669 error = as->hostIf->write(as->hostIf->arg, si->reg, (val & ~mask) | newval);
670 if (error)
671 return (error);
672
673 return (0);
674 }
675
676 int
677 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
678 struct ac97_codec_if *codec_if;
679 char *class, *device, *qualifier;
680 {
681 struct ac97_softc *as = (struct ac97_softc *)codec_if;
682 int idx;
683
684 for (idx = 0; idx < as->num_source_info; idx++) {
685 struct ac97_source_info *si = &as->source_info[idx];
686 if (ac97_str_equal(class, si->class) &&
687 ac97_str_equal(device, si->device) &&
688 ac97_str_equal(qualifier, si->qualifier))
689 return (idx);
690 }
691
692 return (-1);
693 }
694
695 int
696 ac97_mixer_get_port(codec_if, cp)
697 struct ac97_codec_if *codec_if;
698 mixer_ctrl_t *cp;
699 {
700 struct ac97_softc *as = (struct ac97_softc *)codec_if;
701 struct ac97_source_info *si = &as->source_info[cp->dev];
702 u_int16_t mask;
703 u_int16_t val;
704 int error;
705
706 if (cp->dev < 0 || cp->dev >= as->num_source_info)
707 return (EINVAL);
708
709 if (cp->type != si->type)
710 return (EINVAL);
711
712 error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
713 if (error)
714 return (error);
715
716 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
717
718 mask = (1 << si->bits) - 1;
719
720 switch (cp->type) {
721 case AUDIO_MIXER_ENUM:
722 cp->un.ord = (val >> si->ofs) & mask;
723 DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
724 break;
725 case AUDIO_MIXER_VALUE:
726 {
727 struct audio_mixer_value *value = si->info;
728 u_int16_t l, r;
729
730 if ((cp->un.value.num_channels <= 0) ||
731 (cp->un.value.num_channels > value->num_channels))
732 return (EINVAL);
733
734 if (value->num_channels == 1) {
735 l = r = (val >> si->ofs) & mask;
736 } else {
737 l = (val >> si->ofs) & mask;
738 r = (val >> (si->ofs + 8)) & mask;
739 }
740
741 l = (l << (8 - si->bits));
742 r = (r << (8 - si->bits));
743 if (!si->polarity) {
744 l = 255 - l;
745 r = 255 - r;
746 }
747
748 /* The EAP driver averages l and r for stereo
749 channels that are requested in MONO mode. Does this
750 make sense? */
751 if (cp->un.value.num_channels == 1) {
752 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
753 } else if (cp->un.value.num_channels == 2) {
754 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
755 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
756 }
757
758 break;
759 }
760 default:
761 return (EINVAL);
762 }
763
764 return (0);
765 }
766
767