Remove some duplicate FreeBSD CVS IDs, move some IDs to better places.
[dragonfly.git] / sys / dev / sound / pcm / ac97.c
1 /*
2  * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
13  *
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
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.49 2003/11/11 22:15:17 kuriyama Exp $
27  * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.19 2004/07/16 08:13:28 asmodai Exp $
28  */
29
30 #include <dev/sound/pcm/sound.h>
31 #include <dev/sound/pcm/ac97.h>
32 #include <dev/sound/pcm/ac97_patch.h>
33
34 #include "mixer_if.h"
35
36 SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.19 2004/07/16 08:13:28 asmodai Exp $");
37
38 MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
39
40 struct ac97mixtable_entry {
41         int      reg:8;         /* register index               */
42                                 /* reg < 0 if inverted polarity */
43         unsigned bits:4;        /* width of control field       */
44         unsigned ofs:4;         /* offset (only if stereo=0)    */
45         unsigned stereo:1;      /* set for stereo controls      */
46         unsigned mute:1;        /* bit15 is MUTE                */
47         unsigned recidx:4;      /* index in rec mux             */
48         unsigned mask:1;        /* use only masked bits         */
49         unsigned enable:1;      /* entry is enabled             */
50 };
51
52 #define AC97_NAMELEN    16
53 struct ac97_info {
54         kobj_t methods;
55         device_t dev;
56         void *devinfo;
57         u_int32_t id;
58         unsigned count, caps, se, extcaps, extid, extstat, noext:1;
59         u_int32_t flags;
60         struct ac97mixtable_entry mix[32];
61         char name[AC97_NAMELEN];
62         struct mtx *lock;
63 };
64
65 struct ac97_vendorid {
66         u_int32_t   id;
67         const char *name;
68 };
69
70 struct ac97_codecid {
71         u_int32_t  id;
72         u_int8_t   stepmask;
73         u_int8_t   noext:1;
74         char      *name;
75         ac97_patch patch;
76 };
77
78 static const struct ac97mixtable_entry ac97mixtable_default[32] = {
79     /*  [offset]                        reg          bits of st mu re mk en */
80         [SOUND_MIXER_VOLUME]    = { AC97_MIX_MASTER,    5, 0, 1, 1, 6, 0, 1 },
81         [SOUND_MIXER_OGAIN]     = { AC97_MIX_AUXOUT,    5, 0, 1, 1, 0, 0, 0 },
82         [SOUND_MIXER_PHONEOUT]  = { AC97_MIX_MONO,      5, 0, 0, 1, 7, 0, 0 },
83         [SOUND_MIXER_BASS]      = { AC97_MIX_TONE,      4, 8, 0, 0, 0, 1, 0 },
84         [SOUND_MIXER_TREBLE]    = { AC97_MIX_TONE,      4, 0, 0, 0, 0, 1, 0 },
85         [SOUND_MIXER_PCM]       = { AC97_MIX_PCM,       5, 0, 1, 1, 0, 0, 1 },
86         [SOUND_MIXER_SPEAKER]   = { AC97_MIX_BEEP,      4, 1, 0, 1, 0, 0, 0 },
87         [SOUND_MIXER_LINE]      = { AC97_MIX_LINE,      5, 0, 1, 1, 5, 0, 1 },
88         [SOUND_MIXER_PHONEIN]   = { AC97_MIX_PHONE,     5, 0, 0, 1, 8, 0, 0 },
89         [SOUND_MIXER_MIC]       = { AC97_MIX_MIC,       5, 0, 0, 1, 1, 1, 1 },
90 #if 0
91         /* use igain for the mic 20dB boost */
92         [SOUND_MIXER_IGAIN]     = { -AC97_MIX_MIC,      1, 6, 0, 0, 0, 1, 1 },
93 #endif
94         [SOUND_MIXER_CD]        = { AC97_MIX_CD,        5, 0, 1, 1, 2, 0, 1 },
95         [SOUND_MIXER_LINE1]     = { AC97_MIX_AUX,       5, 0, 1, 1, 4, 0, 0 },
96         [SOUND_MIXER_VIDEO]     = { AC97_MIX_VIDEO,     5, 0, 1, 1, 3, 0, 0 },
97         [SOUND_MIXER_RECLEV]    = { -AC97_MIX_RGAIN,    4, 0, 1, 1, 0, 0, 1 }
98 };
99
100 static const struct ac97_vendorid ac97vendorid[] = {
101         { 0x41445300, "Analog Devices" },
102         { 0x414b4d00, "Asahi Kasei" },
103         { 0x414c4300, "Realtek" },
104         { 0x414c4700, "Avance Logic" },         /* Nowadays Realtek */
105         { 0x43525900, "Cirrus Logic" },
106         { 0x434d4900, "C-Media Electronics" },
107         { 0x43585400, "Conexant" },
108         { 0x44543000, "Diamond Technology" },
109         { 0x454d4300, "eMicro" },
110         { 0x45838300, "ESS Technology" },
111         { 0x48525300, "Intersil" },
112         { 0x49434500, "ICEnsemble" },
113         { 0x49544500, "ITE, Inc." },
114         { 0x4e534300, "National Semiconductor" },
115         { 0x50534300, "Philips Semiconductor" },
116         { 0x83847600, "SigmaTel" },
117         { 0x53494c00, "Silicon Laboratories" },
118         { 0x54524100, "TriTech" },
119         { 0x54584e00, "Texas Instruments" },
120         { 0x56494100, "VIA Technologies" },
121         { 0x57454300, "Winbond" },
122         { 0x574d4c00, "Wolfson" },
123         { 0x594d4800, "Yamaha" },
124         { 0x01408300, "Creative" },
125         { 0x00000000, NULL }
126 };
127
128 static struct ac97_codecid ac97codecid[] = {
129         { 0x41445303, 0x00, 0, "AD1819",        0 },
130         { 0x41445340, 0x00, 0, "AD1881",        0 },
131         { 0x41445348, 0x00, 0, "AD1881A",       0 },
132         { 0x41445360, 0x00, 0, "AD1885",        0 },
133         { 0x41445361, 0x00, 0, "AD1886",        ad1886_patch },
134         { 0x41445362, 0x00, 0, "AD1887",        0 },
135         { 0x41445363, 0x00, 0, "AD1886A",       0 },
136         { 0x41445370, 0x00, 0, "AD1980",        ad198x_patch },
137         { 0x41445372, 0x00, 0, "AD1981A",       0 },
138         { 0x41445374, 0x00, 0, "AD1981B",       0 },
139         { 0x41445375, 0x00, 0, "AD1985",        ad198x_patch },
140         { 0x414b4d00, 0x00, 1, "AK4540",        0 },
141         { 0x414b4d01, 0x00, 1, "AK4542",        0 },
142         { 0x414b4d02, 0x00, 1, "AK4543",        0 },
143         { 0x414b4d06, 0x00, 0, "AK4544A",       0 },
144         { 0x454b4d07, 0x00, 0, "AK4545",        0 },
145         { 0x414c4320, 0x0f, 0, "ALC100",        0 },
146         { 0x414c4730, 0x0f, 0, "ALC101",        0 },
147         { 0x414c4710, 0x0f, 0, "ALC200",        0 },
148         { 0x414c4740, 0x0f, 0, "ALC202",        0 },
149         { 0x414c4720, 0x0f, 0, "ALC650",        0 },
150         { 0x414c4750, 0x0f, 0, "ALC250",        0 },
151         { 0x414c4760, 0x0f, 0, "ALC655",        0 },
152         { 0x414c4770, 0x0f, 0, "ALC203",        0 },
153         { 0x414c4780, 0x0f, 0, "ALC658",        0 },
154         { 0x414c4790, 0x0f, 0, "ALC850",        0 },
155         { 0x43525900, 0x07, 0, "CS4297",        0 },
156         { 0x43525910, 0x07, 0, "CS4297A",       0 },
157         { 0x43525920, 0x07, 0, "CS4294/98",     0 },
158         { 0x4352592d, 0x07, 0, "CS4294",        0 },
159         { 0x43525930, 0x07, 0, "CS4299",        0 },
160         { 0x43525940, 0x07, 0, "CS4201",        0 },
161         { 0x43525958, 0x07, 0, "CS4205",        0 },
162         { 0x43525960, 0x07, 0, "CS4291A",       0 },
163         { 0x434d4961, 0x00, 0, "CMI9739",       0 },
164         { 0x434d4941, 0x00, 0, "CMI9738",       0 },
165         { 0x43585421, 0x00, 0, "HSD11246",      0 },
166         { 0x43585428, 0x07, 0, "CX20468",       0 },
167         { 0x44543000, 0x00, 0, "DT0398",        0 },
168         { 0x454d4323, 0x00, 0, "EM28023",       0 },
169         { 0x454d4328, 0x00, 0, "EM28028",       0 },
170         { 0x45838308, 0x00, 0, "ES1988",        0 }, /* Formerly ES1921(?) */
171         { 0x48525300, 0x00, 0, "HMP9701",       0 },
172         { 0x49434501, 0x00, 0, "ICE1230",       0 },
173         { 0x49434511, 0x00, 0, "ICE1232",       0 },
174         { 0x49434514, 0x00, 0, "ICE1232A",      0 },
175         { 0x49434551, 0x03, 0, "VT1616",        0 }, /* Via badged ICE */
176         { 0x49544520, 0x00, 0, "ITE2226E",      0 },
177         { 0x49544560, 0x07, 0, "ITE2646E",      0 }, /* XXX: patch needed */
178         { 0x4e534340, 0x00, 0, "LM4540",        0 }, /* Spec blank on revid */
179         { 0x4e534343, 0x00, 0, "LM4543",        0 }, /* Ditto */
180         { 0x4e534346, 0x00, 0, "LM4546A",       0 },
181         { 0x4e534348, 0x00, 0, "LM4548A",       0 },
182         { 0x4e534331, 0x00, 0, "LM4549",        0 },
183         { 0x4e534349, 0x00, 0, "LM4549A",       0 },
184         { 0x4e534350, 0x00, 0, "LM4550",        0 },
185         { 0x50534301, 0x00, 0, "UCB1510",       0 },
186         { 0x50534304, 0x00, 0, "UCB1400",       0 },
187         { 0x83847600, 0x00, 0, "STAC9700/83/84",        0 },
188         { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 },
189         { 0x83847605, 0x00, 0, "STAC9704",      0 },
190         { 0x83847608, 0x00, 0, "STAC9708/11",   0 },
191         { 0x83847609, 0x00, 0, "STAC9721/23",   0 },
192         { 0x83847644, 0x00, 0, "STAC9744/45",   0 },
193         { 0x83847650, 0x00, 0, "STAC9750/51",   0 },
194         { 0x83847652, 0x00, 0, "STAC9752/53",   0 },
195         { 0x83847656, 0x00, 0, "STAC9756/57",   0 },
196         { 0x83847658, 0x00, 0, "STAC9758/59",   0 },
197         { 0x83847660, 0x00, 0, "STAC9760/61",   0 }, /* Extrapolated */
198         { 0x83847662, 0x00, 0, "STAC9762/63",   0 }, /* Extrapolated */
199         { 0x53494c22, 0x00, 0, "Si3036",        0 },
200         { 0x53494c23, 0x00, 0, "Si3038",        0 },
201         { 0x54524103, 0x00, 0, "TR28023",       0 }, /* Extrapolated */
202         { 0x54524106, 0x00, 0, "TR28026",       0 },
203         { 0x54524108, 0x00, 0, "TR28028",       0 },
204         { 0x54524123, 0x00, 0, "TR28602",       0 },
205         { 0x54524e03, 0x07, 0, "TLV320AIC27",   0 },
206         { 0x54584e20, 0x00, 0, "TLC320AD90",    0 },
207         { 0x56494161, 0x00, 0, "VIA1612A",      0 },
208         { 0x574d4c00, 0x00, 0, "WM9701A",       0 },
209         { 0x574d4c03, 0x00, 0, "WM9703/4/7/8",  0 },
210         { 0x574d4c04, 0x00, 0, "WM9704Q",       0 },
211         { 0x574d4c05, 0x00, 0, "WM9705/10",     0 },
212         { 0x574d4d09, 0x00, 0, "WM9709",        0 },
213         { 0x574d4c12, 0x00, 0, "WM9711/12",     0 }, /* XXX: patch needed */
214         { 0x57454301, 0x00, 0, "W83971D",       0 },
215         { 0x594d4800, 0x00, 0, "YMF743",        0 },
216         { 0x594d4802, 0x00, 0, "YMF752",        0 },
217         { 0x594d4803, 0x00, 0, "YMF753",        0 },
218         { 0x01408384, 0x00, 0, "EV1938",        0 },
219         { 0, 0, 0, NULL, 0 }
220 };
221
222 static char *ac97enhancement[] = {
223         "no 3D Stereo Enhancement",
224         "Analog Devices Phat Stereo",
225         "Creative Stereo Enhancement",
226         "National Semi 3D Stereo Enhancement",
227         "Yamaha Ymersion",
228         "BBE 3D Stereo Enhancement",
229         "Crystal Semi 3D Stereo Enhancement",
230         "Qsound QXpander",
231         "Spatializer 3D Stereo Enhancement",
232         "SRS 3D Stereo Enhancement",
233         "Platform Tech 3D Stereo Enhancement",
234         "AKM 3D Audio",
235         "Aureal Stereo Enhancement",
236         "Aztech 3D Enhancement",
237         "Binaura 3D Audio Enhancement",
238         "ESS Technology Stereo Enhancement",
239         "Harman International VMAx",
240         "Nvidea 3D Stereo Enhancement",
241         "Philips Incredible Sound",
242         "Texas Instruments 3D Stereo Enhancement",
243         "VLSI Technology 3D Stereo Enhancement",
244         "TriTech 3D Stereo Enhancement",
245         "Realtek 3D Stereo Enhancement",
246         "Samsung 3D Stereo Enhancement",
247         "Wolfson Microelectronics 3D Enhancement",
248         "Delta Integration 3D Enhancement",
249         "SigmaTel 3D Enhancement",
250         "Reserved 27",
251         "Rockwell 3D Stereo Enhancement",
252         "Reserved 29",
253         "Reserved 30",
254         "Reserved 31"
255 };
256
257 static char *ac97feature[] = {
258         "mic channel",
259         "reserved",
260         "tone",
261         "simulated stereo",
262         "headphone",
263         "bass boost",
264         "18 bit DAC",
265         "20 bit DAC",
266         "18 bit ADC",
267         "20 bit ADC"
268 };
269
270 static char *ac97extfeature[] = {
271         "variable rate PCM",
272         "double rate PCM",
273         "reserved 1",
274         "variable rate mic",
275         "reserved 2",
276         "reserved 3",
277         "center DAC",
278         "surround DAC",
279         "LFE DAC",
280         "AMAP",
281         "reserved 4",
282         "reserved 5",
283         "reserved 6",
284         "reserved 7",
285 };
286
287 u_int16_t
288 ac97_rdcd(struct ac97_info *codec, int reg)
289 {
290         return AC97_READ(codec->methods, codec->devinfo, reg);
291 }
292
293 void
294 ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
295 {
296         AC97_WRITE(codec->methods, codec->devinfo, reg, val);
297 }
298
299 static void
300 ac97_reset(struct ac97_info *codec)
301 {
302         u_int32_t i, ps;
303         ac97_wrcd(codec, AC97_REG_RESET, 0);
304         for (i = 0; i < 500; i++) {
305                 ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS;
306                 if (ps == AC97_POWER_STATUS)
307                         return;
308                 DELAY(1000);
309         }
310         device_printf(codec->dev, "AC97 reset timed out.\n");
311 }
312
313 int
314 ac97_setrate(struct ac97_info *codec, int which, int rate)
315 {
316         u_int16_t v;
317
318         switch(which) {
319         case AC97_REGEXT_FDACRATE:
320         case AC97_REGEXT_SDACRATE:
321         case AC97_REGEXT_LDACRATE:
322         case AC97_REGEXT_LADCRATE:
323         case AC97_REGEXT_MADCRATE:
324                 break;
325
326         default:
327                 return -1;
328         }
329
330         snd_mtxlock(codec->lock);
331         if (rate != 0) {
332                 v = rate;
333                 if (codec->extstat & AC97_EXTCAP_DRA)
334                         v >>= 1;
335                 ac97_wrcd(codec, which, v);
336         }
337         v = ac97_rdcd(codec, which);
338         if (codec->extstat & AC97_EXTCAP_DRA)
339                 v <<= 1;
340         snd_mtxunlock(codec->lock);
341         return v;
342 }
343
344 int
345 ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
346 {
347         mode &= AC97_EXTCAPS;
348         if ((mode & ~codec->extcaps) != 0) {
349                 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
350                               mode);
351                 return -1;
352         }
353         snd_mtxlock(codec->lock);
354         ac97_wrcd(codec, AC97_REGEXT_STAT, mode);
355         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
356         snd_mtxunlock(codec->lock);
357         return (mode == codec->extstat)? 0 : -1;
358 }
359
360 u_int16_t
361 ac97_getextmode(struct ac97_info *codec)
362 {
363         return codec->extstat;
364 }
365
366 u_int16_t
367 ac97_getextcaps(struct ac97_info *codec)
368 {
369         return codec->extcaps;
370 }
371
372 u_int16_t
373 ac97_getcaps(struct ac97_info *codec)
374 {
375         return codec->caps;
376 }
377
378 static int
379 ac97_setrecsrc(struct ac97_info *codec, int channel)
380 {
381         struct ac97mixtable_entry *e = &codec->mix[channel];
382
383         if (e->recidx > 0) {
384                 int val = e->recidx - 1;
385                 val |= val << 8;
386                 snd_mtxlock(codec->lock);
387                 ac97_wrcd(codec, AC97_REG_RECSEL, val);
388                 snd_mtxunlock(codec->lock);
389                 return 0;
390         } else
391                 return -1;
392 }
393
394 static int
395 ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right)
396 {
397         struct ac97mixtable_entry *e = &codec->mix[channel];
398
399         if (e->reg && e->enable && e->bits) {
400                 int mask, max, val, reg;
401
402                 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register    */
403                 max = (1 << e->bits) - 1;               /* actual range     */
404                 mask = (max << 8) | max;                /* bits of interest */
405
406                 if (!e->stereo)
407                         right = left;
408
409                 /*
410                  * Invert the range if the polarity requires so,
411                  * then scale to 0..max-1 to compute the value to
412                  * write into the codec, and scale back to 0..100
413                  * for the return value.
414                  */
415                 if (e->reg > 0) {
416                         left = 100 - left;
417                         right = 100 - right;
418                 }
419
420                 left = (left * max) / 100;
421                 right = (right * max) / 100;
422
423                 val = (left << 8) | right;
424
425                 left = (left * 100) / max;
426                 right = (right * 100) / max;
427
428                 if (e->reg > 0) {
429                         left = 100 - left;
430                         right = 100 - right;
431                 }
432
433                 /*
434                  * For mono controls, trim val and mask, also taking
435                  * care of e->ofs (offset of control field).
436                  */
437                 if (e->ofs) {
438                         val &= max;
439                         val <<= e->ofs;
440                         mask = (max << e->ofs);
441                 }
442
443                 /*
444                  * If we have a mute bit, add it to the mask and
445                  * update val and set mute if both channels require a
446                  * zero volume.
447                  */
448                 if (e->mute == 1) {
449                         mask |= AC97_MUTE;
450                         if (left == 0 && right == 0)
451                                 val = AC97_MUTE;
452                 }
453
454                 /*
455                  * If the mask bit is set, do not alter the other bits.
456                  */
457                 snd_mtxlock(codec->lock);
458                 if (e->mask) {
459                         int cur = ac97_rdcd(codec, e->reg);
460                         val |= cur & ~(mask);
461                 }
462                 ac97_wrcd(codec, reg, val);
463                 snd_mtxunlock(codec->lock);
464                 return left | (right << 8);
465         } else {
466                 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */
467                 return -1;
468         }
469 }
470
471 static void
472 ac97_fix_auxout(struct ac97_info *codec)
473 {
474         int keep_ogain;
475
476         /*
477          * By default, The ac97 aux_out register (0x04) corresponds to OSS's
478          * OGAIN setting.
479          *
480          * We first check whether aux_out is a valid register.  If not
481          * we may not want to keep ogain.
482          */
483         keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000;
484
485         /*
486          * Determine what AUX_OUT really means, it can be:
487          *
488          * 1. Headphone out.
489          * 2. 4-Channel Out
490          * 3. True line level out (effectively master volume).
491          *
492          * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}.
493          */
494         if (codec->extcaps & AC97_EXTCAP_SDAC &&
495             ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) {
496                 codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND;
497                 keep_ogain = 1;
498         }
499
500         if (keep_ogain == 0) {
501                 bzero(&codec->mix[SOUND_MIXER_OGAIN],
502                       sizeof(codec->mix[SOUND_MIXER_OGAIN]));
503         }
504 }
505
506 static void
507 ac97_fix_tone(struct ac97_info *codec)
508 {
509         /* Hide treble and bass if they don't exist */
510         if ((codec->caps & AC97_CAP_TONE) == 0) {
511                 bzero(&codec->mix[SOUND_MIXER_BASS],
512                       sizeof(codec->mix[SOUND_MIXER_BASS]));
513                 bzero(&codec->mix[SOUND_MIXER_TREBLE],
514                       sizeof(codec->mix[SOUND_MIXER_TREBLE]));
515         }
516 }
517
518 static const char*
519 ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf)
520 {
521         if (cname == NULL) {
522                 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id);
523                 return buf;
524         }
525
526         if (vname == NULL) vname = "Unknown";
527
528         if (bootverbose) {
529                 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id);
530         } else {
531                 sprintf(buf, "%s %s AC97 Codec", vname, cname);
532         }
533         return buf;
534 }
535
536 static unsigned
537 ac97_initmixer(struct ac97_info *codec)
538 {
539         ac97_patch codec_patch;
540         const char *cname, *vname;
541         char desc[80];
542         u_int8_t model, step;
543         unsigned i, j, k, old;
544         u_int32_t id;
545
546         snd_mtxlock(codec->lock);
547         codec->count = AC97_INIT(codec->methods, codec->devinfo);
548         if (codec->count == 0) {
549                 device_printf(codec->dev, "ac97 codec init failed\n");
550                 snd_mtxunlock(codec->lock);
551                 return ENODEV;
552         }
553
554         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
555         ac97_reset(codec);
556         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
557
558         i = ac97_rdcd(codec, AC97_REG_RESET);
559         codec->caps = i & 0x03ff;
560         codec->se =  (i & 0x7c00) >> 10;
561
562         id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2);
563         if (id == 0 || id == 0xffffffff) {
564                 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id);
565                 snd_mtxunlock(codec->lock);
566                 return ENODEV;
567         }
568
569         codec->id = id;
570         codec->noext = 0;
571         codec_patch = NULL;
572
573         cname = NULL;
574         model = step = 0;
575         for (i = 0; ac97codecid[i].id; i++) {
576                 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
577                 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
578                         codec->noext = ac97codecid[i].noext;
579                         codec_patch = ac97codecid[i].patch;
580                         cname = ac97codecid[i].name;
581                         model = (id & modelmask) & 0xff;
582                         step = (id & ~modelmask) & 0xff;
583                         break;
584                 }
585         }
586
587         vname = NULL;
588         for (i = 0; ac97vendorid[i].id; i++) {
589                 if (ac97vendorid[i].id == (id & 0xffffff00)) {
590                         vname = ac97vendorid[i].name;
591                         break;
592                 }
593         }
594
595         codec->extcaps = 0;
596         codec->extid = 0;
597         codec->extstat = 0;
598         if (!codec->noext) {
599                 i = ac97_rdcd(codec, AC97_REGEXT_ID);
600                 if (i != 0xffff) {
601                         codec->extcaps = i & 0x3fff;
602                         codec->extid =  (i & 0xc000) >> 14;
603                         codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS;
604                 }
605         }
606
607         for (i = 0; i < 32; i++) {
608                 codec->mix[i] = ac97mixtable_default[i];
609         }
610         ac97_fix_auxout(codec);
611         ac97_fix_tone(codec);
612         if (codec_patch)
613                 codec_patch(codec);
614
615         for (i = 0; i < 32; i++) {
616                 k = codec->noext? codec->mix[i].enable : 1;
617                 if (k && (codec->mix[i].reg > 0)) {
618                         old = ac97_rdcd(codec, codec->mix[i].reg);
619                         ac97_wrcd(codec, codec->mix[i].reg, 0x3f);
620                         j = ac97_rdcd(codec, codec->mix[i].reg);
621                         ac97_wrcd(codec, codec->mix[i].reg, old);
622                         codec->mix[i].enable = (j != 0 && j != old)? 1 : 0;
623                         for (k = 1; j & (1 << k); k++);
624                         codec->mix[i].bits = j? k - codec->mix[i].ofs : 0;
625                 }
626                 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */
627         }
628
629         device_printf(codec->dev, "<%s>\n",
630                       ac97_hw_desc(codec->id, vname, cname, desc));
631
632         if (bootverbose) {
633                 device_printf(codec->dev, "Codec features ");
634                 for (i = j = 0; i < 10; i++)
635                         if (codec->caps & (1 << i))
636                                 printf("%s%s", j++? ", " : "", ac97feature[i]);
637                 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits);
638                 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]);
639
640                 if (codec->extcaps != 0 || codec->extid) {
641                         device_printf(codec->dev, "%s codec",
642                                       codec->extid? "Secondary" : "Primary");
643                         if (codec->extcaps)
644                                 printf(" extended features ");
645                         for (i = j = 0; i < 14; i++)
646                                 if (codec->extcaps & (1 << i))
647                                         printf("%s%s", j++? ", " : "", ac97extfeature[i]);
648                         printf("\n");
649                 }
650         }
651
652         if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
653                 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
654         snd_mtxunlock(codec->lock);
655         return 0;
656 }
657
658 static unsigned
659 ac97_reinitmixer(struct ac97_info *codec)
660 {
661         snd_mtxlock(codec->lock);
662         codec->count = AC97_INIT(codec->methods, codec->devinfo);
663         if (codec->count == 0) {
664                 device_printf(codec->dev, "ac97 codec init failed\n");
665                 snd_mtxunlock(codec->lock);
666                 return ENODEV;
667         }
668
669         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
670         ac97_reset(codec);
671         ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000);
672
673         if (!codec->noext) {
674                 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat);
675                 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS)
676                     != codec->extstat)
677                         device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n",
678                                       codec->extstat,
679                                       ac97_rdcd(codec, AC97_REGEXT_STAT) &
680                                       AC97_EXTCAPS);
681         }
682
683         if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0)
684                 device_printf(codec->dev, "ac97 codec reports dac not ready\n");
685         snd_mtxunlock(codec->lock);
686         return 0;
687 }
688
689 struct ac97_info *
690 ac97_create(device_t dev, void *devinfo, kobj_class_t cls)
691 {
692         struct ac97_info *codec;
693
694         codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT);
695         if (codec == NULL)
696                 return NULL;
697
698         snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev));
699         codec->lock = snd_mtxcreate(codec->name, "ac97 codec");
700         codec->methods = kobj_create(cls, M_AC97, 0);
701         if (codec->methods == NULL) {
702                 snd_mtxlock(codec->lock);
703                 snd_mtxfree(codec->lock);
704                 free(codec, M_AC97);
705                 return NULL;
706         }
707
708         codec->dev = dev;
709         codec->devinfo = devinfo;
710         codec->flags = 0;
711         return codec;
712 }
713
714 void
715 ac97_destroy(struct ac97_info *codec)
716 {
717         snd_mtxlock(codec->lock);
718         if (codec->methods != NULL)
719                 kobj_delete(codec->methods, M_AC97);
720         snd_mtxfree(codec->lock);
721         free(codec, M_AC97);
722 }
723
724 void
725 ac97_setflags(struct ac97_info *codec, u_int32_t val)
726 {
727         codec->flags = val;
728 }
729
730 u_int32_t
731 ac97_getflags(struct ac97_info *codec)
732 {
733         return codec->flags;
734 }
735
736 /* -------------------------------------------------------------------- */
737
738 static int
739 ac97mix_init(struct snd_mixer *m)
740 {
741         struct ac97_info *codec = mix_getdevinfo(m);
742         u_int32_t i, mask;
743
744         if (codec == NULL)
745                 return -1;
746
747         if (ac97_initmixer(codec))
748                 return -1;
749
750         mask = 0;
751         for (i = 0; i < 32; i++)
752                 mask |= codec->mix[i].enable? 1 << i : 0;
753         mix_setdevs(m, mask);
754
755         mask = 0;
756         for (i = 0; i < 32; i++)
757                 mask |= codec->mix[i].recidx? 1 << i : 0;
758         mix_setrecdevs(m, mask);
759         return 0;
760 }
761
762 static int
763 ac97mix_uninit(struct snd_mixer *m)
764 {
765         struct ac97_info *codec = mix_getdevinfo(m);
766
767         if (codec == NULL)
768                 return -1;
769         /*
770         if (ac97_uninitmixer(codec))
771                 return -1;
772         */
773         ac97_destroy(codec);
774         return 0;
775 }
776
777 static int
778 ac97mix_reinit(struct snd_mixer *m)
779 {
780         struct ac97_info *codec = mix_getdevinfo(m);
781
782         if (codec == NULL)
783                 return -1;
784         return ac97_reinitmixer(codec);
785 }
786
787 static int
788 ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
789 {
790         struct ac97_info *codec = mix_getdevinfo(m);
791
792         if (codec == NULL)
793                 return -1;
794         return ac97_setmixer(codec, dev, left, right);
795 }
796
797 static int
798 ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src)
799 {
800         int i;
801         struct ac97_info *codec = mix_getdevinfo(m);
802
803         if (codec == NULL)
804                 return -1;
805         for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
806                 if ((src & (1 << i)) != 0)
807                         break;
808         return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1;
809 }
810
811 static kobj_method_t ac97mixer_methods[] = {
812         KOBJMETHOD(mixer_init,          ac97mix_init),
813         KOBJMETHOD(mixer_uninit,        ac97mix_uninit),
814         KOBJMETHOD(mixer_reinit,        ac97mix_reinit),
815         KOBJMETHOD(mixer_set,           ac97mix_set),
816         KOBJMETHOD(mixer_setrecsrc,     ac97mix_setrecsrc),
817         { 0, 0 }
818 };
819 MIXER_DECLARE(ac97mixer);
820
821 /* -------------------------------------------------------------------- */
822
823 kobj_class_t
824 ac97_getmixerclass(void)
825 {
826         return &ac97mixer_class;
827 }
828
829