ac97.c revision 113788
159243Sobrien/* 259243Sobrien * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 359243Sobrien * All rights reserved. 459243Sobrien * 559243Sobrien * Redistribution and use in source and binary forms, with or without 659243Sobrien * modification, are permitted provided that the following conditions 759243Sobrien * are met: 859243Sobrien * 1. Redistributions of source code must retain the above copyright 959243Sobrien * notice, this list of conditions and the following disclaimer. 1059243Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1159243Sobrien * notice, this list of conditions and the following disclaimer in the 1259243Sobrien * documentation and/or other materials provided with the distribution. 1359243Sobrien * 1459243Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1559243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1659243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1759243Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1859243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1959243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2059243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2159243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2259243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2359243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2459243Sobrien * SUCH DAMAGE. 2559243Sobrien */ 2659243Sobrien 2759243Sobrien#include <dev/sound/pcm/sound.h> 2859243Sobrien#include <dev/sound/pcm/ac97.h> 2959243Sobrien#include <dev/sound/pcm/ac97_patch.h> 3059243Sobrien 3159243Sobrien#include "mixer_if.h" 3259243Sobrien 3359243SobrienSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/ac97.c 113788 2003-04-21 04:48:40Z orion $"); 3459243Sobrien 3559243SobrienMALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 3659243Sobrien 3759243Sobrienstruct ac97mixtable_entry { 3859243Sobrien int reg:8; /* register index */ 3959243Sobrien /* reg < 0 if inverted polarity */ 4059243Sobrien unsigned bits:4; /* width of control field */ 4159243Sobrien unsigned ofs:4; /* offset (only if stereo=0) */ 4259243Sobrien unsigned stereo:1; /* set for stereo controls */ 4359243Sobrien unsigned mute:1; /* bit15 is MUTE */ 4459243Sobrien unsigned recidx:4; /* index in rec mux */ 4559243Sobrien unsigned mask:1; /* use only masked bits */ 4659243Sobrien unsigned enable:1; /* entry is enabled */ 4759243Sobrien}; 4859243Sobrien 4959243Sobrien#define AC97_NAMELEN 16 5059243Sobrienstruct ac97_info { 5159243Sobrien kobj_t methods; 5259243Sobrien device_t dev; 5359243Sobrien void *devinfo; 5459243Sobrien u_int32_t id; 5559243Sobrien unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5659243Sobrien u_int32_t flags; 5759243Sobrien struct ac97mixtable_entry mix[32]; 5859243Sobrien char name[AC97_NAMELEN]; 5959243Sobrien struct mtx *lock; 6059243Sobrien}; 6159243Sobrien 6259243Sobrienstruct ac97_vendorid { 6359243Sobrien u_int32_t id; 6459243Sobrien const char *name; 6559243Sobrien}; 6659243Sobrien 6759243Sobrienstruct ac97_codecid { 6859243Sobrien u_int32_t id; 6959243Sobrien u_int8_t stepmask; 7059243Sobrien u_int8_t noext:1; 7159243Sobrien char *name; 7259243Sobrien ac97_patch patch; 7359243Sobrien}; 7459243Sobrien 7559243Sobrienstatic const struct ac97mixtable_entry ac97mixtable_default[32] = { 7659243Sobrien /* [offset] reg bits of st mu re mk en */ 7759243Sobrien [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 7859243Sobrien [SOUND_MIXER_MONITOR] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 7959243Sobrien [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 8059243Sobrien [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 8159243Sobrien [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 8259243Sobrien [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 8359243Sobrien [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 8459243Sobrien [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 8559243Sobrien [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 8659243Sobrien [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 8759243Sobrien /* use igain for the mic 20dB boost */ 8859243Sobrien [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 8959243Sobrien [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 9059243Sobrien [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 9159243Sobrien [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 9259243Sobrien [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 9359243Sobrien}; 9459243Sobrien 9559243Sobrienstatic const struct ac97_vendorid ac97vendorid[] = { 9659243Sobrien { 0x41445300, "Analog Devices" }, 9759243Sobrien { 0x414b4d00, "Asahi Kasei" }, 9859243Sobrien { 0x414c4300, "Realtek" }, 9959243Sobrien { 0x414c4700, "Avance Logic" }, 10059243Sobrien { 0x43525900, "Cirrus Logic" }, 10159243Sobrien { 0x434d4900, "C-Media Electronics" }, 10259243Sobrien { 0x43585400, "Conexant" }, 10359243Sobrien { 0x454d4300, "eMicro" }, 10459243Sobrien { 0x45838300, "ESS Technology" }, 10559243Sobrien { 0x49434500, "ICEnsemble" }, 10659243Sobrien { 0x4e534300, "National Semiconductor" }, 10759243Sobrien { 0x50534300, "Philips Semiconductor" }, 10859243Sobrien { 0x83847600, "SigmaTel" }, 10959243Sobrien { 0x53494c00, "Silicon Laboratory" }, 11059243Sobrien { 0x54524100, "TriTech" }, 11159243Sobrien { 0x56494100, "VIA Technologies" }, 11259243Sobrien { 0x574d4c00, "Wolfson" }, 11359243Sobrien { 0x594d4800, "Yamaha" }, 11459243Sobrien { 0x00000000, NULL } 11559243Sobrien}; 11659243Sobrien 11759243Sobrienstatic struct ac97_codecid ac97codecid[] = { 11859243Sobrien { 0x41445303, 0x00, 0, "AD1819", 0 }, 11959243Sobrien { 0x41445340, 0x00, 0, "AD1881", 0 }, 12059243Sobrien { 0x41445348, 0x00, 0, "AD1881A", 0 }, 12159243Sobrien { 0x41445360, 0x00, 0, "AD1885", 0 }, 12259243Sobrien { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 12359243Sobrien { 0x41445362, 0x00, 0, "AD1887", 0 }, 12459243Sobrien { 0x41445363, 0x00, 0, "AD1886A", 0 }, 12559243Sobrien { 0x41445370, 0x00, 0, "AD1980", 0 }, 12659243Sobrien { 0x41445372, 0x00, 0, "AD1981A", 0 }, 12759243Sobrien { 0x41445374, 0x00, 0, "AD1981B", 0 }, 12859243Sobrien { 0x41445375, 0x00, 0, "AD1985", 0 }, 12959243Sobrien { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 13059243Sobrien { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 13159243Sobrien { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 13259243Sobrien { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 13359243Sobrien { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 13459243Sobrien { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 13559243Sobrien { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 13659243Sobrien { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 13759243Sobrien { 0x43525900, 0x07, 0, "CS4297", 0 }, 13859243Sobrien { 0x43525910, 0x07, 0, "CS4297A", 0 }, 13959243Sobrien { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 14059243Sobrien { 0x43525930, 0x07, 0, "CS4299", 0 }, 14159243Sobrien { 0x43525940, 0x07, 0, "CS4201", 0 }, 14259243Sobrien { 0x43525958, 0x07, 0, "CS4205", 0 }, 14359243Sobrien { 0x43525960, 0x07, 0, "CS4291A", 0 }, 14459243Sobrien { 0x434d4961, 0x00, 0, "CMI9739", 0 }, 14559243Sobrien { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 14659243Sobrien { 0x43585429, 0x00, 0, "CX20468", 0 }, 14759243Sobrien { 0x454d4323, 0x00, 0, "EM28023", 0 }, 14859243Sobrien { 0x454d4328, 0x00, 0, "EM28028", 0 }, 14959243Sobrien { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 15059243Sobrien { 0x49434501, 0x00, 0, "ICE1230", 0 }, 15159243Sobrien { 0x49434511, 0x00, 0, "ICE1232", 0 }, 15259243Sobrien { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 15359243Sobrien { 0x49434551, 0x00, 0, "VT1616", 0 }, /* Via badged ICE */ 15459243Sobrien { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 15559243Sobrien { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 15659243Sobrien { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 15759243Sobrien { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 15859243Sobrien { 0x4e534331, 0x00, 0, "LM4549", 0 }, /* (?) */ 15959243Sobrien { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 16059243Sobrien { 0x4e534350, 0x00, 0, "LM4550", 0 }, 16159243Sobrien { 0x50534301, 0x00, 0, "UCB1510", 0 }, 16259243Sobrien { 0x50534304, 0x00, 0, "UCB1400", 0 }, 16359243Sobrien { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 16459243Sobrien { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 16559243Sobrien { 0x83847605, 0x00, 0, "STAC9704", 0 }, 16659243Sobrien { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 16759243Sobrien { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 16859243Sobrien { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 16959243Sobrien { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 17059243Sobrien { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 17159243Sobrien { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 17259243Sobrien { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 17359243Sobrien { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 17459243Sobrien { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 17559243Sobrien { 0x53494c22, 0x00, 0, "Si3036", 0 }, 17659243Sobrien { 0x53494c23, 0x00, 0, "Si3038", 0 }, 17759243Sobrien { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 17859243Sobrien { 0x54524106, 0x00, 0, "TR28026", 0 }, 17959243Sobrien { 0x54524108, 0x00, 0, "TR28028", 0 }, 18059243Sobrien { 0x54524123, 0x00, 0, "TR28602", 0 }, 18159243Sobrien { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 18259243Sobrien { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 18359243Sobrien { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 18459243Sobrien { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 18559243Sobrien { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 18659243Sobrien { 0x594d4800, 0x00, 0, "YMF743", 0 }, 18759243Sobrien { 0x594d4802, 0x00, 0, "YMF752", 0 }, 18859243Sobrien { 0x594d4803, 0x00, 0, "YMF753", 0 }, 18959243Sobrien { 0, 0, 0, NULL, 0 } 19059243Sobrien}; 19159243Sobrien 19259243Sobrienstatic char *ac97enhancement[] = { 19359243Sobrien "no 3D Stereo Enhancement", 19459243Sobrien "Analog Devices Phat Stereo", 19559243Sobrien "Creative Stereo Enhancement", 19659243Sobrien "National Semi 3D Stereo Enhancement", 19759243Sobrien "Yamaha Ymersion", 19859243Sobrien "BBE 3D Stereo Enhancement", 19959243Sobrien "Crystal Semi 3D Stereo Enhancement", 20059243Sobrien "Qsound QXpander", 20159243Sobrien "Spatializer 3D Stereo Enhancement", 20259243Sobrien "SRS 3D Stereo Enhancement", 20359243Sobrien "Platform Tech 3D Stereo Enhancement", 20459243Sobrien "AKM 3D Audio", 20559243Sobrien "Aureal Stereo Enhancement", 20659243Sobrien "Aztech 3D Enhancement", 20759243Sobrien "Binaura 3D Audio Enhancement", 20859243Sobrien "ESS Technology Stereo Enhancement", 20959243Sobrien "Harman International VMAx", 21059243Sobrien "Nvidea 3D Stereo Enhancement", 21159243Sobrien "Philips Incredible Sound", 21259243Sobrien "Texas Instruments 3D Stereo Enhancement", 21359243Sobrien "VLSI Technology 3D Stereo Enhancement", 21459243Sobrien "TriTech 3D Stereo Enhancement", 21559243Sobrien "Realtek 3D Stereo Enhancement", 21659243Sobrien "Samsung 3D Stereo Enhancement", 21759243Sobrien "Wolfson Microelectronics 3D Enhancement", 21859243Sobrien "Delta Integration 3D Enhancement", 21959243Sobrien "SigmaTel 3D Enhancement", 22059243Sobrien "Reserved 27", 22159243Sobrien "Rockwell 3D Stereo Enhancement", 22259243Sobrien "Reserved 29", 22359243Sobrien "Reserved 30", 22459243Sobrien "Reserved 31" 22559243Sobrien}; 22659243Sobrien 22759243Sobrienstatic char *ac97feature[] = { 22859243Sobrien "mic channel", 22959243Sobrien "reserved", 23059243Sobrien "tone", 23159243Sobrien "simulated stereo", 23259243Sobrien "headphone", 23359243Sobrien "bass boost", 23459243Sobrien "18 bit DAC", 23559243Sobrien "20 bit DAC", 23659243Sobrien "18 bit ADC", 23759243Sobrien "20 bit ADC" 23859243Sobrien}; 23959243Sobrien 24059243Sobrienstatic char *ac97extfeature[] = { 24159243Sobrien "variable rate PCM", 24259243Sobrien "double rate PCM", 24359243Sobrien "reserved 1", 24459243Sobrien "variable rate mic", 24559243Sobrien "reserved 2", 24659243Sobrien "reserved 3", 24759243Sobrien "center DAC", 24859243Sobrien "surround DAC", 24959243Sobrien "LFE DAC", 25059243Sobrien "AMAP", 25159243Sobrien "reserved 4", 25259243Sobrien "reserved 5", 25359243Sobrien "reserved 6", 25459243Sobrien "reserved 7", 25559243Sobrien}; 25659243Sobrien 25759243Sobrienu_int16_t 25859243Sobrienac97_rdcd(struct ac97_info *codec, int reg) 25959243Sobrien{ 26059243Sobrien return AC97_READ(codec->methods, codec->devinfo, reg); 26159243Sobrien} 26259243Sobrien 26359243Sobrienvoid 26459243Sobrienac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 26559243Sobrien{ 26659243Sobrien AC97_WRITE(codec->methods, codec->devinfo, reg, val); 26759243Sobrien} 26859243Sobrien 26959243Sobrienstatic void 27059243Sobrienac97_reset(struct ac97_info *codec) 27159243Sobrien{ 27259243Sobrien u_int32_t i, ps; 27359243Sobrien ac97_wrcd(codec, AC97_REG_RESET, 0); 27459243Sobrien for (i = 0; i < 500; i++) { 27559243Sobrien ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 27659243Sobrien if (ps == AC97_POWER_STATUS) 27759243Sobrien return; 27859243Sobrien DELAY(1000); 27959243Sobrien } 28059243Sobrien device_printf(codec->dev, "AC97 reset timed out.\n"); 28159243Sobrien} 28259243Sobrien 28359243Sobrienint 28459243Sobrienac97_setrate(struct ac97_info *codec, int which, int rate) 28559243Sobrien{ 28659243Sobrien u_int16_t v; 28759243Sobrien 28859243Sobrien switch(which) { 289 case AC97_REGEXT_FDACRATE: 290 case AC97_REGEXT_SDACRATE: 291 case AC97_REGEXT_LDACRATE: 292 case AC97_REGEXT_LADCRATE: 293 case AC97_REGEXT_MADCRATE: 294 break; 295 296 default: 297 return -1; 298 } 299 300 snd_mtxlock(codec->lock); 301 if (rate != 0) { 302 v = rate; 303 if (codec->extstat & AC97_EXTCAP_DRA) 304 v >>= 1; 305 ac97_wrcd(codec, which, v); 306 } 307 v = ac97_rdcd(codec, which); 308 if (codec->extstat & AC97_EXTCAP_DRA) 309 v <<= 1; 310 snd_mtxunlock(codec->lock); 311 return v; 312} 313 314int 315ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 316{ 317 mode &= AC97_EXTCAPS; 318 if ((mode & ~codec->extcaps) != 0) { 319 device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 320 mode); 321 return -1; 322 } 323 snd_mtxlock(codec->lock); 324 ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 325 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 326 snd_mtxunlock(codec->lock); 327 return (mode == codec->extstat)? 0 : -1; 328} 329 330u_int16_t 331ac97_getextmode(struct ac97_info *codec) 332{ 333 return codec->extstat; 334} 335 336u_int16_t 337ac97_getextcaps(struct ac97_info *codec) 338{ 339 return codec->extcaps; 340} 341 342u_int16_t 343ac97_getcaps(struct ac97_info *codec) 344{ 345 return codec->caps; 346} 347 348static int 349ac97_setrecsrc(struct ac97_info *codec, int channel) 350{ 351 struct ac97mixtable_entry *e = &codec->mix[channel]; 352 353 if (e->recidx > 0) { 354 int val = e->recidx - 1; 355 val |= val << 8; 356 snd_mtxlock(codec->lock); 357 ac97_wrcd(codec, AC97_REG_RECSEL, val); 358 snd_mtxunlock(codec->lock); 359 return 0; 360 } else 361 return -1; 362} 363 364static int 365ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 366{ 367 struct ac97mixtable_entry *e = &codec->mix[channel]; 368 369 if (e->reg && e->enable && e->bits) { 370 int mask, max, val, reg; 371 372 reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 373 max = (1 << e->bits) - 1; /* actual range */ 374 mask = (max << 8) | max; /* bits of interest */ 375 376 if (!e->stereo) 377 right = left; 378 379 /* 380 * Invert the range if the polarity requires so, 381 * then scale to 0..max-1 to compute the value to 382 * write into the codec, and scale back to 0..100 383 * for the return value. 384 */ 385 if (e->reg > 0) { 386 left = 100 - left; 387 right = 100 - right; 388 } 389 390 left = (left * max) / 100; 391 right = (right * max) / 100; 392 393 val = (left << 8) | right; 394 395 left = (left * 100) / max; 396 right = (right * 100) / max; 397 398 if (e->reg > 0) { 399 left = 100 - left; 400 right = 100 - right; 401 } 402 403 /* 404 * For mono controls, trim val and mask, also taking 405 * care of e->ofs (offset of control field). 406 */ 407 if (e->ofs) { 408 val &= max; 409 val <<= e->ofs; 410 mask = (max << e->ofs); 411 } 412 413 /* 414 * If we have a mute bit, add it to the mask and 415 * update val and set mute if both channels require a 416 * zero volume. 417 */ 418 if (e->mute == 1) { 419 mask |= AC97_MUTE; 420 if (left == 0 && right == 0) 421 val = AC97_MUTE; 422 } 423 424 /* 425 * If the mask bit is set, do not alter the other bits. 426 */ 427 snd_mtxlock(codec->lock); 428 if (e->mask) { 429 int cur = ac97_rdcd(codec, e->reg); 430 val |= cur & ~(mask); 431 } 432 ac97_wrcd(codec, reg, val); 433 snd_mtxunlock(codec->lock); 434 return left | (right << 8); 435 } else { 436 /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 437 return -1; 438 } 439} 440 441#if 0 442static int 443ac97_getmixer(struct ac97_info *codec, int channel) 444{ 445 struct ac97mixtable_entry *e = &codec->mix[channel]; 446 if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 447 int max, val, volume; 448 449 max = (1 << e->bits) - 1; 450 val = ac97_rdcd(code, e->reg); 451 if (val == AC97_MUTE && e->mute == 1) 452 volume = 0; 453 else { 454 if (e->stereo == 0) val >>= e->ofs; 455 val &= max; 456 volume = (val * 100) / max; 457 if (e->reg > 0) volume = 100 - volume; 458 } 459 return volume; 460 } else 461 return -1; 462} 463#endif 464 465static void 466ac97_fix_auxout(struct ac97_info *codec) 467{ 468 /* Determine what AUXOUT really means, it can be: 469 * 470 * 1. Headphone out. 471 * 2. 4-Channel Out 472 * 3. True line level out (effectively master volume). 473 * 474 * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 475 */ 476 if (codec->caps & AC97_CAP_HEADPHONE) { 477 /* XXX We should probably check the AUX_OUT initial value. 478 * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */ 479 return; 480 } else if (codec->extcaps & AC97_EXTCAP_SDAC && 481 ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 482 /* 4-Channel Out, add an additional gain setting. */ 483 codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 484 } else { 485 /* Master volume is/maybe fixed in h/w, not sufficiently 486 * clear in spec to blat SOUND_MIXER_MASTER. */ 487 codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 488 } 489 /* Blat monitor, inappropriate label if we get here */ 490 bzero(&codec->mix[SOUND_MIXER_MONITOR], 491 sizeof(codec->mix[SOUND_MIXER_MONITOR])); 492} 493 494static const char* 495ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 496{ 497 if (cname == NULL) { 498 sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 499 return buf; 500 } 501 502 if (vname == NULL) vname = "Unknown"; 503 504 if (bootverbose) { 505 sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 506 } else { 507 sprintf(buf, "%s %s AC97 Codec", vname, cname); 508 } 509 return buf; 510} 511 512static unsigned 513ac97_initmixer(struct ac97_info *codec) 514{ 515 ac97_patch codec_patch; 516 const char *cname, *vname; 517 char desc[80]; 518 u_int8_t model, step; 519 unsigned i, j, k, old; 520 u_int32_t id; 521 522 snd_mtxlock(codec->lock); 523 codec->count = AC97_INIT(codec->methods, codec->devinfo); 524 if (codec->count == 0) { 525 device_printf(codec->dev, "ac97 codec init failed\n"); 526 snd_mtxunlock(codec->lock); 527 return ENODEV; 528 } 529 530 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 531 ac97_reset(codec); 532 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 533 534 i = ac97_rdcd(codec, AC97_REG_RESET); 535 codec->caps = i & 0x03ff; 536 codec->se = (i & 0x7c00) >> 10; 537 538 id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 539 if (id == 0 || id == 0xffffffff) { 540 device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 541 snd_mtxunlock(codec->lock); 542 return ENODEV; 543 } 544 545 codec->id = id; 546 codec->noext = 0; 547 codec_patch = NULL; 548 549 cname = NULL; 550 model = step = 0; 551 for (i = 0; ac97codecid[i].id; i++) { 552 u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 553 if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 554 codec->noext = ac97codecid[i].noext; 555 codec_patch = ac97codecid[i].patch; 556 cname = ac97codecid[i].name; 557 model = (id & modelmask) & 0xff; 558 step = (id & ~modelmask) & 0xff; 559 break; 560 } 561 } 562 563 vname = NULL; 564 for (i = 0; ac97vendorid[i].id; i++) { 565 if (ac97vendorid[i].id == (id & 0xffffff00)) { 566 vname = ac97vendorid[i].name; 567 break; 568 } 569 } 570 571 codec->extcaps = 0; 572 codec->extid = 0; 573 codec->extstat = 0; 574 if (!codec->noext) { 575 i = ac97_rdcd(codec, AC97_REGEXT_ID); 576 if (i != 0xffff) { 577 codec->extcaps = i & 0x3fff; 578 codec->extid = (i & 0xc000) >> 14; 579 codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 580 } 581 } 582 583 for (i = 0; i < 32; i++) { 584 codec->mix[i] = ac97mixtable_default[i]; 585 } 586 ac97_fix_auxout(codec); 587 if (codec_patch) 588 codec_patch(codec); 589 590 for (i = 0; i < 32; i++) { 591 k = codec->noext? codec->mix[i].enable : 1; 592 if (k && (codec->mix[i].reg > 0)) { 593 old = ac97_rdcd(codec, codec->mix[i].reg); 594 ac97_wrcd(codec, codec->mix[i].reg, 0x3f); 595 j = ac97_rdcd(codec, codec->mix[i].reg); 596 ac97_wrcd(codec, codec->mix[i].reg, old); 597 codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 598 for (k = 1; j & (1 << k); k++); 599 codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 600 } 601 /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 602 } 603 604 device_printf(codec->dev, "<%s>\n", 605 ac97_hw_desc(codec->id, vname, cname, desc)); 606 607 if (bootverbose) { 608 device_printf(codec->dev, "Codec features "); 609 for (i = j = 0; i < 10; i++) 610 if (codec->caps & (1 << i)) 611 printf("%s%s", j++? ", " : "", ac97feature[i]); 612 printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 613 printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 614 615 if (codec->extcaps != 0 || codec->extid) { 616 device_printf(codec->dev, "%s codec", 617 codec->extid? "Secondary" : "Primary"); 618 if (codec->extcaps) 619 printf(" extended features "); 620 for (i = j = 0; i < 14; i++) 621 if (codec->extcaps & (1 << i)) 622 printf("%s%s", j++? ", " : "", ac97extfeature[i]); 623 printf("\n"); 624 } 625 } 626 627 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 628 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 629 snd_mtxunlock(codec->lock); 630 return 0; 631} 632 633static unsigned 634ac97_reinitmixer(struct ac97_info *codec) 635{ 636 snd_mtxlock(codec->lock); 637 codec->count = AC97_INIT(codec->methods, codec->devinfo); 638 if (codec->count == 0) { 639 device_printf(codec->dev, "ac97 codec init failed\n"); 640 snd_mtxunlock(codec->lock); 641 return ENODEV; 642 } 643 644 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 645 ac97_reset(codec); 646 ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 647 648 if (!codec->noext) { 649 ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 650 if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 651 != codec->extstat) 652 device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 653 codec->extstat, 654 ac97_rdcd(codec, AC97_REGEXT_STAT) & 655 AC97_EXTCAPS); 656 } 657 658 if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 659 device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 660 snd_mtxunlock(codec->lock); 661 return 0; 662} 663 664struct ac97_info * 665ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 666{ 667 struct ac97_info *codec; 668 669 codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 670 if (codec == NULL) 671 return NULL; 672 673 snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 674 codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 675 codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 676 if (codec->methods == NULL) { 677 snd_mtxlock(codec->lock); 678 snd_mtxfree(codec->lock); 679 free(codec, M_AC97); 680 return NULL; 681 } 682 683 codec->dev = dev; 684 codec->devinfo = devinfo; 685 codec->flags = 0; 686 return codec; 687} 688 689void 690ac97_destroy(struct ac97_info *codec) 691{ 692 snd_mtxlock(codec->lock); 693 if (codec->methods != NULL) 694 kobj_delete(codec->methods, M_AC97); 695 snd_mtxfree(codec->lock); 696 free(codec, M_AC97); 697} 698 699void 700ac97_setflags(struct ac97_info *codec, u_int32_t val) 701{ 702 codec->flags = val; 703} 704 705u_int32_t 706ac97_getflags(struct ac97_info *codec) 707{ 708 return codec->flags; 709} 710 711/* -------------------------------------------------------------------- */ 712 713static int 714ac97mix_init(struct snd_mixer *m) 715{ 716 struct ac97_info *codec = mix_getdevinfo(m); 717 u_int32_t i, mask; 718 719 if (codec == NULL) 720 return -1; 721 722 if (ac97_initmixer(codec)) 723 return -1; 724 725 mask = 0; 726 for (i = 0; i < 32; i++) 727 mask |= codec->mix[i].enable? 1 << i : 0; 728 mix_setdevs(m, mask); 729 730 mask = 0; 731 for (i = 0; i < 32; i++) 732 mask |= codec->mix[i].recidx? 1 << i : 0; 733 mix_setrecdevs(m, mask); 734 return 0; 735} 736 737static int 738ac97mix_uninit(struct snd_mixer *m) 739{ 740 struct ac97_info *codec = mix_getdevinfo(m); 741 742 if (codec == NULL) 743 return -1; 744 /* 745 if (ac97_uninitmixer(codec)) 746 return -1; 747 */ 748 ac97_destroy(codec); 749 return 0; 750} 751 752static int 753ac97mix_reinit(struct snd_mixer *m) 754{ 755 struct ac97_info *codec = mix_getdevinfo(m); 756 757 if (codec == NULL) 758 return -1; 759 return ac97_reinitmixer(codec); 760} 761 762static int 763ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 764{ 765 struct ac97_info *codec = mix_getdevinfo(m); 766 767 if (codec == NULL) 768 return -1; 769 return ac97_setmixer(codec, dev, left, right); 770} 771 772static int 773ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 774{ 775 int i; 776 struct ac97_info *codec = mix_getdevinfo(m); 777 778 if (codec == NULL) 779 return -1; 780 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 781 if ((src & (1 << i)) != 0) 782 break; 783 return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 784} 785 786static kobj_method_t ac97mixer_methods[] = { 787 KOBJMETHOD(mixer_init, ac97mix_init), 788 KOBJMETHOD(mixer_uninit, ac97mix_uninit), 789 KOBJMETHOD(mixer_reinit, ac97mix_reinit), 790 KOBJMETHOD(mixer_set, ac97mix_set), 791 KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 792 { 0, 0 } 793}; 794MIXER_DECLARE(ac97mixer); 795 796/* -------------------------------------------------------------------- */ 797 798kobj_class_t 799ac97_getmixerclass(void) 800{ 801 return &ac97mixer_class; 802} 803 804 805