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