ac97.c revision 162974
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 2753465Scg#include <dev/sound/pcm/sound.h> 2853465Scg#include <dev/sound/pcm/ac97.h> 29109818Sorion#include <dev/sound/pcm/ac97_patch.h> 3050724Scg 3170134Scg#include "mixer_if.h" 3250724Scg 3382180ScgSND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/ac97.c 162974 2006-10-02 20:46:34Z ariff $"); 3482180Scg 3570134ScgMALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 3650724Scg 3778668Scgstruct ac97mixtable_entry { 38113783Sorion int reg:8; /* register index */ 39113783Sorion /* reg < 0 if inverted polarity */ 40113783Sorion unsigned bits:4; /* width of control field */ 41113783Sorion unsigned ofs:4; /* offset (only if stereo=0) */ 42113783Sorion unsigned stereo:1; /* set for stereo controls */ 43113783Sorion unsigned mute:1; /* bit15 is MUTE */ 44113783Sorion unsigned recidx:4; /* index in rec mux */ 45113783Sorion unsigned mask:1; /* use only masked bits */ 46113783Sorion unsigned enable:1; /* entry is enabled */ 4778668Scg}; 4878668Scg 4974763Scg#define AC97_NAMELEN 16 5074763Scgstruct ac97_info { 5174763Scg kobj_t methods; 5274763Scg device_t dev; 5374763Scg void *devinfo; 54111679Sorion u_int32_t id; 5574763Scg unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5678668Scg u_int32_t flags; 5774763Scg struct ac97mixtable_entry mix[32]; 5874763Scg char name[AC97_NAMELEN]; 59107285Scg struct mtx *lock; 6074763Scg}; 6174763Scg 62111679Sorionstruct ac97_vendorid { 63111679Sorion u_int32_t id; 64111679Sorion const char *name; 65111679Sorion}; 66111679Sorion 6750724Scgstruct ac97_codecid { 68111679Sorion u_int32_t id; 69111679Sorion u_int8_t stepmask; 70111679Sorion u_int8_t noext:1; 71111679Sorion char *name; 72109818Sorion ac97_patch patch; 7350724Scg}; 7450724Scg 7550724Scgstatic const struct ac97mixtable_entry ac97mixtable_default[32] = { 76113783Sorion /* [offset] reg bits of st mu re mk en */ 7766013Scg [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 78119209Sorion [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 7966013Scg [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 8066013Scg [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 8166013Scg [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 8266013Scg [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 8366013Scg [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 8466013Scg [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 8566013Scg [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 86113783Sorion [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 87113783Sorion /* use igain for the mic 20dB boost */ 88113783Sorion [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 8966013Scg [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 9066013Scg [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 9166013Scg [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 9266013Scg [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 9350724Scg}; 9450724Scg 95111679Sorionstatic const struct ac97_vendorid ac97vendorid[] = { 96111679Sorion { 0x41445300, "Analog Devices" }, 97111679Sorion { 0x414b4d00, "Asahi Kasei" }, 98111679Sorion { 0x414c4300, "Realtek" }, 99111679Sorion { 0x414c4700, "Avance Logic" }, 100111679Sorion { 0x43525900, "Cirrus Logic" }, 101111679Sorion { 0x434d4900, "C-Media Electronics" }, 102111679Sorion { 0x43585400, "Conexant" }, 103124875Smatk { 0x44543000, "Diamond Technology" }, 104113788Sorion { 0x454d4300, "eMicro" }, 105111679Sorion { 0x45838300, "ESS Technology" }, 106124875Smatk { 0x48525300, "Intersil" }, 107111679Sorion { 0x49434500, "ICEnsemble" }, 108124875Smatk { 0x49544500, "ITE, Inc." }, 109111679Sorion { 0x4e534300, "National Semiconductor" }, 110111679Sorion { 0x50534300, "Philips Semiconductor" }, 111111679Sorion { 0x83847600, "SigmaTel" }, 112124875Smatk { 0x53494c00, "Silicon Laboratories" }, 113111679Sorion { 0x54524100, "TriTech" }, 114124875Smatk { 0x54584e00, "Texas Instruments" }, 115111679Sorion { 0x56494100, "VIA Technologies" }, 116124875Smatk { 0x57454300, "Winbond" }, 117111679Sorion { 0x574d4c00, "Wolfson" }, 118111679Sorion { 0x594d4800, "Yamaha" }, 119149949Snetchild /* 120149949Snetchild * XXX This is a fluke, really! The real vendor 121149949Snetchild * should be SigmaTel, not this! This should be 122149949Snetchild * removed someday! 123149949Snetchild */ 124119547Sorion { 0x01408300, "Creative" }, 125111679Sorion { 0x00000000, NULL } 126111679Sorion}; 127111679Sorion 12850724Scgstatic struct ac97_codecid ac97codecid[] = { 129111679Sorion { 0x41445303, 0x00, 0, "AD1819", 0 }, 130111679Sorion { 0x41445340, 0x00, 0, "AD1881", 0 }, 131111679Sorion { 0x41445348, 0x00, 0, "AD1881A", 0 }, 132111679Sorion { 0x41445360, 0x00, 0, "AD1885", 0 }, 133111679Sorion { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 134113057Sorion { 0x41445362, 0x00, 0, "AD1887", 0 }, 135113057Sorion { 0x41445363, 0x00, 0, "AD1886A", 0 }, 136144865Sscottl { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 137119209Sorion { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 138113057Sorion { 0x41445372, 0x00, 0, "AD1981A", 0 }, 139113057Sorion { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, 140119209Sorion { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 141154094Sariff { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, 142111679Sorion { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 143111679Sorion { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 144111679Sorion { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 145124875Smatk { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 146124875Smatk { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 147111679Sorion { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 148119209Sorion { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 149111679Sorion { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 150111679Sorion { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 151111679Sorion { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 152152815Sariff { 0x414c4752, 0x0f, 0, "ALC250", 0 }, 153121032Sdes { 0x414c4760, 0x0f, 0, "ALC655", 0 }, 154152939Syongari { 0x414c4770, 0x0f, 0, "ALC203", 0 }, 155122515Skuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 156124875Smatk { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 157111679Sorion { 0x43525900, 0x07, 0, "CS4297", 0 }, 158111679Sorion { 0x43525910, 0x07, 0, "CS4297A", 0 }, 159111679Sorion { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 160129044Ssanpei { 0x4352592d, 0x07, 0, "CS4294", 0 }, 161111679Sorion { 0x43525930, 0x07, 0, "CS4299", 0 }, 162111679Sorion { 0x43525940, 0x07, 0, "CS4201", 0 }, 163112150Sorion { 0x43525958, 0x07, 0, "CS4205", 0 }, 164111679Sorion { 0x43525960, 0x07, 0, "CS4291A", 0 }, 165150825Snetchild { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, 166111679Sorion { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 167150825Snetchild { 0x434d4978, 0x00, 0, "CMI9761", 0 }, 168150825Snetchild { 0x434d4982, 0x00, 0, "CMI9761", 0 }, 169150825Snetchild { 0x434d4983, 0x00, 0, "CMI9761", 0 }, 170124875Smatk { 0x43585421, 0x00, 0, "HSD11246", 0 }, 171124875Smatk { 0x43585428, 0x07, 0, "CX20468", 0 }, 172152422Sariff { 0x43585430, 0x00, 0, "CX20468-21", 0 }, 173124875Smatk { 0x44543000, 0x00, 0, "DT0398", 0 }, 174113788Sorion { 0x454d4323, 0x00, 0, "EM28023", 0 }, 175113788Sorion { 0x454d4328, 0x00, 0, "EM28028", 0 }, 176111679Sorion { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 177124875Smatk { 0x48525300, 0x00, 0, "HMP9701", 0 }, 178111679Sorion { 0x49434501, 0x00, 0, "ICE1230", 0 }, 179111679Sorion { 0x49434511, 0x00, 0, "ICE1232", 0 }, 180111679Sorion { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 181119250Sorion { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 182124875Smatk { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 183124875Smatk { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 184111679Sorion { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 185111679Sorion { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 186111679Sorion { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 187111679Sorion { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 188124875Smatk { 0x4e534331, 0x00, 0, "LM4549", 0 }, 189111679Sorion { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 190111679Sorion { 0x4e534350, 0x00, 0, "LM4550", 0 }, 191111679Sorion { 0x50534301, 0x00, 0, "UCB1510", 0 }, 192111679Sorion { 0x50534304, 0x00, 0, "UCB1400", 0 }, 193111679Sorion { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 194111679Sorion { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 195113788Sorion { 0x83847605, 0x00, 0, "STAC9704", 0 }, 196111679Sorion { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 197111679Sorion { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 198111679Sorion { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 199111679Sorion { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 200111679Sorion { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 201111679Sorion { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 202111679Sorion { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 203111679Sorion { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 204111679Sorion { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 205150825Snetchild { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 206111679Sorion { 0x53494c22, 0x00, 0, "Si3036", 0 }, 207111679Sorion { 0x53494c23, 0x00, 0, "Si3038", 0 }, 208111679Sorion { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 209111679Sorion { 0x54524106, 0x00, 0, "TR28026", 0 }, 210111679Sorion { 0x54524108, 0x00, 0, "TR28028", 0 }, 211111679Sorion { 0x54524123, 0x00, 0, "TR28602", 0 }, 212124875Smatk { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 213124875Smatk { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 214111679Sorion { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 215152422Sariff { 0x56494170, 0x00, 0, "VIA1617A", 0 }, 216111679Sorion { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 217111679Sorion { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 218111679Sorion { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 219111679Sorion { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 220124875Smatk { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 221124875Smatk { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 222124875Smatk { 0x57454301, 0x00, 0, "W83971D", 0 }, 223111679Sorion { 0x594d4800, 0x00, 0, "YMF743", 0 }, 224111679Sorion { 0x594d4802, 0x00, 0, "YMF752", 0 }, 225111679Sorion { 0x594d4803, 0x00, 0, "YMF753", 0 }, 226149949Snetchild /* 227149949Snetchild * XXX This is a fluke, really! The real codec 228149949Snetchild * should be STAC9704, not this! This should be 229149949Snetchild * removed someday! 230149949Snetchild */ 231119547Sorion { 0x01408384, 0x00, 0, "EV1938", 0 }, 232111679Sorion { 0, 0, 0, NULL, 0 } 23350724Scg}; 23450724Scg 23550724Scgstatic char *ac97enhancement[] = { 23660960Scg "no 3D Stereo Enhancement", 23750724Scg "Analog Devices Phat Stereo", 23850724Scg "Creative Stereo Enhancement", 23950724Scg "National Semi 3D Stereo Enhancement", 24050724Scg "Yamaha Ymersion", 24150724Scg "BBE 3D Stereo Enhancement", 24250724Scg "Crystal Semi 3D Stereo Enhancement", 24350724Scg "Qsound QXpander", 24450724Scg "Spatializer 3D Stereo Enhancement", 24550724Scg "SRS 3D Stereo Enhancement", 24650724Scg "Platform Tech 3D Stereo Enhancement", 24750724Scg "AKM 3D Audio", 24850724Scg "Aureal Stereo Enhancement", 24950724Scg "Aztech 3D Enhancement", 25050724Scg "Binaura 3D Audio Enhancement", 25150724Scg "ESS Technology Stereo Enhancement", 25250724Scg "Harman International VMAx", 25350724Scg "Nvidea 3D Stereo Enhancement", 25450724Scg "Philips Incredible Sound", 25550724Scg "Texas Instruments 3D Stereo Enhancement", 25650724Scg "VLSI Technology 3D Stereo Enhancement", 25750724Scg "TriTech 3D Stereo Enhancement", 25850724Scg "Realtek 3D Stereo Enhancement", 25950724Scg "Samsung 3D Stereo Enhancement", 26050724Scg "Wolfson Microelectronics 3D Enhancement", 26150724Scg "Delta Integration 3D Enhancement", 26250724Scg "SigmaTel 3D Enhancement", 26350724Scg "Reserved 27", 26450724Scg "Rockwell 3D Stereo Enhancement", 26550724Scg "Reserved 29", 26650724Scg "Reserved 30", 26750724Scg "Reserved 31" 26850724Scg}; 26950724Scg 27050724Scgstatic char *ac97feature[] = { 27150724Scg "mic channel", 27250724Scg "reserved", 27350724Scg "tone", 27450724Scg "simulated stereo", 27550724Scg "headphone", 27650724Scg "bass boost", 27750724Scg "18 bit DAC", 27850724Scg "20 bit DAC", 27950724Scg "18 bit ADC", 28050724Scg "20 bit ADC" 28150724Scg}; 28250724Scg 28358384Scgstatic char *ac97extfeature[] = { 28458384Scg "variable rate PCM", 28558384Scg "double rate PCM", 28658384Scg "reserved 1", 28758384Scg "variable rate mic", 28858384Scg "reserved 2", 28958384Scg "reserved 3", 29058384Scg "center DAC", 29158384Scg "surround DAC", 29258384Scg "LFE DAC", 29358384Scg "AMAP", 29458384Scg "reserved 4", 29558384Scg "reserved 5", 29658384Scg "reserved 6", 29758384Scg "reserved 7", 29858384Scg}; 29958384Scg 300109818Sorionu_int16_t 301109818Sorionac97_rdcd(struct ac97_info *codec, int reg) 30258384Scg{ 303149949Snetchild if (codec->flags & AC97_F_RDCD_BUG) { 304149949Snetchild u_int16_t i[2], j = 100; 305149949Snetchild 306149949Snetchild i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 307149949Snetchild i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 308149949Snetchild while (i[0] != i[1] && j) 309149949Snetchild i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 310149949Snetchild#if 0 311149949Snetchild if (j < 100) { 312149949Snetchild device_printf(codec->dev, "%s(): Inconsistent register value at" 313149949Snetchild " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 314149949Snetchild } 315149949Snetchild#endif 316149949Snetchild return i[!(j & 1)]; 317149949Snetchild } 31870134Scg return AC97_READ(codec->methods, codec->devinfo, reg); 31958384Scg} 32058384Scg 321109818Sorionvoid 322109818Sorionac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 32358384Scg{ 32470134Scg AC97_WRITE(codec->methods, codec->devinfo, reg, val); 32558384Scg} 32658384Scg 32795499Sorionstatic void 32895499Sorionac97_reset(struct ac97_info *codec) 32995499Sorion{ 33095499Sorion u_int32_t i, ps; 331109818Sorion ac97_wrcd(codec, AC97_REG_RESET, 0); 33295499Sorion for (i = 0; i < 500; i++) { 333109818Sorion ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 334107285Scg if (ps == AC97_POWER_STATUS) 33595499Sorion return; 33695499Sorion DELAY(1000); 33795499Sorion } 338110258Sorion device_printf(codec->dev, "AC97 reset timed out.\n"); 33995499Sorion} 34095499Sorion 34158384Scgint 34258384Scgac97_setrate(struct ac97_info *codec, int which, int rate) 34358384Scg{ 34458384Scg u_int16_t v; 34558384Scg 34658384Scg switch(which) { 347107285Scg case AC97_REGEXT_FDACRATE: 34858384Scg case AC97_REGEXT_SDACRATE: 34958384Scg case AC97_REGEXT_LDACRATE: 350107285Scg case AC97_REGEXT_LADCRATE: 35158384Scg case AC97_REGEXT_MADCRATE: 35258384Scg break; 35358384Scg 35458384Scg default: 35558384Scg return -1; 35658384Scg } 35758384Scg 35874763Scg snd_mtxlock(codec->lock); 35958384Scg if (rate != 0) { 36058384Scg v = rate; 36158384Scg if (codec->extstat & AC97_EXTCAP_DRA) 36258384Scg v >>= 1; 363109818Sorion ac97_wrcd(codec, which, v); 36458384Scg } 365109818Sorion v = ac97_rdcd(codec, which); 36658384Scg if (codec->extstat & AC97_EXTCAP_DRA) 36758384Scg v <<= 1; 36874763Scg snd_mtxunlock(codec->lock); 36958384Scg return v; 37058384Scg} 37158384Scg 37258384Scgint 37358384Scgac97_setextmode(struct ac97_info *codec, u_int16_t mode) 37458384Scg{ 37558384Scg mode &= AC97_EXTCAPS; 37686707Sorion if ((mode & ~codec->extcaps) != 0) { 377107285Scg device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 37886707Sorion mode); 37958384Scg return -1; 38086707Sorion } 38174763Scg snd_mtxlock(codec->lock); 382109818Sorion ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 383109818Sorion codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 38474763Scg snd_mtxunlock(codec->lock); 38558384Scg return (mode == codec->extstat)? 0 : -1; 38658384Scg} 38758384Scg 38867652Scgu_int16_t 38967652Scgac97_getextmode(struct ac97_info *codec) 39067652Scg{ 39167652Scg return codec->extstat; 39267652Scg} 39367652Scg 39467652Scgu_int16_t 39567652Scgac97_getextcaps(struct ac97_info *codec) 39667652Scg{ 39767652Scg return codec->extcaps; 39867652Scg} 39967652Scg 40083612Scgu_int16_t 40183612Scgac97_getcaps(struct ac97_info *codec) 40283612Scg{ 40383612Scg return codec->caps; 40483612Scg} 40583612Scg 40650724Scgstatic int 40750724Scgac97_setrecsrc(struct ac97_info *codec, int channel) 40850724Scg{ 40950724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 41066013Scg 41150724Scg if (e->recidx > 0) { 41250724Scg int val = e->recidx - 1; 41350724Scg val |= val << 8; 41474763Scg snd_mtxlock(codec->lock); 415109818Sorion ac97_wrcd(codec, AC97_REG_RECSEL, val); 41674763Scg snd_mtxunlock(codec->lock); 41750724Scg return 0; 41858384Scg } else 41958384Scg return -1; 42050724Scg} 42150724Scg 42250724Scgstatic int 42350724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 42450724Scg{ 42550724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 42666013Scg 42766013Scg if (e->reg && e->enable && e->bits) { 428113783Sorion int mask, max, val, reg; 42950724Scg 430113783Sorion reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 431113783Sorion max = (1 << e->bits) - 1; /* actual range */ 432113783Sorion mask = (max << 8) | max; /* bits of interest */ 433113783Sorion 43458384Scg if (!e->stereo) 43558384Scg right = left; 436113783Sorion 437113783Sorion /* 438113783Sorion * Invert the range if the polarity requires so, 439113783Sorion * then scale to 0..max-1 to compute the value to 440113783Sorion * write into the codec, and scale back to 0..100 441113783Sorion * for the return value. 442113783Sorion */ 44350724Scg if (e->reg > 0) { 44450724Scg left = 100 - left; 44550724Scg right = 100 - right; 44650724Scg } 44750724Scg 44850724Scg left = (left * max) / 100; 44950724Scg right = (right * max) / 100; 45050724Scg 45150724Scg val = (left << 8) | right; 45250724Scg 45350724Scg left = (left * 100) / max; 45450724Scg right = (right * 100) / max; 45550724Scg 45650724Scg if (e->reg > 0) { 45750724Scg left = 100 - left; 45850724Scg right = 100 - right; 45950724Scg } 46050724Scg 461113783Sorion /* 462113783Sorion * For mono controls, trim val and mask, also taking 463119209Sorion * care of e->ofs (offset of control field). 464113783Sorion */ 465113783Sorion if (e->ofs) { 46650724Scg val &= max; 46750724Scg val <<= e->ofs; 468113783Sorion mask = (max << e->ofs); 46950724Scg } 470113783Sorion 471113783Sorion /* 472113783Sorion * If we have a mute bit, add it to the mask and 473113783Sorion * update val and set mute if both channels require a 474113783Sorion * zero volume. 475113783Sorion */ 476113783Sorion if (e->mute == 1) { 477113783Sorion mask |= AC97_MUTE; 478113783Sorion if (left == 0 && right == 0) 479113783Sorion val = AC97_MUTE; 480113783Sorion } 481113783Sorion 482113783Sorion /* 483113783Sorion * If the mask bit is set, do not alter the other bits. 484113783Sorion */ 48574763Scg snd_mtxlock(codec->lock); 486113783Sorion if (e->mask) { 487149949Snetchild int cur = ac97_rdcd(codec, reg); 488113783Sorion val |= cur & ~(mask); 489113783Sorion } 490109818Sorion ac97_wrcd(codec, reg, val); 49174763Scg snd_mtxunlock(codec->lock); 49250724Scg return left | (right << 8); 49366013Scg } else { 494149949Snetchild#if 0 495149949Snetchild printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 496149949Snetchild#endif 49758384Scg return -1; 49866013Scg } 49950724Scg} 50050724Scg 501102302Sorionstatic void 502102302Sorionac97_fix_auxout(struct ac97_info *codec) 503102302Sorion{ 504119375Sorion int keep_ogain; 505119375Sorion 506119209Sorion /* 507119375Sorion * By default, The ac97 aux_out register (0x04) corresponds to OSS's 508119375Sorion * OGAIN setting. 509102302Sorion * 510119375Sorion * We first check whether aux_out is a valid register. If not 511119375Sorion * we may not want to keep ogain. 512119209Sorion */ 513119375Sorion keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 514119209Sorion 515119209Sorion /* 516119209Sorion * Determine what AUX_OUT really means, it can be: 517119209Sorion * 518102302Sorion * 1. Headphone out. 519102302Sorion * 2. 4-Channel Out 520102302Sorion * 3. True line level out (effectively master volume). 521102302Sorion * 522102302Sorion * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 523102302Sorion */ 524119209Sorion if (codec->extcaps & AC97_EXTCAP_SDAC && 525119209Sorion ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 526119375Sorion codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 527119375Sorion keep_ogain = 1; 528102302Sorion } 529119375Sorion 530119375Sorion if (keep_ogain == 0) { 531119375Sorion bzero(&codec->mix[SOUND_MIXER_OGAIN], 532119375Sorion sizeof(codec->mix[SOUND_MIXER_OGAIN])); 533119375Sorion } 534102302Sorion} 535102302Sorion 536119209Sorionstatic void 537119209Sorionac97_fix_tone(struct ac97_info *codec) 538119209Sorion{ 539119209Sorion /* Hide treble and bass if they don't exist */ 540119209Sorion if ((codec->caps & AC97_CAP_TONE) == 0) { 541119209Sorion bzero(&codec->mix[SOUND_MIXER_BASS], 542119209Sorion sizeof(codec->mix[SOUND_MIXER_BASS])); 543119209Sorion bzero(&codec->mix[SOUND_MIXER_TREBLE], 544119209Sorion sizeof(codec->mix[SOUND_MIXER_TREBLE])); 545119209Sorion } 546119209Sorion} 547119209Sorion 548150825Snetchildstatic const char* 549150825Snetchildac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 550150825Snetchild{ 551150825Snetchild if (cname == NULL) { 552150825Snetchild sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 553150825Snetchild return buf; 554150825Snetchild } 555150825Snetchild 556150825Snetchild if (vname == NULL) vname = "Unknown"; 557150825Snetchild 558150825Snetchild if (bootverbose) { 559150825Snetchild sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 560150825Snetchild } else { 561150825Snetchild sprintf(buf, "%s %s AC97 Codec", vname, cname); 562150825Snetchild } 563150825Snetchild return buf; 564150825Snetchild} 565150825Snetchild 566150825Snetchildstatic unsigned 567150825Snetchildac97_initmixer(struct ac97_info *codec) 568150825Snetchild{ 569150825Snetchild ac97_patch codec_patch; 570150825Snetchild const char *cname, *vname; 571150825Snetchild char desc[80]; 572150825Snetchild u_int8_t model, step; 573150825Snetchild unsigned i, j, k, bit, old; 574150825Snetchild u_int32_t id; 575150825Snetchild int reg; 576150825Snetchild 577150825Snetchild snd_mtxlock(codec->lock); 578150825Snetchild codec->count = AC97_INIT(codec->methods, codec->devinfo); 579150825Snetchild if (codec->count == 0) { 580150825Snetchild device_printf(codec->dev, "ac97 codec init failed\n"); 581150825Snetchild snd_mtxunlock(codec->lock); 582111679Sorion return ENODEV; 583111679Sorion } 584111679Sorion 585111679Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 586111679Sorion ac97_reset(codec); 587111679Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 588111679Sorion 589111679Sorion i = ac97_rdcd(codec, AC97_REG_RESET); 590111679Sorion j = ac97_rdcd(codec, AC97_REG_RESET); 591119209Sorion /* 592111679Sorion * Let see if this codec can return consistent value. 593111679Sorion * If not, turn on aggressive read workaround 594111679Sorion * (STAC9704 comes in mind). 595111679Sorion */ 596111679Sorion if (i != j) { 597111679Sorion codec->flags |= AC97_F_RDCD_BUG; 598111679Sorion i = ac97_rdcd(codec, AC97_REG_RESET); 599111679Sorion } 60050724Scg codec->caps = i & 0x03ff; 60158384Scg codec->se = (i & 0x7c00) >> 10; 60250724Scg 603109818Sorion id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 604111679Sorion if (id == 0 || id == 0xffffffff) { 605111679Sorion device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 606111679Sorion snd_mtxunlock(codec->lock); 607149949Snetchild return ENODEV; 60850724Scg } 609149949Snetchild 61050724Scg codec->id = id; 61174763Scg codec->noext = 0; 61270134Scg codec_patch = NULL; 61370134Scg 61470134Scg cname = NULL; 61574763Scg model = step = 0; 61670134Scg for (i = 0; ac97codecid[i].id; i++) { 61770134Scg u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 61867652Scg if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 619109818Sorion codec->noext = ac97codecid[i].noext; 62095499Sorion codec_patch = ac97codecid[i].patch; 621109818Sorion cname = ac97codecid[i].name; 62250724Scg model = (id & modelmask) & 0xff; 623109818Sorion step = (id & ~modelmask) & 0xff; 624149949Snetchild break; 625149949Snetchild } 626149949Snetchild } 627149949Snetchild 628149949Snetchild vname = NULL; 629149949Snetchild for (i = 0; ac97vendorid[i].id; i++) { 630149949Snetchild if (ac97vendorid[i].id == (id & 0xffffff00)) { 631149949Snetchild vname = ac97vendorid[i].name; 632149949Snetchild break; 633149949Snetchild } 63450724Scg } 63550724Scg 63650724Scg codec->extcaps = 0; 637109818Sorion codec->extid = 0; 63858905Scg codec->extstat = 0; 63958905Scg if (!codec->noext) { 64074763Scg i = ac97_rdcd(codec, AC97_REGEXT_ID); 64158905Scg if (i != 0xffff) { 64258905Scg codec->extcaps = i & 0x3fff; 64350724Scg codec->extid = (i & 0xc000) >> 14; 644111679Sorion codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 64565490Scg } 646109818Sorion } 647111679Sorion 648111679Sorion for (i = 0; i < 32; i++) { 649111679Sorion codec->mix[i] = ac97mixtable_default[i]; 65065490Scg } 651111679Sorion ac97_fix_auxout(codec); 652111679Sorion ac97_fix_tone(codec); 65365490Scg if (codec_patch) 654109818Sorion codec_patch(codec); 655111679Sorion 656111679Sorion for (i = 0; i < 32; i++) { 657111679Sorion k = codec->noext? codec->mix[i].enable : 1; 658111679Sorion reg = codec->mix[i].reg; 65965490Scg if (reg < 0) 66065490Scg reg = -reg; 66158521Scg if (k && reg) { 662111679Sorion j = old = ac97_rdcd(codec, reg); 663111679Sorion /* 664111679Sorion * Test for mute bit (except for AC97_MIX_TONE, 665111679Sorion * where we simply assume it as available). 666111679Sorion */ 667111679Sorion if (codec->mix[i].mute) { 668111679Sorion ac97_wrcd(codec, reg, j | 0x8000); 669111679Sorion j = ac97_rdcd(codec, reg); 67070324Scg } else 67170324Scg j |= 0x8000; 67270324Scg if ((j & 0x8000)) { 67370324Scg /* 674109818Sorion * Test whether the control width should be 67570324Scg * 4, 5 or 6 bit. For 5bit register, we should 67670324Scg * test it whether it's really 5 or 6bit. Leave 67770324Scg * 4bit register alone, because sometimes an 678109818Sorion * attempt to write past 4th bit may cause 67970324Scg * incorrect result especially for AC97_MIX_BEEP 68058521Scg * (ac97 2.3). 68158521Scg */ 68266013Scg bit = codec->mix[i].bits; 683102302Sorion if (bit == 5) 684102302Sorion bit++; 685102302Sorion j = ((1 << bit) - 1) << codec->mix[i].ofs; 686119209Sorion ac97_wrcd(codec, reg, 687150825Snetchild j | (codec->mix[i].mute ? 0x8000 : 0)); 688109818Sorion k = ac97_rdcd(codec, reg) & j; 689109818Sorion k >>= codec->mix[i].ofs; 690102302Sorion if (reg == AC97_MIX_TONE && 691102302Sorion ((k & 0x0001) == 0x0000)) 69266307Scg k >>= 1; 693149949Snetchild for (j = 0; k >> j; j++) 694149949Snetchild ; 695149949Snetchild if (j != 0) { 696149949Snetchild#if 0 697149949Snetchild device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 698149949Snetchild i, k, bit, codec->mix[i].bits, j); 699149949Snetchild#endif 700149949Snetchild codec->mix[i].enable = 1; 701149949Snetchild codec->mix[i].bits = j; 702149949Snetchild } else if (reg == AC97_MIX_BEEP) { 703149949Snetchild /* 704149949Snetchild * Few codec such as CX20468-21 does 705149949Snetchild * have this control register, although 706149949Snetchild * the only usable part is the mute bit. 707148602Snetchild */ 708149949Snetchild codec->mix[i].enable = 1; 709149949Snetchild } else 710149949Snetchild codec->mix[i].enable = 0; 711149949Snetchild } else 712149949Snetchild codec->mix[i].enable = 0; 713149949Snetchild ac97_wrcd(codec, reg, old); 714149949Snetchild } 715149949Snetchild#if 0 716149949Snetchild printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 717149949Snetchild#endif 718149949Snetchild } 719149949Snetchild 720149949Snetchild device_printf(codec->dev, "<%s>\n", 721149949Snetchild ac97_hw_desc(codec->id, vname, cname, desc)); 722149949Snetchild 723149949Snetchild if (bootverbose) { 724149949Snetchild if (codec->flags & AC97_F_RDCD_BUG) 725149949Snetchild device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 726149949Snetchild device_printf(codec->dev, "Codec features "); 727148602Snetchild for (i = j = 0; i < 10; i++) 728149949Snetchild if (codec->caps & (1 << i)) 729149949Snetchild printf("%s%s", j++? ", " : "", ac97feature[i]); 730148602Snetchild printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 731149949Snetchild printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 732149949Snetchild 733149949Snetchild if (codec->extcaps != 0 || codec->extid) { 734149949Snetchild device_printf(codec->dev, "%s codec", 735148602Snetchild codec->extid? "Secondary" : "Primary"); 736148602Snetchild if (codec->extcaps) 737153865Sariff printf(" extended features "); 738153865Sariff for (i = j = 0; i < 14; i++) 739153865Sariff if (codec->extcaps & (1 << i)) 740153865Sariff printf("%s%s", j++? ", " : "", ac97extfeature[i]); 741153865Sariff printf("\n"); 742153865Sariff } 743153865Sariff } 744148602Snetchild 745148602Snetchild i = 0; 746148602Snetchild while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 747148602Snetchild if (++i == 100) { 748149949Snetchild device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 74966013Scg break; 750148602Snetchild } 751148602Snetchild DELAY(1000); 752148602Snetchild } 75366013Scg if (bootverbose) 75450724Scg device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 755111679Sorion snd_mtxunlock(codec->lock); 756111679Sorion return 0; 757110258Sorion} 75850724Scg 759149949Snetchildstatic unsigned 760149949Snetchildac97_reinitmixer(struct ac97_info *codec) 761150825Snetchild{ 762150825Snetchild snd_mtxlock(codec->lock); 763111679Sorion codec->count = AC97_INIT(codec->methods, codec->devinfo); 76458384Scg if (codec->count == 0) { 76558384Scg device_printf(codec->dev, "ac97 codec init failed\n"); 76658384Scg snd_mtxunlock(codec->lock); 76758384Scg return ENODEV; 76858384Scg } 76958384Scg 77058384Scg ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 771111679Sorion ac97_reset(codec); 772111679Sorion ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 77358384Scg 77458384Scg if (!codec->noext) { 77558384Scg ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 77658384Scg if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 77758384Scg != codec->extstat) 77858384Scg device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 77950724Scg codec->extstat, 78050724Scg ac97_rdcd(codec, AC97_REGEXT_STAT) & 78150724Scg AC97_EXTCAPS); 782148602Snetchild } 783148602Snetchild 784148602Snetchild if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 785148602Snetchild device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 786148602Snetchild snd_mtxunlock(codec->lock); 787148602Snetchild return 0; 788148602Snetchild} 789148602Snetchild 790148602Snetchildstruct ac97_info * 791148602Snetchildac97_create(device_t dev, void *devinfo, kobj_class_t cls) 79274763Scg{ 79350724Scg struct ac97_info *codec; 79450724Scg int eapd_inv; 79550724Scg 79667652Scg codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO); 79767652Scg if (codec == NULL) 79867652Scg return NULL; 79974763Scg 80070134Scg snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 80170134Scg codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 80270134Scg codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); 80374763Scg if (codec->methods == NULL) { 80470134Scg snd_mtxlock(codec->lock); 80570134Scg snd_mtxfree(codec->lock); 80667652Scg free(codec, M_AC97); 807109818Sorion return NULL; 80895499Sorion } 809109818Sorion 81067652Scg codec->dev = dev; 81167652Scg codec->devinfo = devinfo; 812109818Sorion codec->flags = 0; 813109818Sorion if (resource_int_value(device_get_name(dev), device_get_unit(dev), 81487623Sguido "ac97_eapd_inv", &eapd_inv) == 0) { 81567652Scg if (eapd_inv != 0) 81687623Sguido codec->flags |= AC97_F_EAPD_INV; 817109818Sorion } 818109818Sorion return codec; 81967652Scg} 82067652Scg 821109818Sorionvoid 82267652Scgac97_destroy(struct ac97_info *codec) 82374763Scg{ 82467652Scg snd_mtxlock(codec->lock); 82567652Scg if (codec->methods != NULL) 82667652Scg kobj_delete(codec->methods, M_AC97); 82750724Scg snd_mtxfree(codec->lock); 82870134Scg free(codec, M_AC97); 82950724Scg} 83050724Scg 83150724Scgvoid 83270134Scgac97_setflags(struct ac97_info *codec, u_int32_t val) 83370134Scg{ 83470134Scg codec->flags = val; 83570134Scg} 83674763Scg 83793816Sjhbu_int32_t 838111119Simpac97_getflags(struct ac97_info *codec) 83970134Scg{ 84074763Scg return codec->flags; 84174763Scg} 84270134Scg 84370134Scg/* -------------------------------------------------------------------- */ 84450724Scg 84570134Scgstatic int 84670134Scgac97mix_init(struct snd_mixer *m) 84770134Scg{ 84878668Scg struct ac97_info *codec = mix_getdevinfo(m); 84950724Scg struct snddev_info *d; 85050724Scg u_int32_t i, mask; 85150724Scg 85265340Scg if (codec == NULL) 85365340Scg return -1; 85465340Scg 85574763Scg if (ac97_initmixer(codec)) 85670134Scg return -1; 85770134Scg 85874763Scg switch (codec->id) { 85970134Scg case 0x41445374: /* AD1981B */ 86065340Scg#if 0 86165340Scg mask = 0; 86278668Scg if (codec->mix[SOUND_MIXER_OGAIN].enable) 86378668Scg mask |= SOUND_MASK_OGAIN; 86478668Scg if (codec->mix[SOUND_MIXER_PHONEOUT].enable) 86578668Scg mask |= SOUND_MASK_PHONEOUT; 86678668Scg /* Tie ogain/phone to master volume */ 86778668Scg if (codec->mix[SOUND_MIXER_VOLUME].enable) 86878668Scg mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); 86978668Scg else { 87078668Scg mix_setparentchild(m, SOUND_MIXER_VOLUME, mask); 87178668Scg mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 87278668Scg } 87378668Scg#endif 87470134Scg break; 87570134Scg case 0x434d4941: /* CMI9738 */ 87650724Scg case 0x434d4961: /* CMI9739 */ 87774763Scg case 0x434d4978: /* CMI9761 */ 87850724Scg case 0x434d4982: /* CMI9761 */ 87950724Scg case 0x434d4983: /* CMI9761 */ 88066013Scg ac97_wrcd(codec, AC97_MIX_PCM, 0); 88166013Scg bzero(&codec->mix[SOUND_MIXER_PCM], 88258384Scg sizeof(codec->mix[SOUND_MIXER_PCM])); 88358384Scg d = device_get_softc(codec->dev); 88466013Scg if (d != NULL) 88558905Scg d->flags |= SD_F_SOFTPCMVOL; 88658905Scg /* XXX How about master volume ? */ 88766013Scg break; 88866013Scg default: 88966013Scg break; 89066013Scg } 89166013Scg 89266013Scg#if 0 89366013Scg /* XXX For the sake of debugging purposes */ 89466013Scg mix_setparentchild(m, SOUND_MIXER_VOLUME, 89566013Scg SOUND_MASK_PCM | SOUND_MASK_CD); 89666013Scg mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 89750724Scg ac97_wrcd(codec, AC97_MIX_MASTER, 0); 89850724Scg#endif 89950724Scg 90050724Scg mask = 0; 90174763Scg for (i = 0; i < 32; i++) 90265340Scg mask |= codec->mix[i].enable? 1 << i : 0; 90365340Scg mix_setdevs(m, mask); 90466013Scg 90565340Scg mask = 0; 90665340Scg for (i = 0; i < 32; i++) 90765340Scg mask |= codec->mix[i].recidx? 1 << i : 0; 90865340Scg mix_setrecdevs(m, mask); 90965340Scg return 0; 91065340Scg} 91165340Scg 91265340Scgstatic int 91365340Scgac97mix_uninit(struct snd_mixer *m) 91465340Scg{ 91565340Scg struct ac97_info *codec = mix_getdevinfo(m); 91674763Scg 91767652Scg if (codec == NULL) 91867652Scg return -1; 91967652Scg /* 92067652Scg if (ac97_uninitmixer(codec)) 92167652Scg return -1; 92267652Scg */ 92367652Scg ac97_destroy(codec); 92467652Scg return 0; 92567652Scg} 92674763Scg 92750724Scgstatic int 92850724Scgac97mix_reinit(struct snd_mixer *m) 92966013Scg{ 93058384Scg struct ac97_info *codec = mix_getdevinfo(m); 93158384Scg 93250724Scg if (codec == NULL) 93350724Scg return -1; 93450724Scg return ac97_reinitmixer(codec); 93550724Scg} 93674763Scg 93750724Scgstatic int 93850724Scgac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 93950724Scg{ 94066013Scg struct ac97_info *codec = mix_getdevinfo(m); 94158384Scg 94258384Scg if (codec == NULL) 94350724Scg return -1; 94458384Scg return ac97_setmixer(codec, dev, left, right); 94558384Scg} 94650724Scg 94750724Scgstatic int 94850724Scgac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 94970134Scg{ 95070134Scg int i; 95170134Scg struct ac97_info *codec = mix_getdevinfo(m); 95270134Scg 95370134Scg if (codec == NULL) 95470134Scg return -1; 95570134Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 95650724Scg if ((src & (1 << i)) != 0) 95770134Scg break; 95850724Scg return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 95970134Scg} 96070134Scg 96170134Scgstatic kobj_method_t ac97mixer_methods[] = { 96270134Scg KOBJMETHOD(mixer_init, ac97mix_init), 96370134Scg KOBJMETHOD(mixer_uninit, ac97mix_uninit), 96470134Scg KOBJMETHOD(mixer_reinit, ac97mix_reinit), 96570134Scg KOBJMETHOD(mixer_set, ac97mix_set), 96670134Scg KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 96770134Scg { 0, 0 } 968}; 969MIXER_DECLARE(ac97mixer); 970 971/* -------------------------------------------------------------------- */ 972 973kobj_class_t 974ac97_getmixerclass(void) 975{ 976 return &ac97mixer_class; 977} 978 979 980