1139749Simp/*- 2119853Scg * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 350724Scg * All rights reserved. 450724Scg * 550724Scg * Redistribution and use in source and binary forms, with or without 650724Scg * modification, are permitted provided that the following conditions 750724Scg * are met: 850724Scg * 1. Redistributions of source code must retain the above copyright 950724Scg * notice, this list of conditions and the following disclaimer. 1050724Scg * 2. Redistributions in binary form must reproduce the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer in the 1250724Scg * documentation and/or other materials provided with the distribution. 1350724Scg * 1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450724Scg * SUCH DAMAGE. 2550724Scg */ 2650724Scg 27193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 28193640Sariff#include "opt_snd.h" 29193640Sariff#endif 30193640Sariff 3153465Scg#include <dev/sound/pcm/sound.h> 3253465Scg#include <dev/sound/pcm/ac97.h> 33109818Sorion#include <dev/sound/pcm/ac97_patch.h> 3450724Scg 35164614Sariff#include <dev/pci/pcivar.h> 36164614Sariff 3770134Scg#include "mixer_if.h" 3850724Scg 3982180ScgSND_DECLARE_FILE("$FreeBSD: releng/10.2/sys/dev/sound/pcm/ac97.c 227293 2011-11-07 06:44:47Z ed $"); 4082180Scg 41227293Sedstatic MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 4250724Scg 4378668Scgstruct ac97mixtable_entry { 44170060Sariff int reg; /* register index */ 45113783Sorion /* reg < 0 if inverted polarity */ 46113783Sorion unsigned bits:4; /* width of control field */ 47113783Sorion unsigned ofs:4; /* offset (only if stereo=0) */ 48113783Sorion unsigned stereo:1; /* set for stereo controls */ 49113783Sorion unsigned mute:1; /* bit15 is MUTE */ 50113783Sorion unsigned recidx:4; /* index in rec mux */ 51113783Sorion unsigned mask:1; /* use only masked bits */ 52113783Sorion unsigned enable:1; /* entry is enabled */ 5378668Scg}; 5478668Scg 55170342Sariff#define AC97_MIXER_SIZE SOUND_MIXER_NRDEVICES 56170342Sariff 5774763Scgstruct ac97_info { 5874763Scg kobj_t methods; 5974763Scg device_t dev; 6074763Scg void *devinfo; 61111679Sorion u_int32_t id; 62168861Sariff u_int32_t subvendor; 6374763Scg unsigned count, caps, se, extcaps, extid, extstat, noext:1; 6478668Scg u_int32_t flags; 65170342Sariff struct ac97mixtable_entry mix[AC97_MIXER_SIZE]; 66170342Sariff char name[16]; 67107285Scg struct mtx *lock; 6874763Scg}; 6974763Scg 70111679Sorionstruct ac97_vendorid { 71111679Sorion u_int32_t id; 72111679Sorion const char *name; 73111679Sorion}; 74111679Sorion 7550724Scgstruct ac97_codecid { 76111679Sorion u_int32_t id; 77111679Sorion u_int8_t stepmask; 78111679Sorion u_int8_t noext:1; 79111679Sorion char *name; 80109818Sorion ac97_patch patch; 8150724Scg}; 8250724Scg 83170342Sariffstatic const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = { 84113783Sorion /* [offset] reg bits of st mu re mk en */ 8566013Scg [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 86119209Sorion [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 8766013Scg [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 8866013Scg [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 8966013Scg [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 9066013Scg [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 9166013Scg [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 9266013Scg [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 9366013Scg [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 94113783Sorion [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 95113783Sorion /* use igain for the mic 20dB boost */ 96113783Sorion [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 9766013Scg [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 9866013Scg [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 9966013Scg [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 10066013Scg [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 10150724Scg}; 10250724Scg 103111679Sorionstatic const struct ac97_vendorid ac97vendorid[] = { 104111679Sorion { 0x41445300, "Analog Devices" }, 105111679Sorion { 0x414b4d00, "Asahi Kasei" }, 106111679Sorion { 0x414c4300, "Realtek" }, 107111679Sorion { 0x414c4700, "Avance Logic" }, 108111679Sorion { 0x43525900, "Cirrus Logic" }, 109111679Sorion { 0x434d4900, "C-Media Electronics" }, 110111679Sorion { 0x43585400, "Conexant" }, 111124875Smatk { 0x44543000, "Diamond Technology" }, 112113788Sorion { 0x454d4300, "eMicro" }, 113111679Sorion { 0x45838300, "ESS Technology" }, 114124875Smatk { 0x48525300, "Intersil" }, 115111679Sorion { 0x49434500, "ICEnsemble" }, 116124875Smatk { 0x49544500, "ITE, Inc." }, 117111679Sorion { 0x4e534300, "National Semiconductor" }, 118111679Sorion { 0x50534300, "Philips Semiconductor" }, 119111679Sorion { 0x83847600, "SigmaTel" }, 120124875Smatk { 0x53494c00, "Silicon Laboratories" }, 121111679Sorion { 0x54524100, "TriTech" }, 122124875Smatk { 0x54584e00, "Texas Instruments" }, 123111679Sorion { 0x56494100, "VIA Technologies" }, 124124875Smatk { 0x57454300, "Winbond" }, 125111679Sorion { 0x574d4c00, "Wolfson" }, 126111679Sorion { 0x594d4800, "Yamaha" }, 127149949Snetchild /* 128149949Snetchild * XXX This is a fluke, really! The real vendor 129149949Snetchild * should be SigmaTel, not this! This should be 130149949Snetchild * removed someday! 131149949Snetchild */ 132119547Sorion { 0x01408300, "Creative" }, 133111679Sorion { 0x00000000, NULL } 134111679Sorion}; 135111679Sorion 13650724Scgstatic struct ac97_codecid ac97codecid[] = { 137111679Sorion { 0x41445303, 0x00, 0, "AD1819", 0 }, 138111679Sorion { 0x41445340, 0x00, 0, "AD1881", 0 }, 139111679Sorion { 0x41445348, 0x00, 0, "AD1881A", 0 }, 140111679Sorion { 0x41445360, 0x00, 0, "AD1885", 0 }, 141111679Sorion { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 142113057Sorion { 0x41445362, 0x00, 0, "AD1887", 0 }, 143113057Sorion { 0x41445363, 0x00, 0, "AD1886A", 0 }, 144144865Sscottl { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 145119209Sorion { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 146113057Sorion { 0x41445372, 0x00, 0, "AD1981A", 0 }, 147162738Sariff { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, 148119209Sorion { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 149154094Sariff { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, 150111679Sorion { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 151111679Sorion { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 152111679Sorion { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 153124875Smatk { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 154124875Smatk { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 155111679Sorion { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 156119209Sorion { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 157111679Sorion { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 158111679Sorion { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 159111679Sorion { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 160152815Sariff { 0x414c4752, 0x0f, 0, "ALC250", 0 }, 161168861Sariff { 0x414c4760, 0x0f, 0, "ALC655", alc655_patch }, 162152939Syongari { 0x414c4770, 0x0f, 0, "ALC203", 0 }, 163122515Skuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 164124875Smatk { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 165111679Sorion { 0x43525900, 0x07, 0, "CS4297", 0 }, 166111679Sorion { 0x43525910, 0x07, 0, "CS4297A", 0 }, 167111679Sorion { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 168129044Ssanpei { 0x4352592d, 0x07, 0, "CS4294", 0 }, 169111679Sorion { 0x43525930, 0x07, 0, "CS4299", 0 }, 170111679Sorion { 0x43525940, 0x07, 0, "CS4201", 0 }, 171112150Sorion { 0x43525958, 0x07, 0, "CS4205", 0 }, 172111679Sorion { 0x43525960, 0x07, 0, "CS4291A", 0 }, 173150825Snetchild { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, 174111679Sorion { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 175150825Snetchild { 0x434d4978, 0x00, 0, "CMI9761", 0 }, 176150825Snetchild { 0x434d4982, 0x00, 0, "CMI9761", 0 }, 177150825Snetchild { 0x434d4983, 0x00, 0, "CMI9761", 0 }, 178124875Smatk { 0x43585421, 0x00, 0, "HSD11246", 0 }, 179124875Smatk { 0x43585428, 0x07, 0, "CX20468", 0 }, 180152422Sariff { 0x43585430, 0x00, 0, "CX20468-21", 0 }, 181124875Smatk { 0x44543000, 0x00, 0, "DT0398", 0 }, 182113788Sorion { 0x454d4323, 0x00, 0, "EM28023", 0 }, 183113788Sorion { 0x454d4328, 0x00, 0, "EM28028", 0 }, 184111679Sorion { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 185124875Smatk { 0x48525300, 0x00, 0, "HMP9701", 0 }, 186111679Sorion { 0x49434501, 0x00, 0, "ICE1230", 0 }, 187111679Sorion { 0x49434511, 0x00, 0, "ICE1232", 0 }, 188111679Sorion { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 189119250Sorion { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 190124875Smatk { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 191124875Smatk { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 192111679Sorion { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 193111679Sorion { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 194111679Sorion { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 195111679Sorion { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 196124875Smatk { 0x4e534331, 0x00, 0, "LM4549", 0 }, 197111679Sorion { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 198111679Sorion { 0x4e534350, 0x00, 0, "LM4550", 0 }, 199111679Sorion { 0x50534301, 0x00, 0, "UCB1510", 0 }, 200111679Sorion { 0x50534304, 0x00, 0, "UCB1400", 0 }, 201111679Sorion { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 202111679Sorion { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 203113788Sorion { 0x83847605, 0x00, 0, "STAC9704", 0 }, 204111679Sorion { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 205111679Sorion { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 206111679Sorion { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 207111679Sorion { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 208111679Sorion { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 209111679Sorion { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 210111679Sorion { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 211111679Sorion { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 212111679Sorion { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 213150825Snetchild { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 214111679Sorion { 0x53494c22, 0x00, 0, "Si3036", 0 }, 215111679Sorion { 0x53494c23, 0x00, 0, "Si3038", 0 }, 216111679Sorion { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 217111679Sorion { 0x54524106, 0x00, 0, "TR28026", 0 }, 218111679Sorion { 0x54524108, 0x00, 0, "TR28028", 0 }, 219111679Sorion { 0x54524123, 0x00, 0, "TR28602", 0 }, 220124875Smatk { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 221124875Smatk { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 222111679Sorion { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 223152422Sariff { 0x56494170, 0x00, 0, "VIA1617A", 0 }, 224111679Sorion { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 225111679Sorion { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 226111679Sorion { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 227111679Sorion { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 228124875Smatk { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 229124875Smatk { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 230124875Smatk { 0x57454301, 0x00, 0, "W83971D", 0 }, 231111679Sorion { 0x594d4800, 0x00, 0, "YMF743", 0 }, 232111679Sorion { 0x594d4802, 0x00, 0, "YMF752", 0 }, 233111679Sorion { 0x594d4803, 0x00, 0, "YMF753", 0 }, 234149949Snetchild /* 235149949Snetchild * XXX This is a fluke, really! The real codec 236149949Snetchild * should be STAC9704, not this! This should be 237149949Snetchild * removed someday! 238149949Snetchild */ 239119547Sorion { 0x01408384, 0x00, 0, "EV1938", 0 }, 240111679Sorion { 0, 0, 0, NULL, 0 } 24150724Scg}; 24250724Scg 24350724Scgstatic char *ac97enhancement[] = { 24460960Scg "no 3D Stereo Enhancement", 24550724Scg "Analog Devices Phat Stereo", 24650724Scg "Creative Stereo Enhancement", 24750724Scg "National Semi 3D Stereo Enhancement", 24850724Scg "Yamaha Ymersion", 24950724Scg "BBE 3D Stereo Enhancement", 25050724Scg "Crystal Semi 3D Stereo Enhancement", 25150724Scg "Qsound QXpander", 25250724Scg "Spatializer 3D Stereo Enhancement", 25350724Scg "SRS 3D Stereo Enhancement", 25450724Scg "Platform Tech 3D Stereo Enhancement", 25550724Scg "AKM 3D Audio", 25650724Scg "Aureal Stereo Enhancement", 25750724Scg "Aztech 3D Enhancement", 25850724Scg "Binaura 3D Audio Enhancement", 25950724Scg "ESS Technology Stereo Enhancement", 26050724Scg "Harman International VMAx", 26150724Scg "Nvidea 3D Stereo Enhancement", 26250724Scg "Philips Incredible Sound", 26350724Scg "Texas Instruments 3D Stereo Enhancement", 26450724Scg "VLSI Technology 3D Stereo Enhancement", 26550724Scg "TriTech 3D Stereo Enhancement", 26650724Scg "Realtek 3D Stereo Enhancement", 26750724Scg "Samsung 3D Stereo Enhancement", 26850724Scg "Wolfson Microelectronics 3D Enhancement", 26950724Scg "Delta Integration 3D Enhancement", 27050724Scg "SigmaTel 3D Enhancement", 27150724Scg "Reserved 27", 27250724Scg "Rockwell 3D Stereo Enhancement", 27350724Scg "Reserved 29", 27450724Scg "Reserved 30", 27550724Scg "Reserved 31" 27650724Scg}; 27750724Scg 27850724Scgstatic char *ac97feature[] = { 27950724Scg "mic channel", 28050724Scg "reserved", 28150724Scg "tone", 28250724Scg "simulated stereo", 28350724Scg "headphone", 28450724Scg "bass boost", 28550724Scg "18 bit DAC", 28650724Scg "20 bit DAC", 28750724Scg "18 bit ADC", 28850724Scg "20 bit ADC" 28950724Scg}; 29050724Scg 29158384Scgstatic char *ac97extfeature[] = { 29258384Scg "variable rate PCM", 29358384Scg "double rate PCM", 29458384Scg "reserved 1", 29558384Scg "variable rate mic", 29658384Scg "reserved 2", 29758384Scg "reserved 3", 29858384Scg "center DAC", 29958384Scg "surround DAC", 30058384Scg "LFE DAC", 30158384Scg "AMAP", 30258384Scg "reserved 4", 30358384Scg "reserved 5", 30458384Scg "reserved 6", 30558384Scg "reserved 7", 30658384Scg}; 30758384Scg 308109818Sorionu_int16_t 309109818Sorionac97_rdcd(struct ac97_info *codec, int reg) 31058384Scg{ 311149949Snetchild if (codec->flags & AC97_F_RDCD_BUG) { 312149949Snetchild u_int16_t i[2], j = 100; 313149949Snetchild 314149949Snetchild i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 315149949Snetchild i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 316149949Snetchild while (i[0] != i[1] && j) 317149949Snetchild i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 318149949Snetchild#if 0 319149949Snetchild if (j < 100) { 320149949Snetchild device_printf(codec->dev, "%s(): Inconsistent register value at" 321149949Snetchild " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 322149949Snetchild } 323149949Snetchild#endif 324149949Snetchild return i[!(j & 1)]; 325149949Snetchild } 32670134Scg return AC97_READ(codec->methods, codec->devinfo, reg); 32758384Scg} 32858384Scg 329109818Sorionvoid 330109818Sorionac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 33158384Scg{ 33270134Scg AC97_WRITE(codec->methods, codec->devinfo, reg, val); 33358384Scg} 33458384Scg 33595499Sorionstatic void 33695499Sorionac97_reset(struct ac97_info *codec) 33795499Sorion{ 33895499Sorion u_int32_t i, ps; 339109818Sorion ac97_wrcd(codec, AC97_REG_RESET, 0); 34095499Sorion for (i = 0; i < 500; i++) { 341109818Sorion ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 342107285Scg if (ps == AC97_POWER_STATUS) 34395499Sorion return; 34495499Sorion DELAY(1000); 34595499Sorion } 346110258Sorion device_printf(codec->dev, "AC97 reset timed out.\n"); 34795499Sorion} 34895499Sorion 34958384Scgint 35058384Scgac97_setrate(struct ac97_info *codec, int which, int rate) 35158384Scg{ 35258384Scg u_int16_t v; 35358384Scg 35458384Scg switch(which) { 355107285Scg case AC97_REGEXT_FDACRATE: 35658384Scg case AC97_REGEXT_SDACRATE: 35758384Scg case AC97_REGEXT_LDACRATE: 358107285Scg case AC97_REGEXT_LADCRATE: 35958384Scg case AC97_REGEXT_MADCRATE: 36058384Scg break; 36158384Scg 36258384Scg default: 36358384Scg return -1; 36458384Scg } 36558384Scg 36674763Scg snd_mtxlock(codec->lock); 36758384Scg if (rate != 0) { 36858384Scg v = rate; 36958384Scg if (codec->extstat & AC97_EXTCAP_DRA) 37058384Scg v >>= 1; 371109818Sorion ac97_wrcd(codec, which, v); 37258384Scg } 373109818Sorion v = ac97_rdcd(codec, which); 37458384Scg if (codec->extstat & AC97_EXTCAP_DRA) 37558384Scg v <<= 1; 37674763Scg snd_mtxunlock(codec->lock); 37758384Scg return v; 37858384Scg} 37958384Scg 38058384Scgint 38158384Scgac97_setextmode(struct ac97_info *codec, u_int16_t mode) 38258384Scg{ 38358384Scg mode &= AC97_EXTCAPS; 38486707Sorion if ((mode & ~codec->extcaps) != 0) { 385107285Scg device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 38686707Sorion mode); 38758384Scg return -1; 38886707Sorion } 38974763Scg snd_mtxlock(codec->lock); 390109818Sorion ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 391109818Sorion codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 39274763Scg snd_mtxunlock(codec->lock); 39358384Scg return (mode == codec->extstat)? 0 : -1; 39458384Scg} 39558384Scg 39667652Scgu_int16_t 39767652Scgac97_getextmode(struct ac97_info *codec) 39867652Scg{ 39967652Scg return codec->extstat; 40067652Scg} 40167652Scg 40267652Scgu_int16_t 40367652Scgac97_getextcaps(struct ac97_info *codec) 40467652Scg{ 40567652Scg return codec->extcaps; 40667652Scg} 40767652Scg 40883612Scgu_int16_t 40983612Scgac97_getcaps(struct ac97_info *codec) 41083612Scg{ 41183612Scg return codec->caps; 41283612Scg} 41383612Scg 414168861Sariffu_int32_t 415168861Sariffac97_getsubvendor(struct ac97_info *codec) 416168861Sariff{ 417168861Sariff return codec->subvendor; 418168861Sariff} 419168861Sariff 42050724Scgstatic int 42150724Scgac97_setrecsrc(struct ac97_info *codec, int channel) 42250724Scg{ 42350724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 42466013Scg 42550724Scg if (e->recidx > 0) { 42650724Scg int val = e->recidx - 1; 42750724Scg val |= val << 8; 42874763Scg snd_mtxlock(codec->lock); 429109818Sorion ac97_wrcd(codec, AC97_REG_RECSEL, val); 43074763Scg snd_mtxunlock(codec->lock); 43150724Scg return 0; 43258384Scg } else 43358384Scg return -1; 43450724Scg} 43550724Scg 43650724Scgstatic int 43750724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 43850724Scg{ 43950724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 44066013Scg 44166013Scg if (e->reg && e->enable && e->bits) { 442113783Sorion int mask, max, val, reg; 44350724Scg 444113783Sorion reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 445113783Sorion max = (1 << e->bits) - 1; /* actual range */ 446113783Sorion mask = (max << 8) | max; /* bits of interest */ 447113783Sorion 44858384Scg if (!e->stereo) 44958384Scg right = left; 450113783Sorion 451113783Sorion /* 452113783Sorion * Invert the range if the polarity requires so, 453113783Sorion * then scale to 0..max-1 to compute the value to 454113783Sorion * write into the codec, and scale back to 0..100 455113783Sorion * for the return value. 456113783Sorion */ 45750724Scg if (e->reg > 0) { 45850724Scg left = 100 - left; 45950724Scg right = 100 - right; 46050724Scg } 46150724Scg 46250724Scg left = (left * max) / 100; 46350724Scg right = (right * max) / 100; 46450724Scg 46550724Scg val = (left << 8) | right; 46650724Scg 46750724Scg left = (left * 100) / max; 46850724Scg right = (right * 100) / max; 46950724Scg 47050724Scg if (e->reg > 0) { 47150724Scg left = 100 - left; 47250724Scg right = 100 - right; 47350724Scg } 47450724Scg 475113783Sorion /* 476113783Sorion * For mono controls, trim val and mask, also taking 477119209Sorion * care of e->ofs (offset of control field). 478113783Sorion */ 479113783Sorion if (e->ofs) { 48050724Scg val &= max; 48150724Scg val <<= e->ofs; 482113783Sorion mask = (max << e->ofs); 48350724Scg } 484113783Sorion 485113783Sorion /* 486113783Sorion * If we have a mute bit, add it to the mask and 487113783Sorion * update val and set mute if both channels require a 488113783Sorion * zero volume. 489113783Sorion */ 490113783Sorion if (e->mute == 1) { 491113783Sorion mask |= AC97_MUTE; 492113783Sorion if (left == 0 && right == 0) 493113783Sorion val = AC97_MUTE; 494113783Sorion } 495113783Sorion 496113783Sorion /* 497113783Sorion * If the mask bit is set, do not alter the other bits. 498113783Sorion */ 49974763Scg snd_mtxlock(codec->lock); 500113783Sorion if (e->mask) { 501149949Snetchild int cur = ac97_rdcd(codec, reg); 502113783Sorion val |= cur & ~(mask); 503113783Sorion } 504109818Sorion ac97_wrcd(codec, reg, val); 50574763Scg snd_mtxunlock(codec->lock); 50650724Scg return left | (right << 8); 50766013Scg } else { 508149949Snetchild#if 0 509149949Snetchild printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 510149949Snetchild#endif 51158384Scg return -1; 51266013Scg } 51350724Scg} 51450724Scg 515102302Sorionstatic void 516102302Sorionac97_fix_auxout(struct ac97_info *codec) 517102302Sorion{ 518119375Sorion int keep_ogain; 519119375Sorion 520119209Sorion /* 521119375Sorion * By default, The ac97 aux_out register (0x04) corresponds to OSS's 522119375Sorion * OGAIN setting. 523102302Sorion * 524119375Sorion * We first check whether aux_out is a valid register. If not 525119375Sorion * we may not want to keep ogain. 526119209Sorion */ 527119375Sorion keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 528119209Sorion 529119209Sorion /* 530119209Sorion * Determine what AUX_OUT really means, it can be: 531119209Sorion * 532102302Sorion * 1. Headphone out. 533102302Sorion * 2. 4-Channel Out 534102302Sorion * 3. True line level out (effectively master volume). 535102302Sorion * 536102302Sorion * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 537102302Sorion */ 538119209Sorion if (codec->extcaps & AC97_EXTCAP_SDAC && 539119209Sorion ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 540119375Sorion codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 541119375Sorion keep_ogain = 1; 542102302Sorion } 543119375Sorion 544119375Sorion if (keep_ogain == 0) { 545119375Sorion bzero(&codec->mix[SOUND_MIXER_OGAIN], 546119375Sorion sizeof(codec->mix[SOUND_MIXER_OGAIN])); 547119375Sorion } 548102302Sorion} 549102302Sorion 550119209Sorionstatic void 551119209Sorionac97_fix_tone(struct ac97_info *codec) 552119209Sorion{ 553167256Sariff /* 554167256Sariff * YMF chips does not indicate tone and 3D enhancement capability 555167256Sariff * in the AC97_REG_RESET register. 556167256Sariff */ 557167256Sariff switch (codec->id) { 558167256Sariff case 0x594d4800: /* YMF743 */ 559167256Sariff case 0x594d4803: /* YMF753 */ 560167256Sariff codec->caps |= AC97_CAP_TONE; 561167256Sariff codec->se |= 0x04; 562167256Sariff break; 563167256Sariff case 0x594d4802: /* YMF752 */ 564167256Sariff codec->se |= 0x04; 565167256Sariff break; 566167256Sariff default: 567167256Sariff break; 568167256Sariff } 569167256Sariff 570119209Sorion /* Hide treble and bass if they don't exist */ 571119209Sorion if ((codec->caps & AC97_CAP_TONE) == 0) { 572119209Sorion bzero(&codec->mix[SOUND_MIXER_BASS], 573119209Sorion sizeof(codec->mix[SOUND_MIXER_BASS])); 574119209Sorion bzero(&codec->mix[SOUND_MIXER_TREBLE], 575119209Sorion sizeof(codec->mix[SOUND_MIXER_TREBLE])); 576119209Sorion } 577119209Sorion} 578119209Sorion 579111679Sorionstatic const char* 580111679Sorionac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 581111679Sorion{ 582111679Sorion if (cname == NULL) { 583111679Sorion sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 584111679Sorion return buf; 585111679Sorion } 586111679Sorion 587111679Sorion if (vname == NULL) vname = "Unknown"; 588119209Sorion 589111679Sorion if (bootverbose) { 590111679Sorion sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 591111679Sorion } else { 592111679Sorion sprintf(buf, "%s %s AC97 Codec", vname, cname); 593111679Sorion } 594111679Sorion return buf; 595111679Sorion} 596111679Sorion 59750724Scgstatic unsigned 59858384Scgac97_initmixer(struct ac97_info *codec) 59950724Scg{ 600109818Sorion ac97_patch codec_patch; 601111679Sorion const char *cname, *vname; 602111679Sorion char desc[80]; 603111679Sorion u_int8_t model, step; 604149949Snetchild unsigned i, j, k, bit, old; 60550724Scg u_int32_t id; 606149949Snetchild int reg; 60750724Scg 60874763Scg snd_mtxlock(codec->lock); 60970134Scg codec->count = AC97_INIT(codec->methods, codec->devinfo); 61070134Scg if (codec->count == 0) { 61170134Scg device_printf(codec->dev, "ac97 codec init failed\n"); 61274763Scg snd_mtxunlock(codec->lock); 61370134Scg return ENODEV; 61470134Scg } 61567652Scg 616109818Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 61795499Sorion ac97_reset(codec); 618109818Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 61950724Scg 620109818Sorion i = ac97_rdcd(codec, AC97_REG_RESET); 621149949Snetchild j = ac97_rdcd(codec, AC97_REG_RESET); 622170342Sariff k = ac97_rdcd(codec, AC97_REG_RESET); 623149949Snetchild /* 624149949Snetchild * Let see if this codec can return consistent value. 625149949Snetchild * If not, turn on aggressive read workaround 626149949Snetchild * (STAC9704 comes in mind). 627149949Snetchild */ 628170342Sariff if (i != j || j != k) { 629149949Snetchild codec->flags |= AC97_F_RDCD_BUG; 630149949Snetchild i = ac97_rdcd(codec, AC97_REG_RESET); 631149949Snetchild } 63250724Scg codec->caps = i & 0x03ff; 63350724Scg codec->se = (i & 0x7c00) >> 10; 63450724Scg 635109818Sorion id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 63658905Scg if (id == 0 || id == 0xffffffff) { 63758905Scg device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 63874763Scg snd_mtxunlock(codec->lock); 63958905Scg return ENODEV; 64058905Scg } 64150724Scg 642111679Sorion codec->id = id; 643168861Sariff codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16; 644168861Sariff codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) & 645168861Sariff 0x0000ffff; 64665490Scg codec->noext = 0; 647109818Sorion codec_patch = NULL; 648111679Sorion 649111679Sorion cname = NULL; 650111679Sorion model = step = 0; 65165490Scg for (i = 0; ac97codecid[i].id; i++) { 652111679Sorion u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 653111679Sorion if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 65465490Scg codec->noext = ac97codecid[i].noext; 655109818Sorion codec_patch = ac97codecid[i].patch; 656111679Sorion cname = ac97codecid[i].name; 657111679Sorion model = (id & modelmask) & 0xff; 658111679Sorion step = (id & ~modelmask) & 0xff; 659111679Sorion break; 66065490Scg } 66165490Scg } 66258521Scg 663111679Sorion vname = NULL; 664111679Sorion for (i = 0; ac97vendorid[i].id; i++) { 665111679Sorion if (ac97vendorid[i].id == (id & 0xffffff00)) { 666111679Sorion vname = ac97vendorid[i].name; 667111679Sorion break; 668111679Sorion } 669111679Sorion } 670111679Sorion 67170324Scg codec->extcaps = 0; 67270324Scg codec->extid = 0; 67370324Scg codec->extstat = 0; 67470324Scg if (!codec->noext) { 675109818Sorion i = ac97_rdcd(codec, AC97_REGEXT_ID); 67670324Scg if (i != 0xffff) { 67770324Scg codec->extcaps = i & 0x3fff; 67870324Scg codec->extid = (i & 0xc000) >> 14; 679109818Sorion codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 68070324Scg } 68158521Scg } 68258521Scg 683170342Sariff for (i = 0; i < AC97_MIXER_SIZE; i++) { 684102302Sorion codec->mix[i] = ac97mixtable_default[i]; 685102302Sorion } 686102302Sorion ac97_fix_auxout(codec); 687119209Sorion ac97_fix_tone(codec); 688109818Sorion if (codec_patch) 689109818Sorion codec_patch(codec); 690102302Sorion 691170342Sariff for (i = 0; i < AC97_MIXER_SIZE; i++) { 69266307Scg k = codec->noext? codec->mix[i].enable : 1; 693149949Snetchild reg = codec->mix[i].reg; 694149949Snetchild if (reg < 0) 695149949Snetchild reg = -reg; 696149949Snetchild if (k && reg) { 697149949Snetchild j = old = ac97_rdcd(codec, reg); 698149949Snetchild /* 699149949Snetchild * Test for mute bit (except for AC97_MIX_TONE, 700149949Snetchild * where we simply assume it as available). 701149949Snetchild */ 702149949Snetchild if (codec->mix[i].mute) { 703149949Snetchild ac97_wrcd(codec, reg, j | 0x8000); 704149949Snetchild j = ac97_rdcd(codec, reg); 705149949Snetchild } else 706149949Snetchild j |= 0x8000; 707148602Snetchild if ((j & 0x8000)) { 708149949Snetchild /* 709149949Snetchild * Test whether the control width should be 710149949Snetchild * 4, 5 or 6 bit. For 5bit register, we should 711149949Snetchild * test it whether it's really 5 or 6bit. Leave 712149949Snetchild * 4bit register alone, because sometimes an 713149949Snetchild * attempt to write past 4th bit may cause 714149949Snetchild * incorrect result especially for AC97_MIX_BEEP 715149949Snetchild * (ac97 2.3). 716149949Snetchild */ 717149949Snetchild bit = codec->mix[i].bits; 718149949Snetchild if (bit == 5) 719149949Snetchild bit++; 720149949Snetchild j = ((1 << bit) - 1) << codec->mix[i].ofs; 721149949Snetchild ac97_wrcd(codec, reg, 722149949Snetchild j | (codec->mix[i].mute ? 0x8000 : 0)); 723149949Snetchild k = ac97_rdcd(codec, reg) & j; 724149949Snetchild k >>= codec->mix[i].ofs; 725149949Snetchild if (reg == AC97_MIX_TONE && 726149949Snetchild ((k & 0x0001) == 0x0000)) 727148602Snetchild k >>= 1; 728149949Snetchild for (j = 0; k >> j; j++) 729149949Snetchild ; 730148602Snetchild if (j != 0) { 731149949Snetchild#if 0 732149949Snetchild device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 733149949Snetchild i, k, bit, codec->mix[i].bits, j); 734149949Snetchild#endif 735148602Snetchild codec->mix[i].enable = 1; 736148602Snetchild codec->mix[i].bits = j; 737153865Sariff } else if (reg == AC97_MIX_BEEP) { 738153865Sariff /* 739153865Sariff * Few codec such as CX20468-21 does 740153865Sariff * have this control register, although 741153865Sariff * the only usable part is the mute bit. 742153865Sariff */ 743153865Sariff codec->mix[i].enable = 1; 744148602Snetchild } else 745148602Snetchild codec->mix[i].enable = 0; 746148602Snetchild } else 747148602Snetchild codec->mix[i].enable = 0; 748149949Snetchild ac97_wrcd(codec, reg, old); 74966013Scg } 750148602Snetchild#if 0 751148602Snetchild printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 752148602Snetchild#endif 75366013Scg } 75450724Scg 755111679Sorion device_printf(codec->dev, "<%s>\n", 756111679Sorion ac97_hw_desc(codec->id, vname, cname, desc)); 757110258Sorion 75850724Scg if (bootverbose) { 759149949Snetchild if (codec->flags & AC97_F_RDCD_BUG) 760149949Snetchild device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 761111679Sorion device_printf(codec->dev, "Codec features "); 76258384Scg for (i = j = 0; i < 10; i++) 76358384Scg if (codec->caps & (1 << i)) 76458384Scg printf("%s%s", j++? ", " : "", ac97feature[i]); 76558384Scg printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 76658384Scg printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 76758384Scg 76858384Scg if (codec->extcaps != 0 || codec->extid) { 769111679Sorion device_printf(codec->dev, "%s codec", 770111679Sorion codec->extid? "Secondary" : "Primary"); 77158384Scg if (codec->extcaps) 77258384Scg printf(" extended features "); 77358384Scg for (i = j = 0; i < 14; i++) 77458384Scg if (codec->extcaps & (1 << i)) 77558384Scg printf("%s%s", j++? ", " : "", ac97extfeature[i]); 77658384Scg printf("\n"); 77750724Scg } 77850724Scg } 77950724Scg 780148602Snetchild i = 0; 781148602Snetchild while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 782148602Snetchild if (++i == 100) { 783148602Snetchild device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 784148602Snetchild break; 785148602Snetchild } 786148602Snetchild DELAY(1000); 787148602Snetchild } 788148602Snetchild if (bootverbose) 789148602Snetchild device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 79074763Scg snd_mtxunlock(codec->lock); 79150724Scg return 0; 79250724Scg} 79350724Scg 79467652Scgstatic unsigned 79567652Scgac97_reinitmixer(struct ac97_info *codec) 79667652Scg{ 79774763Scg snd_mtxlock(codec->lock); 79870134Scg codec->count = AC97_INIT(codec->methods, codec->devinfo); 79970134Scg if (codec->count == 0) { 80070134Scg device_printf(codec->dev, "ac97 codec init failed\n"); 80174763Scg snd_mtxunlock(codec->lock); 80270134Scg return ENODEV; 80370134Scg } 80467652Scg 805109818Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 80695499Sorion ac97_reset(codec); 807109818Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 80867652Scg 80967652Scg if (!codec->noext) { 810109818Sorion ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 811109818Sorion if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 81287623Sguido != codec->extstat) 81367652Scg device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 81487623Sguido codec->extstat, 815109818Sorion ac97_rdcd(codec, AC97_REGEXT_STAT) & 816109818Sorion AC97_EXTCAPS); 81767652Scg } 81867652Scg 819109818Sorion if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 82067652Scg device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 82174763Scg snd_mtxunlock(codec->lock); 82267652Scg return 0; 82367652Scg} 82467652Scg 82550724Scgstruct ac97_info * 82670134Scgac97_create(device_t dev, void *devinfo, kobj_class_t cls) 82750724Scg{ 82850724Scg struct ac97_info *codec; 829193640Sariff int i; 83050724Scg 831170873Sariff codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO); 832170342Sariff snprintf(codec->name, sizeof(codec->name), "%s:ac97", 833170342Sariff device_get_nameunit(dev)); 83493816Sjhb codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 835162738Sariff codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); 83670134Scg codec->dev = dev; 83770134Scg codec->devinfo = devinfo; 83878668Scg codec->flags = 0; 839193640Sariff 840162738Sariff if (resource_int_value(device_get_name(dev), device_get_unit(dev), 841193640Sariff "eapdinv", &i) == 0 && i != 0) 842193640Sariff codec->flags |= AC97_F_EAPD_INV; 843193640Sariff 844193640Sariff if (resource_int_value(device_get_name(dev), device_get_unit(dev), 845193640Sariff "softpcmvol", &i) == 0 && i != 0) 846193640Sariff pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); 847193640Sariff 84850724Scg return codec; 84950724Scg} 85050724Scg 85165340Scgvoid 85265340Scgac97_destroy(struct ac97_info *codec) 85365340Scg{ 85474763Scg snd_mtxlock(codec->lock); 85570134Scg if (codec->methods != NULL) 85670134Scg kobj_delete(codec->methods, M_AC97); 85774763Scg snd_mtxfree(codec->lock); 85870134Scg free(codec, M_AC97); 85965340Scg} 86065340Scg 86178668Scgvoid 86278668Scgac97_setflags(struct ac97_info *codec, u_int32_t val) 86378668Scg{ 86478668Scg codec->flags = val; 86578668Scg} 86678668Scg 86778668Scgu_int32_t 86878668Scgac97_getflags(struct ac97_info *codec) 86978668Scg{ 87078668Scg return codec->flags; 87178668Scg} 87278668Scg 87370134Scg/* -------------------------------------------------------------------- */ 87470134Scg 87550724Scgstatic int 876164614Sariffsysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) 877164614Sariff{ 878164614Sariff struct ac97_info *codec; 879164614Sariff int ea, inv, err = 0; 880164614Sariff u_int16_t val; 881164614Sariff 882164614Sariff codec = oidp->oid_arg1; 883164614Sariff if (codec == NULL || codec->id == 0 || codec->lock == NULL) 884164614Sariff return EINVAL; 885164614Sariff snd_mtxlock(codec->lock); 886164614Sariff val = ac97_rdcd(codec, AC97_REG_POWER); 887164614Sariff inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1; 888164614Sariff ea = (val >> 15) ^ inv; 889164614Sariff snd_mtxunlock(codec->lock); 890170289Sdwmalone err = sysctl_handle_int(oidp, &ea, 0, req); 891164614Sariff if (err == 0 && req->newptr != NULL) { 892164614Sariff if (ea != 0 && ea != 1) 893164614Sariff return EINVAL; 894164614Sariff if (ea != ((val >> 15) ^ inv)) { 895164614Sariff snd_mtxlock(codec->lock); 896164614Sariff ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000); 897164614Sariff snd_mtxunlock(codec->lock); 898164614Sariff } 899164614Sariff } 900164614Sariff return err; 901164614Sariff} 902164614Sariff 903164614Sariffstatic void 904164614Sariffac97_init_sysctl(struct ac97_info *codec) 905164614Sariff{ 906164614Sariff u_int16_t orig, val; 907164614Sariff 908164614Sariff if (codec == NULL || codec->dev == NULL) 909164614Sariff return; 910164614Sariff snd_mtxlock(codec->lock); 911164614Sariff orig = ac97_rdcd(codec, AC97_REG_POWER); 912164614Sariff ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000); 913164614Sariff val = ac97_rdcd(codec, AC97_REG_POWER); 914164614Sariff ac97_wrcd(codec, AC97_REG_POWER, orig); 915164614Sariff snd_mtxunlock(codec->lock); 916164614Sariff if ((val & 0x8000) == (orig & 0x8000)) 917164614Sariff return; 918164614Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev), 919164614Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)), 920164614Sariff OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW, 921164614Sariff codec, sizeof(codec), sysctl_hw_snd_ac97_eapd, 922164614Sariff "I", "AC97 External Amplifier"); 923164614Sariff} 924164614Sariff 925164614Sariffstatic int 92674763Scgac97mix_init(struct snd_mixer *m) 92750724Scg{ 92850724Scg struct ac97_info *codec = mix_getdevinfo(m); 92966013Scg u_int32_t i, mask; 93066013Scg 93158384Scg if (codec == NULL) 93258384Scg return -1; 93366013Scg 93458905Scg if (ac97_initmixer(codec)) 93558905Scg return -1; 93666013Scg 937162738Sariff switch (codec->id) { 938162738Sariff case 0x41445374: /* AD1981B */ 939173039Sariff switch (codec->subvendor) { 940173039Sariff case 0x02d91014: 941168861Sariff /* 942168861Sariff * IBM Thinkcentre: 943173039Sariff * 944173039Sariff * Tie "ogain" and "phout" to "vol" since its 945168861Sariff * master volume is basically useless and can't 946168861Sariff * control anything. 947168861Sariff */ 948164614Sariff mask = 0; 949164614Sariff if (codec->mix[SOUND_MIXER_OGAIN].enable) 950164614Sariff mask |= SOUND_MASK_OGAIN; 951164614Sariff if (codec->mix[SOUND_MIXER_PHONEOUT].enable) 952164614Sariff mask |= SOUND_MASK_PHONEOUT; 953164614Sariff if (codec->mix[SOUND_MIXER_VOLUME].enable) 954164614Sariff mix_setparentchild(m, SOUND_MIXER_VOLUME, 955164614Sariff mask); 956164614Sariff else { 957164614Sariff mix_setparentchild(m, SOUND_MIXER_VOLUME, 958164614Sariff mask); 959164614Sariff mix_setrealdev(m, SOUND_MIXER_VOLUME, 960164614Sariff SOUND_MIXER_NONE); 961164614Sariff } 962173039Sariff break; 963173039Sariff case 0x099c103c: 964173039Sariff /* 965173039Sariff * HP nx6110: 966173039Sariff * 967173039Sariff * By default, "vol" is controlling internal speakers 968173039Sariff * (not a master volume!) and "ogain" is controlling 969173039Sariff * headphone. Enable dummy "phout" so it can be 970173039Sariff * remapped to internal speakers and virtualize 971173039Sariff * "vol" to control both. 972173039Sariff */ 973173039Sariff codec->mix[SOUND_MIXER_OGAIN].enable = 1; 974173039Sariff codec->mix[SOUND_MIXER_PHONEOUT].enable = 1; 975173039Sariff mix_setrealdev(m, SOUND_MIXER_PHONEOUT, 976173039Sariff SOUND_MIXER_VOLUME); 977173039Sariff mix_setrealdev(m, SOUND_MIXER_VOLUME, 978173039Sariff SOUND_MIXER_NONE); 979173039Sariff mix_setparentchild(m, SOUND_MIXER_VOLUME, 980173039Sariff SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT); 981173039Sariff break; 982173039Sariff default: 983173039Sariff break; 984162738Sariff } 985162738Sariff break; 986162738Sariff case 0x434d4941: /* CMI9738 */ 987162738Sariff case 0x434d4961: /* CMI9739 */ 988162738Sariff case 0x434d4978: /* CMI9761 */ 989162738Sariff case 0x434d4982: /* CMI9761 */ 990162738Sariff case 0x434d4983: /* CMI9761 */ 991162738Sariff bzero(&codec->mix[SOUND_MIXER_PCM], 992162738Sariff sizeof(codec->mix[SOUND_MIXER_PCM])); 993170207Sariff pcm_setflags(codec->dev, pcm_getflags(codec->dev) | 994170207Sariff SD_F_SOFTPCMVOL); 995162738Sariff /* XXX How about master volume ? */ 996162738Sariff break; 997162738Sariff default: 998162738Sariff break; 999162738Sariff } 1000162738Sariff 1001193640Sariff if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL) 1002193640Sariff ac97_wrcd(codec, AC97_MIX_PCM, 0); 1003162738Sariff#if 0 1004162738Sariff /* XXX For the sake of debugging purposes */ 1005162738Sariff mix_setparentchild(m, SOUND_MIXER_VOLUME, 1006162738Sariff SOUND_MASK_PCM | SOUND_MASK_CD); 1007162738Sariff mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 1008162738Sariff ac97_wrcd(codec, AC97_MIX_MASTER, 0); 1009162738Sariff#endif 1010162738Sariff 101166013Scg mask = 0; 1012170342Sariff for (i = 0; i < AC97_MIXER_SIZE; i++) 101366013Scg mask |= codec->mix[i].enable? 1 << i : 0; 101466013Scg mix_setdevs(m, mask); 101566013Scg 101666013Scg mask = 0; 1017170342Sariff for (i = 0; i < AC97_MIXER_SIZE; i++) 101866013Scg mask |= codec->mix[i].recidx? 1 << i : 0; 101966013Scg mix_setrecdevs(m, mask); 1020164614Sariff 1021164614Sariff ac97_init_sysctl(codec); 1022164614Sariff 102350724Scg return 0; 102450724Scg} 102550724Scg 102650724Scgstatic int 102774763Scgac97mix_uninit(struct snd_mixer *m) 102865340Scg{ 102965340Scg struct ac97_info *codec = mix_getdevinfo(m); 103066013Scg 103165340Scg if (codec == NULL) 103265340Scg return -1; 103365340Scg /* 103465340Scg if (ac97_uninitmixer(codec)) 103565340Scg return -1; 103665340Scg */ 103765340Scg ac97_destroy(codec); 103865340Scg return 0; 103965340Scg} 104065340Scg 104165340Scgstatic int 104274763Scgac97mix_reinit(struct snd_mixer *m) 104367652Scg{ 104467652Scg struct ac97_info *codec = mix_getdevinfo(m); 104567652Scg 104667652Scg if (codec == NULL) 104767652Scg return -1; 104867652Scg return ac97_reinitmixer(codec); 104967652Scg} 105067652Scg 105167652Scgstatic int 105274763Scgac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 105350724Scg{ 105450724Scg struct ac97_info *codec = mix_getdevinfo(m); 105566013Scg 1056170342Sariff if (codec == NULL || dev >= AC97_MIXER_SIZE) 105758384Scg return -1; 105850724Scg return ac97_setmixer(codec, dev, left, right); 105950724Scg} 106050724Scg 1061193640Sariffstatic u_int32_t 106274763Scgac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 106350724Scg{ 106450724Scg int i; 106550724Scg struct ac97_info *codec = mix_getdevinfo(m); 106666013Scg 106758384Scg if (codec == NULL) 106858384Scg return -1; 1069170342Sariff for (i = 0; i < AC97_MIXER_SIZE; i++) 107058384Scg if ((src & (1 << i)) != 0) 107158384Scg break; 1072193640Sariff return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU; 107350724Scg} 107450724Scg 107570134Scgstatic kobj_method_t ac97mixer_methods[] = { 107670134Scg KOBJMETHOD(mixer_init, ac97mix_init), 107770134Scg KOBJMETHOD(mixer_uninit, ac97mix_uninit), 107870134Scg KOBJMETHOD(mixer_reinit, ac97mix_reinit), 107970134Scg KOBJMETHOD(mixer_set, ac97mix_set), 108070134Scg KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 1081193640Sariff KOBJMETHOD_END 108250724Scg}; 108370134ScgMIXER_DECLARE(ac97mixer); 108450724Scg 108570134Scg/* -------------------------------------------------------------------- */ 108670134Scg 108770134Scgkobj_class_t 108870134Scgac97_getmixerclass(void) 108970134Scg{ 109070134Scg return &ac97mixer_class; 109170134Scg} 1092