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 }, 876faa2f6dSJohn Baldwin #if 0 8896524a52SOrion Hodson /* use igain for the mic 20dB boost */ 8996524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 906faa2f6dSJohn Baldwin #endif 91341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 92341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 93341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 94341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 95987e5972SCameron Grant }; 96987e5972SCameron Grant 9719921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9819921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 9919921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 10019921b23SOrion Hodson { 0x414c4300, "Realtek" }, 10119921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10219921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 10319921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 10419921b23SOrion Hodson { 0x43585400, "Conexant" }, 1053c6b655dSMathew Kanner { 0x44543000, "Diamond Technology" }, 10661a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 10719921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 1083c6b655dSMathew Kanner { 0x48525300, "Intersil" }, 10919921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 1103c6b655dSMathew Kanner { 0x49544500, "ITE, Inc." }, 11119921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 11219921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 11319921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 1143c6b655dSMathew Kanner { 0x53494c00, "Silicon Laboratories" }, 11519921b23SOrion Hodson { 0x54524100, "TriTech" }, 1163c6b655dSMathew Kanner { 0x54584e00, "Texas Instruments" }, 11719921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 1183c6b655dSMathew Kanner { 0x57454300, "Winbond" }, 11919921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 12019921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 12186336196SAlexander Leidinger /* 12286336196SAlexander Leidinger * XXX This is a fluke, really! The real vendor 12386336196SAlexander Leidinger * should be SigmaTel, not this! This should be 12486336196SAlexander Leidinger * removed someday! 12586336196SAlexander Leidinger */ 1266f0182bdSOrion Hodson { 0x01408300, "Creative" }, 12719921b23SOrion Hodson { 0x00000000, NULL } 12819921b23SOrion Hodson }; 12919921b23SOrion Hodson 130987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 13119921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 13219921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 13319921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 13419921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 13519921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 136ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 137ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 138c5cb8d60SScott Long { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 139a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 140ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 141ba548c64SOrion Hodson { 0x41445374, 0x00, 0, "AD1981B", 0 }, 142a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 14319921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 14419921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 14519921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 1463c6b655dSMathew Kanner { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 1473c6b655dSMathew Kanner { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 14819921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1499963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 15019921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 15119921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 15219921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 1535197cdc1SDag-Erling Smørgrav { 0x414c4760, 0x0f, 0, "ALC655", 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 }, 1713c6b655dSMathew Kanner { 0x44543000, 0x00, 0, "DT0398", 0 }, 17261a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 17361a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 17419921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 1753c6b655dSMathew Kanner { 0x48525300, 0x00, 0, "HMP9701", 0 }, 17619921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 17719921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 17819921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 17949fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 1803c6b655dSMathew Kanner { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 1813c6b655dSMathew Kanner { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 18219921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 18319921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 18419921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 18519921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 1863c6b655dSMathew Kanner { 0x4e534331, 0x00, 0, "LM4549", 0 }, 18719921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 18819921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 18919921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 19019921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 19119921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 19219921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 19319921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 19419921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 19519921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 19619921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 19719921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 19819921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 19919921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 20019921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 20119921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 20219921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 203cb44f623SAlexander Leidinger { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 20419921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 20519921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 20619921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 20719921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 20819921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 20919921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 2103c6b655dSMathew Kanner { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 2113c6b655dSMathew Kanner { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 21219921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 21319921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 21419921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 21519921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 21619921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 2173c6b655dSMathew Kanner { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 2183c6b655dSMathew Kanner { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 2193c6b655dSMathew Kanner { 0x57454301, 0x00, 0, "W83971D", 0 }, 22019921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 22119921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 22219921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 22386336196SAlexander Leidinger /* 22486336196SAlexander Leidinger * XXX This is a fluke, really! The real codec 22586336196SAlexander Leidinger * should be STAC9704, not this! This should be 22686336196SAlexander Leidinger * removed someday! 22786336196SAlexander Leidinger */ 2286f0182bdSOrion Hodson { 0x01408384, 0x00, 0, "EV1938", 0 }, 22919921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 230987e5972SCameron Grant }; 231987e5972SCameron Grant 232987e5972SCameron Grant static char *ac97enhancement[] = { 23304553e63SCameron Grant "no 3D Stereo Enhancement", 234987e5972SCameron Grant "Analog Devices Phat Stereo", 235987e5972SCameron Grant "Creative Stereo Enhancement", 236987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 237987e5972SCameron Grant "Yamaha Ymersion", 238987e5972SCameron Grant "BBE 3D Stereo Enhancement", 239987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 240987e5972SCameron Grant "Qsound QXpander", 241987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 242987e5972SCameron Grant "SRS 3D Stereo Enhancement", 243987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 244987e5972SCameron Grant "AKM 3D Audio", 245987e5972SCameron Grant "Aureal Stereo Enhancement", 246987e5972SCameron Grant "Aztech 3D Enhancement", 247987e5972SCameron Grant "Binaura 3D Audio Enhancement", 248987e5972SCameron Grant "ESS Technology Stereo Enhancement", 249987e5972SCameron Grant "Harman International VMAx", 250987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 251987e5972SCameron Grant "Philips Incredible Sound", 252987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 253987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 254987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 255987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 256987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 257987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 258987e5972SCameron Grant "Delta Integration 3D Enhancement", 259987e5972SCameron Grant "SigmaTel 3D Enhancement", 260987e5972SCameron Grant "Reserved 27", 261987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 262987e5972SCameron Grant "Reserved 29", 263987e5972SCameron Grant "Reserved 30", 264987e5972SCameron Grant "Reserved 31" 265987e5972SCameron Grant }; 266987e5972SCameron Grant 267987e5972SCameron Grant static char *ac97feature[] = { 268987e5972SCameron Grant "mic channel", 269987e5972SCameron Grant "reserved", 270987e5972SCameron Grant "tone", 271987e5972SCameron Grant "simulated stereo", 272987e5972SCameron Grant "headphone", 273987e5972SCameron Grant "bass boost", 274987e5972SCameron Grant "18 bit DAC", 275987e5972SCameron Grant "20 bit DAC", 276987e5972SCameron Grant "18 bit ADC", 277987e5972SCameron Grant "20 bit ADC" 278987e5972SCameron Grant }; 279987e5972SCameron Grant 28039004e69SCameron Grant static char *ac97extfeature[] = { 28139004e69SCameron Grant "variable rate PCM", 28239004e69SCameron Grant "double rate PCM", 28339004e69SCameron Grant "reserved 1", 28439004e69SCameron Grant "variable rate mic", 28539004e69SCameron Grant "reserved 2", 28639004e69SCameron Grant "reserved 3", 28739004e69SCameron Grant "center DAC", 28839004e69SCameron Grant "surround DAC", 28939004e69SCameron Grant "LFE DAC", 29039004e69SCameron Grant "AMAP", 29139004e69SCameron Grant "reserved 4", 29239004e69SCameron Grant "reserved 5", 29339004e69SCameron Grant "reserved 6", 29439004e69SCameron Grant "reserved 7", 29539004e69SCameron Grant }; 29639004e69SCameron Grant 297f9eb1409SOrion Hodson u_int16_t 298f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 29939004e69SCameron Grant { 30086336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) { 30186336196SAlexander Leidinger u_int16_t i[2], j = 100; 30286336196SAlexander Leidinger 30386336196SAlexander Leidinger i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 30486336196SAlexander Leidinger i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 30586336196SAlexander Leidinger while (i[0] != i[1] && j) 30686336196SAlexander Leidinger i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 30786336196SAlexander Leidinger #if 0 30886336196SAlexander Leidinger if (j < 100) { 30986336196SAlexander Leidinger device_printf(codec->dev, "%s(): Inconsistent register value at" 31086336196SAlexander Leidinger " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 31186336196SAlexander Leidinger } 31286336196SAlexander Leidinger #endif 31386336196SAlexander Leidinger return i[!(j & 1)]; 31486336196SAlexander Leidinger } 3150f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 31639004e69SCameron Grant } 31739004e69SCameron Grant 318f9eb1409SOrion Hodson void 319f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 32039004e69SCameron Grant { 3210f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 32239004e69SCameron Grant } 32339004e69SCameron Grant 324c6d4b83aSOrion Hodson static void 325c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 326c6d4b83aSOrion Hodson { 327c6d4b83aSOrion Hodson u_int32_t i, ps; 328f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 329c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 330f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 331c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 332c6d4b83aSOrion Hodson return; 333c6d4b83aSOrion Hodson DELAY(1000); 334c6d4b83aSOrion Hodson } 335a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 336c6d4b83aSOrion Hodson } 337c6d4b83aSOrion Hodson 33839004e69SCameron Grant int 33939004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 34039004e69SCameron Grant { 34139004e69SCameron Grant u_int16_t v; 34239004e69SCameron Grant 34339004e69SCameron Grant switch(which) { 34439004e69SCameron Grant case AC97_REGEXT_FDACRATE: 34539004e69SCameron Grant case AC97_REGEXT_SDACRATE: 34639004e69SCameron Grant case AC97_REGEXT_LDACRATE: 34739004e69SCameron Grant case AC97_REGEXT_LADCRATE: 34839004e69SCameron Grant case AC97_REGEXT_MADCRATE: 34939004e69SCameron Grant break; 35039004e69SCameron Grant 35139004e69SCameron Grant default: 35239004e69SCameron Grant return -1; 35339004e69SCameron Grant } 35439004e69SCameron Grant 35566ef8af5SCameron Grant snd_mtxlock(codec->lock); 35639004e69SCameron Grant if (rate != 0) { 35739004e69SCameron Grant v = rate; 35839004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 35939004e69SCameron Grant v >>= 1; 360f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 36139004e69SCameron Grant } 362f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 36339004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 36439004e69SCameron Grant v <<= 1; 36566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 36639004e69SCameron Grant return v; 36739004e69SCameron Grant } 36839004e69SCameron Grant 36939004e69SCameron Grant int 37039004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 37139004e69SCameron Grant { 37239004e69SCameron Grant mode &= AC97_EXTCAPS; 373647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 374647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 375647fbfebSOrion Hodson mode); 37639004e69SCameron Grant return -1; 377647fbfebSOrion Hodson } 37866ef8af5SCameron Grant snd_mtxlock(codec->lock); 379f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 380f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 38166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 38239004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 38339004e69SCameron Grant } 38439004e69SCameron Grant 3859ec437a3SCameron Grant u_int16_t 3869ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3879ec437a3SCameron Grant { 3889ec437a3SCameron Grant return codec->extstat; 3899ec437a3SCameron Grant } 3909ec437a3SCameron Grant 3919ec437a3SCameron Grant u_int16_t 3929ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3939ec437a3SCameron Grant { 3949ec437a3SCameron Grant return codec->extcaps; 3959ec437a3SCameron Grant } 3969ec437a3SCameron Grant 3975d91ad67SCameron Grant u_int16_t 3985d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 3995d91ad67SCameron Grant { 4005d91ad67SCameron Grant return codec->caps; 4015d91ad67SCameron Grant } 4025d91ad67SCameron Grant 403987e5972SCameron Grant static int 404987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 405987e5972SCameron Grant { 406987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 407341f16ccSCameron Grant 408987e5972SCameron Grant if (e->recidx > 0) { 409987e5972SCameron Grant int val = e->recidx - 1; 410987e5972SCameron Grant val |= val << 8; 41166ef8af5SCameron Grant snd_mtxlock(codec->lock); 412f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 41366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 414987e5972SCameron Grant return 0; 41539004e69SCameron Grant } else 41639004e69SCameron Grant return -1; 417987e5972SCameron Grant } 418987e5972SCameron Grant 419987e5972SCameron Grant static int 420987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 421987e5972SCameron Grant { 422987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 423341f16ccSCameron Grant 424341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 42596524a52SOrion Hodson int mask, max, val, reg; 42696524a52SOrion Hodson 42796524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 42896524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 42996524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 430987e5972SCameron Grant 43139004e69SCameron Grant if (!e->stereo) 43239004e69SCameron Grant right = left; 43396524a52SOrion Hodson 43496524a52SOrion Hodson /* 43596524a52SOrion Hodson * Invert the range if the polarity requires so, 43696524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 43796524a52SOrion Hodson * write into the codec, and scale back to 0..100 43896524a52SOrion Hodson * for the return value. 43996524a52SOrion Hodson */ 440987e5972SCameron Grant if (e->reg > 0) { 441987e5972SCameron Grant left = 100 - left; 442987e5972SCameron Grant right = 100 - right; 443987e5972SCameron Grant } 444987e5972SCameron Grant 445987e5972SCameron Grant left = (left * max) / 100; 446987e5972SCameron Grant right = (right * max) / 100; 447987e5972SCameron Grant 448987e5972SCameron Grant val = (left << 8) | right; 449987e5972SCameron Grant 450987e5972SCameron Grant left = (left * 100) / max; 451987e5972SCameron Grant right = (right * 100) / max; 452987e5972SCameron Grant 453987e5972SCameron Grant if (e->reg > 0) { 454987e5972SCameron Grant left = 100 - left; 455987e5972SCameron Grant right = 100 - right; 456987e5972SCameron Grant } 457987e5972SCameron Grant 45896524a52SOrion Hodson /* 45996524a52SOrion Hodson * For mono controls, trim val and mask, also taking 46096524a52SOrion Hodson * care of e->ofs (offset of control field). 46196524a52SOrion Hodson */ 46296524a52SOrion Hodson if (e->ofs) { 463987e5972SCameron Grant val &= max; 464987e5972SCameron Grant val <<= e->ofs; 46596524a52SOrion Hodson mask = (max << e->ofs); 46696524a52SOrion Hodson } 46796524a52SOrion Hodson 46896524a52SOrion Hodson /* 46996524a52SOrion Hodson * If we have a mute bit, add it to the mask and 47096524a52SOrion Hodson * update val and set mute if both channels require a 47196524a52SOrion Hodson * zero volume. 47296524a52SOrion Hodson */ 47396524a52SOrion Hodson if (e->mute == 1) { 47496524a52SOrion Hodson mask |= AC97_MUTE; 47596524a52SOrion Hodson if (left == 0 && right == 0) 47696524a52SOrion Hodson val = AC97_MUTE; 47796524a52SOrion Hodson } 47896524a52SOrion Hodson 47996524a52SOrion Hodson /* 48096524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 48196524a52SOrion Hodson */ 48296524a52SOrion Hodson snd_mtxlock(codec->lock); 483987e5972SCameron Grant if (e->mask) { 48486336196SAlexander Leidinger int cur = ac97_rdcd(codec, reg); 48596524a52SOrion Hodson val |= cur & ~(mask); 486987e5972SCameron Grant } 487f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 48866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 489987e5972SCameron Grant return left | (right << 8); 490341f16ccSCameron Grant } else { 49186336196SAlexander Leidinger #if 0 49286336196SAlexander Leidinger printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 49386336196SAlexander Leidinger #endif 49439004e69SCameron Grant return -1; 495987e5972SCameron Grant } 496341f16ccSCameron Grant } 497987e5972SCameron Grant 498108082c4SOrion Hodson static void 499108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 500108082c4SOrion Hodson { 501cfd5696dSOrion Hodson int keep_ogain; 502cfd5696dSOrion Hodson 503a52604cfSOrion Hodson /* 504cfd5696dSOrion Hodson * By default, The ac97 aux_out register (0x04) corresponds to OSS's 505cfd5696dSOrion Hodson * OGAIN setting. 506a52604cfSOrion Hodson * 507cfd5696dSOrion Hodson * We first check whether aux_out is a valid register. If not 508cfd5696dSOrion Hodson * we may not want to keep ogain. 509a52604cfSOrion Hodson */ 510cfd5696dSOrion Hodson keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 511a52604cfSOrion Hodson 512a52604cfSOrion Hodson /* 513a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 514108082c4SOrion Hodson * 515108082c4SOrion Hodson * 1. Headphone out. 516108082c4SOrion Hodson * 2. 4-Channel Out 517108082c4SOrion Hodson * 3. True line level out (effectively master volume). 518108082c4SOrion Hodson * 519108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 520108082c4SOrion Hodson */ 521a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 522f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 523cfd5696dSOrion Hodson codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 524cfd5696dSOrion Hodson keep_ogain = 1; 525cfd5696dSOrion Hodson } 526cfd5696dSOrion Hodson 527cfd5696dSOrion Hodson if (keep_ogain == 0) { 528cfd5696dSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 529cfd5696dSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 530108082c4SOrion Hodson } 531a52604cfSOrion Hodson } 532a52604cfSOrion Hodson 533a52604cfSOrion Hodson static void 534a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 535a52604cfSOrion Hodson { 536a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 537a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 538a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 539a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 540a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 541a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 542a52604cfSOrion Hodson } 543108082c4SOrion Hodson } 544108082c4SOrion Hodson 545cb44f623SAlexander Leidinger static void 546cb44f623SAlexander Leidinger ac97_fix_volume(struct ac97_info *codec) 547cb44f623SAlexander Leidinger { 548cb44f623SAlexander Leidinger struct snddev_info *d = device_get_softc(codec->dev); 549cb44f623SAlexander Leidinger 550cb44f623SAlexander Leidinger #if 0 551cb44f623SAlexander Leidinger /* XXX For the sake of debugging purposes */ 552cb44f623SAlexander Leidinger ac97_wrcd(codec, AC97_MIX_PCM, 0); 553cb44f623SAlexander Leidinger bzero(&codec->mix[SOUND_MIXER_PCM], 554cb44f623SAlexander Leidinger sizeof(codec->mix[SOUND_MIXER_PCM])); 555cb44f623SAlexander Leidinger codec->flags |= AC97_F_SOFTVOL; 556cb44f623SAlexander Leidinger if (d) 557cb44f623SAlexander Leidinger d->flags |= SD_F_SOFTVOL; 558cb44f623SAlexander Leidinger return; 559cb44f623SAlexander Leidinger #endif 560cb44f623SAlexander Leidinger switch (codec->id) { 561cb44f623SAlexander Leidinger case 0x434d4941: /* CMI9738 */ 562cb44f623SAlexander Leidinger case 0x434d4961: /* CMI9739 */ 563cb44f623SAlexander Leidinger case 0x434d4978: /* CMI9761 */ 564cb44f623SAlexander Leidinger case 0x434d4982: /* CMI9761 */ 565cb44f623SAlexander Leidinger case 0x434d4983: /* CMI9761 */ 566cb44f623SAlexander Leidinger ac97_wrcd(codec, AC97_MIX_PCM, 0); 567cb44f623SAlexander Leidinger break; 568cb44f623SAlexander Leidinger default: 569cb44f623SAlexander Leidinger return; 570cb44f623SAlexander Leidinger break; 571cb44f623SAlexander Leidinger } 572cb44f623SAlexander Leidinger bzero(&codec->mix[SOUND_MIXER_PCM], 573cb44f623SAlexander Leidinger sizeof(codec->mix[SOUND_MIXER_PCM])); 574cb44f623SAlexander Leidinger codec->flags |= AC97_F_SOFTVOL; 575cb44f623SAlexander Leidinger if (d) 576cb44f623SAlexander Leidinger d->flags |= SD_F_SOFTVOL; 577cb44f623SAlexander Leidinger } 578cb44f623SAlexander Leidinger 57919921b23SOrion Hodson static const char* 58019921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 58119921b23SOrion Hodson { 58219921b23SOrion Hodson if (cname == NULL) { 58319921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 58419921b23SOrion Hodson return buf; 58519921b23SOrion Hodson } 58619921b23SOrion Hodson 58719921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 58819921b23SOrion Hodson 58919921b23SOrion Hodson if (bootverbose) { 59019921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 59119921b23SOrion Hodson } else { 59219921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 59319921b23SOrion Hodson } 59419921b23SOrion Hodson return buf; 59519921b23SOrion Hodson } 59619921b23SOrion Hodson 597987e5972SCameron Grant static unsigned 59839004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 599987e5972SCameron Grant { 600f9eb1409SOrion Hodson ac97_patch codec_patch; 60119921b23SOrion Hodson const char *cname, *vname; 60219921b23SOrion Hodson char desc[80]; 60319921b23SOrion Hodson u_int8_t model, step; 60486336196SAlexander Leidinger unsigned i, j, k, bit, old; 605987e5972SCameron Grant u_int32_t id; 60686336196SAlexander Leidinger int reg; 607987e5972SCameron Grant 60866ef8af5SCameron Grant snd_mtxlock(codec->lock); 6090f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 610cd2c103aSCameron Grant if (codec->count == 0) { 61104553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 61266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 61304553e63SCameron Grant return ENODEV; 61404553e63SCameron Grant } 6159ec437a3SCameron Grant 616f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 617c6d4b83aSOrion Hodson ac97_reset(codec); 618f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 619987e5972SCameron Grant 620f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 62186336196SAlexander Leidinger j = ac97_rdcd(codec, AC97_REG_RESET); 62286336196SAlexander Leidinger /* 62386336196SAlexander Leidinger * Let see if this codec can return consistent value. 62486336196SAlexander Leidinger * If not, turn on aggressive read workaround 62586336196SAlexander Leidinger * (STAC9704 comes in mind). 62686336196SAlexander Leidinger */ 62786336196SAlexander Leidinger if (i != j) { 62886336196SAlexander Leidinger codec->flags |= AC97_F_RDCD_BUG; 62986336196SAlexander Leidinger i = ac97_rdcd(codec, AC97_REG_RESET); 63086336196SAlexander Leidinger } 631987e5972SCameron Grant codec->caps = i & 0x03ff; 632987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 633987e5972SCameron Grant 634f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 635e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 636e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 63766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 638e620d959SCameron Grant return ENODEV; 639e620d959SCameron Grant } 6406b4b88f7SCameron Grant 64119921b23SOrion Hodson codec->id = id; 642cd2c103aSCameron Grant codec->noext = 0; 643f9eb1409SOrion Hodson codec_patch = NULL; 64419921b23SOrion Hodson 64519921b23SOrion Hodson cname = NULL; 64619921b23SOrion Hodson model = step = 0; 647cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 64819921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 64919921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 650cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 651f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 65219921b23SOrion Hodson cname = ac97codecid[i].name; 65319921b23SOrion Hodson model = (id & modelmask) & 0xff; 65419921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 65519921b23SOrion Hodson break; 65619921b23SOrion Hodson } 65719921b23SOrion Hodson } 65819921b23SOrion Hodson 65919921b23SOrion Hodson vname = NULL; 66019921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 66119921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 66219921b23SOrion Hodson vname = ac97vendorid[i].name; 66319921b23SOrion Hodson break; 664cd2c103aSCameron Grant } 665cd2c103aSCameron Grant } 6666b4b88f7SCameron Grant 667cd2c103aSCameron Grant codec->extcaps = 0; 668cd2c103aSCameron Grant codec->extid = 0; 669cd2c103aSCameron Grant codec->extstat = 0; 6706a6ee5bbSCameron Grant if (!codec->noext) { 671f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 6726a6ee5bbSCameron Grant if (i != 0xffff) { 67339004e69SCameron Grant codec->extcaps = i & 0x3fff; 67439004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 675f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 6766b4b88f7SCameron Grant } 6776a6ee5bbSCameron Grant } 678987e5972SCameron Grant 679341f16ccSCameron Grant for (i = 0; i < 32; i++) { 680108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 681108082c4SOrion Hodson } 682108082c4SOrion Hodson ac97_fix_auxout(codec); 683a52604cfSOrion Hodson ac97_fix_tone(codec); 684cb44f623SAlexander Leidinger ac97_fix_volume(codec); 685f9eb1409SOrion Hodson if (codec_patch) 686f9eb1409SOrion Hodson codec_patch(codec); 687108082c4SOrion Hodson 688108082c4SOrion Hodson for (i = 0; i < 32; i++) { 68933c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 69086336196SAlexander Leidinger reg = codec->mix[i].reg; 69186336196SAlexander Leidinger if (reg < 0) 69286336196SAlexander Leidinger reg = -reg; 69386336196SAlexander Leidinger if (k && reg) { 69486336196SAlexander Leidinger j = old = ac97_rdcd(codec, reg); 69586336196SAlexander Leidinger /* 69686336196SAlexander Leidinger * Test for mute bit (except for AC97_MIX_TONE, 69786336196SAlexander Leidinger * where we simply assume it as available). 69886336196SAlexander Leidinger */ 69986336196SAlexander Leidinger if (codec->mix[i].mute) { 70086336196SAlexander Leidinger ac97_wrcd(codec, reg, j | 0x8000); 70186336196SAlexander Leidinger j = ac97_rdcd(codec, reg); 70286336196SAlexander Leidinger } else 703fe435202SAlexander Leidinger j |= 0x8000; 70486336196SAlexander Leidinger if ((j & 0x8000)) { 70586336196SAlexander Leidinger /* 70686336196SAlexander Leidinger * Test whether the control width should be 70786336196SAlexander Leidinger * 4, 5 or 6 bit. For 5bit register, we should 70886336196SAlexander Leidinger * test it whether it's really 5 or 6bit. Leave 70986336196SAlexander Leidinger * 4bit register alone, because sometimes an 71086336196SAlexander Leidinger * attempt to write past 4th bit may cause 71186336196SAlexander Leidinger * incorrect result especially for AC97_MIX_BEEP 71286336196SAlexander Leidinger * (ac97 2.3). 71386336196SAlexander Leidinger */ 71486336196SAlexander Leidinger bit = codec->mix[i].bits; 71586336196SAlexander Leidinger if (bit == 5) 71686336196SAlexander Leidinger bit++; 71786336196SAlexander Leidinger j = ((1 << bit) - 1) << codec->mix[i].ofs; 71886336196SAlexander Leidinger ac97_wrcd(codec, reg, 71986336196SAlexander Leidinger j | (codec->mix[i].mute ? 0x8000 : 0)); 72086336196SAlexander Leidinger k = ac97_rdcd(codec, reg) & j; 72186336196SAlexander Leidinger k >>= codec->mix[i].ofs; 72286336196SAlexander Leidinger if (reg == AC97_MIX_TONE && 72386336196SAlexander Leidinger ((k & 0x0001) == 0x0000)) 724fe435202SAlexander Leidinger k >>= 1; 72586336196SAlexander Leidinger for (j = 0; k >> j; j++) 72686336196SAlexander Leidinger ; 727fe435202SAlexander Leidinger if (j != 0) { 728fe435202SAlexander Leidinger #if 0 72986336196SAlexander Leidinger device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 73086336196SAlexander Leidinger i, k, bit, codec->mix[i].bits, j); 731fe435202SAlexander Leidinger #endif 73286336196SAlexander Leidinger codec->mix[i].enable = 1; 73386336196SAlexander Leidinger codec->mix[i].bits = j; 734fe435202SAlexander Leidinger } else 735fe435202SAlexander Leidinger codec->mix[i].enable = 0; 736fe435202SAlexander Leidinger } else 737fe435202SAlexander Leidinger codec->mix[i].enable = 0; 73886336196SAlexander Leidinger ac97_wrcd(codec, reg, old); 739fe435202SAlexander Leidinger } 740fe435202SAlexander Leidinger #if 0 741fe435202SAlexander Leidinger printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 742fe435202SAlexander Leidinger #endif 743341f16ccSCameron Grant } 744987e5972SCameron Grant 74519921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 74619921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 747a825c6e5SOrion Hodson 748987e5972SCameron Grant if (bootverbose) { 74986336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) 75086336196SAlexander Leidinger device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 751cb44f623SAlexander Leidinger if (codec->flags & AC97_F_SOFTVOL) 752cb44f623SAlexander Leidinger device_printf(codec->dev, "Soft PCM volume\n"); 75319921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 75439004e69SCameron Grant for (i = j = 0; i < 10; i++) 75539004e69SCameron Grant if (codec->caps & (1 << i)) 75639004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 75739004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 758987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 75939004e69SCameron Grant 76039004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 76119921b23SOrion Hodson device_printf(codec->dev, "%s codec", 76219921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 76339004e69SCameron Grant if (codec->extcaps) 76439004e69SCameron Grant printf(" extended features "); 76539004e69SCameron Grant for (i = j = 0; i < 14; i++) 76639004e69SCameron Grant if (codec->extcaps & (1 << i)) 76739004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 76839004e69SCameron Grant printf("\n"); 76939004e69SCameron Grant } 770987e5972SCameron Grant } 771987e5972SCameron Grant 772fe435202SAlexander Leidinger i = 0; 773fe435202SAlexander Leidinger while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 774fe435202SAlexander Leidinger if (++i == 100) { 77503a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 776fe435202SAlexander Leidinger break; 777fe435202SAlexander Leidinger } 778fe435202SAlexander Leidinger DELAY(1000); 779fe435202SAlexander Leidinger } 780fe435202SAlexander Leidinger if (bootverbose) 781fe435202SAlexander Leidinger device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 78266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 783987e5972SCameron Grant return 0; 784987e5972SCameron Grant } 785987e5972SCameron Grant 7869ec437a3SCameron Grant static unsigned 7879ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 7889ec437a3SCameron Grant { 78966ef8af5SCameron Grant snd_mtxlock(codec->lock); 7900f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 7919ec437a3SCameron Grant if (codec->count == 0) { 7929ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 79366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 7949ec437a3SCameron Grant return ENODEV; 7959ec437a3SCameron Grant } 7969ec437a3SCameron Grant 797f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 798c6d4b83aSOrion Hodson ac97_reset(codec); 799f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 8009ec437a3SCameron Grant 8019ec437a3SCameron Grant if (!codec->noext) { 802f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 803f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 804b60e55dbSGuido van Rooij != codec->extstat) 8059ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 806b60e55dbSGuido van Rooij codec->extstat, 807f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 808b60e55dbSGuido van Rooij AC97_EXTCAPS); 8099ec437a3SCameron Grant } 8109ec437a3SCameron Grant 811f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 8129ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 81366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8149ec437a3SCameron Grant return 0; 8159ec437a3SCameron Grant } 8169ec437a3SCameron Grant 817987e5972SCameron Grant struct ac97_info * 8180f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 819987e5972SCameron Grant { 820987e5972SCameron Grant struct ac97_info *codec; 821987e5972SCameron Grant 8220f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 8230f55ac6cSCameron Grant if (codec == NULL) 8240f55ac6cSCameron Grant return NULL; 8250f55ac6cSCameron Grant 82666ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 827489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 828a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 8290f55ac6cSCameron Grant if (codec->methods == NULL) { 83066ef8af5SCameron Grant snd_mtxlock(codec->lock); 83166ef8af5SCameron Grant snd_mtxfree(codec->lock); 8320f55ac6cSCameron Grant free(codec, M_AC97); 8330f55ac6cSCameron Grant return NULL; 834987e5972SCameron Grant } 8350f55ac6cSCameron Grant 8360f55ac6cSCameron Grant codec->dev = dev; 8370f55ac6cSCameron Grant codec->devinfo = devinfo; 83879bb7d52SCameron Grant codec->flags = 0; 839987e5972SCameron Grant return codec; 840987e5972SCameron Grant } 841987e5972SCameron Grant 84233dbf14aSCameron Grant void 84333dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 84433dbf14aSCameron Grant { 84566ef8af5SCameron Grant snd_mtxlock(codec->lock); 8460f55ac6cSCameron Grant if (codec->methods != NULL) 8470f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 84866ef8af5SCameron Grant snd_mtxfree(codec->lock); 8490f55ac6cSCameron Grant free(codec, M_AC97); 85033dbf14aSCameron Grant } 85133dbf14aSCameron Grant 85279bb7d52SCameron Grant void 85379bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 85479bb7d52SCameron Grant { 85579bb7d52SCameron Grant codec->flags = val; 85679bb7d52SCameron Grant } 85779bb7d52SCameron Grant 85879bb7d52SCameron Grant u_int32_t 85979bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 86079bb7d52SCameron Grant { 86179bb7d52SCameron Grant return codec->flags; 86279bb7d52SCameron Grant } 86379bb7d52SCameron Grant 8640f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 8650f55ac6cSCameron Grant 866987e5972SCameron Grant static int 86766ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 868987e5972SCameron Grant { 869987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 870341f16ccSCameron Grant u_int32_t i, mask; 871341f16ccSCameron Grant 87239004e69SCameron Grant if (codec == NULL) 87339004e69SCameron Grant return -1; 874341f16ccSCameron Grant 875e620d959SCameron Grant if (ac97_initmixer(codec)) 876e620d959SCameron Grant return -1; 877341f16ccSCameron Grant 878341f16ccSCameron Grant mask = 0; 879341f16ccSCameron Grant for (i = 0; i < 32; i++) 880341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 881341f16ccSCameron Grant mix_setdevs(m, mask); 882341f16ccSCameron Grant 883341f16ccSCameron Grant mask = 0; 884341f16ccSCameron Grant for (i = 0; i < 32; i++) 885341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 886341f16ccSCameron Grant mix_setrecdevs(m, mask); 887987e5972SCameron Grant return 0; 888987e5972SCameron Grant } 889987e5972SCameron Grant 890987e5972SCameron Grant static int 89166ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 89233dbf14aSCameron Grant { 89333dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 894341f16ccSCameron Grant 89533dbf14aSCameron Grant if (codec == NULL) 89633dbf14aSCameron Grant return -1; 89733dbf14aSCameron Grant /* 89833dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 89933dbf14aSCameron Grant return -1; 90033dbf14aSCameron Grant */ 90133dbf14aSCameron Grant ac97_destroy(codec); 90233dbf14aSCameron Grant return 0; 90333dbf14aSCameron Grant } 90433dbf14aSCameron Grant 90533dbf14aSCameron Grant static int 90666ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 9079ec437a3SCameron Grant { 9089ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 9099ec437a3SCameron Grant 9109ec437a3SCameron Grant if (codec == NULL) 9119ec437a3SCameron Grant return -1; 9129ec437a3SCameron Grant return ac97_reinitmixer(codec); 9139ec437a3SCameron Grant } 9149ec437a3SCameron Grant 9159ec437a3SCameron Grant static int 91666ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 917987e5972SCameron Grant { 918987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 919341f16ccSCameron Grant 92039004e69SCameron Grant if (codec == NULL) 92139004e69SCameron Grant return -1; 922987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 923987e5972SCameron Grant } 924987e5972SCameron Grant 925987e5972SCameron Grant static int 92666ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 927987e5972SCameron Grant { 928987e5972SCameron Grant int i; 929987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 930341f16ccSCameron Grant 93139004e69SCameron Grant if (codec == NULL) 93239004e69SCameron Grant return -1; 933987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 93439004e69SCameron Grant if ((src & (1 << i)) != 0) 93539004e69SCameron Grant break; 936987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 937987e5972SCameron Grant } 938987e5972SCameron Grant 9390f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 9400f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 9410f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 9420f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 9430f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 9440f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 9450f55ac6cSCameron Grant { 0, 0 } 946987e5972SCameron Grant }; 9470f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 9480f55ac6cSCameron Grant 9490f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 9500f55ac6cSCameron Grant 9510f55ac6cSCameron Grant kobj_class_t 9520f55ac6cSCameron Grant ac97_getmixerclass(void) 9530f55ac6cSCameron Grant { 9540f55ac6cSCameron Grant return &ac97mixer_class; 9550f55ac6cSCameron Grant } 9560f55ac6cSCameron Grant 957987e5972SCameron Grant 958