1098ca2bdSWarner Losh /*- 23f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 3987e5972SCameron Grant * All rights reserved. 4987e5972SCameron Grant * 5987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 6987e5972SCameron Grant * modification, are permitted provided that the following conditions 7987e5972SCameron Grant * are met: 8987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 9987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 10987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 11987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 12987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 13987e5972SCameron Grant * 14987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24987e5972SCameron Grant * SUCH DAMAGE. 25987e5972SCameron Grant */ 26987e5972SCameron Grant 27ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 28ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 29f9eb1409SOrion Hodson #include <dev/sound/pcm/ac97_patch.h> 30987e5972SCameron Grant 31a580b31aSAriff Abdullah #include <dev/pci/pcivar.h> 32a580b31aSAriff Abdullah 330f55ac6cSCameron Grant #include "mixer_if.h" 34987e5972SCameron Grant 3567b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3667b1dce3SCameron Grant 370f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 38987e5972SCameron Grant 3979bb7d52SCameron Grant struct ac97mixtable_entry { 4096524a52SOrion Hodson int reg:8; /* register index */ 4196524a52SOrion Hodson /* reg < 0 if inverted polarity */ 4296524a52SOrion Hodson unsigned bits:4; /* width of control field */ 4396524a52SOrion Hodson unsigned ofs:4; /* offset (only if stereo=0) */ 4496524a52SOrion Hodson unsigned stereo:1; /* set for stereo controls */ 4596524a52SOrion Hodson unsigned mute:1; /* bit15 is MUTE */ 4696524a52SOrion Hodson unsigned recidx:4; /* index in rec mux */ 4796524a52SOrion Hodson unsigned mask:1; /* use only masked bits */ 4896524a52SOrion Hodson unsigned enable:1; /* entry is enabled */ 4979bb7d52SCameron Grant }; 5079bb7d52SCameron Grant 5166ef8af5SCameron Grant #define AC97_NAMELEN 16 5266ef8af5SCameron Grant struct ac97_info { 5366ef8af5SCameron Grant kobj_t methods; 5466ef8af5SCameron Grant device_t dev; 5566ef8af5SCameron Grant void *devinfo; 5619921b23SOrion Hodson u_int32_t id; 57fd7390d6SAriff Abdullah u_int32_t subvendor; 5866ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5979bb7d52SCameron Grant u_int32_t flags; 6066ef8af5SCameron Grant struct ac97mixtable_entry mix[32]; 6166ef8af5SCameron Grant char name[AC97_NAMELEN]; 6200acb133SCameron Grant struct mtx *lock; 6366ef8af5SCameron Grant }; 6466ef8af5SCameron Grant 6519921b23SOrion Hodson struct ac97_vendorid { 6619921b23SOrion Hodson u_int32_t id; 6719921b23SOrion Hodson const char *name; 6819921b23SOrion Hodson }; 6919921b23SOrion Hodson 70987e5972SCameron Grant struct ac97_codecid { 7119921b23SOrion Hodson u_int32_t id; 7219921b23SOrion Hodson u_int8_t stepmask; 7319921b23SOrion Hodson u_int8_t noext:1; 74987e5972SCameron Grant char *name; 75f9eb1409SOrion Hodson ac97_patch patch; 76987e5972SCameron Grant }; 77987e5972SCameron Grant 78987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 7996524a52SOrion Hodson /* [offset] reg bits of st mu re mk en */ 80341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 81a52604cfSOrion Hodson [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 82341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 83341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 84341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 85341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 86341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 87341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 88341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 8996524a52SOrion Hodson [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 9096524a52SOrion Hodson /* use igain for the mic 20dB boost */ 9196524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 92341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 93341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 94341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 95341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 96987e5972SCameron Grant }; 97987e5972SCameron Grant 9819921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9919921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 10019921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 10119921b23SOrion Hodson { 0x414c4300, "Realtek" }, 10219921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10319921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 10419921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 10519921b23SOrion Hodson { 0x43585400, "Conexant" }, 1063c6b655dSMathew Kanner { 0x44543000, "Diamond Technology" }, 10761a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 10819921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 1093c6b655dSMathew Kanner { 0x48525300, "Intersil" }, 11019921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 1113c6b655dSMathew Kanner { 0x49544500, "ITE, Inc." }, 11219921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 11319921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 11419921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 1153c6b655dSMathew Kanner { 0x53494c00, "Silicon Laboratories" }, 11619921b23SOrion Hodson { 0x54524100, "TriTech" }, 1173c6b655dSMathew Kanner { 0x54584e00, "Texas Instruments" }, 11819921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 1193c6b655dSMathew Kanner { 0x57454300, "Winbond" }, 12019921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 12119921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 12286336196SAlexander Leidinger /* 12386336196SAlexander Leidinger * XXX This is a fluke, really! The real vendor 12486336196SAlexander Leidinger * should be SigmaTel, not this! This should be 12586336196SAlexander Leidinger * removed someday! 12686336196SAlexander Leidinger */ 1276f0182bdSOrion Hodson { 0x01408300, "Creative" }, 12819921b23SOrion Hodson { 0x00000000, NULL } 12919921b23SOrion Hodson }; 13019921b23SOrion Hodson 131987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 13219921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 13319921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 13419921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 13519921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 13619921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 137ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 138ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 139c5cb8d60SScott Long { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 140a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 141ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 1427699548fSAriff Abdullah { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, 143a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 144e1e05d5dSAriff Abdullah { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, 14519921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 14619921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 14719921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 1483c6b655dSMathew Kanner { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 1493c6b655dSMathew Kanner { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 15019921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1519963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 15219921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 15319921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 15419921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 155b327ee51SAriff Abdullah { 0x414c4752, 0x0f, 0, "ALC250", 0 }, 156fd7390d6SAriff Abdullah { 0x414c4760, 0x0f, 0, "ALC655", alc655_patch }, 157b7994e34SPyun YongHyeon { 0x414c4770, 0x0f, 0, "ALC203", 0 }, 15894ed763dSJun Kuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 1593c6b655dSMathew Kanner { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 16019921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 16119921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 16219921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 163e5728f83SMIHIRA Sanpei Yoshiro { 0x4352592d, 0x07, 0, "CS4294", 0 }, 16419921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 16519921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 166b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 16719921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 168cb44f623SAlexander Leidinger { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, 16919921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 170cb44f623SAlexander Leidinger { 0x434d4978, 0x00, 0, "CMI9761", 0 }, 171cb44f623SAlexander Leidinger { 0x434d4982, 0x00, 0, "CMI9761", 0 }, 172cb44f623SAlexander Leidinger { 0x434d4983, 0x00, 0, "CMI9761", 0 }, 1733c6b655dSMathew Kanner { 0x43585421, 0x00, 0, "HSD11246", 0 }, 1743c6b655dSMathew Kanner { 0x43585428, 0x07, 0, "CX20468", 0 }, 17580138937SAriff Abdullah { 0x43585430, 0x00, 0, "CX20468-21", 0 }, 1763c6b655dSMathew Kanner { 0x44543000, 0x00, 0, "DT0398", 0 }, 17761a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 17861a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 17919921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 1803c6b655dSMathew Kanner { 0x48525300, 0x00, 0, "HMP9701", 0 }, 18119921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 18219921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 18319921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 18449fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 1853c6b655dSMathew Kanner { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 1863c6b655dSMathew Kanner { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 18719921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 18819921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 18919921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 19019921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 1913c6b655dSMathew Kanner { 0x4e534331, 0x00, 0, "LM4549", 0 }, 19219921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 19319921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 19419921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 19519921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 19619921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 19719921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 19819921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 19919921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 20019921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 20119921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 20219921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 20319921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 20419921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 20519921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 20619921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 20719921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 208cb44f623SAlexander Leidinger { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 20919921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 21019921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 21119921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 21219921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 21319921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 21419921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 2153c6b655dSMathew Kanner { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 2163c6b655dSMathew Kanner { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 21719921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 21880138937SAriff Abdullah { 0x56494170, 0x00, 0, "VIA1617A", 0 }, 21919921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 22019921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 22119921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 22219921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 2233c6b655dSMathew Kanner { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 2243c6b655dSMathew Kanner { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 2253c6b655dSMathew Kanner { 0x57454301, 0x00, 0, "W83971D", 0 }, 22619921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 22719921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 22819921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 22986336196SAlexander Leidinger /* 23086336196SAlexander Leidinger * XXX This is a fluke, really! The real codec 23186336196SAlexander Leidinger * should be STAC9704, not this! This should be 23286336196SAlexander Leidinger * removed someday! 23386336196SAlexander Leidinger */ 2346f0182bdSOrion Hodson { 0x01408384, 0x00, 0, "EV1938", 0 }, 23519921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 236987e5972SCameron Grant }; 237987e5972SCameron Grant 238987e5972SCameron Grant static char *ac97enhancement[] = { 23904553e63SCameron Grant "no 3D Stereo Enhancement", 240987e5972SCameron Grant "Analog Devices Phat Stereo", 241987e5972SCameron Grant "Creative Stereo Enhancement", 242987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 243987e5972SCameron Grant "Yamaha Ymersion", 244987e5972SCameron Grant "BBE 3D Stereo Enhancement", 245987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 246987e5972SCameron Grant "Qsound QXpander", 247987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 248987e5972SCameron Grant "SRS 3D Stereo Enhancement", 249987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 250987e5972SCameron Grant "AKM 3D Audio", 251987e5972SCameron Grant "Aureal Stereo Enhancement", 252987e5972SCameron Grant "Aztech 3D Enhancement", 253987e5972SCameron Grant "Binaura 3D Audio Enhancement", 254987e5972SCameron Grant "ESS Technology Stereo Enhancement", 255987e5972SCameron Grant "Harman International VMAx", 256987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 257987e5972SCameron Grant "Philips Incredible Sound", 258987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 259987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 260987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 261987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 262987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 263987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 264987e5972SCameron Grant "Delta Integration 3D Enhancement", 265987e5972SCameron Grant "SigmaTel 3D Enhancement", 266987e5972SCameron Grant "Reserved 27", 267987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 268987e5972SCameron Grant "Reserved 29", 269987e5972SCameron Grant "Reserved 30", 270987e5972SCameron Grant "Reserved 31" 271987e5972SCameron Grant }; 272987e5972SCameron Grant 273987e5972SCameron Grant static char *ac97feature[] = { 274987e5972SCameron Grant "mic channel", 275987e5972SCameron Grant "reserved", 276987e5972SCameron Grant "tone", 277987e5972SCameron Grant "simulated stereo", 278987e5972SCameron Grant "headphone", 279987e5972SCameron Grant "bass boost", 280987e5972SCameron Grant "18 bit DAC", 281987e5972SCameron Grant "20 bit DAC", 282987e5972SCameron Grant "18 bit ADC", 283987e5972SCameron Grant "20 bit ADC" 284987e5972SCameron Grant }; 285987e5972SCameron Grant 28639004e69SCameron Grant static char *ac97extfeature[] = { 28739004e69SCameron Grant "variable rate PCM", 28839004e69SCameron Grant "double rate PCM", 28939004e69SCameron Grant "reserved 1", 29039004e69SCameron Grant "variable rate mic", 29139004e69SCameron Grant "reserved 2", 29239004e69SCameron Grant "reserved 3", 29339004e69SCameron Grant "center DAC", 29439004e69SCameron Grant "surround DAC", 29539004e69SCameron Grant "LFE DAC", 29639004e69SCameron Grant "AMAP", 29739004e69SCameron Grant "reserved 4", 29839004e69SCameron Grant "reserved 5", 29939004e69SCameron Grant "reserved 6", 30039004e69SCameron Grant "reserved 7", 30139004e69SCameron Grant }; 30239004e69SCameron Grant 303f9eb1409SOrion Hodson u_int16_t 304f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 30539004e69SCameron Grant { 30686336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) { 30786336196SAlexander Leidinger u_int16_t i[2], j = 100; 30886336196SAlexander Leidinger 30986336196SAlexander Leidinger i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 31086336196SAlexander Leidinger i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 31186336196SAlexander Leidinger while (i[0] != i[1] && j) 31286336196SAlexander Leidinger i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 31386336196SAlexander Leidinger #if 0 31486336196SAlexander Leidinger if (j < 100) { 31586336196SAlexander Leidinger device_printf(codec->dev, "%s(): Inconsistent register value at" 31686336196SAlexander Leidinger " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 31786336196SAlexander Leidinger } 31886336196SAlexander Leidinger #endif 31986336196SAlexander Leidinger return i[!(j & 1)]; 32086336196SAlexander Leidinger } 3210f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 32239004e69SCameron Grant } 32339004e69SCameron Grant 324f9eb1409SOrion Hodson void 325f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 32639004e69SCameron Grant { 3270f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 32839004e69SCameron Grant } 32939004e69SCameron Grant 330c6d4b83aSOrion Hodson static void 331c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 332c6d4b83aSOrion Hodson { 333c6d4b83aSOrion Hodson u_int32_t i, ps; 334f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 335c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 336f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 337c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 338c6d4b83aSOrion Hodson return; 339c6d4b83aSOrion Hodson DELAY(1000); 340c6d4b83aSOrion Hodson } 341a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 342c6d4b83aSOrion Hodson } 343c6d4b83aSOrion Hodson 34439004e69SCameron Grant int 34539004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 34639004e69SCameron Grant { 34739004e69SCameron Grant u_int16_t v; 34839004e69SCameron Grant 34939004e69SCameron Grant switch(which) { 35039004e69SCameron Grant case AC97_REGEXT_FDACRATE: 35139004e69SCameron Grant case AC97_REGEXT_SDACRATE: 35239004e69SCameron Grant case AC97_REGEXT_LDACRATE: 35339004e69SCameron Grant case AC97_REGEXT_LADCRATE: 35439004e69SCameron Grant case AC97_REGEXT_MADCRATE: 35539004e69SCameron Grant break; 35639004e69SCameron Grant 35739004e69SCameron Grant default: 35839004e69SCameron Grant return -1; 35939004e69SCameron Grant } 36039004e69SCameron Grant 36166ef8af5SCameron Grant snd_mtxlock(codec->lock); 36239004e69SCameron Grant if (rate != 0) { 36339004e69SCameron Grant v = rate; 36439004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 36539004e69SCameron Grant v >>= 1; 366f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 36739004e69SCameron Grant } 368f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 36939004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 37039004e69SCameron Grant v <<= 1; 37166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 37239004e69SCameron Grant return v; 37339004e69SCameron Grant } 37439004e69SCameron Grant 37539004e69SCameron Grant int 37639004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 37739004e69SCameron Grant { 37839004e69SCameron Grant mode &= AC97_EXTCAPS; 379647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 380647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 381647fbfebSOrion Hodson mode); 38239004e69SCameron Grant return -1; 383647fbfebSOrion Hodson } 38466ef8af5SCameron Grant snd_mtxlock(codec->lock); 385f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 386f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 38766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 38839004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 38939004e69SCameron Grant } 39039004e69SCameron Grant 3919ec437a3SCameron Grant u_int16_t 3929ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3939ec437a3SCameron Grant { 3949ec437a3SCameron Grant return codec->extstat; 3959ec437a3SCameron Grant } 3969ec437a3SCameron Grant 3979ec437a3SCameron Grant u_int16_t 3989ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3999ec437a3SCameron Grant { 4009ec437a3SCameron Grant return codec->extcaps; 4019ec437a3SCameron Grant } 4029ec437a3SCameron Grant 4035d91ad67SCameron Grant u_int16_t 4045d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 4055d91ad67SCameron Grant { 4065d91ad67SCameron Grant return codec->caps; 4075d91ad67SCameron Grant } 4085d91ad67SCameron Grant 409fd7390d6SAriff Abdullah u_int32_t 410fd7390d6SAriff Abdullah ac97_getsubvendor(struct ac97_info *codec) 411fd7390d6SAriff Abdullah { 412fd7390d6SAriff Abdullah return codec->subvendor; 413fd7390d6SAriff Abdullah } 414fd7390d6SAriff Abdullah 415987e5972SCameron Grant static int 416987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 417987e5972SCameron Grant { 418987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 419341f16ccSCameron Grant 420987e5972SCameron Grant if (e->recidx > 0) { 421987e5972SCameron Grant int val = e->recidx - 1; 422987e5972SCameron Grant val |= val << 8; 42366ef8af5SCameron Grant snd_mtxlock(codec->lock); 424f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 42566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 426987e5972SCameron Grant return 0; 42739004e69SCameron Grant } else 42839004e69SCameron Grant return -1; 429987e5972SCameron Grant } 430987e5972SCameron Grant 431987e5972SCameron Grant static int 432987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 433987e5972SCameron Grant { 434987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 435341f16ccSCameron Grant 436341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 43796524a52SOrion Hodson int mask, max, val, reg; 43896524a52SOrion Hodson 43996524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 44096524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 44196524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 442987e5972SCameron Grant 44339004e69SCameron Grant if (!e->stereo) 44439004e69SCameron Grant right = left; 44596524a52SOrion Hodson 44696524a52SOrion Hodson /* 44796524a52SOrion Hodson * Invert the range if the polarity requires so, 44896524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 44996524a52SOrion Hodson * write into the codec, and scale back to 0..100 45096524a52SOrion Hodson * for the return value. 45196524a52SOrion Hodson */ 452987e5972SCameron Grant if (e->reg > 0) { 453987e5972SCameron Grant left = 100 - left; 454987e5972SCameron Grant right = 100 - right; 455987e5972SCameron Grant } 456987e5972SCameron Grant 457987e5972SCameron Grant left = (left * max) / 100; 458987e5972SCameron Grant right = (right * max) / 100; 459987e5972SCameron Grant 460987e5972SCameron Grant val = (left << 8) | right; 461987e5972SCameron Grant 462987e5972SCameron Grant left = (left * 100) / max; 463987e5972SCameron Grant right = (right * 100) / max; 464987e5972SCameron Grant 465987e5972SCameron Grant if (e->reg > 0) { 466987e5972SCameron Grant left = 100 - left; 467987e5972SCameron Grant right = 100 - right; 468987e5972SCameron Grant } 469987e5972SCameron Grant 47096524a52SOrion Hodson /* 47196524a52SOrion Hodson * For mono controls, trim val and mask, also taking 47296524a52SOrion Hodson * care of e->ofs (offset of control field). 47396524a52SOrion Hodson */ 47496524a52SOrion Hodson if (e->ofs) { 475987e5972SCameron Grant val &= max; 476987e5972SCameron Grant val <<= e->ofs; 47796524a52SOrion Hodson mask = (max << e->ofs); 47896524a52SOrion Hodson } 47996524a52SOrion Hodson 48096524a52SOrion Hodson /* 48196524a52SOrion Hodson * If we have a mute bit, add it to the mask and 48296524a52SOrion Hodson * update val and set mute if both channels require a 48396524a52SOrion Hodson * zero volume. 48496524a52SOrion Hodson */ 48596524a52SOrion Hodson if (e->mute == 1) { 48696524a52SOrion Hodson mask |= AC97_MUTE; 48796524a52SOrion Hodson if (left == 0 && right == 0) 48896524a52SOrion Hodson val = AC97_MUTE; 48996524a52SOrion Hodson } 49096524a52SOrion Hodson 49196524a52SOrion Hodson /* 49296524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 49396524a52SOrion Hodson */ 49496524a52SOrion Hodson snd_mtxlock(codec->lock); 495987e5972SCameron Grant if (e->mask) { 49686336196SAlexander Leidinger int cur = ac97_rdcd(codec, reg); 49796524a52SOrion Hodson val |= cur & ~(mask); 498987e5972SCameron Grant } 499f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 50066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 501987e5972SCameron Grant return left | (right << 8); 502341f16ccSCameron Grant } else { 50386336196SAlexander Leidinger #if 0 50486336196SAlexander Leidinger printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 50586336196SAlexander Leidinger #endif 50639004e69SCameron Grant return -1; 507987e5972SCameron Grant } 508341f16ccSCameron Grant } 509987e5972SCameron Grant 510108082c4SOrion Hodson static void 511108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 512108082c4SOrion Hodson { 513cfd5696dSOrion Hodson int keep_ogain; 514cfd5696dSOrion Hodson 515a52604cfSOrion Hodson /* 516cfd5696dSOrion Hodson * By default, The ac97 aux_out register (0x04) corresponds to OSS's 517cfd5696dSOrion Hodson * OGAIN setting. 518a52604cfSOrion Hodson * 519cfd5696dSOrion Hodson * We first check whether aux_out is a valid register. If not 520cfd5696dSOrion Hodson * we may not want to keep ogain. 521a52604cfSOrion Hodson */ 522cfd5696dSOrion Hodson keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 523a52604cfSOrion Hodson 524a52604cfSOrion Hodson /* 525a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 526108082c4SOrion Hodson * 527108082c4SOrion Hodson * 1. Headphone out. 528108082c4SOrion Hodson * 2. 4-Channel Out 529108082c4SOrion Hodson * 3. True line level out (effectively master volume). 530108082c4SOrion Hodson * 531108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 532108082c4SOrion Hodson */ 533a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 534f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 535cfd5696dSOrion Hodson codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 536cfd5696dSOrion Hodson keep_ogain = 1; 537cfd5696dSOrion Hodson } 538cfd5696dSOrion Hodson 539cfd5696dSOrion Hodson if (keep_ogain == 0) { 540cfd5696dSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 541cfd5696dSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 542108082c4SOrion Hodson } 543a52604cfSOrion Hodson } 544a52604cfSOrion Hodson 545a52604cfSOrion Hodson static void 546a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 547a52604cfSOrion Hodson { 548f0c4d272SAriff Abdullah /* 549f0c4d272SAriff Abdullah * YMF chips does not indicate tone and 3D enhancement capability 550f0c4d272SAriff Abdullah * in the AC97_REG_RESET register. 551f0c4d272SAriff Abdullah */ 552f0c4d272SAriff Abdullah switch (codec->id) { 553f0c4d272SAriff Abdullah case 0x594d4800: /* YMF743 */ 554f0c4d272SAriff Abdullah case 0x594d4803: /* YMF753 */ 555f0c4d272SAriff Abdullah codec->caps |= AC97_CAP_TONE; 556f0c4d272SAriff Abdullah codec->se |= 0x04; 557f0c4d272SAriff Abdullah break; 558f0c4d272SAriff Abdullah case 0x594d4802: /* YMF752 */ 559f0c4d272SAriff Abdullah codec->se |= 0x04; 560f0c4d272SAriff Abdullah break; 561f0c4d272SAriff Abdullah default: 562f0c4d272SAriff Abdullah break; 563f0c4d272SAriff Abdullah } 564f0c4d272SAriff Abdullah 565a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 566a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 567a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 568a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 569a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 570a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 571a52604cfSOrion Hodson } 572108082c4SOrion Hodson } 573108082c4SOrion Hodson 57419921b23SOrion Hodson static const char* 57519921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 57619921b23SOrion Hodson { 57719921b23SOrion Hodson if (cname == NULL) { 57819921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 57919921b23SOrion Hodson return buf; 58019921b23SOrion Hodson } 58119921b23SOrion Hodson 58219921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 58319921b23SOrion Hodson 58419921b23SOrion Hodson if (bootverbose) { 58519921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 58619921b23SOrion Hodson } else { 58719921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 58819921b23SOrion Hodson } 58919921b23SOrion Hodson return buf; 59019921b23SOrion Hodson } 59119921b23SOrion Hodson 592987e5972SCameron Grant static unsigned 59339004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 594987e5972SCameron Grant { 595f9eb1409SOrion Hodson ac97_patch codec_patch; 59619921b23SOrion Hodson const char *cname, *vname; 59719921b23SOrion Hodson char desc[80]; 59819921b23SOrion Hodson u_int8_t model, step; 59986336196SAlexander Leidinger unsigned i, j, k, bit, old; 600987e5972SCameron Grant u_int32_t id; 60186336196SAlexander Leidinger int reg; 602987e5972SCameron Grant 60366ef8af5SCameron Grant snd_mtxlock(codec->lock); 6040f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 605cd2c103aSCameron Grant if (codec->count == 0) { 60604553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 60766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 60804553e63SCameron Grant return ENODEV; 60904553e63SCameron Grant } 6109ec437a3SCameron Grant 611f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 612c6d4b83aSOrion Hodson ac97_reset(codec); 613f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 614987e5972SCameron Grant 615f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 61686336196SAlexander Leidinger j = ac97_rdcd(codec, AC97_REG_RESET); 61786336196SAlexander Leidinger /* 61886336196SAlexander Leidinger * Let see if this codec can return consistent value. 61986336196SAlexander Leidinger * If not, turn on aggressive read workaround 62086336196SAlexander Leidinger * (STAC9704 comes in mind). 62186336196SAlexander Leidinger */ 62286336196SAlexander Leidinger if (i != j) { 62386336196SAlexander Leidinger codec->flags |= AC97_F_RDCD_BUG; 62486336196SAlexander Leidinger i = ac97_rdcd(codec, AC97_REG_RESET); 62586336196SAlexander Leidinger } 626987e5972SCameron Grant codec->caps = i & 0x03ff; 627987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 628987e5972SCameron Grant 629f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 630e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 631e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 63266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 633e620d959SCameron Grant return ENODEV; 634e620d959SCameron Grant } 6356b4b88f7SCameron Grant 63619921b23SOrion Hodson codec->id = id; 637fd7390d6SAriff Abdullah codec->subvendor = (u_int32_t)pci_get_subdevice(codec->dev) << 16; 638fd7390d6SAriff Abdullah codec->subvendor |= (u_int32_t)pci_get_subvendor(codec->dev) & 639fd7390d6SAriff Abdullah 0x0000ffff; 640cd2c103aSCameron Grant codec->noext = 0; 641f9eb1409SOrion Hodson codec_patch = NULL; 64219921b23SOrion Hodson 64319921b23SOrion Hodson cname = NULL; 64419921b23SOrion Hodson model = step = 0; 645cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 64619921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 64719921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 648cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 649f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 65019921b23SOrion Hodson cname = ac97codecid[i].name; 65119921b23SOrion Hodson model = (id & modelmask) & 0xff; 65219921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 65319921b23SOrion Hodson break; 65419921b23SOrion Hodson } 65519921b23SOrion Hodson } 65619921b23SOrion Hodson 65719921b23SOrion Hodson vname = NULL; 65819921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 65919921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 66019921b23SOrion Hodson vname = ac97vendorid[i].name; 66119921b23SOrion Hodson break; 662cd2c103aSCameron Grant } 663cd2c103aSCameron Grant } 6646b4b88f7SCameron Grant 665cd2c103aSCameron Grant codec->extcaps = 0; 666cd2c103aSCameron Grant codec->extid = 0; 667cd2c103aSCameron Grant codec->extstat = 0; 6686a6ee5bbSCameron Grant if (!codec->noext) { 669f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 6706a6ee5bbSCameron Grant if (i != 0xffff) { 67139004e69SCameron Grant codec->extcaps = i & 0x3fff; 67239004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 673f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 6746b4b88f7SCameron Grant } 6756a6ee5bbSCameron Grant } 676987e5972SCameron Grant 677341f16ccSCameron Grant for (i = 0; i < 32; i++) { 678108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 679108082c4SOrion Hodson } 680108082c4SOrion Hodson ac97_fix_auxout(codec); 681a52604cfSOrion Hodson ac97_fix_tone(codec); 682f9eb1409SOrion Hodson if (codec_patch) 683f9eb1409SOrion Hodson codec_patch(codec); 684108082c4SOrion Hodson 685108082c4SOrion Hodson for (i = 0; i < 32; i++) { 68633c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 68786336196SAlexander Leidinger reg = codec->mix[i].reg; 68886336196SAlexander Leidinger if (reg < 0) 68986336196SAlexander Leidinger reg = -reg; 69086336196SAlexander Leidinger if (k && reg) { 69186336196SAlexander Leidinger j = old = ac97_rdcd(codec, reg); 69286336196SAlexander Leidinger /* 69386336196SAlexander Leidinger * Test for mute bit (except for AC97_MIX_TONE, 69486336196SAlexander Leidinger * where we simply assume it as available). 69586336196SAlexander Leidinger */ 69686336196SAlexander Leidinger if (codec->mix[i].mute) { 69786336196SAlexander Leidinger ac97_wrcd(codec, reg, j | 0x8000); 69886336196SAlexander Leidinger j = ac97_rdcd(codec, reg); 69986336196SAlexander Leidinger } else 700fe435202SAlexander Leidinger j |= 0x8000; 70186336196SAlexander Leidinger if ((j & 0x8000)) { 70286336196SAlexander Leidinger /* 70386336196SAlexander Leidinger * Test whether the control width should be 70486336196SAlexander Leidinger * 4, 5 or 6 bit. For 5bit register, we should 70586336196SAlexander Leidinger * test it whether it's really 5 or 6bit. Leave 70686336196SAlexander Leidinger * 4bit register alone, because sometimes an 70786336196SAlexander Leidinger * attempt to write past 4th bit may cause 70886336196SAlexander Leidinger * incorrect result especially for AC97_MIX_BEEP 70986336196SAlexander Leidinger * (ac97 2.3). 71086336196SAlexander Leidinger */ 71186336196SAlexander Leidinger bit = codec->mix[i].bits; 71286336196SAlexander Leidinger if (bit == 5) 71386336196SAlexander Leidinger bit++; 71486336196SAlexander Leidinger j = ((1 << bit) - 1) << codec->mix[i].ofs; 71586336196SAlexander Leidinger ac97_wrcd(codec, reg, 71686336196SAlexander Leidinger j | (codec->mix[i].mute ? 0x8000 : 0)); 71786336196SAlexander Leidinger k = ac97_rdcd(codec, reg) & j; 71886336196SAlexander Leidinger k >>= codec->mix[i].ofs; 71986336196SAlexander Leidinger if (reg == AC97_MIX_TONE && 72086336196SAlexander Leidinger ((k & 0x0001) == 0x0000)) 721fe435202SAlexander Leidinger k >>= 1; 72286336196SAlexander Leidinger for (j = 0; k >> j; j++) 72386336196SAlexander Leidinger ; 724fe435202SAlexander Leidinger if (j != 0) { 725fe435202SAlexander Leidinger #if 0 72686336196SAlexander Leidinger device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 72786336196SAlexander Leidinger i, k, bit, codec->mix[i].bits, j); 728fe435202SAlexander Leidinger #endif 72986336196SAlexander Leidinger codec->mix[i].enable = 1; 73086336196SAlexander Leidinger codec->mix[i].bits = j; 731eaf70083SAriff Abdullah } else if (reg == AC97_MIX_BEEP) { 732eaf70083SAriff Abdullah /* 733eaf70083SAriff Abdullah * Few codec such as CX20468-21 does 734eaf70083SAriff Abdullah * have this control register, although 735eaf70083SAriff Abdullah * the only usable part is the mute bit. 736eaf70083SAriff Abdullah */ 737eaf70083SAriff Abdullah codec->mix[i].enable = 1; 738fe435202SAlexander Leidinger } else 739fe435202SAlexander Leidinger codec->mix[i].enable = 0; 740fe435202SAlexander Leidinger } else 741fe435202SAlexander Leidinger codec->mix[i].enable = 0; 74286336196SAlexander Leidinger ac97_wrcd(codec, reg, old); 743fe435202SAlexander Leidinger } 744fe435202SAlexander Leidinger #if 0 745fe435202SAlexander Leidinger printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 746fe435202SAlexander Leidinger #endif 747341f16ccSCameron Grant } 748987e5972SCameron Grant 74919921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 75019921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 751a825c6e5SOrion Hodson 752987e5972SCameron Grant if (bootverbose) { 75386336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) 75486336196SAlexander Leidinger device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 75519921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 75639004e69SCameron Grant for (i = j = 0; i < 10; i++) 75739004e69SCameron Grant if (codec->caps & (1 << i)) 75839004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 75939004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 760987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 76139004e69SCameron Grant 76239004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 76319921b23SOrion Hodson device_printf(codec->dev, "%s codec", 76419921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 76539004e69SCameron Grant if (codec->extcaps) 76639004e69SCameron Grant printf(" extended features "); 76739004e69SCameron Grant for (i = j = 0; i < 14; i++) 76839004e69SCameron Grant if (codec->extcaps & (1 << i)) 76939004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 77039004e69SCameron Grant printf("\n"); 77139004e69SCameron Grant } 772987e5972SCameron Grant } 773987e5972SCameron Grant 774fe435202SAlexander Leidinger i = 0; 775fe435202SAlexander Leidinger while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 776fe435202SAlexander Leidinger if (++i == 100) { 77703a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 778fe435202SAlexander Leidinger break; 779fe435202SAlexander Leidinger } 780fe435202SAlexander Leidinger DELAY(1000); 781fe435202SAlexander Leidinger } 782fe435202SAlexander Leidinger if (bootverbose) 783fe435202SAlexander Leidinger device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 78466ef8af5SCameron Grant snd_mtxunlock(codec->lock); 785987e5972SCameron Grant return 0; 786987e5972SCameron Grant } 787987e5972SCameron Grant 7889ec437a3SCameron Grant static unsigned 7899ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 7909ec437a3SCameron Grant { 79166ef8af5SCameron Grant snd_mtxlock(codec->lock); 7920f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 7939ec437a3SCameron Grant if (codec->count == 0) { 7949ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 79566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 7969ec437a3SCameron Grant return ENODEV; 7979ec437a3SCameron Grant } 7989ec437a3SCameron Grant 799f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 800c6d4b83aSOrion Hodson ac97_reset(codec); 801f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 8029ec437a3SCameron Grant 8039ec437a3SCameron Grant if (!codec->noext) { 804f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 805f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 806b60e55dbSGuido van Rooij != codec->extstat) 8079ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 808b60e55dbSGuido van Rooij codec->extstat, 809f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 810b60e55dbSGuido van Rooij AC97_EXTCAPS); 8119ec437a3SCameron Grant } 8129ec437a3SCameron Grant 813f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 8149ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 81566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8169ec437a3SCameron Grant return 0; 8179ec437a3SCameron Grant } 8189ec437a3SCameron Grant 819987e5972SCameron Grant struct ac97_info * 8200f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 821987e5972SCameron Grant { 822987e5972SCameron Grant struct ac97_info *codec; 823a580b31aSAriff Abdullah int eapdinv; 824987e5972SCameron Grant 8257699548fSAriff Abdullah codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT | M_ZERO); 8260f55ac6cSCameron Grant if (codec == NULL) 8270f55ac6cSCameron Grant return NULL; 8280f55ac6cSCameron Grant 82966ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 830489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 8317699548fSAriff Abdullah codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); 8320f55ac6cSCameron Grant codec->dev = dev; 8330f55ac6cSCameron Grant codec->devinfo = devinfo; 83479bb7d52SCameron Grant codec->flags = 0; 8357699548fSAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 836a580b31aSAriff Abdullah "eapdinv", &eapdinv) == 0) { 837a580b31aSAriff Abdullah if (eapdinv != 0) 8387699548fSAriff Abdullah codec->flags |= AC97_F_EAPD_INV; 8397699548fSAriff Abdullah } 840987e5972SCameron Grant return codec; 841987e5972SCameron Grant } 842987e5972SCameron Grant 84333dbf14aSCameron Grant void 84433dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 84533dbf14aSCameron Grant { 84666ef8af5SCameron Grant snd_mtxlock(codec->lock); 8470f55ac6cSCameron Grant if (codec->methods != NULL) 8480f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 84966ef8af5SCameron Grant snd_mtxfree(codec->lock); 8500f55ac6cSCameron Grant free(codec, M_AC97); 85133dbf14aSCameron Grant } 85233dbf14aSCameron Grant 85379bb7d52SCameron Grant void 85479bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 85579bb7d52SCameron Grant { 85679bb7d52SCameron Grant codec->flags = val; 85779bb7d52SCameron Grant } 85879bb7d52SCameron Grant 85979bb7d52SCameron Grant u_int32_t 86079bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 86179bb7d52SCameron Grant { 86279bb7d52SCameron Grant return codec->flags; 86379bb7d52SCameron Grant } 86479bb7d52SCameron Grant 8650f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 8660f55ac6cSCameron Grant 867a580b31aSAriff Abdullah #ifdef SND_DYNSYSCTL 868a580b31aSAriff Abdullah static int 869a580b31aSAriff Abdullah sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) 870a580b31aSAriff Abdullah { 871a580b31aSAriff Abdullah struct ac97_info *codec; 872a580b31aSAriff Abdullah int ea, inv, err = 0; 873a580b31aSAriff Abdullah u_int16_t val; 874a580b31aSAriff Abdullah 875a580b31aSAriff Abdullah codec = oidp->oid_arg1; 876a580b31aSAriff Abdullah if (codec == NULL || codec->id == 0 || codec->lock == NULL) 877a580b31aSAriff Abdullah return EINVAL; 878a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 879a580b31aSAriff Abdullah val = ac97_rdcd(codec, AC97_REG_POWER); 880a580b31aSAriff Abdullah inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1; 881a580b31aSAriff Abdullah ea = (val >> 15) ^ inv; 882a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 883a580b31aSAriff Abdullah err = sysctl_handle_int(oidp, &ea, sizeof(ea), req); 884a580b31aSAriff Abdullah if (err == 0 && req->newptr != NULL) { 885a580b31aSAriff Abdullah if (ea != 0 && ea != 1) 886a580b31aSAriff Abdullah return EINVAL; 887a580b31aSAriff Abdullah if (ea != ((val >> 15) ^ inv)) { 888a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 889a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000); 890a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 891a580b31aSAriff Abdullah } 892a580b31aSAriff Abdullah } 893a580b31aSAriff Abdullah return err; 894a580b31aSAriff Abdullah } 895a580b31aSAriff Abdullah #endif 896a580b31aSAriff Abdullah 897a580b31aSAriff Abdullah static void 898a580b31aSAriff Abdullah ac97_init_sysctl(struct ac97_info *codec) 899a580b31aSAriff Abdullah { 900a580b31aSAriff Abdullah #ifdef SND_DYNSYSCTL 901a580b31aSAriff Abdullah u_int16_t orig, val; 902a580b31aSAriff Abdullah 903a580b31aSAriff Abdullah if (codec == NULL || codec->dev == NULL) 904a580b31aSAriff Abdullah return; 905a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 906a580b31aSAriff Abdullah orig = ac97_rdcd(codec, AC97_REG_POWER); 907a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000); 908a580b31aSAriff Abdullah val = ac97_rdcd(codec, AC97_REG_POWER); 909a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, orig); 910a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 911a580b31aSAriff Abdullah if ((val & 0x8000) == (orig & 0x8000)) 912a580b31aSAriff Abdullah return; 913a580b31aSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev), 914a580b31aSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)), 915a580b31aSAriff Abdullah OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW, 916a580b31aSAriff Abdullah codec, sizeof(codec), sysctl_hw_snd_ac97_eapd, 917a580b31aSAriff Abdullah "I", "AC97 External Amplifier"); 918a580b31aSAriff Abdullah #endif 919a580b31aSAriff Abdullah } 920a580b31aSAriff Abdullah 921987e5972SCameron Grant static int 92266ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 923987e5972SCameron Grant { 924987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 9257699548fSAriff Abdullah struct snddev_info *d; 926341f16ccSCameron Grant u_int32_t i, mask; 927341f16ccSCameron Grant 92839004e69SCameron Grant if (codec == NULL) 92939004e69SCameron Grant return -1; 930341f16ccSCameron Grant 931e620d959SCameron Grant if (ac97_initmixer(codec)) 932e620d959SCameron Grant return -1; 933341f16ccSCameron Grant 9347699548fSAriff Abdullah switch (codec->id) { 9357699548fSAriff Abdullah case 0x41445374: /* AD1981B */ 936fd7390d6SAriff Abdullah if (codec->subvendor == 0x02d91014) { 937fd7390d6SAriff Abdullah /* 938fd7390d6SAriff Abdullah * IBM Thinkcentre: 939fd7390d6SAriff Abdullah * Tie "ogain" and "phone" to "vol" since its 940fd7390d6SAriff Abdullah * master volume is basically useless and can't 941fd7390d6SAriff Abdullah * control anything. 942fd7390d6SAriff Abdullah */ 9437699548fSAriff Abdullah mask = 0; 9447699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_OGAIN].enable) 9457699548fSAriff Abdullah mask |= SOUND_MASK_OGAIN; 9467699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_PHONEOUT].enable) 9477699548fSAriff Abdullah mask |= SOUND_MASK_PHONEOUT; 9487699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_VOLUME].enable) 949a580b31aSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 950a580b31aSAriff Abdullah mask); 9517699548fSAriff Abdullah else { 952a580b31aSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 953a580b31aSAriff Abdullah mask); 954a580b31aSAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, 955a580b31aSAriff Abdullah SOUND_MIXER_NONE); 9567699548fSAriff Abdullah } 957a580b31aSAriff Abdullah } 9587699548fSAriff Abdullah break; 9597699548fSAriff Abdullah case 0x434d4941: /* CMI9738 */ 9607699548fSAriff Abdullah case 0x434d4961: /* CMI9739 */ 9617699548fSAriff Abdullah case 0x434d4978: /* CMI9761 */ 9627699548fSAriff Abdullah case 0x434d4982: /* CMI9761 */ 9637699548fSAriff Abdullah case 0x434d4983: /* CMI9761 */ 9647699548fSAriff Abdullah ac97_wrcd(codec, AC97_MIX_PCM, 0); 9657699548fSAriff Abdullah bzero(&codec->mix[SOUND_MIXER_PCM], 9667699548fSAriff Abdullah sizeof(codec->mix[SOUND_MIXER_PCM])); 9677699548fSAriff Abdullah d = device_get_softc(codec->dev); 9687699548fSAriff Abdullah if (d != NULL) 9697699548fSAriff Abdullah d->flags |= SD_F_SOFTPCMVOL; 9707699548fSAriff Abdullah /* XXX How about master volume ? */ 9717699548fSAriff Abdullah break; 9727699548fSAriff Abdullah default: 9737699548fSAriff Abdullah break; 9747699548fSAriff Abdullah } 9757699548fSAriff Abdullah 9767699548fSAriff Abdullah #if 0 9777699548fSAriff Abdullah /* XXX For the sake of debugging purposes */ 9787699548fSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 9797699548fSAriff Abdullah SOUND_MASK_PCM | SOUND_MASK_CD); 9807699548fSAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 9817699548fSAriff Abdullah ac97_wrcd(codec, AC97_MIX_MASTER, 0); 9827699548fSAriff Abdullah #endif 9837699548fSAriff Abdullah 984341f16ccSCameron Grant mask = 0; 985341f16ccSCameron Grant for (i = 0; i < 32; i++) 986341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 987341f16ccSCameron Grant mix_setdevs(m, mask); 988341f16ccSCameron Grant 989341f16ccSCameron Grant mask = 0; 990341f16ccSCameron Grant for (i = 0; i < 32; i++) 991341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 992341f16ccSCameron Grant mix_setrecdevs(m, mask); 993a580b31aSAriff Abdullah 994a580b31aSAriff Abdullah ac97_init_sysctl(codec); 995a580b31aSAriff Abdullah 996987e5972SCameron Grant return 0; 997987e5972SCameron Grant } 998987e5972SCameron Grant 999987e5972SCameron Grant static int 100066ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 100133dbf14aSCameron Grant { 100233dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1003341f16ccSCameron Grant 100433dbf14aSCameron Grant if (codec == NULL) 100533dbf14aSCameron Grant return -1; 100633dbf14aSCameron Grant /* 100733dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 100833dbf14aSCameron Grant return -1; 100933dbf14aSCameron Grant */ 101033dbf14aSCameron Grant ac97_destroy(codec); 101133dbf14aSCameron Grant return 0; 101233dbf14aSCameron Grant } 101333dbf14aSCameron Grant 101433dbf14aSCameron Grant static int 101566ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 10169ec437a3SCameron Grant { 10179ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 10189ec437a3SCameron Grant 10199ec437a3SCameron Grant if (codec == NULL) 10209ec437a3SCameron Grant return -1; 10219ec437a3SCameron Grant return ac97_reinitmixer(codec); 10229ec437a3SCameron Grant } 10239ec437a3SCameron Grant 10249ec437a3SCameron Grant static int 102566ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 1026987e5972SCameron Grant { 1027987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1028341f16ccSCameron Grant 102939004e69SCameron Grant if (codec == NULL) 103039004e69SCameron Grant return -1; 1031987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 1032987e5972SCameron Grant } 1033987e5972SCameron Grant 1034987e5972SCameron Grant static int 103566ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 1036987e5972SCameron Grant { 1037987e5972SCameron Grant int i; 1038987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1039341f16ccSCameron Grant 104039004e69SCameron Grant if (codec == NULL) 104139004e69SCameron Grant return -1; 1042987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 104339004e69SCameron Grant if ((src & (1 << i)) != 0) 104439004e69SCameron Grant break; 1045987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 1046987e5972SCameron Grant } 1047987e5972SCameron Grant 10480f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 10490f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 10500f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 10510f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 10520f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 10530f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 10540f55ac6cSCameron Grant { 0, 0 } 1055987e5972SCameron Grant }; 10560f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 10570f55ac6cSCameron Grant 10580f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 10590f55ac6cSCameron Grant 10600f55ac6cSCameron Grant kobj_class_t 10610f55ac6cSCameron Grant ac97_getmixerclass(void) 10620f55ac6cSCameron Grant { 10630f55ac6cSCameron Grant return &ac97mixer_class; 10640f55ac6cSCameron Grant } 1065