1098ca2bdSWarner Losh /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 43f225978SCameron Grant * Copyright (c) 1999 Cameron Grant <cg@freebsd.org> 5987e5972SCameron Grant * All rights reserved. 6987e5972SCameron Grant * 7987e5972SCameron Grant * Redistribution and use in source and binary forms, with or without 8987e5972SCameron Grant * modification, are permitted provided that the following conditions 9987e5972SCameron Grant * are met: 10987e5972SCameron Grant * 1. Redistributions of source code must retain the above copyright 11987e5972SCameron Grant * notice, this list of conditions and the following disclaimer. 12987e5972SCameron Grant * 2. Redistributions in binary form must reproduce the above copyright 13987e5972SCameron Grant * notice, this list of conditions and the following disclaimer in the 14987e5972SCameron Grant * documentation and/or other materials provided with the distribution. 15987e5972SCameron Grant * 16987e5972SCameron Grant * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17987e5972SCameron Grant * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18987e5972SCameron Grant * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19987e5972SCameron Grant * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20987e5972SCameron Grant * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21987e5972SCameron Grant * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22987e5972SCameron Grant * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23987e5972SCameron Grant * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24987e5972SCameron Grant * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25987e5972SCameron Grant * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26987e5972SCameron Grant * SUCH DAMAGE. 27987e5972SCameron Grant */ 28987e5972SCameron Grant 2990da2b28SAriff Abdullah #ifdef HAVE_KERNEL_OPTION_HEADERS 3090da2b28SAriff Abdullah #include "opt_snd.h" 3190da2b28SAriff Abdullah #endif 3290da2b28SAriff Abdullah 33ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 34ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 35987e5972SCameron Grant 36a580b31aSAriff Abdullah #include <dev/pci/pcivar.h> 37a580b31aSAriff Abdullah 380f55ac6cSCameron Grant #include "mixer_if.h" 39987e5972SCameron Grant 40d745c852SEd Schouten static MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 41987e5972SCameron Grant 42*0b4e3291SChristos Margiolis typedef void (*ac97_patch)(struct ac97_info *); 43*0b4e3291SChristos Margiolis 4479bb7d52SCameron Grant struct ac97mixtable_entry { 4583234531SAriff Abdullah int reg; /* register index */ 4696524a52SOrion Hodson /* reg < 0 if inverted polarity */ 4796524a52SOrion Hodson unsigned bits:4; /* width of control field */ 4896524a52SOrion Hodson unsigned ofs:4; /* offset (only if stereo=0) */ 4996524a52SOrion Hodson unsigned stereo:1; /* set for stereo controls */ 5096524a52SOrion Hodson unsigned mute:1; /* bit15 is MUTE */ 5196524a52SOrion Hodson unsigned recidx:4; /* index in rec mux */ 5296524a52SOrion Hodson unsigned mask:1; /* use only masked bits */ 5396524a52SOrion Hodson unsigned enable:1; /* entry is enabled */ 5479bb7d52SCameron Grant }; 5579bb7d52SCameron Grant 5669f6d261SAriff Abdullah #define AC97_MIXER_SIZE SOUND_MIXER_NRDEVICES 5769f6d261SAriff Abdullah 5866ef8af5SCameron Grant struct ac97_info { 5966ef8af5SCameron Grant kobj_t methods; 6066ef8af5SCameron Grant device_t dev; 6166ef8af5SCameron Grant void *devinfo; 6219921b23SOrion Hodson u_int32_t id; 63fd7390d6SAriff Abdullah u_int32_t subvendor; 6466ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 6579bb7d52SCameron Grant u_int32_t flags; 6669f6d261SAriff Abdullah struct ac97mixtable_entry mix[AC97_MIXER_SIZE]; 6769f6d261SAriff Abdullah char name[16]; 6800acb133SCameron Grant struct mtx *lock; 6966ef8af5SCameron Grant }; 7066ef8af5SCameron Grant 7119921b23SOrion Hodson struct ac97_vendorid { 7219921b23SOrion Hodson u_int32_t id; 7319921b23SOrion Hodson const char *name; 7419921b23SOrion Hodson }; 7519921b23SOrion Hodson 76987e5972SCameron Grant struct ac97_codecid { 7719921b23SOrion Hodson u_int32_t id; 7819921b23SOrion Hodson u_int8_t stepmask; 7919921b23SOrion Hodson u_int8_t noext:1; 80987e5972SCameron Grant char *name; 81f9eb1409SOrion Hodson ac97_patch patch; 82987e5972SCameron Grant }; 83987e5972SCameron Grant 8469f6d261SAriff Abdullah static const struct ac97mixtable_entry ac97mixtable_default[AC97_MIXER_SIZE] = { 8596524a52SOrion Hodson /* [offset] reg bits of st mu re mk en */ 86341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 87a52604cfSOrion Hodson [SOUND_MIXER_OGAIN] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 88341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 89341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 90341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 91341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 92341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 93341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 94341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 9596524a52SOrion Hodson [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 1, 1 }, 9696524a52SOrion Hodson /* use igain for the mic 20dB boost */ 9796524a52SOrion Hodson [SOUND_MIXER_IGAIN] = { -AC97_MIX_MIC, 1, 6, 0, 0, 0, 1, 1 }, 98341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 99341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 100341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 101341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 102987e5972SCameron Grant }; 103987e5972SCameron Grant 10419921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 10519921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 10619921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 10719921b23SOrion Hodson { 0x414c4300, "Realtek" }, 10819921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 10919921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 11019921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 11119921b23SOrion Hodson { 0x43585400, "Conexant" }, 1123c6b655dSMathew Kanner { 0x44543000, "Diamond Technology" }, 11361a0da1dSOrion Hodson { 0x454d4300, "eMicro" }, 11419921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 1153c6b655dSMathew Kanner { 0x48525300, "Intersil" }, 11619921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 1173c6b655dSMathew Kanner { 0x49544500, "ITE, Inc." }, 11819921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 11919921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 12019921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 1213c6b655dSMathew Kanner { 0x53494c00, "Silicon Laboratories" }, 12219921b23SOrion Hodson { 0x54524100, "TriTech" }, 1233c6b655dSMathew Kanner { 0x54584e00, "Texas Instruments" }, 12419921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 1253c6b655dSMathew Kanner { 0x57454300, "Winbond" }, 12619921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 12719921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 12886336196SAlexander Leidinger /* 12986336196SAlexander Leidinger * XXX This is a fluke, really! The real vendor 13086336196SAlexander Leidinger * should be SigmaTel, not this! This should be 13186336196SAlexander Leidinger * removed someday! 13286336196SAlexander Leidinger */ 1336f0182bdSOrion Hodson { 0x01408300, "Creative" }, 13419921b23SOrion Hodson { 0x00000000, NULL } 13519921b23SOrion Hodson }; 13619921b23SOrion Hodson 137*0b4e3291SChristos Margiolis static void ad1886_patch(struct ac97_info *); 138*0b4e3291SChristos Margiolis static void ad198x_patch(struct ac97_info *); 139*0b4e3291SChristos Margiolis static void ad1981b_patch(struct ac97_info *); 140*0b4e3291SChristos Margiolis static void cmi9739_patch(struct ac97_info *); 141*0b4e3291SChristos Margiolis static void alc655_patch(struct ac97_info *); 142*0b4e3291SChristos Margiolis 143987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 14419921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 14519921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 14619921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 14719921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 14819921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 149ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 150ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 151c5cb8d60SScott Long { 0x41445368, 0x00, 0, "AD1888", ad198x_patch }, 152a52604cfSOrion Hodson { 0x41445370, 0x00, 0, "AD1980", ad198x_patch }, 153ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 1547699548fSAriff Abdullah { 0x41445374, 0x00, 0, "AD1981B", ad1981b_patch }, 155a52604cfSOrion Hodson { 0x41445375, 0x00, 0, "AD1985", ad198x_patch }, 156e1e05d5dSAriff Abdullah { 0x41445378, 0x00, 0, "AD1986", ad198x_patch }, 15719921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 15819921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 15919921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 1603c6b655dSMathew Kanner { 0x414b4d06, 0x00, 0, "AK4544A", 0 }, 1613c6b655dSMathew Kanner { 0x454b4d07, 0x00, 0, "AK4545", 0 }, 16219921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1639963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 16419921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 16519921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 16619921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 167b327ee51SAriff Abdullah { 0x414c4752, 0x0f, 0, "ALC250", 0 }, 168fd7390d6SAriff Abdullah { 0x414c4760, 0x0f, 0, "ALC655", alc655_patch }, 169b7994e34SPyun YongHyeon { 0x414c4770, 0x0f, 0, "ALC203", 0 }, 17094ed763dSJun Kuriyama { 0x414c4780, 0x0f, 0, "ALC658", 0 }, 1713c6b655dSMathew Kanner { 0x414c4790, 0x0f, 0, "ALC850", 0 }, 17219921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 17319921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 17419921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 175e5728f83SMIHIRA Sanpei Yoshiro { 0x4352592d, 0x07, 0, "CS4294", 0 }, 17619921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 17719921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 178b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 17919921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 180cb44f623SAlexander Leidinger { 0x434d4961, 0x00, 0, "CMI9739", cmi9739_patch }, 18119921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 182cb44f623SAlexander Leidinger { 0x434d4978, 0x00, 0, "CMI9761", 0 }, 183cb44f623SAlexander Leidinger { 0x434d4982, 0x00, 0, "CMI9761", 0 }, 184cb44f623SAlexander Leidinger { 0x434d4983, 0x00, 0, "CMI9761", 0 }, 1853c6b655dSMathew Kanner { 0x43585421, 0x00, 0, "HSD11246", 0 }, 1863c6b655dSMathew Kanner { 0x43585428, 0x07, 0, "CX20468", 0 }, 18780138937SAriff Abdullah { 0x43585430, 0x00, 0, "CX20468-21", 0 }, 1883c6b655dSMathew Kanner { 0x44543000, 0x00, 0, "DT0398", 0 }, 18961a0da1dSOrion Hodson { 0x454d4323, 0x00, 0, "EM28023", 0 }, 19061a0da1dSOrion Hodson { 0x454d4328, 0x00, 0, "EM28028", 0 }, 19119921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 1923c6b655dSMathew Kanner { 0x48525300, 0x00, 0, "HMP9701", 0 }, 19319921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 19419921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 19519921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 19649fd6905SOrion Hodson { 0x49434551, 0x03, 0, "VT1616", 0 }, /* Via badged ICE */ 1973c6b655dSMathew Kanner { 0x49544520, 0x00, 0, "ITE2226E", 0 }, 1983c6b655dSMathew Kanner { 0x49544560, 0x07, 0, "ITE2646E", 0 }, /* XXX: patch needed */ 19919921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 20019921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 20119921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 20219921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 2033c6b655dSMathew Kanner { 0x4e534331, 0x00, 0, "LM4549", 0 }, 20419921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 20519921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 20619921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 20719921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 20819921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 20919921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 21019921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 21119921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 21219921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 21319921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 21419921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 21519921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 21619921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 21719921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 21819921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 21919921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 220cb44f623SAlexander Leidinger { 0x83847666, 0x00, 0, "STAC9766/67", 0 }, 22119921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 22219921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 22319921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 22419921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 22519921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 22619921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 2273c6b655dSMathew Kanner { 0x54524e03, 0x07, 0, "TLV320AIC27", 0 }, 2283c6b655dSMathew Kanner { 0x54584e20, 0x00, 0, "TLC320AD90", 0 }, 22919921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 23080138937SAriff Abdullah { 0x56494170, 0x00, 0, "VIA1617A", 0 }, 23119921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 23219921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 23319921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 23419921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 2353c6b655dSMathew Kanner { 0x574d4d09, 0x00, 0, "WM9709", 0 }, 2363c6b655dSMathew Kanner { 0x574d4c12, 0x00, 0, "WM9711/12", 0 }, /* XXX: patch needed */ 2373c6b655dSMathew Kanner { 0x57454301, 0x00, 0, "W83971D", 0 }, 23819921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 23919921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 24019921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 24186336196SAlexander Leidinger /* 24286336196SAlexander Leidinger * XXX This is a fluke, really! The real codec 24386336196SAlexander Leidinger * should be STAC9704, not this! This should be 24486336196SAlexander Leidinger * removed someday! 24586336196SAlexander Leidinger */ 2466f0182bdSOrion Hodson { 0x01408384, 0x00, 0, "EV1938", 0 }, 24719921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 248987e5972SCameron Grant }; 249987e5972SCameron Grant 250987e5972SCameron Grant static char *ac97enhancement[] = { 25104553e63SCameron Grant "no 3D Stereo Enhancement", 252987e5972SCameron Grant "Analog Devices Phat Stereo", 253987e5972SCameron Grant "Creative Stereo Enhancement", 254987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 255987e5972SCameron Grant "Yamaha Ymersion", 256987e5972SCameron Grant "BBE 3D Stereo Enhancement", 257987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 258987e5972SCameron Grant "Qsound QXpander", 259987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 260987e5972SCameron Grant "SRS 3D Stereo Enhancement", 261987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 262987e5972SCameron Grant "AKM 3D Audio", 263987e5972SCameron Grant "Aureal Stereo Enhancement", 264987e5972SCameron Grant "Aztech 3D Enhancement", 265987e5972SCameron Grant "Binaura 3D Audio Enhancement", 266987e5972SCameron Grant "ESS Technology Stereo Enhancement", 267987e5972SCameron Grant "Harman International VMAx", 268987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 269987e5972SCameron Grant "Philips Incredible Sound", 270987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 271987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 272987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 273987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 274987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 275987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 276987e5972SCameron Grant "Delta Integration 3D Enhancement", 277987e5972SCameron Grant "SigmaTel 3D Enhancement", 278987e5972SCameron Grant "Reserved 27", 279987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 280987e5972SCameron Grant "Reserved 29", 281987e5972SCameron Grant "Reserved 30", 282987e5972SCameron Grant "Reserved 31" 283987e5972SCameron Grant }; 284987e5972SCameron Grant 285987e5972SCameron Grant static char *ac97feature[] = { 286987e5972SCameron Grant "mic channel", 287987e5972SCameron Grant "reserved", 288987e5972SCameron Grant "tone", 289987e5972SCameron Grant "simulated stereo", 290987e5972SCameron Grant "headphone", 291987e5972SCameron Grant "bass boost", 292987e5972SCameron Grant "18 bit DAC", 293987e5972SCameron Grant "20 bit DAC", 294987e5972SCameron Grant "18 bit ADC", 295987e5972SCameron Grant "20 bit ADC" 296987e5972SCameron Grant }; 297987e5972SCameron Grant 29839004e69SCameron Grant static char *ac97extfeature[] = { 29939004e69SCameron Grant "variable rate PCM", 30039004e69SCameron Grant "double rate PCM", 30139004e69SCameron Grant "reserved 1", 30239004e69SCameron Grant "variable rate mic", 30339004e69SCameron Grant "reserved 2", 30439004e69SCameron Grant "reserved 3", 30539004e69SCameron Grant "center DAC", 30639004e69SCameron Grant "surround DAC", 30739004e69SCameron Grant "LFE DAC", 30839004e69SCameron Grant "AMAP", 30939004e69SCameron Grant "reserved 4", 31039004e69SCameron Grant "reserved 5", 31139004e69SCameron Grant "reserved 6", 31239004e69SCameron Grant "reserved 7", 31339004e69SCameron Grant }; 31439004e69SCameron Grant 315f9eb1409SOrion Hodson u_int16_t 316f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 31739004e69SCameron Grant { 31886336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) { 31986336196SAlexander Leidinger u_int16_t i[2], j = 100; 32086336196SAlexander Leidinger 32186336196SAlexander Leidinger i[0] = AC97_READ(codec->methods, codec->devinfo, reg); 32286336196SAlexander Leidinger i[1] = AC97_READ(codec->methods, codec->devinfo, reg); 32386336196SAlexander Leidinger while (i[0] != i[1] && j) 32486336196SAlexander Leidinger i[j-- & 1] = AC97_READ(codec->methods, codec->devinfo, reg); 32586336196SAlexander Leidinger #if 0 32686336196SAlexander Leidinger if (j < 100) { 32786336196SAlexander Leidinger device_printf(codec->dev, "%s(): Inconsistent register value at" 32886336196SAlexander Leidinger " 0x%08x (retry: %d)\n", __func__, reg, 100 - j); 32986336196SAlexander Leidinger } 33086336196SAlexander Leidinger #endif 33186336196SAlexander Leidinger return i[!(j & 1)]; 33286336196SAlexander Leidinger } 3330f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 33439004e69SCameron Grant } 33539004e69SCameron Grant 336f9eb1409SOrion Hodson void 337f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 33839004e69SCameron Grant { 3390f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 34039004e69SCameron Grant } 34139004e69SCameron Grant 342c6d4b83aSOrion Hodson static void 343c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 344c6d4b83aSOrion Hodson { 345c6d4b83aSOrion Hodson u_int32_t i, ps; 346f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 347c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 348f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 349c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 350c6d4b83aSOrion Hodson return; 351c6d4b83aSOrion Hodson DELAY(1000); 352c6d4b83aSOrion Hodson } 353a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 354c6d4b83aSOrion Hodson } 355c6d4b83aSOrion Hodson 35639004e69SCameron Grant int 35739004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 35839004e69SCameron Grant { 35939004e69SCameron Grant u_int16_t v; 36039004e69SCameron Grant 36139004e69SCameron Grant switch(which) { 36239004e69SCameron Grant case AC97_REGEXT_FDACRATE: 36339004e69SCameron Grant case AC97_REGEXT_SDACRATE: 36439004e69SCameron Grant case AC97_REGEXT_LDACRATE: 36539004e69SCameron Grant case AC97_REGEXT_LADCRATE: 36639004e69SCameron Grant case AC97_REGEXT_MADCRATE: 36739004e69SCameron Grant break; 36839004e69SCameron Grant 36939004e69SCameron Grant default: 37039004e69SCameron Grant return -1; 37139004e69SCameron Grant } 37239004e69SCameron Grant 37366ef8af5SCameron Grant snd_mtxlock(codec->lock); 37439004e69SCameron Grant if (rate != 0) { 37539004e69SCameron Grant v = rate; 37639004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 37739004e69SCameron Grant v >>= 1; 378f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 37939004e69SCameron Grant } 380f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 38139004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 38239004e69SCameron Grant v <<= 1; 38366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 38439004e69SCameron Grant return v; 38539004e69SCameron Grant } 38639004e69SCameron Grant 38739004e69SCameron Grant int 38839004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 38939004e69SCameron Grant { 39039004e69SCameron Grant mode &= AC97_EXTCAPS; 391647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 392647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 393647fbfebSOrion Hodson mode); 39439004e69SCameron Grant return -1; 395647fbfebSOrion Hodson } 39666ef8af5SCameron Grant snd_mtxlock(codec->lock); 397f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 398f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 39966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 40039004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 40139004e69SCameron Grant } 40239004e69SCameron Grant 4039ec437a3SCameron Grant u_int16_t 4049ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 4059ec437a3SCameron Grant { 4069ec437a3SCameron Grant return codec->extstat; 4079ec437a3SCameron Grant } 4089ec437a3SCameron Grant 4099ec437a3SCameron Grant u_int16_t 4109ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 4119ec437a3SCameron Grant { 4129ec437a3SCameron Grant return codec->extcaps; 4139ec437a3SCameron Grant } 4149ec437a3SCameron Grant 4155d91ad67SCameron Grant u_int16_t 4165d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 4175d91ad67SCameron Grant { 4185d91ad67SCameron Grant return codec->caps; 4195d91ad67SCameron Grant } 4205d91ad67SCameron Grant 421fd7390d6SAriff Abdullah u_int32_t 422fd7390d6SAriff Abdullah ac97_getsubvendor(struct ac97_info *codec) 423fd7390d6SAriff Abdullah { 424fd7390d6SAriff Abdullah return codec->subvendor; 425fd7390d6SAriff Abdullah } 426fd7390d6SAriff Abdullah 427987e5972SCameron Grant static int 428987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 429987e5972SCameron Grant { 430987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 431341f16ccSCameron Grant 432987e5972SCameron Grant if (e->recidx > 0) { 433987e5972SCameron Grant int val = e->recidx - 1; 434987e5972SCameron Grant val |= val << 8; 43566ef8af5SCameron Grant snd_mtxlock(codec->lock); 436f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 43766ef8af5SCameron Grant snd_mtxunlock(codec->lock); 438987e5972SCameron Grant return 0; 43939004e69SCameron Grant } else 44039004e69SCameron Grant return -1; 441987e5972SCameron Grant } 442987e5972SCameron Grant 443987e5972SCameron Grant static int 444987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 445987e5972SCameron Grant { 446987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 447341f16ccSCameron Grant 448341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 44996524a52SOrion Hodson int mask, max, val, reg; 45096524a52SOrion Hodson 45196524a52SOrion Hodson reg = (e->reg >= 0) ? e->reg : -e->reg; /* AC97 register */ 45296524a52SOrion Hodson max = (1 << e->bits) - 1; /* actual range */ 45396524a52SOrion Hodson mask = (max << 8) | max; /* bits of interest */ 454987e5972SCameron Grant 45539004e69SCameron Grant if (!e->stereo) 45639004e69SCameron Grant right = left; 45796524a52SOrion Hodson 45896524a52SOrion Hodson /* 45996524a52SOrion Hodson * Invert the range if the polarity requires so, 46096524a52SOrion Hodson * then scale to 0..max-1 to compute the value to 46196524a52SOrion Hodson * write into the codec, and scale back to 0..100 46296524a52SOrion Hodson * for the return value. 46396524a52SOrion Hodson */ 464987e5972SCameron Grant if (e->reg > 0) { 465987e5972SCameron Grant left = 100 - left; 466987e5972SCameron Grant right = 100 - right; 467987e5972SCameron Grant } 468987e5972SCameron Grant 469987e5972SCameron Grant left = (left * max) / 100; 470987e5972SCameron Grant right = (right * max) / 100; 471987e5972SCameron Grant 472987e5972SCameron Grant val = (left << 8) | right; 473987e5972SCameron Grant 474987e5972SCameron Grant left = (left * 100) / max; 475987e5972SCameron Grant right = (right * 100) / max; 476987e5972SCameron Grant 477987e5972SCameron Grant if (e->reg > 0) { 478987e5972SCameron Grant left = 100 - left; 479987e5972SCameron Grant right = 100 - right; 480987e5972SCameron Grant } 481987e5972SCameron Grant 48296524a52SOrion Hodson /* 48396524a52SOrion Hodson * For mono controls, trim val and mask, also taking 48496524a52SOrion Hodson * care of e->ofs (offset of control field). 48596524a52SOrion Hodson */ 48696524a52SOrion Hodson if (e->ofs) { 487987e5972SCameron Grant val &= max; 488987e5972SCameron Grant val <<= e->ofs; 48996524a52SOrion Hodson mask = (max << e->ofs); 49096524a52SOrion Hodson } 49196524a52SOrion Hodson 49296524a52SOrion Hodson /* 49396524a52SOrion Hodson * If we have a mute bit, add it to the mask and 49496524a52SOrion Hodson * update val and set mute if both channels require a 49596524a52SOrion Hodson * zero volume. 49696524a52SOrion Hodson */ 49796524a52SOrion Hodson if (e->mute == 1) { 49896524a52SOrion Hodson mask |= AC97_MUTE; 49996524a52SOrion Hodson if (left == 0 && right == 0) 50096524a52SOrion Hodson val = AC97_MUTE; 50196524a52SOrion Hodson } 50296524a52SOrion Hodson 50396524a52SOrion Hodson /* 50496524a52SOrion Hodson * If the mask bit is set, do not alter the other bits. 50596524a52SOrion Hodson */ 50696524a52SOrion Hodson snd_mtxlock(codec->lock); 507987e5972SCameron Grant if (e->mask) { 50886336196SAlexander Leidinger int cur = ac97_rdcd(codec, reg); 50996524a52SOrion Hodson val |= cur & ~(mask); 510987e5972SCameron Grant } 511f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 51266ef8af5SCameron Grant snd_mtxunlock(codec->lock); 513987e5972SCameron Grant return left | (right << 8); 514341f16ccSCameron Grant } else { 51586336196SAlexander Leidinger #if 0 51686336196SAlexander Leidinger printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); 51786336196SAlexander Leidinger #endif 51839004e69SCameron Grant return -1; 519987e5972SCameron Grant } 520341f16ccSCameron Grant } 521987e5972SCameron Grant 522108082c4SOrion Hodson static void 523108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 524108082c4SOrion Hodson { 525cfd5696dSOrion Hodson int keep_ogain; 526cfd5696dSOrion Hodson 527a52604cfSOrion Hodson /* 528cfd5696dSOrion Hodson * By default, The ac97 aux_out register (0x04) corresponds to OSS's 529cfd5696dSOrion Hodson * OGAIN setting. 530a52604cfSOrion Hodson * 531cfd5696dSOrion Hodson * We first check whether aux_out is a valid register. If not 532cfd5696dSOrion Hodson * we may not want to keep ogain. 533a52604cfSOrion Hodson */ 534cfd5696dSOrion Hodson keep_ogain = ac97_rdcd(codec, AC97_MIX_AUXOUT) & 0x8000; 535a52604cfSOrion Hodson 536a52604cfSOrion Hodson /* 537a52604cfSOrion Hodson * Determine what AUX_OUT really means, it can be: 538108082c4SOrion Hodson * 539108082c4SOrion Hodson * 1. Headphone out. 540108082c4SOrion Hodson * 2. 4-Channel Out 541108082c4SOrion Hodson * 3. True line level out (effectively master volume). 542108082c4SOrion Hodson * 543108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 544108082c4SOrion Hodson */ 545a52604cfSOrion Hodson if (codec->extcaps & AC97_EXTCAP_SDAC && 546f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 547cfd5696dSOrion Hodson codec->mix[SOUND_MIXER_OGAIN].reg = AC97_MIXEXT_SURROUND; 548cfd5696dSOrion Hodson keep_ogain = 1; 549cfd5696dSOrion Hodson } 550cfd5696dSOrion Hodson 551cfd5696dSOrion Hodson if (keep_ogain == 0) { 552cfd5696dSOrion Hodson bzero(&codec->mix[SOUND_MIXER_OGAIN], 553cfd5696dSOrion Hodson sizeof(codec->mix[SOUND_MIXER_OGAIN])); 554108082c4SOrion Hodson } 555a52604cfSOrion Hodson } 556a52604cfSOrion Hodson 557a52604cfSOrion Hodson static void 558a52604cfSOrion Hodson ac97_fix_tone(struct ac97_info *codec) 559a52604cfSOrion Hodson { 560f0c4d272SAriff Abdullah /* 561f0c4d272SAriff Abdullah * YMF chips does not indicate tone and 3D enhancement capability 562f0c4d272SAriff Abdullah * in the AC97_REG_RESET register. 563f0c4d272SAriff Abdullah */ 564f0c4d272SAriff Abdullah switch (codec->id) { 565f0c4d272SAriff Abdullah case 0x594d4800: /* YMF743 */ 566f0c4d272SAriff Abdullah case 0x594d4803: /* YMF753 */ 567f0c4d272SAriff Abdullah codec->caps |= AC97_CAP_TONE; 568f0c4d272SAriff Abdullah codec->se |= 0x04; 569f0c4d272SAriff Abdullah break; 570f0c4d272SAriff Abdullah case 0x594d4802: /* YMF752 */ 571f0c4d272SAriff Abdullah codec->se |= 0x04; 572f0c4d272SAriff Abdullah break; 573f0c4d272SAriff Abdullah default: 574f0c4d272SAriff Abdullah break; 575f0c4d272SAriff Abdullah } 576f0c4d272SAriff Abdullah 577a52604cfSOrion Hodson /* Hide treble and bass if they don't exist */ 578a52604cfSOrion Hodson if ((codec->caps & AC97_CAP_TONE) == 0) { 579a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_BASS], 580a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_BASS])); 581a52604cfSOrion Hodson bzero(&codec->mix[SOUND_MIXER_TREBLE], 582a52604cfSOrion Hodson sizeof(codec->mix[SOUND_MIXER_TREBLE])); 583a52604cfSOrion Hodson } 584108082c4SOrion Hodson } 585108082c4SOrion Hodson 58619921b23SOrion Hodson static const char* 58719921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 58819921b23SOrion Hodson { 58919921b23SOrion Hodson if (cname == NULL) { 59019921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 59119921b23SOrion Hodson return buf; 59219921b23SOrion Hodson } 59319921b23SOrion Hodson 59419921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 59519921b23SOrion Hodson 59619921b23SOrion Hodson if (bootverbose) { 59719921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 59819921b23SOrion Hodson } else { 59919921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 60019921b23SOrion Hodson } 60119921b23SOrion Hodson return buf; 60219921b23SOrion Hodson } 60319921b23SOrion Hodson 604987e5972SCameron Grant static unsigned 60539004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 606987e5972SCameron Grant { 607f9eb1409SOrion Hodson ac97_patch codec_patch; 60819921b23SOrion Hodson const char *cname, *vname; 60919921b23SOrion Hodson char desc[80]; 6102acbe677STai-hwa Liang device_t pdev; 61186336196SAlexander Leidinger unsigned i, j, k, bit, old; 612987e5972SCameron Grant u_int32_t id; 61386336196SAlexander Leidinger int reg; 614987e5972SCameron Grant 61566ef8af5SCameron Grant snd_mtxlock(codec->lock); 6160f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 617cd2c103aSCameron Grant if (codec->count == 0) { 61804553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 61966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 62004553e63SCameron Grant return ENODEV; 62104553e63SCameron Grant } 6229ec437a3SCameron Grant 623f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 624c6d4b83aSOrion Hodson ac97_reset(codec); 625f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 626987e5972SCameron Grant 627f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 62886336196SAlexander Leidinger j = ac97_rdcd(codec, AC97_REG_RESET); 62969f6d261SAriff Abdullah k = ac97_rdcd(codec, AC97_REG_RESET); 63086336196SAlexander Leidinger /* 63186336196SAlexander Leidinger * Let see if this codec can return consistent value. 63286336196SAlexander Leidinger * If not, turn on aggressive read workaround 63386336196SAlexander Leidinger * (STAC9704 comes in mind). 63486336196SAlexander Leidinger */ 63569f6d261SAriff Abdullah if (i != j || j != k) { 63686336196SAlexander Leidinger codec->flags |= AC97_F_RDCD_BUG; 63786336196SAlexander Leidinger i = ac97_rdcd(codec, AC97_REG_RESET); 63886336196SAlexander Leidinger } 639987e5972SCameron Grant codec->caps = i & 0x03ff; 640987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 641987e5972SCameron Grant 642f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 643e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 644e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 64566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 646e620d959SCameron Grant return ENODEV; 647e620d959SCameron Grant } 6486b4b88f7SCameron Grant 6492acbe677STai-hwa Liang pdev = codec->dev; 6502acbe677STai-hwa Liang while (strcmp(device_get_name(device_get_parent(pdev)), "pci") != 0) { 6512acbe677STai-hwa Liang /* find the top-level PCI device handler */ 6522acbe677STai-hwa Liang pdev = device_get_parent(pdev); 6532acbe677STai-hwa Liang } 65419921b23SOrion Hodson codec->id = id; 6552acbe677STai-hwa Liang codec->subvendor = (u_int32_t)pci_get_subdevice(pdev) << 16; 6562acbe677STai-hwa Liang codec->subvendor |= (u_int32_t)pci_get_subvendor(pdev) & 657fd7390d6SAriff Abdullah 0x0000ffff; 658cd2c103aSCameron Grant codec->noext = 0; 659f9eb1409SOrion Hodson codec_patch = NULL; 66019921b23SOrion Hodson 66119921b23SOrion Hodson cname = NULL; 662cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 66319921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 66419921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 665cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 666f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 66719921b23SOrion Hodson cname = ac97codecid[i].name; 66819921b23SOrion Hodson break; 66919921b23SOrion Hodson } 67019921b23SOrion Hodson } 67119921b23SOrion Hodson 67219921b23SOrion Hodson vname = NULL; 67319921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 67419921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 67519921b23SOrion Hodson vname = ac97vendorid[i].name; 67619921b23SOrion Hodson break; 677cd2c103aSCameron Grant } 678cd2c103aSCameron Grant } 6796b4b88f7SCameron Grant 680cd2c103aSCameron Grant codec->extcaps = 0; 681cd2c103aSCameron Grant codec->extid = 0; 682cd2c103aSCameron Grant codec->extstat = 0; 6836a6ee5bbSCameron Grant if (!codec->noext) { 684f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 6856a6ee5bbSCameron Grant if (i != 0xffff) { 68639004e69SCameron Grant codec->extcaps = i & 0x3fff; 68739004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 688f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 6896b4b88f7SCameron Grant } 6906a6ee5bbSCameron Grant } 691987e5972SCameron Grant 69269f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) { 693108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 694108082c4SOrion Hodson } 695108082c4SOrion Hodson ac97_fix_auxout(codec); 696a52604cfSOrion Hodson ac97_fix_tone(codec); 697f9eb1409SOrion Hodson if (codec_patch) 698f9eb1409SOrion Hodson codec_patch(codec); 699108082c4SOrion Hodson 70069f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) { 70133c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 70286336196SAlexander Leidinger reg = codec->mix[i].reg; 70386336196SAlexander Leidinger if (reg < 0) 70486336196SAlexander Leidinger reg = -reg; 70586336196SAlexander Leidinger if (k && reg) { 70686336196SAlexander Leidinger j = old = ac97_rdcd(codec, reg); 70786336196SAlexander Leidinger /* 70886336196SAlexander Leidinger * Test for mute bit (except for AC97_MIX_TONE, 70986336196SAlexander Leidinger * where we simply assume it as available). 71086336196SAlexander Leidinger */ 71186336196SAlexander Leidinger if (codec->mix[i].mute) { 71286336196SAlexander Leidinger ac97_wrcd(codec, reg, j | 0x8000); 71386336196SAlexander Leidinger j = ac97_rdcd(codec, reg); 71486336196SAlexander Leidinger } else 715fe435202SAlexander Leidinger j |= 0x8000; 71686336196SAlexander Leidinger if ((j & 0x8000)) { 71786336196SAlexander Leidinger /* 71886336196SAlexander Leidinger * Test whether the control width should be 71986336196SAlexander Leidinger * 4, 5 or 6 bit. For 5bit register, we should 72086336196SAlexander Leidinger * test it whether it's really 5 or 6bit. Leave 72186336196SAlexander Leidinger * 4bit register alone, because sometimes an 72286336196SAlexander Leidinger * attempt to write past 4th bit may cause 72386336196SAlexander Leidinger * incorrect result especially for AC97_MIX_BEEP 72486336196SAlexander Leidinger * (ac97 2.3). 72586336196SAlexander Leidinger */ 72686336196SAlexander Leidinger bit = codec->mix[i].bits; 72786336196SAlexander Leidinger if (bit == 5) 72886336196SAlexander Leidinger bit++; 72986336196SAlexander Leidinger j = ((1 << bit) - 1) << codec->mix[i].ofs; 73086336196SAlexander Leidinger ac97_wrcd(codec, reg, 73186336196SAlexander Leidinger j | (codec->mix[i].mute ? 0x8000 : 0)); 73286336196SAlexander Leidinger k = ac97_rdcd(codec, reg) & j; 73386336196SAlexander Leidinger k >>= codec->mix[i].ofs; 73486336196SAlexander Leidinger if (reg == AC97_MIX_TONE && 73586336196SAlexander Leidinger ((k & 0x0001) == 0x0000)) 736fe435202SAlexander Leidinger k >>= 1; 73786336196SAlexander Leidinger for (j = 0; k >> j; j++) 73886336196SAlexander Leidinger ; 739fe435202SAlexander Leidinger if (j != 0) { 740fe435202SAlexander Leidinger #if 0 74186336196SAlexander Leidinger device_printf(codec->dev, "%2d: [ac97_rdcd() = %d] [Testbit = %d] %d -> %d\n", 74286336196SAlexander Leidinger i, k, bit, codec->mix[i].bits, j); 743fe435202SAlexander Leidinger #endif 74486336196SAlexander Leidinger codec->mix[i].enable = 1; 74586336196SAlexander Leidinger codec->mix[i].bits = j; 746eaf70083SAriff Abdullah } else if (reg == AC97_MIX_BEEP) { 747eaf70083SAriff Abdullah /* 748eaf70083SAriff Abdullah * Few codec such as CX20468-21 does 749eaf70083SAriff Abdullah * have this control register, although 750eaf70083SAriff Abdullah * the only usable part is the mute bit. 751eaf70083SAriff Abdullah */ 752eaf70083SAriff Abdullah codec->mix[i].enable = 1; 753fe435202SAlexander Leidinger } else 754fe435202SAlexander Leidinger codec->mix[i].enable = 0; 755fe435202SAlexander Leidinger } else 756fe435202SAlexander Leidinger codec->mix[i].enable = 0; 75786336196SAlexander Leidinger ac97_wrcd(codec, reg, old); 758fe435202SAlexander Leidinger } 759fe435202SAlexander Leidinger #if 0 760fe435202SAlexander Leidinger printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); 761fe435202SAlexander Leidinger #endif 762341f16ccSCameron Grant } 763987e5972SCameron Grant 76419921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 76519921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 766a825c6e5SOrion Hodson 767987e5972SCameron Grant if (bootverbose) { 76886336196SAlexander Leidinger if (codec->flags & AC97_F_RDCD_BUG) 76986336196SAlexander Leidinger device_printf(codec->dev, "Buggy AC97 Codec: aggressive ac97_rdcd() workaround enabled\n"); 77019921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 77139004e69SCameron Grant for (i = j = 0; i < 10; i++) 77239004e69SCameron Grant if (codec->caps & (1 << i)) 77339004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 77439004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 775987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 77639004e69SCameron Grant 77739004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 77819921b23SOrion Hodson device_printf(codec->dev, "%s codec", 77919921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 78039004e69SCameron Grant if (codec->extcaps) 78139004e69SCameron Grant printf(" extended features "); 78239004e69SCameron Grant for (i = j = 0; i < 14; i++) 78339004e69SCameron Grant if (codec->extcaps & (1 << i)) 78439004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 78539004e69SCameron Grant printf("\n"); 78639004e69SCameron Grant } 787987e5972SCameron Grant } 788987e5972SCameron Grant 789fe435202SAlexander Leidinger i = 0; 790fe435202SAlexander Leidinger while ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) { 791fe435202SAlexander Leidinger if (++i == 100) { 79203a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 793fe435202SAlexander Leidinger break; 794fe435202SAlexander Leidinger } 795fe435202SAlexander Leidinger DELAY(1000); 796fe435202SAlexander Leidinger } 797fe435202SAlexander Leidinger if (bootverbose) 798fe435202SAlexander Leidinger device_printf(codec->dev, "ac97 codec dac ready count: %d\n", i); 79966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 800987e5972SCameron Grant return 0; 801987e5972SCameron Grant } 802987e5972SCameron Grant 8039ec437a3SCameron Grant static unsigned 8049ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 8059ec437a3SCameron Grant { 80666ef8af5SCameron Grant snd_mtxlock(codec->lock); 8070f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 8089ec437a3SCameron Grant if (codec->count == 0) { 8099ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 81066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8119ec437a3SCameron Grant return ENODEV; 8129ec437a3SCameron Grant } 8139ec437a3SCameron Grant 814f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 815c6d4b83aSOrion Hodson ac97_reset(codec); 816f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 8179ec437a3SCameron Grant 8189ec437a3SCameron Grant if (!codec->noext) { 819f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 820f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 821b60e55dbSGuido van Rooij != codec->extstat) 8229ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 823b60e55dbSGuido van Rooij codec->extstat, 824f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 825b60e55dbSGuido van Rooij AC97_EXTCAPS); 8269ec437a3SCameron Grant } 8279ec437a3SCameron Grant 828f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 8299ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 83066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 8319ec437a3SCameron Grant return 0; 8329ec437a3SCameron Grant } 8339ec437a3SCameron Grant 834987e5972SCameron Grant struct ac97_info * 8350f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 836987e5972SCameron Grant { 837987e5972SCameron Grant struct ac97_info *codec; 83890da2b28SAriff Abdullah int i; 839987e5972SCameron Grant 840082f6383SAriff Abdullah codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO); 84169f6d261SAriff Abdullah snprintf(codec->name, sizeof(codec->name), "%s:ac97", 84269f6d261SAriff Abdullah device_get_nameunit(dev)); 843489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 8447699548fSAriff Abdullah codec->methods = kobj_create(cls, M_AC97, M_WAITOK | M_ZERO); 8450f55ac6cSCameron Grant codec->dev = dev; 8460f55ac6cSCameron Grant codec->devinfo = devinfo; 84779bb7d52SCameron Grant codec->flags = 0; 84890da2b28SAriff Abdullah 8497699548fSAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 85090da2b28SAriff Abdullah "eapdinv", &i) == 0 && i != 0) 8517699548fSAriff Abdullah codec->flags |= AC97_F_EAPD_INV; 85290da2b28SAriff Abdullah 85390da2b28SAriff Abdullah if (resource_int_value(device_get_name(dev), device_get_unit(dev), 85490da2b28SAriff Abdullah "softpcmvol", &i) == 0 && i != 0) 85590da2b28SAriff Abdullah pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); 85690da2b28SAriff Abdullah 857987e5972SCameron Grant return codec; 858987e5972SCameron Grant } 859987e5972SCameron Grant 86033dbf14aSCameron Grant void 86133dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 86233dbf14aSCameron Grant { 86366ef8af5SCameron Grant snd_mtxlock(codec->lock); 8640f55ac6cSCameron Grant if (codec->methods != NULL) 8650f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 86666ef8af5SCameron Grant snd_mtxfree(codec->lock); 8670f55ac6cSCameron Grant free(codec, M_AC97); 86833dbf14aSCameron Grant } 86933dbf14aSCameron Grant 87079bb7d52SCameron Grant void 87179bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 87279bb7d52SCameron Grant { 87379bb7d52SCameron Grant codec->flags = val; 87479bb7d52SCameron Grant } 87579bb7d52SCameron Grant 87679bb7d52SCameron Grant u_int32_t 87779bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 87879bb7d52SCameron Grant { 87979bb7d52SCameron Grant return codec->flags; 88079bb7d52SCameron Grant } 88179bb7d52SCameron Grant 882*0b4e3291SChristos Margiolis static void 883*0b4e3291SChristos Margiolis ad1886_patch(struct ac97_info *codec) 884*0b4e3291SChristos Margiolis { 885*0b4e3291SChristos Margiolis #define AC97_AD_JACK_SPDIF 0x72 886*0b4e3291SChristos Margiolis /* 887*0b4e3291SChristos Margiolis * Presario700 workaround 888*0b4e3291SChristos Margiolis * for Jack Sense/SPDIF Register misetting causing 889*0b4e3291SChristos Margiolis * no audible output 890*0b4e3291SChristos Margiolis * by Santiago Nullo 04/05/2002 891*0b4e3291SChristos Margiolis */ 892*0b4e3291SChristos Margiolis ac97_wrcd(codec, AC97_AD_JACK_SPDIF, 0x0010); 893*0b4e3291SChristos Margiolis } 894*0b4e3291SChristos Margiolis 895*0b4e3291SChristos Margiolis static void 896*0b4e3291SChristos Margiolis ad198x_patch(struct ac97_info *codec) 897*0b4e3291SChristos Margiolis { 898*0b4e3291SChristos Margiolis switch (ac97_getsubvendor(codec)) { 899*0b4e3291SChristos Margiolis case 0x11931043: /* Not for ASUS A9T (probably else too). */ 900*0b4e3291SChristos Margiolis break; 901*0b4e3291SChristos Margiolis default: 902*0b4e3291SChristos Margiolis ac97_wrcd(codec, 0x76, ac97_rdcd(codec, 0x76) | 0x0420); 903*0b4e3291SChristos Margiolis break; 904*0b4e3291SChristos Margiolis } 905*0b4e3291SChristos Margiolis } 906*0b4e3291SChristos Margiolis 907*0b4e3291SChristos Margiolis static void 908*0b4e3291SChristos Margiolis ad1981b_patch(struct ac97_info *codec) 909*0b4e3291SChristos Margiolis { 910*0b4e3291SChristos Margiolis /* 911*0b4e3291SChristos Margiolis * Enable headphone jack sensing. 912*0b4e3291SChristos Margiolis */ 913*0b4e3291SChristos Margiolis switch (ac97_getsubvendor(codec)) { 914*0b4e3291SChristos Margiolis case 0x02d91014: /* IBM Thinkcentre */ 915*0b4e3291SChristos Margiolis case 0x099c103c: /* HP nx6110 */ 916*0b4e3291SChristos Margiolis ac97_wrcd(codec, AC97_AD_JACK_SPDIF, 917*0b4e3291SChristos Margiolis ac97_rdcd(codec, AC97_AD_JACK_SPDIF) | 0x0800); 918*0b4e3291SChristos Margiolis break; 919*0b4e3291SChristos Margiolis default: 920*0b4e3291SChristos Margiolis break; 921*0b4e3291SChristos Margiolis } 922*0b4e3291SChristos Margiolis } 923*0b4e3291SChristos Margiolis 924*0b4e3291SChristos Margiolis static void 925*0b4e3291SChristos Margiolis cmi9739_patch(struct ac97_info *codec) 926*0b4e3291SChristos Margiolis { 927*0b4e3291SChristos Margiolis /* 928*0b4e3291SChristos Margiolis * Few laptops need extra register initialization 929*0b4e3291SChristos Margiolis * to power up the internal speakers. 930*0b4e3291SChristos Margiolis */ 931*0b4e3291SChristos Margiolis switch (ac97_getsubvendor(codec)) { 932*0b4e3291SChristos Margiolis case 0x18431043: /* ASUS W1000N */ 933*0b4e3291SChristos Margiolis ac97_wrcd(codec, AC97_REG_POWER, 0x000f); 934*0b4e3291SChristos Margiolis ac97_wrcd(codec, AC97_MIXEXT_CLFE, 0x0000); 935*0b4e3291SChristos Margiolis ac97_wrcd(codec, 0x64, 0x7110); 936*0b4e3291SChristos Margiolis break; 937*0b4e3291SChristos Margiolis default: 938*0b4e3291SChristos Margiolis break; 939*0b4e3291SChristos Margiolis } 940*0b4e3291SChristos Margiolis } 941*0b4e3291SChristos Margiolis 942*0b4e3291SChristos Margiolis static void 943*0b4e3291SChristos Margiolis alc655_patch(struct ac97_info *codec) 944*0b4e3291SChristos Margiolis { 945*0b4e3291SChristos Margiolis /* 946*0b4e3291SChristos Margiolis * MSI (Micro-Star International) specific EAPD quirk. 947*0b4e3291SChristos Margiolis */ 948*0b4e3291SChristos Margiolis switch (ac97_getsubvendor(codec)) { 949*0b4e3291SChristos Margiolis case 0x00611462: /* MSI S250 */ 950*0b4e3291SChristos Margiolis case 0x01311462: /* MSI S270 */ 951*0b4e3291SChristos Margiolis case 0x01611462: /* LG K1 Express */ 952*0b4e3291SChristos Margiolis case 0x03511462: /* MSI L725 */ 953*0b4e3291SChristos Margiolis ac97_wrcd(codec, 0x7a, ac97_rdcd(codec, 0x7a) & 0xfffd); 954*0b4e3291SChristos Margiolis break; 955*0b4e3291SChristos Margiolis case 0x10ca1734: 956*0b4e3291SChristos Margiolis /* 957*0b4e3291SChristos Margiolis * Amilo Pro V2055 with ALC655 has phone out by default 958*0b4e3291SChristos Margiolis * disabled (surround on), leaving us only with internal 959*0b4e3291SChristos Margiolis * speakers. This should really go to mixer. We write the 960*0b4e3291SChristos Margiolis * Data Flow Control reg. 961*0b4e3291SChristos Margiolis */ 962*0b4e3291SChristos Margiolis ac97_wrcd(codec, 0x6a, ac97_rdcd(codec, 0x6a) | 0x0001); 963*0b4e3291SChristos Margiolis break; 964*0b4e3291SChristos Margiolis default: 965*0b4e3291SChristos Margiolis break; 966*0b4e3291SChristos Margiolis } 967*0b4e3291SChristos Margiolis } 968*0b4e3291SChristos Margiolis 9690f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 9700f55ac6cSCameron Grant 971a580b31aSAriff Abdullah static int 972a580b31aSAriff Abdullah sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) 973a580b31aSAriff Abdullah { 974a580b31aSAriff Abdullah struct ac97_info *codec; 975a580b31aSAriff Abdullah int ea, inv, err = 0; 976a580b31aSAriff Abdullah u_int16_t val; 977a580b31aSAriff Abdullah 978a580b31aSAriff Abdullah codec = oidp->oid_arg1; 979a580b31aSAriff Abdullah if (codec == NULL || codec->id == 0 || codec->lock == NULL) 980a580b31aSAriff Abdullah return EINVAL; 981a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 982a580b31aSAriff Abdullah val = ac97_rdcd(codec, AC97_REG_POWER); 983a580b31aSAriff Abdullah inv = (codec->flags & AC97_F_EAPD_INV) ? 0 : 1; 984a580b31aSAriff Abdullah ea = (val >> 15) ^ inv; 985a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 986041b706bSDavid Malone err = sysctl_handle_int(oidp, &ea, 0, req); 987a580b31aSAriff Abdullah if (err == 0 && req->newptr != NULL) { 988a580b31aSAriff Abdullah if (ea != 0 && ea != 1) 989a580b31aSAriff Abdullah return EINVAL; 990a580b31aSAriff Abdullah if (ea != ((val >> 15) ^ inv)) { 991a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 992a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, val ^ 0x8000); 993a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 994a580b31aSAriff Abdullah } 995a580b31aSAriff Abdullah } 996a580b31aSAriff Abdullah return err; 997a580b31aSAriff Abdullah } 998a580b31aSAriff Abdullah 999a580b31aSAriff Abdullah static void 1000a580b31aSAriff Abdullah ac97_init_sysctl(struct ac97_info *codec) 1001a580b31aSAriff Abdullah { 1002a580b31aSAriff Abdullah u_int16_t orig, val; 1003a580b31aSAriff Abdullah 1004a580b31aSAriff Abdullah if (codec == NULL || codec->dev == NULL) 1005a580b31aSAriff Abdullah return; 1006a580b31aSAriff Abdullah snd_mtxlock(codec->lock); 1007a580b31aSAriff Abdullah orig = ac97_rdcd(codec, AC97_REG_POWER); 1008a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, orig ^ 0x8000); 1009a580b31aSAriff Abdullah val = ac97_rdcd(codec, AC97_REG_POWER); 1010a580b31aSAriff Abdullah ac97_wrcd(codec, AC97_REG_POWER, orig); 1011a580b31aSAriff Abdullah snd_mtxunlock(codec->lock); 1012a580b31aSAriff Abdullah if ((val & 0x8000) == (orig & 0x8000)) 1013a580b31aSAriff Abdullah return; 1014a580b31aSAriff Abdullah SYSCTL_ADD_PROC(device_get_sysctl_ctx(codec->dev), 1015a580b31aSAriff Abdullah SYSCTL_CHILDREN(device_get_sysctl_tree(codec->dev)), 10167029da5cSPawel Biernacki OID_AUTO, "eapd", 10177029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 1018a580b31aSAriff Abdullah codec, sizeof(codec), sysctl_hw_snd_ac97_eapd, 1019a580b31aSAriff Abdullah "I", "AC97 External Amplifier"); 1020a580b31aSAriff Abdullah } 1021a580b31aSAriff Abdullah 1022987e5972SCameron Grant static int 102366ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 1024987e5972SCameron Grant { 1025987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1026341f16ccSCameron Grant u_int32_t i, mask; 1027341f16ccSCameron Grant 102839004e69SCameron Grant if (codec == NULL) 102939004e69SCameron Grant return -1; 1030341f16ccSCameron Grant 1031e620d959SCameron Grant if (ac97_initmixer(codec)) 1032e620d959SCameron Grant return -1; 1033341f16ccSCameron Grant 10347699548fSAriff Abdullah switch (codec->id) { 10357699548fSAriff Abdullah case 0x41445374: /* AD1981B */ 103655431491SAriff Abdullah switch (codec->subvendor) { 103755431491SAriff Abdullah case 0x02d91014: 1038fd7390d6SAriff Abdullah /* 1039fd7390d6SAriff Abdullah * IBM Thinkcentre: 104055431491SAriff Abdullah * 104155431491SAriff Abdullah * Tie "ogain" and "phout" to "vol" since its 1042fd7390d6SAriff Abdullah * master volume is basically useless and can't 1043fd7390d6SAriff Abdullah * control anything. 1044fd7390d6SAriff Abdullah */ 10457699548fSAriff Abdullah mask = 0; 10467699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_OGAIN].enable) 10477699548fSAriff Abdullah mask |= SOUND_MASK_OGAIN; 10487699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_PHONEOUT].enable) 10497699548fSAriff Abdullah mask |= SOUND_MASK_PHONEOUT; 10507699548fSAriff Abdullah if (codec->mix[SOUND_MIXER_VOLUME].enable) 1051a580b31aSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 1052a580b31aSAriff Abdullah mask); 10537699548fSAriff Abdullah else { 1054a580b31aSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 1055a580b31aSAriff Abdullah mask); 1056a580b31aSAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, 1057a580b31aSAriff Abdullah SOUND_MIXER_NONE); 10587699548fSAriff Abdullah } 105955431491SAriff Abdullah break; 106055431491SAriff Abdullah case 0x099c103c: 106155431491SAriff Abdullah /* 106255431491SAriff Abdullah * HP nx6110: 106355431491SAriff Abdullah * 106455431491SAriff Abdullah * By default, "vol" is controlling internal speakers 106555431491SAriff Abdullah * (not a master volume!) and "ogain" is controlling 106655431491SAriff Abdullah * headphone. Enable dummy "phout" so it can be 106755431491SAriff Abdullah * remapped to internal speakers and virtualize 106855431491SAriff Abdullah * "vol" to control both. 106955431491SAriff Abdullah */ 107055431491SAriff Abdullah codec->mix[SOUND_MIXER_OGAIN].enable = 1; 107155431491SAriff Abdullah codec->mix[SOUND_MIXER_PHONEOUT].enable = 1; 107255431491SAriff Abdullah mix_setrealdev(m, SOUND_MIXER_PHONEOUT, 107355431491SAriff Abdullah SOUND_MIXER_VOLUME); 107455431491SAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, 107555431491SAriff Abdullah SOUND_MIXER_NONE); 107655431491SAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 107755431491SAriff Abdullah SOUND_MASK_OGAIN | SOUND_MASK_PHONEOUT); 107855431491SAriff Abdullah break; 107955431491SAriff Abdullah default: 108055431491SAriff Abdullah break; 1081a580b31aSAriff Abdullah } 10827699548fSAriff Abdullah break; 10837699548fSAriff Abdullah case 0x434d4941: /* CMI9738 */ 10847699548fSAriff Abdullah case 0x434d4961: /* CMI9739 */ 10857699548fSAriff Abdullah case 0x434d4978: /* CMI9761 */ 10867699548fSAriff Abdullah case 0x434d4982: /* CMI9761 */ 10877699548fSAriff Abdullah case 0x434d4983: /* CMI9761 */ 10887699548fSAriff Abdullah bzero(&codec->mix[SOUND_MIXER_PCM], 10897699548fSAriff Abdullah sizeof(codec->mix[SOUND_MIXER_PCM])); 1090e510f521SAriff Abdullah pcm_setflags(codec->dev, pcm_getflags(codec->dev) | 1091e510f521SAriff Abdullah SD_F_SOFTPCMVOL); 10927699548fSAriff Abdullah /* XXX How about master volume ? */ 10937699548fSAriff Abdullah break; 10947699548fSAriff Abdullah default: 10957699548fSAriff Abdullah break; 10967699548fSAriff Abdullah } 10977699548fSAriff Abdullah 109890da2b28SAriff Abdullah if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL) 109990da2b28SAriff Abdullah ac97_wrcd(codec, AC97_MIX_PCM, 0); 11007699548fSAriff Abdullah #if 0 11017699548fSAriff Abdullah /* XXX For the sake of debugging purposes */ 11027699548fSAriff Abdullah mix_setparentchild(m, SOUND_MIXER_VOLUME, 11037699548fSAriff Abdullah SOUND_MASK_PCM | SOUND_MASK_CD); 11047699548fSAriff Abdullah mix_setrealdev(m, SOUND_MIXER_VOLUME, SOUND_MIXER_NONE); 11057699548fSAriff Abdullah ac97_wrcd(codec, AC97_MIX_MASTER, 0); 11067699548fSAriff Abdullah #endif 11077699548fSAriff Abdullah 1108341f16ccSCameron Grant mask = 0; 110969f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) 1110341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 1111341f16ccSCameron Grant mix_setdevs(m, mask); 1112341f16ccSCameron Grant 1113341f16ccSCameron Grant mask = 0; 111469f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) 1115341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 1116341f16ccSCameron Grant mix_setrecdevs(m, mask); 1117a580b31aSAriff Abdullah 1118a580b31aSAriff Abdullah ac97_init_sysctl(codec); 1119a580b31aSAriff Abdullah 1120987e5972SCameron Grant return 0; 1121987e5972SCameron Grant } 1122987e5972SCameron Grant 1123987e5972SCameron Grant static int 112466ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 112533dbf14aSCameron Grant { 112633dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1127341f16ccSCameron Grant 112833dbf14aSCameron Grant if (codec == NULL) 112933dbf14aSCameron Grant return -1; 113033dbf14aSCameron Grant /* 113133dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 113233dbf14aSCameron Grant return -1; 113333dbf14aSCameron Grant */ 113433dbf14aSCameron Grant ac97_destroy(codec); 113533dbf14aSCameron Grant return 0; 113633dbf14aSCameron Grant } 113733dbf14aSCameron Grant 113833dbf14aSCameron Grant static int 113966ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 11409ec437a3SCameron Grant { 11419ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 11429ec437a3SCameron Grant 11439ec437a3SCameron Grant if (codec == NULL) 11449ec437a3SCameron Grant return -1; 11459ec437a3SCameron Grant return ac97_reinitmixer(codec); 11469ec437a3SCameron Grant } 11479ec437a3SCameron Grant 11489ec437a3SCameron Grant static int 114966ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 1150987e5972SCameron Grant { 1151987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1152341f16ccSCameron Grant 115369f6d261SAriff Abdullah if (codec == NULL || dev >= AC97_MIXER_SIZE) 115439004e69SCameron Grant return -1; 1155987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 1156987e5972SCameron Grant } 1157987e5972SCameron Grant 115890da2b28SAriff Abdullah static u_int32_t 115966ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 1160987e5972SCameron Grant { 1161987e5972SCameron Grant int i; 1162987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 1163341f16ccSCameron Grant 116439004e69SCameron Grant if (codec == NULL) 116539004e69SCameron Grant return -1; 116669f6d261SAriff Abdullah for (i = 0; i < AC97_MIXER_SIZE; i++) 116739004e69SCameron Grant if ((src & (1 << i)) != 0) 116839004e69SCameron Grant break; 116990da2b28SAriff Abdullah return (ac97_setrecsrc(codec, i) == 0)? 1U << i : 0xffffffffU; 1170987e5972SCameron Grant } 1171987e5972SCameron Grant 11720f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 11730f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 11740f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 11750f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 11760f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 11770f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 117890da2b28SAriff Abdullah KOBJMETHOD_END 1179987e5972SCameron Grant }; 11800f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 11810f55ac6cSCameron Grant 11820f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 11830f55ac6cSCameron Grant 11840f55ac6cSCameron Grant kobj_class_t 11850f55ac6cSCameron Grant ac97_getmixerclass(void) 11860f55ac6cSCameron Grant { 11870f55ac6cSCameron Grant return &ac97mixer_class; 11880f55ac6cSCameron Grant } 1189