Remove some duplicate FreeBSD CVS IDs, move some IDs to better places.
[dragonfly.git] / sys / dev / sound / pcm / ac97.c
CommitLineData
984263bc 1/*
3b18b37b 2 * Copyright (c) 1999 Cameron Grant <cg@freebsd.org>
984263bc
MD
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.
1de703da 25 *
3b18b37b 26 * $FreeBSD: src/sys/dev/sound/pcm/ac97.c,v 1.49 2003/11/11 22:15:17 kuriyama Exp $
1b38fcac 27 * $DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.19 2004/07/16 08:13:28 asmodai Exp $
984263bc
MD
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
1b38fcac 36SND_DECLARE_FILE("$DragonFly: src/sys/dev/sound/pcm/ac97.c,v 1.19 2004/07/16 08:13:28 asmodai Exp $");
984263bc
MD
37
38MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec");
39
40struct ac97mixtable_entry {
3b18b37b
JR
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 */
984263bc
MD
50};
51
52#define AC97_NAMELEN 16
53struct ac97_info {
54 kobj_t methods;
55 device_t dev;
56 void *devinfo;
3b18b37b 57 u_int32_t id;
984263bc
MD
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];
3b18b37b
JR
62 struct mtx *lock;
63};
64
65struct ac97_vendorid {
66 u_int32_t id;
67 const char *name;
984263bc
MD
68};
69
70struct ac97_codecid {
3b18b37b
JR
71 u_int32_t id;
72 u_int8_t stepmask;
73 u_int8_t noext:1;
74 char *name;
984263bc
MD
75 ac97_patch patch;
76};
77
78static const struct ac97mixtable_entry ac97mixtable_default[32] = {
3b18b37b 79 /* [offset] reg bits of st mu re mk en */
984263bc 80 [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 },
3b18b37b 81 [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 },
984263bc
MD
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 },
3b18b37b
JR
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
984263bc
MD
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
3b18b37b
JR
100static const struct ac97_vendorid ac97vendorid[] = {
101 { 0x41445300, "Analog Devices" },
102 { 0x414b4d00, "Asahi Kasei" },
103 { 0x414c4300, "Realtek" },
bb3dbfbc 104 { 0x414c4700, "Avance Logic" }, /* Nowadays Realtek */
3b18b37b
JR
105 { 0x43525900, "Cirrus Logic" },
106 { 0x434d4900, "C-Media Electronics" },
107 { 0x43585400, "Conexant" },
a90e5575 108 { 0x44543000, "Diamond Technology" },
3b18b37b
JR
109 { 0x454d4300, "eMicro" },
110 { 0x45838300, "ESS Technology" },
a90e5575 111 { 0x48525300, "Intersil" },
3b18b37b 112 { 0x49434500, "ICEnsemble" },
a90e5575 113 { 0x49544500, "ITE, Inc." },
3b18b37b
JR
114 { 0x4e534300, "National Semiconductor" },
115 { 0x50534300, "Philips Semiconductor" },
116 { 0x83847600, "SigmaTel" },
72dd51de 117 { 0x53494c00, "Silicon Laboratories" },
3b18b37b 118 { 0x54524100, "TriTech" },
a90e5575 119 { 0x54584e00, "Texas Instruments" },
3b18b37b 120 { 0x56494100, "VIA Technologies" },
a90e5575 121 { 0x57454300, "Winbond" },
3b18b37b
JR
122 { 0x574d4c00, "Wolfson" },
123 { 0x594d4800, "Yamaha" },
124 { 0x01408300, "Creative" },
125 { 0x00000000, NULL }
126};
127
984263bc 128static struct ac97_codecid ac97codecid[] = {
3b18b37b
JR
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 },
0d13af7a
JR
143 { 0x414b4d06, 0x00, 0, "AK4544A", 0 },
144 { 0x454b4d07, 0x00, 0, "AK4545", 0 },
3b18b37b
JR
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 },
1b38fcac 150 { 0x414c4750, 0x0f, 0, "ALC250", 0 },
3b18b37b 151 { 0x414c4760, 0x0f, 0, "ALC655", 0 },
1b38fcac 152 { 0x414c4770, 0x0f, 0, "ALC203", 0 },
3b18b37b 153 { 0x414c4780, 0x0f, 0, "ALC658", 0 },
9b196f1d 154 { 0x414c4790, 0x0f, 0, "ALC850", 0 },
3b18b37b
JR
155 { 0x43525900, 0x07, 0, "CS4297", 0 },
156 { 0x43525910, 0x07, 0, "CS4297A", 0 },
157 { 0x43525920, 0x07, 0, "CS4294/98", 0 },
69e77cea 158 { 0x4352592d, 0x07, 0, "CS4294", 0 },
3b18b37b
JR
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 },
2f89ae6d 165 { 0x43585421, 0x00, 0, "HSD11246", 0 },
6712c3d2 166 { 0x43585428, 0x07, 0, "CX20468", 0 },
a90e5575 167 { 0x44543000, 0x00, 0, "DT0398", 0 },
3b18b37b
JR
168 { 0x454d4323, 0x00, 0, "EM28023", 0 },
169 { 0x454d4328, 0x00, 0, "EM28028", 0 },
170 { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */
a90e5575 171 { 0x48525300, 0x00, 0, "HMP9701", 0 },
3b18b37b
JR
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 */
a90e5575
JR
176 { 0x49544520, 0x00, 0, "ITE2226E", 0 },
177 { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */
3b18b37b
JR
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 },
8a3b02c0 182 { 0x4e534331, 0x00, 0, "LM4549", 0 },
3b18b37b
JR
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 },
0a08c3d0 205 { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 },
e3c19dc3 206 { 0x54584e20, 0x00, 0, "TLC320AD90", 0 },
3b18b37b
JR
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 },
8a88993f 212 { 0x574d4d09, 0x00, 0, "WM9709", 0 },
d24774df 213 { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */
a90e5575 214 { 0x57454301, 0x00, 0, "W83971D", 0 },
3b18b37b
JR
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 }
984263bc
MD
220};
221
222static 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
257static 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
270static 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
287u_int16_t
288ac97_rdcd(struct ac97_info *codec, int reg)
289{
290 return AC97_READ(codec->methods, codec->devinfo, reg);
291}
292
293void
294ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val)
295{
296 AC97_WRITE(codec->methods, codec->devinfo, reg, val);
297}
298
299static void
300ac97_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
313int
314ac97_setrate(struct ac97_info *codec, int which, int rate)
315{
316 u_int16_t v;
317
318 switch(which) {
3b18b37b 319 case AC97_REGEXT_FDACRATE:
984263bc
MD
320 case AC97_REGEXT_SDACRATE:
321 case AC97_REGEXT_LDACRATE:
3b18b37b 322 case AC97_REGEXT_LADCRATE:
984263bc
MD
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
344int
345ac97_setextmode(struct ac97_info *codec, u_int16_t mode)
346{
347 mode &= AC97_EXTCAPS;
348 if ((mode & ~codec->extcaps) != 0) {
3b18b37b 349 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n",
984263bc
MD
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
360u_int16_t
361ac97_getextmode(struct ac97_info *codec)
362{
363 return codec->extstat;
364}
365
366u_int16_t
367ac97_getextcaps(struct ac97_info *codec)
368{
369 return codec->extcaps;
370}
371
372u_int16_t
373ac97_getcaps(struct ac97_info *codec)
374{
375 return codec->caps;
376}
377
378static int
379ac97_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
394static int
395ac97_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) {
3b18b37b
JR
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 */
984263bc
MD
405
406 if (!e->stereo)
407 right = left;
3b18b37b
JR
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 */
984263bc
MD
415 if (e->reg > 0) {
416 left = 100 - left;
417 right = 100 - right;
418 }
419
984263bc
MD
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
3b18b37b
JR
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) {
984263bc
MD
438 val &= max;
439 val <<= e->ofs;
3b18b37b 440 mask = (max << e->ofs);
984263bc 441 }
3b18b37b
JR
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 */
984263bc 457 snd_mtxlock(codec->lock);
3b18b37b
JR
458 if (e->mask) {
459 int cur = ac97_rdcd(codec, e->reg);
460 val |= cur & ~(mask);
461 }
984263bc
MD
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
984263bc
MD
471static void
472ac97_fix_auxout(struct ac97_info *codec)
473{
3b18b37b
JR
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:
984263bc
MD
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 */
3b18b37b
JR
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
506static void
507ac97_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
518static const char*
519ac97_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);
984263bc 530 } else {
3b18b37b 531 sprintf(buf, "%s %s AC97 Codec", vname, cname);
984263bc 532 }
3b18b37b 533 return buf;
984263bc
MD
534}
535
536static unsigned
537ac97_initmixer(struct ac97_info *codec)
538{
539 ac97_patch codec_patch;
3b18b37b
JR
540 const char *cname, *vname;
541 char desc[80];
542 u_int8_t model, step;
984263bc
MD
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);
984263bc
MD
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
3b18b37b 569 codec->id = id;
984263bc 570 codec->noext = 0;
984263bc 571 codec_patch = NULL;
3b18b37b
JR
572
573 cname = NULL;
574 model = step = 0;
984263bc 575 for (i = 0; ac97codecid[i].id; i++) {
3b18b37b
JR
576 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask;
577 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) {
984263bc
MD
578 codec->noext = ac97codecid[i].noext;
579 codec_patch = ac97codecid[i].patch;
3b18b37b
JR
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;
984263bc
MD
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);
3b18b37b 611 ac97_fix_tone(codec);
984263bc
MD
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
3b18b37b
JR
629 device_printf(codec->dev, "<%s>\n",
630 ac97_hw_desc(codec->id, vname, cname, desc));
984263bc
MD
631
632 if (bootverbose) {
3b18b37b 633 device_printf(codec->dev, "Codec features ");
984263bc
MD
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) {
3b18b37b
JR
641 device_printf(codec->dev, "%s codec",
642 codec->extid? "Secondary" : "Primary");
984263bc
MD
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
658static unsigned
659ac97_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
689struct ac97_info *
690ac97_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
714void
715ac97_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
724void
725ac97_setflags(struct ac97_info *codec, u_int32_t val)
726{
727 codec->flags = val;
728}
729
730u_int32_t
731ac97_getflags(struct ac97_info *codec)
732{
733 return codec->flags;
734}
735
736/* -------------------------------------------------------------------- */
737
738static int
739ac97mix_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
762static int
763ac97mix_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
777static int
778ac97mix_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
787static int
788ac97mix_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
797static int
798ac97mix_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
811static 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};
819MIXER_DECLARE(ac97mixer);
820
821/* -------------------------------------------------------------------- */
822
823kobj_class_t
824ac97_getmixerclass(void)
825{
826 return &ac97mixer_class;
827}
828
829