ac97.c revision 1.2 1 /* $NetBSD: ac97.c,v 1.2 1999/11/02 05:41:27 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 #include <dev/ic/ac97.h>
71
72 #define AC97_REG_RESET 0x00
73 #define AC97_SOUND_ENHANCEMENT(reg) (((reg) >> 10) & 0x1f)
74 #define AC97_REG_MASTER_VOLUME 0x02
75 #define AC97_REG_HEADPHONE_VOLUME 0x04
76 #define AC97_REG_MASTER_VOLUME_MONO 0x06
77 #define AC97_REG_MASTER_TONE 0x08
78 #define AC97_REG_PCBEEP_VOLUME 0x0a
79 #define AC97_REG_PHONE_VOLUME 0x0c
80 #define AC97_REG_MIC_VOLUME 0x0e
81 #define AC97_REG_LINEIN_VOLUME 0x10
82 #define AC97_REG_CD_VOLUME 0x12
83 #define AC97_REG_VIDEO_VOLUME 0x14
84 #define AC97_REG_AUX_VOLUME 0x16
85 #define AC97_REG_PCMOUT_VOLUME 0x18
86 #define AC97_REG_RECORD_SELECT 0x1a
87 #define AC97_REG_RECORD_GAIN 0x1c
88 #define AC97_REG_RECORD_GAIN_MIC 0x1e
89 #define AC97_REG_GP 0x20
90 #define AC97_REG_3D_CONTROL 0x22
91 #define AC97_REG_POWER 0x26
92 #define AC97_REG_VENDOR_ID1 0x7c
93 #define AC97_REG_VENDOR_ID2 0x7e
94
95 static struct audio_mixer_enum ac97_on_off = { 2,
96 { { { AudioNoff } , 0 },
97 { { AudioNon } , 1 } }};
98
99
100 static struct audio_mixer_enum ac97_mic_select = { 2,
101 { { { AudioNmicrophone "0" },
102 0 },
103 { { AudioNmicrophone "1" },
104 1 } }};
105
106 static struct audio_mixer_enum ac97_mono_select = { 2,
107 { { { AudioNmixerout },
108 0 },
109 { { AudioNmicrophone },
110 1 } }};
111
112 static struct audio_mixer_enum ac97_source = { 8,
113 { { { AudioNmicrophone } , 0 },
114 { { AudioNcd }, 1 },
115 { { "video" }, 2 },
116 { { AudioNaux }, 3 },
117 { { AudioNline }, 4 },
118 { { AudioNmixerout }, 5 },
119 { { AudioNmixerout AudioNmono }, 6 },
120 { { "phone" }, 7 }}};
121
122 static struct audio_mixer_value ac97_volume_stereo = { { AudioNvolume },
123 2 };
124
125
126 static struct audio_mixer_value ac97_volume_mono = { { AudioNvolume },
127 1 };
128
129 #define WRAP(a) &a, sizeof(a)
130
131 struct ac97_source_info {
132 char *class;
133 char *device;
134 char *qualifier;
135 int type;
136
137 void *info;
138 int info_size;
139
140 u_int8_t reg;
141 u_int8_t bits:3;
142 u_int8_t ofs:4;
143 u_int8_t mute:1;
144 u_int8_t polarity:1; /* Does 0 == MAX or MIN */
145
146 int prev;
147 int next;
148 int mixer_class;
149 } source_info[] = {
150 { AudioCinputs , NULL, NULL, AUDIO_MIXER_CLASS,
151 },
152 { AudioCoutputs, NULL, NULL, AUDIO_MIXER_CLASS,
153 },
154 { AudioCrecord , NULL, NULL, AUDIO_MIXER_CLASS,
155 },
156 /* Stereo master volume*/
157 { AudioCoutputs, AudioNmaster, NULL, AUDIO_MIXER_VALUE,
158 WRAP(ac97_volume_stereo),
159 AC97_REG_MASTER_VOLUME, 5, 0, 1,
160 },
161 /* Mono volume */
162 { AudioCoutputs, AudioNmono, NULL, AUDIO_MIXER_VALUE,
163 WRAP(ac97_volume_mono),
164 AC97_REG_MASTER_VOLUME_MONO, 6, 0, 1,
165 },
166 { AudioCoutputs, AudioNmono,AudioNsource, AUDIO_MIXER_ENUM,
167 WRAP(ac97_mono_select),
168 AC97_REG_GP, 1, 9, 0,
169 },
170 /* Headphone volume */
171 { AudioCoutputs, AudioNheadphone, NULL, AUDIO_MIXER_VALUE,
172 WRAP(ac97_volume_stereo),
173 AC97_REG_HEADPHONE_VOLUME, 6, 0, 1,
174 },
175 /* Tone */
176 { AudioCoutputs, "tone", NULL, AUDIO_MIXER_VALUE,
177 WRAP(ac97_volume_stereo),
178 AC97_REG_MASTER_TONE, 4, 0, 0,
179 },
180 /* PC Beep Volume */
181 { AudioCinputs, AudioNspeaker, NULL, AUDIO_MIXER_VALUE,
182 WRAP(ac97_volume_mono),
183 AC97_REG_PCBEEP_VOLUME, 4, 1, 1,
184 },
185 /* Phone */
186 { AudioCinputs, "phone", NULL, AUDIO_MIXER_VALUE,
187 WRAP(ac97_volume_mono),
188 AC97_REG_PHONE_VOLUME, 5, 0, 1,
189 },
190 /* Mic Volume */
191 { AudioCinputs, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
192 WRAP(ac97_volume_mono),
193 AC97_REG_MIC_VOLUME, 5, 0, 1,
194 },
195 { AudioCinputs, AudioNmicrophone, AudioNpreamp, AUDIO_MIXER_ENUM,
196 WRAP(ac97_on_off),
197 AC97_REG_MIC_VOLUME, 1, 6, 0,
198 },
199 { AudioCinputs, AudioNmicrophone, AudioNsource, AUDIO_MIXER_ENUM,
200 WRAP(ac97_mic_select),
201 AC97_REG_GP, 1, 8, 0,
202 },
203 /* Line in Volume */
204 { AudioCinputs, AudioNline, NULL, AUDIO_MIXER_VALUE,
205 WRAP(ac97_volume_stereo),
206 AC97_REG_LINEIN_VOLUME, 5, 0, 1,
207 },
208 /* CD Volume */
209 { AudioCinputs, AudioNcd, NULL, AUDIO_MIXER_VALUE,
210 WRAP(ac97_volume_stereo),
211 AC97_REG_CD_VOLUME, 5, 0, 1,
212 },
213 /* Video Volume */
214 { AudioCinputs, "video", NULL, AUDIO_MIXER_VALUE,
215 WRAP(ac97_volume_stereo),
216 AC97_REG_VIDEO_VOLUME, 5, 0, 1,
217 },
218 /* AUX volume */
219 { AudioCinputs, AudioNaux, NULL, AUDIO_MIXER_VALUE,
220 WRAP(ac97_volume_stereo),
221 AC97_REG_AUX_VOLUME, 5, 0, 1,
222 },
223 /* PCM out volume */
224 { AudioCinputs, AudioNdac, NULL, AUDIO_MIXER_VALUE,
225 WRAP(ac97_volume_stereo),
226 AC97_REG_PCMOUT_VOLUME, 5, 0, 1,
227 },
228 /* Record Source - some logic for this is hard coded - see below */
229 { AudioCrecord, AudioNsource, NULL, AUDIO_MIXER_ENUM,
230 WRAP(ac97_source),
231 AC97_REG_RECORD_SELECT, 3, 0, 0,
232 },
233 /* Record Gain */
234 { AudioCrecord, AudioNvolume, NULL, AUDIO_MIXER_VALUE,
235 WRAP(ac97_volume_stereo),
236 AC97_REG_RECORD_GAIN, 4, 0, 1,
237 },
238 /* Record Gain mic */
239 { AudioCrecord, AudioNmicrophone, NULL, AUDIO_MIXER_VALUE,
240 WRAP(ac97_volume_mono),
241 AC97_REG_RECORD_GAIN_MIC, 4, 0, 1, 1,
242 },
243 /* */
244 { AudioCoutputs, AudioNloudness, NULL, AUDIO_MIXER_ENUM,
245 WRAP(ac97_on_off),
246 AC97_REG_GP, 1, 12, 0,
247 },
248 { AudioCoutputs, AudioNspatial, NULL, AUDIO_MIXER_ENUM,
249 WRAP(ac97_on_off),
250 AC97_REG_GP, 1, 13, 0,
251 },
252 { AudioCoutputs, AudioNspatial, "center", AUDIO_MIXER_VALUE,
253 WRAP(ac97_volume_mono),
254 AC97_REG_3D_CONTROL, 4, 8, 0, 1,
255 },
256 { AudioCoutputs, AudioNspatial, "depth", AUDIO_MIXER_VALUE,
257 WRAP(ac97_volume_mono),
258 AC97_REG_3D_CONTROL, 4, 0, 0, 1,
259 },
260
261 /* Missing features: Simulated Stereo, POP, Loopback mode */
262 } ;
263
264 #define SOURCE_INFO_SIZE (sizeof(source_info)/sizeof(source_info[0]))
265
266 /*
267 * Check out http://developer.intel.com/pc-supp/platform/ac97/ for
268 * information on AC-97
269 */
270
271 struct ac97_softc {
272 struct ac97_codec_if codecIf;
273
274 struct ac97_host_if *hostIf;
275
276 struct ac97_source_info source_info[2 * SOURCE_INFO_SIZE];
277 int num_source_info;
278 };
279
280 int ac97_mixer_get_port __P((struct ac97_codec_if *self, mixer_ctrl_t *cp));
281 int ac97_mixer_set_port __P((struct ac97_codec_if *self, mixer_ctrl_t *));
282 int ac97_query_devinfo __P((struct ac97_codec_if *self, mixer_devinfo_t *));
283 int ac97_get_portnum_by_name __P((struct ac97_codec_if *, char *, char *,
284 char *));
285
286 struct ac97_codec_if_vtbl ac97civ = {
287 ac97_mixer_get_port,
288 ac97_mixer_set_port,
289 ac97_query_devinfo,
290 ac97_get_portnum_by_name
291 };
292
293 static struct ac97_codecid {
294 u_int32_t id;
295 char *name;
296 } ac97codecid[] = {
297 { 0x414B4D00, "Asahi Kasei AK4540" },
298 { 0x43525900, "Cirrus Logic CS4297" },
299 { 0x83847600, "SigmaTel STAC????" },
300 { 0x83847604, "SigmaTel STAC9701/3/4/5" },
301 { 0x83847605, "SigmaTel STAC9704" },
302 { 0x83847608, "SigmaTel STAC9708" },
303 { 0x83847609, "SigmaTel STAC9721" },
304 { 0, NULL }
305 };
306
307 static char *ac97enhancement[] = {
308 "no 3D stereo",
309 "Analog Devices Phat Stereo",
310 "Creative"
311 "National Semi 3D",
312 "Yamaha Ymersion",
313 "BBE 3D",
314 "Crystal Semi 3D"
315 "Qsound QXpander",
316 "Spatializer 3D",
317 "SRS 3D",
318 "Platform Tech 3D",
319 "AKM 3D",
320 "Aureal",
321 "AZTECH 3D",
322 "Binaura 3D",
323 "ESS Technology",
324 "Harman International VMAx",
325 "Nvidea 3D",
326 "Philips Incredible Sound",
327 "Texas Instruments' 3D",
328 "VLSI Technology 3D",
329 "TriTech 3D",
330 "Realtek 3D",
331 "Samsung 3D",
332 "Wolfson Microelectronics 3D",
333 "Delta Integration 3D",
334 "SigmaTel 3D",
335 "Unknown 3D",
336 "Rockwell 3D",
337 "Unknown 3D",
338 "Unknown 3D",
339 "Unknown 3D",
340 };
341
342 static char *ac97feature[] = {
343 "mic channel",
344 "reserved",
345 "tone",
346 "simulated stereo",
347 "headphone",
348 "bass boost",
349 "18 bit DAC",
350 "20 bit DAC",
351 "18 bit ADC",
352 "20 bit ADC"
353 };
354
355
356 int ac97_str_equal __P((char *, char *));
357 void ac97_setup_source_info __P((struct ac97_softc *));
358
359 /* #define AC97_DEBUG 10 */
360
361 #ifdef AUDIO_DEBUG
362 #define DPRINTF(x) if (ac97debug) printf x
363 #define DPRINTFN(n,x) if (ac97debug>(n)) printf x
364 #ifdef AC97_DEBUG
365 int ac97debug = AC97_DEBUG;
366 #else
367 int ac97debug = 0;
368 #endif
369 #else
370 #define DPRINTF(x)
371 #define DPRINTFN(n,x)
372 #endif
373
374
375 int
376 ac97_str_equal(a, b)
377 char *a, *b;
378 {
379 return ((a == b) || (a && b && (!strcmp(a, b))));
380 }
381
382 void
383 ac97_setup_source_info(as)
384 struct ac97_softc *as;
385 {
386 int idx, ouridx;
387 struct ac97_source_info *si, *si2;
388
389 for (idx = 0, ouridx = 0; idx < SOURCE_INFO_SIZE; idx++) {
390 si = &as->source_info[ouridx];
391
392 bcopy(&source_info[idx], si, sizeof(*si));
393
394 switch (si->type) {
395 case AUDIO_MIXER_CLASS:
396 si->mixer_class = ouridx;
397 ouridx++;
398 break;
399 case AUDIO_MIXER_VALUE:
400 /* Todo - Test to see if it works */
401 ouridx++;
402
403 /* Add an entry for mute, if necessary */
404 if (si->mute) {
405 si = &as->source_info[ouridx];
406 bcopy(&source_info[idx], si, sizeof(*si));
407 si->qualifier = AudioNmute;
408 si->type = AUDIO_MIXER_ENUM;
409 si->info = &ac97_on_off;
410 si->info_size = sizeof(ac97_on_off);
411 si->bits = 1;
412 si->ofs = 15;
413 si->mute = 0;
414 si->polarity = 0;
415 ouridx++;
416 }
417 break;
418 case AUDIO_MIXER_ENUM:
419 /* Todo - Test to see if it works */
420 ouridx++;
421 break;
422 default:
423 printf ("ac97: shouldn't get here\n");
424 break;
425 }
426 }
427
428 as->num_source_info = ouridx;
429
430 for (idx = 0; idx < as->num_source_info; idx++) {
431 int idx2, previdx;
432
433 si = &as->source_info[idx];
434
435 /* Find mixer class */
436 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
437 si2 = &as->source_info[idx2];
438
439 if (si2->type == AUDIO_MIXER_CLASS &&
440 ac97_str_equal(si->class,
441 si2->class)) {
442 si->mixer_class = idx2;
443 }
444 }
445
446
447 /* Setup prev and next pointers */
448 if (si->prev != 0)
449 continue;
450
451 if (si->qualifier)
452 continue;
453
454 si->prev = AUDIO_MIXER_LAST;
455 previdx = idx;
456
457 for (idx2 = 0; idx2 < as->num_source_info; idx2++) {
458 if (idx2 == idx)
459 continue;
460
461 si2 = &as->source_info[idx2];
462
463 if (!si2->prev &&
464 ac97_str_equal(si->class, si2->class) &&
465 ac97_str_equal(si->device, si2->device)) {
466 as->source_info[previdx].next = idx2;
467 as->source_info[idx2].prev = previdx;
468
469 previdx = idx2;
470 }
471 }
472
473 as->source_info[previdx].next = AUDIO_MIXER_LAST;
474 }
475 }
476
477 int
478 ac97_attach(hostIf)
479 struct ac97_host_if *hostIf;
480 {
481 struct ac97_softc *as;
482 struct device *sc_dev = (struct device *)hostIf->arg;
483 int error, i, j;
484 u_int16_t id1, id2, caps;
485 u_int32_t id;
486
487 as = malloc(sizeof(struct ac97_softc), M_DEVBUF, M_WAITOK);
488
489 if (as == NULL)
490 return (ENOMEM);
491
492 as->codecIf.vtbl = &ac97civ;
493 as->hostIf = hostIf;
494
495 if ((error = hostIf->attach(hostIf->arg, &as->codecIf))) {
496 free (as, M_DEVBUF);
497 return (error);
498 }
499
500 hostIf->reset(hostIf->arg);
501 delay(1000); /* XXX shouldn't delay for so long */
502
503 hostIf->write(hostIf->arg, AC97_REG_POWER, 0);
504 hostIf->write(hostIf->arg, AC97_REG_RESET, 0);
505 delay(10000); /* XXX shouldn't delay for so long */
506
507 if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID1, &id1)))
508 return (error);
509
510 if ((error = hostIf->read(hostIf->arg, AC97_REG_VENDOR_ID2, &id2)))
511 return (error);
512
513 if ((error = hostIf->read(hostIf->arg, AC97_REG_RESET, &caps)))
514 return (error);
515
516 id = (id1 << 16) | id2;
517
518 printf("%s: ", sc_dev->dv_xname);
519
520 for (i = 0; ; i++) {
521 if (ac97codecid[i].id == id) {
522 printf("%s", ac97codecid[i].name);
523 break;
524 }
525 if (ac97codecid[i].id == 0) {
526 printf("unknown (0x%8x)", id);
527 break;
528 }
529 }
530 printf(" codec; ");
531 for (i = j = 0; i < 10; i++) {
532 if (caps & (1 << i)) {
533 printf("%s%s", j? ", " : "", ac97feature[i]);
534 j++;
535 }
536 }
537
538 printf("%s%s\n", j? ", " : "", ac97enhancement[(caps >> 10) & 0x1f]);
539
540 ac97_setup_source_info(as);
541
542 return (0);
543 }
544
545
546 int
547 ac97_query_devinfo(codec_if, dip)
548 struct ac97_codec_if *codec_if;
549 mixer_devinfo_t *dip;
550 {
551 struct ac97_softc *as = (struct ac97_softc *)codec_if;
552
553 if (dip->index < as->num_source_info) {
554 struct ac97_source_info *si = &as->source_info[dip->index];
555 char *name;
556
557 dip->type = si->type;
558 dip->mixer_class = si->mixer_class;
559 dip->prev = si->prev;
560 dip->next = si->next;
561
562 if (si->qualifier)
563 name = si->qualifier;
564 else if (si->device)
565 name = si->device;
566 else if (si->class)
567 name = si->class;
568
569 if (name)
570 strcpy(dip->label.name, name);
571
572 bcopy(si->info, &dip->un, si->info_size);
573 return (0);
574 }
575
576 return (ENXIO);
577 }
578
579
580
581 int
582 ac97_mixer_set_port(codec_if, cp)
583 struct ac97_codec_if *codec_if;
584 mixer_ctrl_t *cp;
585 {
586 struct ac97_softc *as = (struct ac97_softc *)codec_if;
587 struct ac97_source_info *si = &as->source_info[cp->dev];
588 u_int16_t mask;
589 u_int16_t val, newval;
590 int error;
591
592 if (cp->dev < 0 || cp->dev >= as->num_source_info)
593 return (EINVAL);
594
595 if (cp->type != si->type)
596 return (EINVAL);
597
598 error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
599 if (error)
600 return (error);
601
602 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
603
604 mask = (1 << si->bits) - 1;
605
606 switch (cp->type) {
607 case AUDIO_MIXER_ENUM:
608 if (cp->un.ord > mask || cp->un.ord < 0)
609 return (EINVAL);
610
611 newval = (cp->un.ord << si->ofs);
612 if (si->reg == AC97_REG_RECORD_SELECT) {
613 newval |= (newval << (8 + si->ofs));
614 mask |= (mask << 8);
615 }
616 break;
617 case AUDIO_MIXER_VALUE:
618 {
619 struct audio_mixer_value *value = si->info;
620 u_int16_t l, r;
621
622 if ((cp->un.value.num_channels <= 0) ||
623 (cp->un.value.num_channels > value->num_channels))
624 return (EINVAL);
625
626 if (cp->un.value.num_channels == 1) {
627 l = r = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
628 } else {
629 l = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
630 r = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
631 }
632
633 if (!si->polarity) {
634 l = 255 - l;
635 r = 255 - r;
636 }
637
638 l = l >> (8 - si->bits);
639 r = r >> (8 - si->bits);
640
641 newval = ((l & mask) << si->ofs);
642 if (value->num_channels == 2) {
643 newval |= ((r & mask) << (si->ofs + 8));
644 mask |= (mask << 8);
645 }
646
647 break;
648 }
649 default:
650 return (EINVAL);
651 }
652
653 mask = mask << si->ofs;
654 error = as->hostIf->write(as->hostIf->arg, si->reg, (val & ~mask) | newval);
655 if (error)
656 return (error);
657
658 return (0);
659 }
660
661 int
662 ac97_get_portnum_by_name(codec_if, class, device, qualifier)
663 struct ac97_codec_if *codec_if;
664 char *class, *device, *qualifier;
665 {
666 struct ac97_softc *as = (struct ac97_softc *)codec_if;
667 int idx;
668
669 for (idx = 0; idx < as->num_source_info; idx++) {
670 struct ac97_source_info *si = &as->source_info[idx];
671 if (ac97_str_equal(class, si->class) &&
672 ac97_str_equal(device, si->device) &&
673 ac97_str_equal(qualifier, si->qualifier))
674 return (idx);
675 }
676
677 return (-1);
678 }
679
680 int
681 ac97_mixer_get_port(codec_if, cp)
682 struct ac97_codec_if *codec_if;
683 mixer_ctrl_t *cp;
684 {
685 struct ac97_softc *as = (struct ac97_softc *)codec_if;
686 struct ac97_source_info *si = &as->source_info[cp->dev];
687 u_int16_t mask;
688 u_int16_t val;
689 int error;
690
691 if (cp->dev < 0 || cp->dev >= as->num_source_info)
692 return (EINVAL);
693
694 if (cp->type != si->type)
695 return (EINVAL);
696
697 error = as->hostIf->read(as->hostIf->arg, si->reg, &val);
698 if (error)
699 return (error);
700
701 DPRINTFN(5, ("read(%x) = %x\n", si->reg, val));
702
703 mask = (1 << si->bits) - 1;
704
705 switch (cp->type) {
706 case AUDIO_MIXER_ENUM:
707 cp->un.ord = (val >> si->ofs) & mask;
708 DPRINTFN(4, ("AUDIO_MIXER_ENUM: %x %d %x %d\n", val, si->ofs, mask, cp->un.ord));
709 break;
710 case AUDIO_MIXER_VALUE:
711 {
712 struct audio_mixer_value *value = si->info;
713 u_int16_t l, r;
714
715 if ((cp->un.value.num_channels <= 0) ||
716 (cp->un.value.num_channels > value->num_channels))
717 return (EINVAL);
718
719 if (value->num_channels == 1) {
720 l = r = (val >> si->ofs) & mask;
721 } else {
722 l = (val >> si->ofs) & mask;
723 r = (val >> (si->ofs + 8)) & mask;
724 }
725
726 l = (l << (8 - si->bits));
727 r = (r << (8 - si->bits));
728 if (!si->polarity) {
729 l = 255 - l;
730 r = 255 - r;
731 }
732
733 /* The EAP driver averages l and r for stereo
734 channels that are requested in MONO mode. Does this
735 make sense? */
736 if (cp->un.value.num_channels == 1) {
737 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = l;
738 } else if (cp->un.value.num_channels == 2) {
739 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
740 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
741 }
742
743 break;
744 }
745 default:
746 return (EINVAL);
747 }
748
749 return (0);
750 }
751
752