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