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