2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.5.2.14 2003/03/11 15:08:30 orion Exp $
27 * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.2 2003/06/17 04:28:31 dillon Exp $
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/ac97.h>
32 #include <dev/sound/pcm/ac97_patch.h>
37 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.2 2003/06/17 04:28:31 dillon Exp $");
39 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
41 struct ac97mixtable_entry {
52 #define AC97_NAMELEN 16
59 unsigned count, caps, se, extcaps, extid, extstat, noext:1;
61 struct ac97mixtable_entry mix[32];
62 char name[AC97_NAMELEN];
67 u_int32_t id, noext:1;
72 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
73 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 },
74 [SOUND_MIXER_MONITOR] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 },
75 [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 },
76 [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 },
77 [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 },
78 [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 },
79 [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 },
80 [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 },
81 [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 },
82 [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 },
83 [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 },
84 [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 },
85 [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 },
86 [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 }
89 static struct ac97_codecid ac97codecid[] = {
90 { 0x41445303, 0, "Analog Devices AD1819", 0 },
91 { 0x41445340, 0, "Analog Devices AD1881", 0 },
92 { 0x41445348, 0, "Analog Devices AD1881A", 0 },
93 { 0x41445360, 0, "Analog Devices AD1885", 0 },
94 { 0x41445361, 0, "Analog Devices AD1886", ad1886_patch },
95 { 0x414b4d00, 1, "Asahi Kasei AK4540", 0 },
96 { 0x414b4d01, 1, "Asahi Kasei AK4542", 0 },
97 { 0x414b4d02, 1, "Asahi Kasei AK4543", 0 },
98 { 0x414c4710, 0, "Avance Logic ALC200/200P", 0 },
99 { 0x414c4720, 0, "Realtek ALC650", 0 },
100 { 0x43525900, 0, "Cirrus Logic CS4297", 0 },
101 { 0x43525903, 0, "Cirrus Logic CS4297", 0 },
102 { 0x43525913, 0, "Cirrus Logic CS4297A", 0 },
103 { 0x43525914, 0, "Cirrus Logic CS4297B", 0 },
104 { 0x43525923, 0, "Cirrus Logic CS4294C", 0 },
105 { 0x4352592b, 0, "Cirrus Logic CS4298C", 0 },
106 { 0x43525931, 0, "Cirrus Logic CS4299A", 0 },
107 { 0x43525933, 0, "Cirrus Logic CS4299C", 0 },
108 { 0x43525934, 0, "Cirrus Logic CS4299D/E/F/G/H", 0 },
109 { 0x43525935, 0, "Cirrus Logic CS4299K", 0 },
110 { 0x43525936, 0, "Cirrus Logic CS4299L", 0 },
111 { 0x43525941, 0, "Cirrus Logic CS4201A", 0 },
112 { 0x43525951, 0, "Cirrus Logic CS4205A", 0 },
113 { 0x43525961, 0, "Cirrus Logic CS4291A", 0 },
114 { 0x43585429, 0, "Conexant CX20468", 0 },
115 { 0x45838308, 0, "ESS Technology ES1921", 0 },
116 { 0x49434511, 0, "ICEnsemble ICE1232", 0 },
117 { 0x4e534331, 0, "National Semiconductor LM4549", 0 },
118 { 0x83847600, 0, "SigmaTel STAC9700/9783/9784", 0 },
119 { 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705", 0 },
120 { 0x83847605, 0, "SigmaTel STAC9704", 0 },
121 { 0x83847608, 0, "SigmaTel STAC9708/9711", 0 },
122 { 0x83847609, 0, "SigmaTel STAC9721/9723", 0 },
123 { 0x83847644, 0, "SigmaTel STAC9744", 0 },
124 { 0x83847656, 0, "SigmaTel STAC9756/9757", 0 },
125 { 0x53494c22, 0, "Silicon Laboratory Si3036", 0 },
126 { 0x53494c23, 0, "Silicon Laboratory Si3038", 0 },
127 { 0x54524103, 0, "TriTech TR28023", 0 },
128 { 0x54524106, 0, "TriTech TR28026", 0 },
129 { 0x54524108, 0, "TriTech TR28028", 0 },
130 { 0x54524123, 0, "TriTech TR28602", 0 },
131 { 0x574d4c00, 0, "Wolfson WM9701A", 0 },
132 { 0x574d4c03, 0, "Wolfson WM9703/9704", 0 },
133 { 0x574d4c04, 0, "Wolfson WM9704 (quad)", 0 },
137 static char *ac97enhancement[] = {
138 "no 3D Stereo Enhancement",
139 "Analog Devices Phat Stereo",
140 "Creative Stereo Enhancement",
141 "National Semi 3D Stereo Enhancement",
143 "BBE 3D Stereo Enhancement",
144 "Crystal Semi 3D Stereo Enhancement",
146 "Spatializer 3D Stereo Enhancement",
147 "SRS 3D Stereo Enhancement",
148 "Platform Tech 3D Stereo Enhancement",
150 "Aureal Stereo Enhancement",
151 "Aztech 3D Enhancement",
152 "Binaura 3D Audio Enhancement",
153 "ESS Technology Stereo Enhancement",
154 "Harman International VMAx",
155 "Nvidea 3D Stereo Enhancement",
156 "Philips Incredible Sound",
157 "Texas Instruments 3D Stereo Enhancement",
158 "VLSI Technology 3D Stereo Enhancement",
159 "TriTech 3D Stereo Enhancement",
160 "Realtek 3D Stereo Enhancement",
161 "Samsung 3D Stereo Enhancement",
162 "Wolfson Microelectronics 3D Enhancement",
163 "Delta Integration 3D Enhancement",
164 "SigmaTel 3D Enhancement",
166 "Rockwell 3D Stereo Enhancement",
172 static char *ac97feature[] = {
185 static char *ac97extfeature[] = {
203 ac97_rdcd(struct ac97_info *codec, int reg)
205 return AC97_READ(codec->methods, codec->devinfo, reg);
209 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
211 AC97_WRITE(codec->methods, codec->devinfo, reg, val);
215 ac97_reset(struct ac97_info *codec)
218 ac97_wrcd(codec, AC97_REG_RESET, 0);
219 for (i = 0; i < 500; i++) {
220 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
221 if (ps == AC97_POWER_STATUS)
225 device_printf(codec->dev, "AC97 reset timed out.\n");
229 ac97_setrate(struct ac97_info *codec, int which, int rate)
234 case AC97_REGEXT_FDACRATE:
235 case AC97_REGEXT_SDACRATE:
236 case AC97_REGEXT_LDACRATE:
237 case AC97_REGEXT_LADCRATE:
238 case AC97_REGEXT_MADCRATE:
245 snd_mtxlock(codec->lock);
248 if (codec->extstat & AC97_EXTCAP_DRA)
250 ac97_wrcd(codec, which, v);
252 v = ac97_rdcd(codec, which);
253 if (codec->extstat & AC97_EXTCAP_DRA)
255 snd_mtxunlock(codec->lock);
260 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
262 mode &= AC97_EXTCAPS;
263 if ((mode & ~codec->extcaps) != 0) {
264 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
268 snd_mtxlock(codec->lock);
269 ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
270 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
271 snd_mtxunlock(codec->lock);
272 return (mode == codec->extstat)? 0 : -1;
276 ac97_getextmode(struct ac97_info *codec)
278 return codec->extstat;
282 ac97_getextcaps(struct ac97_info *codec)
284 return codec->extcaps;
288 ac97_getcaps(struct ac97_info *codec)
294 ac97_setrecsrc(struct ac97_info *codec, int channel)
296 struct ac97mixtable_entry *e = &codec->mix[channel];
299 int val = e->recidx - 1;
301 snd_mtxlock(codec->lock);
302 ac97_wrcd(codec, AC97_REG_RECSEL, val);
303 snd_mtxunlock(codec->lock);
310 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
312 struct ac97mixtable_entry *e = &codec->mix[channel];
314 if (e->reg && e->enable && e->bits) {
315 int max, val, reg = (e->reg >= 0)? e->reg : -e->reg;
324 max = (1 << e->bits) - 1;
325 left = (left * max) / 100;
326 right = (right * max) / 100;
328 val = (left << 8) | right;
330 left = (left * 100) / max;
331 right = (right * 100) / max;
342 int cur = ac97_rdcd(codec, e->reg);
343 val |= cur & ~(max << e->ofs);
346 if (left == 0 && right == 0 && e->mute == 1)
348 snd_mtxlock(codec->lock);
349 ac97_wrcd(codec, reg, val);
350 snd_mtxunlock(codec->lock);
351 return left | (right << 8);
353 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
360 ac97_getmixer(struct ac97_info *codec, int channel)
362 struct ac97mixtable_entry *e = &codec->mix[channel];
363 if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) {
364 int max, val, volume;
366 max = (1 << e->bits) - 1;
367 val = ac97_rdcd(code, e->reg);
368 if (val == AC97_MUTE && e->mute == 1)
371 if (e->stereo == 0) val >>= e->ofs;
373 volume = (val * 100) / max;
374 if (e->reg > 0) volume = 100 - volume;
383 ac97_fix_auxout(struct ac97_info *codec)
385 /* Determine what AUXOUT really means, it can be:
389 * 3. True line level out (effectively master volume).
391 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
393 if (codec->caps & AC97_CAP_HEADPHONE) {
394 /* XXX We should probably check the AUX_OUT initial value.
395 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */
397 } else if (codec->extcaps & AC97_EXTCAP_SDAC &&
398 ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
399 /* 4-Channel Out, add an additional gain setting. */
400 codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
402 /* Master volume is/maybe fixed in h/w, not sufficiently
403 * clear in spec to blat SOUND_MIXER_MASTER. */
404 codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR];
406 /* Blat monitor, inappropriate label if we get here */
407 bzero(&codec->mix[SOUND_MIXER_MONITOR],
408 sizeof(codec->mix[SOUND_MIXER_MONITOR]));
412 ac97_initmixer(struct ac97_info *codec)
414 ac97_patch codec_patch;
415 unsigned i, j, k, old;
418 snd_mtxlock(codec->lock);
419 codec->count = AC97_INIT(codec->methods, codec->devinfo);
420 if (codec->count == 0) {
421 device_printf(codec->dev, "ac97 codec init failed\n");
422 snd_mtxunlock(codec->lock);
426 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
428 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
430 i = ac97_rdcd(codec, AC97_REG_RESET);
431 codec->caps = i & 0x03ff;
432 codec->se = (i & 0x7c00) >> 10;
434 id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
435 codec->rev = id & 0x000000ff;
436 if (id == 0 || id == 0xffffffff) {
437 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
438 snd_mtxunlock(codec->lock);
445 for (i = 0; ac97codecid[i].id; i++) {
446 if (ac97codecid[i].id == id) {
447 codec->id = ac97codecid[i].name;
448 codec->noext = ac97codecid[i].noext;
449 codec_patch = ac97codecid[i].patch;
457 i = ac97_rdcd(codec, AC97_REGEXT_ID);
459 codec->extcaps = i & 0x3fff;
460 codec->extid = (i & 0xc000) >> 14;
461 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
465 for (i = 0; i < 32; i++) {
466 codec->mix[i] = ac97mixtable_default[i];
468 ac97_fix_auxout(codec);
472 for (i = 0; i < 32; i++) {
473 k = codec->noext? codec->mix[i].enable : 1;
474 if (k && (codec->mix[i].reg > 0)) {
475 old = ac97_rdcd(codec, codec->mix[i].reg);
476 ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
477 j = ac97_rdcd(codec, codec->mix[i].reg);
478 ac97_wrcd(codec, codec->mix[i].reg, old);
479 codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
480 for (k = 1; j & (1 << k); k++);
481 codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
483 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
487 device_printf(codec->dev, "<%s ac97 codec>\n", codec->id);
489 device_printf(codec->dev,
490 "<unknown ac97 codec> (id=0x%08x)\n", id);
494 device_printf(codec->dev, "ac97 codec features ");
495 for (i = j = 0; i < 10; i++)
496 if (codec->caps & (1 << i))
497 printf("%s%s", j++? ", " : "", ac97feature[i]);
498 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
499 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
501 if (codec->extcaps != 0 || codec->extid) {
502 device_printf(codec->dev, "ac97 %s codec",
503 codec->extid? "secondary" : "primary");
505 printf(" extended features ");
506 for (i = j = 0; i < 14; i++)
507 if (codec->extcaps & (1 << i))
508 printf("%s%s", j++? ", " : "", ac97extfeature[i]);
513 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
514 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
515 snd_mtxunlock(codec->lock);
520 ac97_reinitmixer(struct ac97_info *codec)
522 snd_mtxlock(codec->lock);
523 codec->count = AC97_INIT(codec->methods, codec->devinfo);
524 if (codec->count == 0) {
525 device_printf(codec->dev, "ac97 codec init failed\n");
526 snd_mtxunlock(codec->lock);
530 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
532 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
535 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
536 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
538 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
540 ac97_rdcd(codec, AC97_REGEXT_STAT) &
544 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
545 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
546 snd_mtxunlock(codec->lock);
551 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
553 struct ac97_info *codec;
555 codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
559 snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
560 codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
561 codec->methods = kobj_create(cls, M_AC97, 0);
562 if (codec->methods == NULL) {
563 snd_mtxlock(codec->lock);
564 snd_mtxfree(codec->lock);
570 codec->devinfo = devinfo;
576 ac97_destroy(struct ac97_info *codec)
578 snd_mtxlock(codec->lock);
579 if (codec->methods != NULL)
580 kobj_delete(codec->methods, M_AC97);
581 snd_mtxfree(codec->lock);
586 ac97_setflags(struct ac97_info *codec, u_int32_t val)
592 ac97_getflags(struct ac97_info *codec)
597 /* -------------------------------------------------------------------- */
600 ac97mix_init(struct snd_mixer *m)
602 struct ac97_info *codec = mix_getdevinfo(m);
608 if (ac97_initmixer(codec))
612 for (i = 0; i < 32; i++)
613 mask |= codec->mix[i].enable? 1 << i : 0;
614 mix_setdevs(m, mask);
617 for (i = 0; i < 32; i++)
618 mask |= codec->mix[i].recidx? 1 << i : 0;
619 mix_setrecdevs(m, mask);
624 ac97mix_uninit(struct snd_mixer *m)
626 struct ac97_info *codec = mix_getdevinfo(m);
631 if (ac97_uninitmixer(codec))
639 ac97mix_reinit(struct snd_mixer *m)
641 struct ac97_info *codec = mix_getdevinfo(m);
645 return ac97_reinitmixer(codec);
649 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
651 struct ac97_info *codec = mix_getdevinfo(m);
655 return ac97_setmixer(codec, dev, left, right);
659 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
662 struct ac97_info *codec = mix_getdevinfo(m);
666 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
667 if ((src & (1 << i)) != 0)
669 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
672 static kobj_method_t ac97mixer_methods[] = {
673 KOBJMETHOD(mixer_init, ac97mix_init),
674 KOBJMETHOD(mixer_uninit, ac97mix_uninit),
675 KOBJMETHOD(mixer_reinit, ac97mix_reinit),
676 KOBJMETHOD(mixer_set, ac97mix_set),
677 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc),
680 MIXER_DECLARE(ac97mixer);
682 /* -------------------------------------------------------------------- */
685 ac97_getmixerclass(void)
687 return &ac97mixer_class;