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