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 310f55ac6cSCameron Grant #include "mixer_if.h" 32987e5972SCameron Grant 3367b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$"); 3467b1dce3SCameron Grant 350f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 36987e5972SCameron Grant 3779bb7d52SCameron Grant struct ac97mixtable_entry { 3896524a52SOrion Hodson int reg:8; /* register index */ 3996524a52SOrion Hodson /* reg < 0 if inverted polarity */ 4096524a52SOrion Hodson unsigned bits:4; /* width of control field */ 4196524a52SOrion Hodson unsigned ofs:4; /* offset (only if stereo=0) */ 4296524a52SOrion Hodson unsigned stereo:1; /* set for stereo controls */ 4396524a52SOrion Hodson unsigned mute:1; /* bit15 is MUTE */ 4496524a52SOrion Hodson unsigned recidx:4; /* index in rec mux */ 4596524a52SOrion Hodson unsigned mask:1; /* use only masked bits */ 4696524a52SOrion Hodson unsigned enable:1; /* entry is enabled */ 4779bb7d52SCameron Grant }; 4879bb7d52SCameron Grant 4966ef8af5SCameron Grant #define AC97_NAMELEN 16 5066ef8af5SCameron Grant struct ac97_info { 5166ef8af5SCameron Grant kobj_t methods; 5266ef8af5SCameron Grant device_t dev; 5366ef8af5SCameron Grant void *devinfo; 5419921b23SOrion Hodson u_int32_t id; 5566ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5679bb7d52SCameron Grant u_int32_t flags; 5766ef8af5SCameron Grant struct ac97mixtable_entry mix[32]; 5866ef8af5SCameron Grant char name[AC97_NAMELEN]; 5900acb133SCameron Grant struct mtx *lock; 6066ef8af5SCameron Grant }; 6166ef8af5SCameron Grant 6219921b23SOrion Hodson struct ac97_vendorid { 6319921b23SOrion Hodson u_int32_t id; 6419921b23SOrion Hodson const char *name; 6519921b23SOrion Hodson }; 6619921b23SOrion Hodson 67987e5972SCameron Grant struct ac97_codecid { 6819921b23SOrion Hodson u_int32_t id; 6919921b23SOrion Hodson u_int8_t stepmask; 7019921b23SOrion Hodson u_int8_t noext:1; 71987e5972SCameron Grant char *name; 72f9eb1409SOrion Hodson ac97_patch patch; 73987e5972SCameron Grant }; 74987e5972SCameron Grant 75987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 7696524a52SOrion Hodson /* [offset] reg bits of st mu re mk en */ 77341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 78a52604cfSOrion Hodson [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 79341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 80341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 81341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 82341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 83341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 84341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 85341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 8696524a52SOrion Hodson [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 8796524a52SOrion Hodson /* use igain for the mic 20dB boost */ 8896524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 89341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 90341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 91341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 92341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 93987e5972SCameron Grant }; 94987e5972SCameron Grant 9519921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9619921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 9719921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 9819921b23SOrion Hodson { 0x414c4300, "Realtek" }, 9919921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10019921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 10119921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 10219921b23SOrion Hodson { 0x43585400, "Conexant" }, 1033c6b655dSMathew Kanner { 0x44543000, "Diamond Technology" }, 10461a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 10519921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 1063c6b655dSMathew Kanner { 0x48525300, "Intersil" }, 10719921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 1083c6b655dSMathew Kanner { 0x49544500, "ITE, Inc." }, 10919921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 11019921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 11119921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 1123c6b655dSMathew Kanner { 0x53494c00, "Silicon Laboratories" }, 11319921b23SOrion Hodson { 0x54524100, "TriTech" }, 1143c6b655dSMathew Kanner { 0x54584e00, "Texas Instruments" }, 11519921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 1163c6b655dSMathew Kanner { 0x57454300, "Winbond" }, 11719921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 11819921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 11986336196SAlexander Leidinger /* 12086336196SAlexander Leidinger * XXX This is a fluke, really! The real vendor 12186336196SAlexander Leidinger * should be SigmaTel, not this! This should be 12286336196SAlexander Leidinger * removed someday! 12386336196SAlexander Leidinger */ 1246f0182bdSOrion Hodson { 0x01408300, "Creative" }, 12519921b23SOrion Hodson { 0x00000000, NULL } 12619921b23SOrion Hodson }; 12719921b23SOrion Hodson 128987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 12919921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 13019921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 13119921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 13219921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 13319921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 134ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 135ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 136c5cb8d60SScott Long { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 137a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 138ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 139ba548c64SOrion Hodson { 0x41445374, 0x00, 0, "AD1981B", 0 }, 140a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 14119921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 14219921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 14319921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 1443c6b655dSMathew Kanner { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 1453c6b655dSMathew Kanner { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 14619921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1479963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 14819921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 14919921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 15019921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 151b327ee51SAriff Abdullah { 0x414c4752, 0x0f, 0, "ALC250", 0 }, 1525197cdc1SDag-Erling Smørgrav { 0x414c4760, 0x0f, 0, "ALC655", 0 }, 153b7994e34SPyun YongHyeon { 0x414c4770, 0x0f, 0, "ALC203", 0 }, 15494ed763dSJun Kuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 1553c6b655dSMathew Kanner { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 15619921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 15719921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 15819921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 159e5728f83SMIHIRA Sanpei Yoshiro { 0x4352592d, 0x07, 0, "CS4294", 0 }, 16019921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 16119921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 162b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 16319921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 164cb44f623SAlexander Leidinger { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, 16519921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 166cb44f623SAlexander Leidinger { 0x434d4978, 0x00, 0, "CMI9761", 0 }, 167cb44f623SAlexander Leidinger { 0x434d4982, 0x00, 0, "CMI9761", 0 }, 168cb44f623SAlexander Leidinger { 0x434d4983, 0x00, 0, "CMI9761", 0 }, 1693c6b655dSMathew Kanner { 0x43585421, 0x00, 0, "HSD11246", 0 }, 1703c6b655dSMathew Kanner { 0x43585428, 0x07, 0, "CX20468", 0 }, 17180138937SAriff Abdullah { 0x43585430, 0x00, 0, "CX20468-21", 0 }, 1723c6b655dSMathew Kanner { 0x44543000, 0x00, 0, "DT0398", 0 }, 17361a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 17461a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 17519921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 1763c6b655dSMathew Kanner { 0x48525300, 0x00, 0, "HMP9701", 0 }, 17719921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 17819921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 17919921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 18049fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 1813c6b655dSMathew Kanner { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 1823c6b655dSMathew Kanner { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 18319921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 18419921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 18519921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 18619921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 1873c6b655dSMathew Kanner { 0x4e534331, 0x00, 0, "LM4549", 0 }, 18819921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 18919921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 19019921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 19119921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 19219921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 19319921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 19419921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 19519921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 19619921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 19719921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 19819921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 19919921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 20019921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 20119921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 20219921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 20319921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 204cb44f623SAlexander Leidinger { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 20519921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 20619921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 20719921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 20819921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 20919921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 21019921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 2113c6b655dSMathew Kanner { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 2123c6b655dSMathew Kanner { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 21319921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 21480138937SAriff Abdullah { 0x56494170, 0x00, 0, "VIA1617A", 0 }, 21519921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 21619921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 21719921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 21819921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 2193c6b655dSMathew Kanner { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 2203c6b655dSMathew Kanner { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 2213c6b655dSMathew Kanner { 0x57454301, 0x00, 0, "W83971D", 0 }, 22219921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 22319921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 22419921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 22586336196SAlexander Leidinger /* 22686336196SAlexander Leidinger * XXX This is a fluke, really! The real codec 22786336196SAlexander Leidinger * should be STAC9704, not this! This should be 22886336196SAlexander Leidinger * removed someday! 22986336196SAlexander Leidinger */ 2306f0182bdSOrion Hodson { 0x01408384, 0x00, 0, "EV1938", 0 }, 23119921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 232987e5972SCameron Grant }; 233987e5972SCameron Grant 234987e5972SCameron Grant static char *ac97enhancement[] = { 23504553e63SCameron Grant "no 3D Stereo Enhancement", 236987e5972SCameron Grant "Analog Devices Phat Stereo", 237987e5972SCameron Grant "Creative Stereo Enhancement", 238987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 239987e5972SCameron Grant "Yamaha Ymersion", 240987e5972SCameron Grant "BBE 3D Stereo Enhancement", 241987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 242987e5972SCameron Grant "Qsound QXpander", 243987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 244987e5972SCameron Grant "SRS 3D Stereo Enhancement", 245987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 246987e5972SCameron Grant "AKM 3D Audio", 247987e5972SCameron Grant "Aureal Stereo Enhancement", 248987e5972SCameron Grant "Aztech 3D Enhancement", 249987e5972SCameron Grant "Binaura 3D Audio Enhancement", 250987e5972SCameron Grant "ESS Technology Stereo Enhancement", 251987e5972SCameron Grant "Harman International VMAx", 252987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 253987e5972SCameron Grant "Philips Incredible Sound", 254987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 255987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 256987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 257987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 258987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 259987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 260987e5972SCameron Grant "Delta Integration 3D Enhancement", 261987e5972SCameron Grant "SigmaTel 3D Enhancement", 262987e5972SCameron Grant "Reserved 27", 263987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 264987e5972SCameron Grant "Reserved 29", 265987e5972SCameron Grant "Reserved 30", 266987e5972SCameron Grant "Reserved 31" 267987e5972SCameron Grant }; 268987e5972SCameron Grant 269987e5972SCameron Grant static char *ac97feature[] = { 270987e5972SCameron Grant "mic channel", 271987e5972SCameron Grant "reserved", 272987e5972SCameron Grant "tone", 273987e5972SCameron Grant "simulated stereo", 274987e5972SCameron Grant "headphone", 275987e5972SCameron Grant "bass boost", 276987e5972SCameron Grant "18 bit DAC", 277987e5972SCameron Grant "20 bit DAC", 278987e5972SCameron Grant "18 bit ADC", 279987e5972SCameron Grant "20 bit ADC" 280987e5972SCameron Grant }; 281987e5972SCameron Grant 28239004e69SCameron Grant static char *ac97extfeature[] = { 28339004e69SCameron Grant "variable rate PCM", 28439004e69SCameron Grant "double rate PCM", 28539004e69SCameron Grant "reserved 1", 28639004e69SCameron Grant "variable rate mic", 28739004e69SCameron Grant "reserved 2", 28839004e69SCameron Grant "reserved 3", 28939004e69SCameron Grant "center DAC", 29039004e69SCameron Grant "surround DAC", 29139004e69SCameron Grant "LFE DAC", 29239004e69SCameron Grant "AMAP", 29339004e69SCameron Grant "reserved 4", 29439004e69SCameron Grant "reserved 5", 29539004e69SCameron Grant "reserved 6", 29639004e69SCameron Grant "reserved 7", 29739004e69SCameron Grant }; 29839004e69SCameron Grant 299f9eb1409SOrion Hodson u_int16_t 300f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 30139004e69SCameron Grant { 30286336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) { 30386336196SAlexander Leidinger u_int16_t i[2], j = 100; 30486336196SAlexander Leidinger 30586336196SAlexander Leidinger i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 30686336196SAlexander Leidinger i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 30786336196SAlexander Leidinger while (i[0] != i[1] && j) 30886336196SAlexander Leidinger i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 30986336196SAlexander Leidinger #if 0 31086336196SAlexander Leidinger if (j < 100) { 31186336196SAlexander Leidinger device_printf(codec->dev, "%s(): Inconsistent register value at" 31286336196SAlexander Leidinger " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 31386336196SAlexander Leidinger } 31486336196SAlexander Leidinger #endif 31586336196SAlexander Leidinger return i[!(j & 1)]; 31686336196SAlexander Leidinger } 3170f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 31839004e69SCameron Grant } 31939004e69SCameron Grant 320f9eb1409SOrion Hodson void 321f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 32239004e69SCameron Grant { 3230f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 32439004e69SCameron Grant } 32539004e69SCameron Grant 326c6d4b83aSOrion Hodson static void 327c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 328c6d4b83aSOrion Hodson { 329c6d4b83aSOrion Hodson u_int32_t i, ps; 330f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 331c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 332f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 333c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 334c6d4b83aSOrion Hodson return; 335c6d4b83aSOrion Hodson DELAY(1000); 336c6d4b83aSOrion Hodson } 337a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 338c6d4b83aSOrion Hodson } 339c6d4b83aSOrion Hodson 34039004e69SCameron Grant int 34139004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 34239004e69SCameron Grant { 34339004e69SCameron Grant u_int16_t v; 34439004e69SCameron Grant 34539004e69SCameron Grant switch(which) { 34639004e69SCameron Grant case AC97_REGEXT_FDACRATE: 34739004e69SCameron Grant case AC97_REGEXT_SDACRATE: 34839004e69SCameron Grant case AC97_REGEXT_LDACRATE: 34939004e69SCameron Grant case AC97_REGEXT_LADCRATE: 35039004e69SCameron Grant case AC97_REGEXT_MADCRATE: 35139004e69SCameron Grant break; 35239004e69SCameron Grant 35339004e69SCameron Grant default: 35439004e69SCameron Grant return -1; 35539004e69SCameron Grant } 35639004e69SCameron Grant 35766ef8af5SCameron Grant snd_mtxlock(codec->lock); 35839004e69SCameron Grant if (rate != 0) { 35939004e69SCameron Grant v = rate; 36039004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 36139004e69SCameron Grant v >>= 1; 362f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 36339004e69SCameron Grant } 364f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 36539004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 36639004e69SCameron Grant v <<= 1; 36766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 36839004e69SCameron Grant return v; 36939004e69SCameron Grant } 37039004e69SCameron Grant 37139004e69SCameron Grant int 37239004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 37339004e69SCameron Grant { 37439004e69SCameron Grant mode &= AC97_EXTCAPS; 375647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 376647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 377647fbfebSOrion Hodson mode); 37839004e69SCameron Grant return -1; 379647fbfebSOrion Hodson } 38066ef8af5SCameron Grant snd_mtxlock(codec->lock); 381f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 382f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 38366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 38439004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 38539004e69SCameron Grant } 38639004e69SCameron Grant 3879ec437a3SCameron Grant u_int16_t 3889ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3899ec437a3SCameron Grant { 3909ec437a3SCameron Grant return codec->extstat; 3919ec437a3SCameron Grant } 3929ec437a3SCameron Grant 3939ec437a3SCameron Grant u_int16_t 3949ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3959ec437a3SCameron Grant { 3969ec437a3SCameron Grant return codec->extcaps; 3979ec437a3SCameron Grant } 3989ec437a3SCameron Grant 3995d91ad67SCameron Grant u_int16_t 4005d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 4015d91ad67SCameron Grant { 4025d91ad67SCameron Grant return codec->caps; 4035d91ad67SCameron Grant } 4045d91ad67SCameron Grant 405987e5972SCameron Grant static int 406987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 407987e5972SCameron Grant { 408987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 409341f16ccSCameron Grant 410987e5972SCameron Grant if (e->recidx > 0) { 411987e5972SCameron Grant int val = e->recidx - 1; 412987e5972SCameron Grant val |= val << 8; 41366ef8af5SCameron Grant snd_mtxlock(codec->lock); 414f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 41566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 416987e5972SCameron Grant return 0; 41739004e69SCameron Grant } else 41839004e69SCameron Grant return -1; 419987e5972SCameron Grant } 420987e5972SCameron Grant 421987e5972SCameron Grant static int 422987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 423987e5972SCameron Grant { 424987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 425341f16ccSCameron Grant 426341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 42796524a52SOrion Hodson int mask, max, val, reg; 42896524a52SOrion Hodson 42996524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 43096524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 43196524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 432987e5972SCameron Grant 43339004e69SCameron Grant if (!e->stereo) 43439004e69SCameron Grant right = left; 43596524a52SOrion Hodson 43696524a52SOrion Hodson /* 43796524a52SOrion Hodson * Invert the range if the polarity requires so, 43896524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 43996524a52SOrion Hodson * write into the codec, and scale back to 0..100 44096524a52SOrion Hodson * for the return value. 44196524a52SOrion Hodson */ 442987e5972SCameron Grant if (e->reg > 0) { 443987e5972SCameron Grant left = 100 - left; 444987e5972SCameron Grant right = 100 - right; 445987e5972SCameron Grant } 446987e5972SCameron Grant 447987e5972SCameron Grant left = (left * max) / 100; 448987e5972SCameron Grant right = (right * max) / 100; 449987e5972SCameron Grant 450987e5972SCameron Grant val = (left << 8) | right; 451987e5972SCameron Grant 452987e5972SCameron Grant left = (left * 100) / max; 453987e5972SCameron Grant right = (right * 100) / max; 454987e5972SCameron Grant 455987e5972SCameron Grant if (e->reg > 0) { 456987e5972SCameron Grant left = 100 - left; 457987e5972SCameron Grant right = 100 - right; 458987e5972SCameron Grant } 459987e5972SCameron Grant 46096524a52SOrion Hodson /* 46196524a52SOrion Hodson * For mono controls, trim val and mask, also taking 46296524a52SOrion Hodson * care of e->ofs (offset of control field). 46396524a52SOrion Hodson */ 46496524a52SOrion Hodson if (e->ofs) { 465987e5972SCameron Grant val &= max; 466987e5972SCameron Grant val <<= e->ofs; 46796524a52SOrion Hodson mask = (max << e->ofs); 46896524a52SOrion Hodson } 46996524a52SOrion Hodson 47096524a52SOrion Hodson /* 47196524a52SOrion Hodson * If we have a mute bit, add it to the mask and 47296524a52SOrion Hodson * update val and set mute if both channels require a 47396524a52SOrion Hodson * zero volume. 47496524a52SOrion Hodson */ 47596524a52SOrion Hodson if (e->mute == 1) { 47696524a52SOrion Hodson mask |= AC97_MUTE; 47796524a52SOrion Hodson if (left == 0 && right == 0) 47896524a52SOrion Hodson val = AC97_MUTE; 47996524a52SOrion Hodson } 48096524a52SOrion Hodson 48196524a52SOrion Hodson /* 48296524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 48396524a52SOrion Hodson */ 48496524a52SOrion Hodson snd_mtxlock(codec->lock); 485987e5972SCameron Grant if (e->mask) { 48686336196SAlexander Leidinger int cur = ac97_rdcd(codec, reg); 48796524a52SOrion Hodson val |= cur & ~(mask); 488987e5972SCameron Grant } 489f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 49066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 491987e5972SCameron Grant return left | (right << 8); 492341f16ccSCameron Grant } else { 49386336196SAlexander Leidinger #if 0 49486336196SAlexander Leidinger printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 49586336196SAlexander Leidinger #endif 49639004e69SCameron Grant return -1; 497987e5972SCameron Grant } 498341f16ccSCameron Grant } 499987e5972SCameron Grant 500108082c4SOrion Hodson static void 501108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 502108082c4SOrion Hodson { 503cfd5696dSOrion Hodson int keep_ogain; 504cfd5696dSOrion Hodson 505a52604cfSOrion Hodson /* 506cfd5696dSOrion Hodson * By default, The ac97 aux_out register (0x04) corresponds to OSS's 507cfd5696dSOrion Hodson * OGAIN setting. 508a52604cfSOrion Hodson * 509cfd5696dSOrion Hodson * We first check whether aux_out is a valid register. If not 510cfd5696dSOrion Hodson * we may not want to keep ogain. 511a52604cfSOrion Hodson */ 512cfd5696dSOrion Hodson keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 513a52604cfSOrion Hodson 514a52604cfSOrion Hodson /* 515a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 516108082c4SOrion Hodson * 517108082c4SOrion Hodson * 1. Headphone out. 518108082c4SOrion Hodson * 2. 4-Channel Out 519108082c4SOrion Hodson * 3. True line level out (effectively master volume). 520108082c4SOrion Hodson * 521108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 522108082c4SOrion Hodson */ 523a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 524f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 525cfd5696dSOrion Hodson codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 526cfd5696dSOrion Hodson keep_ogain = 1; 527cfd5696dSOrion Hodson } 528cfd5696dSOrion Hodson 529cfd5696dSOrion Hodson if (keep_ogain == 0) { 530cfd5696dSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 531cfd5696dSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 532108082c4SOrion Hodson } 533a52604cfSOrion Hodson } 534a52604cfSOrion Hodson 535a52604cfSOrion Hodson static void 536a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 537a52604cfSOrion Hodson { 538a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 539a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 540a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 541a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 542a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 543a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 544a52604cfSOrion Hodson } 545108082c4SOrion Hodson } 546108082c4SOrion Hodson 547cb44f623SAlexander Leidinger static void 548cb44f623SAlexander Leidinger ac97_fix_volume(struct ac97_info *codec) 549cb44f623SAlexander Leidinger { 550cb44f623SAlexander Leidinger struct snddev_info *d = device_get_softc(codec->dev); 551cb44f623SAlexander Leidinger 552cb44f623SAlexander Leidinger #if 0 553cb44f623SAlexander Leidinger /* XXX For the sake of debugging purposes */ 554cb44f623SAlexander Leidinger ac97_wrcd(codec, AC97_MIX_PCM, 0); 555cb44f623SAlexander Leidinger bzero(&codec->mix[SOUND_MIXER_PCM], 556cb44f623SAlexander Leidinger sizeof(codec->mix[SOUND_MIXER_PCM])); 557cb44f623SAlexander Leidinger codec->flags |= AC97_F_SOFTVOL; 558cb44f623SAlexander Leidinger if (d) 559cb44f623SAlexander Leidinger d->flags |= SD_F_SOFTVOL; 560cb44f623SAlexander Leidinger return; 561cb44f623SAlexander Leidinger #endif 562cb44f623SAlexander Leidinger switch (codec->id) { 563cb44f623SAlexander Leidinger case 0x434d4941: /* CMI9738 */ 564cb44f623SAlexander Leidinger case 0x434d4961: /* CMI9739 */ 565cb44f623SAlexander Leidinger case 0x434d4978: /* CMI9761 */ 566cb44f623SAlexander Leidinger case 0x434d4982: /* CMI9761 */ 567cb44f623SAlexander Leidinger case 0x434d4983: /* CMI9761 */ 568cb44f623SAlexander Leidinger ac97_wrcd(codec, AC97_MIX_PCM, 0); 569cb44f623SAlexander Leidinger break; 570cb44f623SAlexander Leidinger default: 571cb44f623SAlexander Leidinger return; 572cb44f623SAlexander Leidinger break; 573cb44f623SAlexander Leidinger } 574cb44f623SAlexander Leidinger bzero(&codec->mix[SOUND_MIXER_PCM], 575cb44f623SAlexander Leidinger sizeof(codec->mix[SOUND_MIXER_PCM])); 576cb44f623SAlexander Leidinger codec->flags |= AC97_F_SOFTVOL; 577cb44f623SAlexander Leidinger if (d) 578cb44f623SAlexander Leidinger d->flags |= SD_F_SOFTVOL; 579cb44f623SAlexander Leidinger } 580cb44f623SAlexander Leidinger 58119921b23SOrion Hodson static const char* 58219921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 58319921b23SOrion Hodson { 58419921b23SOrion Hodson if (cname == NULL) { 58519921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 58619921b23SOrion Hodson return buf; 58719921b23SOrion Hodson } 58819921b23SOrion Hodson 58919921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 59019921b23SOrion Hodson 59119921b23SOrion Hodson if (bootverbose) { 59219921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 59319921b23SOrion Hodson } else { 59419921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 59519921b23SOrion Hodson } 59619921b23SOrion Hodson return buf; 59719921b23SOrion Hodson } 59819921b23SOrion Hodson 599987e5972SCameron Grant static unsigned 60039004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 601987e5972SCameron Grant { 602f9eb1409SOrion Hodson ac97_patch codec_patch; 60319921b23SOrion Hodson const char *cname, *vname; 60419921b23SOrion Hodson char desc[80]; 60519921b23SOrion Hodson u_int8_t model, step; 60686336196SAlexander Leidinger unsigned i, j, k, bit, old; 607987e5972SCameron Grant u_int32_t id; 60886336196SAlexander Leidinger int reg; 609987e5972SCameron Grant 61066ef8af5SCameron Grant snd_mtxlock(codec->lock); 6110f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 612cd2c103aSCameron Grant if (codec->count == 0) { 61304553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 61466ef8af5SCameron Grant snd_mtxunlock(codec->lock); 61504553e63SCameron Grant return ENODEV; 61604553e63SCameron Grant } 6179ec437a3SCameron Grant 618f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 619c6d4b83aSOrion Hodson ac97_reset(codec); 620f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 621987e5972SCameron Grant 622f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 62386336196SAlexander Leidinger j = ac97_rdcd(codec, AC97_REG_RESET); 62486336196SAlexander Leidinger /* 62586336196SAlexander Leidinger * Let see if this codec can return consistent value. 62686336196SAlexander Leidinger * If not, turn on aggressive read workaround 62786336196SAlexander Leidinger * (STAC9704 comes in mind). 62886336196SAlexander Leidinger */ 62986336196SAlexander Leidinger if (i != j) { 63086336196SAlexander Leidinger codec->flags |= AC97_F_RDCD_BUG; 63186336196SAlexander Leidinger i = ac97_rdcd(codec, AC97_REG_RESET); 63286336196SAlexander Leidinger } 633987e5972SCameron Grant codec->caps = i & 0x03ff; 634987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 635987e5972SCameron Grant 636f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 637e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 638e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 63966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 640e620d959SCameron Grant return ENODEV; 641e620d959SCameron Grant } 6426b4b88f7SCameron Grant 64319921b23SOrion Hodson codec->id = id; 644cd2c103aSCameron Grant codec->noext = 0; 645f9eb1409SOrion Hodson codec_patch = NULL; 64619921b23SOrion Hodson 64719921b23SOrion Hodson cname = NULL; 64819921b23SOrion Hodson model = step = 0; 649cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 65019921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 65119921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 652cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 653f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 65419921b23SOrion Hodson cname = ac97codecid[i].name; 65519921b23SOrion Hodson model = (id & modelmask) & 0xff; 65619921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 65719921b23SOrion Hodson break; 65819921b23SOrion Hodson } 65919921b23SOrion Hodson } 66019921b23SOrion Hodson 66119921b23SOrion Hodson vname = NULL; 66219921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 66319921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 66419921b23SOrion Hodson vname = ac97vendorid[i].name; 66519921b23SOrion Hodson break; 666cd2c103aSCameron Grant } 667cd2c103aSCameron Grant } 6686b4b88f7SCameron Grant 669cd2c103aSCameron Grant codec->extcaps = 0; 670cd2c103aSCameron Grant codec->extid = 0; 671cd2c103aSCameron Grant codec->extstat = 0; 6726a6ee5bbSCameron Grant if (!codec->noext) { 673f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 6746a6ee5bbSCameron Grant if (i != 0xffff) { 67539004e69SCameron Grant codec->extcaps = i & 0x3fff; 67639004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 677f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 6786b4b88f7SCameron Grant } 6796a6ee5bbSCameron Grant } 680987e5972SCameron Grant 681341f16ccSCameron Grant for (i = 0; i < 32; i++) { 682108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 683108082c4SOrion Hodson } 684108082c4SOrion Hodson ac97_fix_auxout(codec); 685a52604cfSOrion Hodson ac97_fix_tone(codec); 686cb44f623SAlexander Leidinger ac97_fix_volume(codec); 687f9eb1409SOrion Hodson if (codec_patch) 688f9eb1409SOrion Hodson codec_patch(codec); 689108082c4SOrion Hodson 690108082c4SOrion Hodson for (i = 0; i < 32; i++) { 69133c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 69286336196SAlexander Leidinger reg = codec->mix[i].reg; 69386336196SAlexander Leidinger if (reg < 0) 69486336196SAlexander Leidinger reg = -reg; 69586336196SAlexander Leidinger if (k && reg) { 69686336196SAlexander Leidinger j = old = ac97_rdcd(codec, reg); 69786336196SAlexander Leidinger /* 69886336196SAlexander Leidinger * Test for mute bit (except for AC97_MIX_TONE, 69986336196SAlexander Leidinger * where we simply assume it as available). 70086336196SAlexander Leidinger */ 70186336196SAlexander Leidinger if (codec->mix[i].mute) { 70286336196SAlexander Leidinger ac97_wrcd(codec, reg, j | 0x8000); 70386336196SAlexander Leidinger j = ac97_rdcd(codec, reg); 70486336196SAlexander Leidinger } else 705fe435202SAlexander Leidinger j |= 0x8000; 70686336196SAlexander Leidinger if ((j & 0x8000)) { 70786336196SAlexander Leidinger /* 70886336196SAlexander Leidinger * Test whether the control width should be 70986336196SAlexander Leidinger * 4, 5 or 6 bit. For 5bit register, we should 71086336196SAlexander Leidinger * test it whether it's really 5 or 6bit. Leave 71186336196SAlexander Leidinger * 4bit register alone, because sometimes an 71286336196SAlexander Leidinger * attempt to write past 4th bit may cause 71386336196SAlexander Leidinger * incorrect result especially for AC97_MIX_BEEP 71486336196SAlexander Leidinger * (ac97 2.3). 71586336196SAlexander Leidinger */ 71686336196SAlexander Leidinger bit = codec->mix[i].bits; 71786336196SAlexander Leidinger if (bit == 5) 71886336196SAlexander Leidinger bit++; 71986336196SAlexander Leidinger j = ((1 << bit) - 1) << codec->mix[i].ofs; 72086336196SAlexander Leidinger ac97_wrcd(codec, reg, 72186336196SAlexander Leidinger j | (codec->mix[i].mute ? 0x8000 : 0)); 72286336196SAlexander Leidinger k = ac97_rdcd(codec, reg) & j; 72386336196SAlexander Leidinger k >>= codec->mix[i].ofs; 72486336196SAlexander Leidinger if (reg == AC97_MIX_TONE && 72586336196SAlexander Leidinger ((k & 0x0001) == 0x0000)) 726fe435202SAlexander Leidinger k >>= 1; 72786336196SAlexander Leidinger for (j = 0; k >> j; j++) 72886336196SAlexander Leidinger ; 729fe435202SAlexander Leidinger if (j != 0) { 730fe435202SAlexander Leidinger #if 0 73186336196SAlexander Leidinger device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 73286336196SAlexander Leidinger i, k, bit, codec->mix[i].bits, j); 733fe435202SAlexander Leidinger #endif 73486336196SAlexander Leidinger codec->mix[i].enable = 1; 73586336196SAlexander Leidinger codec->mix[i].bits = j; 736eaf70083SAriff Abdullah } else if (reg == AC97_MIX_BEEP) { 737eaf70083SAriff Abdullah /* 738eaf70083SAriff Abdullah * Few codec such as CX20468-21 does 739eaf70083SAriff Abdullah * have this control register, although 740eaf70083SAriff Abdullah * the only usable part is the mute bit. 741eaf70083SAriff Abdullah */ 742eaf70083SAriff Abdullah codec->mix[i].enable = 1; 743fe435202SAlexander Leidinger } else 744fe435202SAlexander Leidinger codec->mix[i].enable = 0; 745fe435202SAlexander Leidinger } else 746fe435202SAlexander Leidinger codec->mix[i].enable = 0; 74786336196SAlexander Leidinger ac97_wrcd(codec, reg, old); 748fe435202SAlexander Leidinger } 749fe435202SAlexander Leidinger #if 0 750fe435202SAlexander Leidinger printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 751fe435202SAlexander Leidinger #endif 752341f16ccSCameron Grant } 753987e5972SCameron Grant 75419921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 75519921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 756a825c6e5SOrion Hodson 757987e5972SCameron Grant if (bootverbose) { 75886336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) 75986336196SAlexander Leidinger device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 760cb44f623SAlexander Leidinger if (codec->flags & AC97_F_SOFTVOL) 761cb44f623SAlexander Leidinger device_printf(codec->dev, "Soft PCM volume\n"); 76219921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 76339004e69SCameron Grant for (i = j = 0; i < 10; i++) 76439004e69SCameron Grant if (codec->caps & (1 << i)) 76539004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 76639004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 767987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 76839004e69SCameron Grant 76939004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 77019921b23SOrion Hodson device_printf(codec->dev, "%s codec", 77119921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 77239004e69SCameron Grant if (codec->extcaps) 77339004e69SCameron Grant printf(" extended features "); 77439004e69SCameron Grant for (i = j = 0; i < 14; i++) 77539004e69SCameron Grant if (codec->extcaps & (1 << i)) 77639004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 77739004e69SCameron Grant printf("\n"); 77839004e69SCameron Grant } 779987e5972SCameron Grant } 780987e5972SCameron Grant 781fe435202SAlexander Leidinger i = 0; 782fe435202SAlexander Leidinger while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 783fe435202SAlexander Leidinger if (++i == 100) { 78403a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 785fe435202SAlexander Leidinger break; 786fe435202SAlexander Leidinger } 787fe435202SAlexander Leidinger DELAY(1000); 788fe435202SAlexander Leidinger } 789fe435202SAlexander Leidinger if (bootverbose) 790fe435202SAlexander Leidinger device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 79166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 792987e5972SCameron Grant return 0; 793987e5972SCameron Grant } 794987e5972SCameron Grant 7959ec437a3SCameron Grant static unsigned 7969ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 7979ec437a3SCameron Grant { 79866ef8af5SCameron Grant snd_mtxlock(codec->lock); 7990f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 8009ec437a3SCameron Grant if (codec->count == 0) { 8019ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 80266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8039ec437a3SCameron Grant return ENODEV; 8049ec437a3SCameron Grant } 8059ec437a3SCameron Grant 806f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 807c6d4b83aSOrion Hodson ac97_reset(codec); 808f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 8099ec437a3SCameron Grant 8109ec437a3SCameron Grant if (!codec->noext) { 811f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 812f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 813b60e55dbSGuido van Rooij != codec->extstat) 8149ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 815b60e55dbSGuido van Rooij codec->extstat, 816f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 817b60e55dbSGuido van Rooij AC97_EXTCAPS); 8189ec437a3SCameron Grant } 8199ec437a3SCameron Grant 820f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 8219ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 82266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8239ec437a3SCameron Grant return 0; 8249ec437a3SCameron Grant } 8259ec437a3SCameron Grant 826987e5972SCameron Grant struct ac97_info * 8270f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 828987e5972SCameron Grant { 829987e5972SCameron Grant struct ac97_info *codec; 830987e5972SCameron Grant 8310f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 8320f55ac6cSCameron Grant if (codec == NULL) 8330f55ac6cSCameron Grant return NULL; 8340f55ac6cSCameron Grant 83566ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 836489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 837a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 8380f55ac6cSCameron Grant if (codec->methods == NULL) { 83966ef8af5SCameron Grant snd_mtxlock(codec->lock); 84066ef8af5SCameron Grant snd_mtxfree(codec->lock); 8410f55ac6cSCameron Grant free(codec, M_AC97); 8420f55ac6cSCameron Grant return NULL; 843987e5972SCameron Grant } 8440f55ac6cSCameron Grant 8450f55ac6cSCameron Grant codec->dev = dev; 8460f55ac6cSCameron Grant codec->devinfo = devinfo; 84779bb7d52SCameron Grant codec->flags = 0; 848987e5972SCameron Grant return codec; 849987e5972SCameron Grant } 850987e5972SCameron Grant 85133dbf14aSCameron Grant void 85233dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 85333dbf14aSCameron Grant { 85466ef8af5SCameron Grant snd_mtxlock(codec->lock); 8550f55ac6cSCameron Grant if (codec->methods != NULL) 8560f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 85766ef8af5SCameron Grant snd_mtxfree(codec->lock); 8580f55ac6cSCameron Grant free(codec, M_AC97); 85933dbf14aSCameron Grant } 86033dbf14aSCameron Grant 86179bb7d52SCameron Grant void 86279bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 86379bb7d52SCameron Grant { 86479bb7d52SCameron Grant codec->flags = val; 86579bb7d52SCameron Grant } 86679bb7d52SCameron Grant 86779bb7d52SCameron Grant u_int32_t 86879bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 86979bb7d52SCameron Grant { 87079bb7d52SCameron Grant return codec->flags; 87179bb7d52SCameron Grant } 87279bb7d52SCameron Grant 8730f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 8740f55ac6cSCameron Grant 875987e5972SCameron Grant static int 87666ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 877987e5972SCameron Grant { 878987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 879341f16ccSCameron Grant u_int32_t i, mask; 880341f16ccSCameron Grant 88139004e69SCameron Grant if (codec == NULL) 88239004e69SCameron Grant return -1; 883341f16ccSCameron Grant 884e620d959SCameron Grant if (ac97_initmixer(codec)) 885e620d959SCameron Grant return -1; 886341f16ccSCameron Grant 887341f16ccSCameron Grant mask = 0; 888341f16ccSCameron Grant for (i = 0; i < 32; i++) 889341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 890341f16ccSCameron Grant mix_setdevs(m, mask); 891341f16ccSCameron Grant 892341f16ccSCameron Grant mask = 0; 893341f16ccSCameron Grant for (i = 0; i < 32; i++) 894341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 895341f16ccSCameron Grant mix_setrecdevs(m, mask); 896987e5972SCameron Grant return 0; 897987e5972SCameron Grant } 898987e5972SCameron Grant 899987e5972SCameron Grant static int 90066ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 90133dbf14aSCameron Grant { 90233dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 903341f16ccSCameron Grant 90433dbf14aSCameron Grant if (codec == NULL) 90533dbf14aSCameron Grant return -1; 90633dbf14aSCameron Grant /* 90733dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 90833dbf14aSCameron Grant return -1; 90933dbf14aSCameron Grant */ 91033dbf14aSCameron Grant ac97_destroy(codec); 91133dbf14aSCameron Grant return 0; 91233dbf14aSCameron Grant } 91333dbf14aSCameron Grant 91433dbf14aSCameron Grant static int 91566ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 9169ec437a3SCameron Grant { 9179ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 9189ec437a3SCameron Grant 9199ec437a3SCameron Grant if (codec == NULL) 9209ec437a3SCameron Grant return -1; 9219ec437a3SCameron Grant return ac97_reinitmixer(codec); 9229ec437a3SCameron Grant } 9239ec437a3SCameron Grant 9249ec437a3SCameron Grant static int 92566ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 926987e5972SCameron Grant { 927987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 928341f16ccSCameron Grant 92939004e69SCameron Grant if (codec == NULL) 93039004e69SCameron Grant return -1; 931987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 932987e5972SCameron Grant } 933987e5972SCameron Grant 934987e5972SCameron Grant static int 93566ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 936987e5972SCameron Grant { 937987e5972SCameron Grant int i; 938987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 939341f16ccSCameron Grant 94039004e69SCameron Grant if (codec == NULL) 94139004e69SCameron Grant return -1; 942987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 94339004e69SCameron Grant if ((src & (1 << i)) != 0) 94439004e69SCameron Grant break; 945987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 946987e5972SCameron Grant } 947987e5972SCameron Grant 9480f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 9490f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 9500f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 9510f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 9520f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 9530f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 9540f55ac6cSCameron Grant { 0, 0 } 955987e5972SCameron Grant }; 9560f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 9570f55ac6cSCameron Grant 9580f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 9590f55ac6cSCameron Grant 9600f55ac6cSCameron Grant kobj_class_t 9610f55ac6cSCameron Grant ac97_getmixerclass(void) 9620f55ac6cSCameron Grant { 9630f55ac6cSCameron Grant return &ac97mixer_class; 9640f55ac6cSCameron Grant } 9650f55ac6cSCameron Grant 966987e5972SCameron Grant 967