1987e5972SCameron Grant /* 2987e5972SCameron Grant * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 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 { 3879bb7d52SCameron Grant int reg:8; 3979bb7d52SCameron Grant unsigned bits:4; 4079bb7d52SCameron Grant unsigned ofs:4; 4179bb7d52SCameron Grant unsigned stereo:1; 4279bb7d52SCameron Grant unsigned mute:1; 4379bb7d52SCameron Grant unsigned recidx:4; 4479bb7d52SCameron Grant unsigned mask:1; 4579bb7d52SCameron Grant unsigned enable:1; 4679bb7d52SCameron Grant }; 4779bb7d52SCameron Grant 4866ef8af5SCameron Grant #define AC97_NAMELEN 16 4966ef8af5SCameron Grant struct ac97_info { 5066ef8af5SCameron Grant kobj_t methods; 5166ef8af5SCameron Grant device_t dev; 5266ef8af5SCameron Grant void *devinfo; 5319921b23SOrion Hodson u_int32_t id; 5466ef8af5SCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 5579bb7d52SCameron Grant u_int32_t flags; 5666ef8af5SCameron Grant struct ac97mixtable_entry mix[32]; 5766ef8af5SCameron Grant char name[AC97_NAMELEN]; 5800acb133SCameron Grant struct mtx *lock; 5966ef8af5SCameron Grant }; 6066ef8af5SCameron Grant 6119921b23SOrion Hodson struct ac97_vendorid { 6219921b23SOrion Hodson u_int32_t id; 6319921b23SOrion Hodson const char *name; 6419921b23SOrion Hodson }; 6519921b23SOrion Hodson 66987e5972SCameron Grant struct ac97_codecid { 6719921b23SOrion Hodson u_int32_t id; 6819921b23SOrion Hodson u_int8_t stepmask; 6919921b23SOrion Hodson u_int8_t noext:1; 70987e5972SCameron Grant char *name; 71f9eb1409SOrion Hodson ac97_patch patch; 72987e5972SCameron Grant }; 73987e5972SCameron Grant 74987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 75341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 76108082c4SOrion Hodson [SOUND_MIXER_MONITOR] = { AC97_MIX_AUXOUT, 5, 0, 1, 1, 0, 0, 0 }, 77341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 78341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 79341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 80341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 81341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 82341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 83341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 84341f16ccSCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 85341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 86341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 87341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 88341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 89987e5972SCameron Grant }; 90987e5972SCameron Grant 9119921b23SOrion Hodson static const struct ac97_vendorid ac97vendorid[] = { 9219921b23SOrion Hodson { 0x41445300, "Analog Devices" }, 9319921b23SOrion Hodson { 0x414b4d00, "Asahi Kasei" }, 9419921b23SOrion Hodson { 0x414c4300, "Realtek" }, 9519921b23SOrion Hodson { 0x414c4700, "Avance Logic" }, 9619921b23SOrion Hodson { 0x43525900, "Cirrus Logic" }, 9719921b23SOrion Hodson { 0x434d4900, "C-Media Electronics" }, 9819921b23SOrion Hodson { 0x43585400, "Conexant" }, 9919921b23SOrion Hodson { 0x45838300, "ESS Technology" }, 10019921b23SOrion Hodson { 0x49434500, "ICEnsemble" }, 10119921b23SOrion Hodson { 0x4e534300, "National Semiconductor" }, 10219921b23SOrion Hodson { 0x50534300, "Philips Semiconductor" }, 10319921b23SOrion Hodson { 0x83847600, "SigmaTel" }, 10419921b23SOrion Hodson { 0x53494c00, "Silicon Laboratory" }, 10519921b23SOrion Hodson { 0x54524100, "TriTech" }, 10619921b23SOrion Hodson { 0x56494100, "VIA Technologies" }, 10719921b23SOrion Hodson { 0x574d4c00, "Wolfson" }, 10819921b23SOrion Hodson { 0x594d4800, "Yamaha" }, 10919921b23SOrion Hodson { 0x00000000, NULL } 11019921b23SOrion Hodson }; 11119921b23SOrion Hodson 112987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 11319921b23SOrion Hodson { 0x41445303, 0x00, 0, "AD1819", 0 }, 11419921b23SOrion Hodson { 0x41445340, 0x00, 0, "AD1881", 0 }, 11519921b23SOrion Hodson { 0x41445348, 0x00, 0, "AD1881A", 0 }, 11619921b23SOrion Hodson { 0x41445360, 0x00, 0, "AD1885", 0 }, 11719921b23SOrion Hodson { 0x41445361, 0x00, 0, "AD1886", ad1886_patch }, 118ba548c64SOrion Hodson { 0x41445362, 0x00, 0, "AD1887", 0 }, 119ba548c64SOrion Hodson { 0x41445363, 0x00, 0, "AD1886A", 0 }, 120ba548c64SOrion Hodson { 0x41445370, 0x00, 0, "AD1980", 0 }, 121ba548c64SOrion Hodson { 0x41445372, 0x00, 0, "AD1981A", 0 }, 122ba548c64SOrion Hodson { 0x41445374, 0x00, 0, "AD1981B", 0 }, 123ba548c64SOrion Hodson { 0x41445375, 0x00, 0, "AD1985", 0 }, 12419921b23SOrion Hodson { 0x414b4d00, 0x00, 1, "AK4540", 0 }, 12519921b23SOrion Hodson { 0x414b4d01, 0x00, 1, "AK4542", 0 }, 12619921b23SOrion Hodson { 0x414b4d02, 0x00, 1, "AK4543", 0 }, 12719921b23SOrion Hodson { 0x414c4320, 0x0f, 0, "ALC100", 0 }, 1289963235aSOrion Hodson { 0x414c4730, 0x0f, 0, "ALC101", 0 }, 12919921b23SOrion Hodson { 0x414c4710, 0x0f, 0, "ALC200", 0 }, 13019921b23SOrion Hodson { 0x414c4740, 0x0f, 0, "ALC202", 0 }, 13119921b23SOrion Hodson { 0x414c4720, 0x0f, 0, "ALC650", 0 }, 13219921b23SOrion Hodson { 0x43525900, 0x07, 0, "CS4297", 0 }, 13319921b23SOrion Hodson { 0x43525910, 0x07, 0, "CS4297A", 0 }, 13419921b23SOrion Hodson { 0x43525920, 0x07, 0, "CS4294/98", 0 }, 13519921b23SOrion Hodson { 0x43525930, 0x07, 0, "CS4299", 0 }, 13619921b23SOrion Hodson { 0x43525940, 0x07, 0, "CS4201", 0 }, 137b2a0f525SOrion Hodson { 0x43525958, 0x07, 0, "CS4205", 0 }, 13819921b23SOrion Hodson { 0x43525960, 0x07, 0, "CS4291A", 0 }, 13919921b23SOrion Hodson { 0x434d4961, 0x00, 0, "CMI9739", 0 }, 14019921b23SOrion Hodson { 0x434d4941, 0x00, 0, "CMI9738", 0 }, 14119921b23SOrion Hodson { 0x43585429, 0x00, 0, "CX20468", 0 }, 14219921b23SOrion Hodson { 0x45838308, 0x00, 0, "ES1988", 0 }, /* Formerly ES1921(?) */ 14319921b23SOrion Hodson { 0x49434501, 0x00, 0, "ICE1230", 0 }, 14419921b23SOrion Hodson { 0x49434511, 0x00, 0, "ICE1232", 0 }, 14519921b23SOrion Hodson { 0x49434514, 0x00, 0, "ICE1232A", 0 }, 14619921b23SOrion Hodson { 0x49434551, 0x00, 0, "VT1616", 0 }, /* Via badged ICE */ 14719921b23SOrion Hodson { 0x4e534340, 0x00, 0, "LM4540", 0 }, /* Spec blank on revid */ 14819921b23SOrion Hodson { 0x4e534343, 0x00, 0, "LM4543", 0 }, /* Ditto */ 14919921b23SOrion Hodson { 0x4e534346, 0x00, 0, "LM4546A", 0 }, 15019921b23SOrion Hodson { 0x4e534348, 0x00, 0, "LM4548A", 0 }, 15119921b23SOrion Hodson { 0x4e534331, 0x00, 0, "LM4549", 0 }, /* (?) */ 15219921b23SOrion Hodson { 0x4e534349, 0x00, 0, "LM4549A", 0 }, 15319921b23SOrion Hodson { 0x4e534350, 0x00, 0, "LM4550", 0 }, 15419921b23SOrion Hodson { 0x50534301, 0x00, 0, "UCB1510", 0 }, 15519921b23SOrion Hodson { 0x50534304, 0x00, 0, "UCB1400", 0 }, 15619921b23SOrion Hodson { 0x83847600, 0x00, 0, "STAC9700/83/84", 0 }, 15719921b23SOrion Hodson { 0x83847604, 0x00, 0, "STAC9701/03/04/05", 0 }, 15819921b23SOrion Hodson { 0x83847605, 0x00, 0, "STAC9704", 0 }, 15919921b23SOrion Hodson { 0x83847608, 0x00, 0, "STAC9708/11", 0 }, 16019921b23SOrion Hodson { 0x83847609, 0x00, 0, "STAC9721/23", 0 }, 16119921b23SOrion Hodson { 0x83847644, 0x00, 0, "STAC9744/45", 0 }, 16219921b23SOrion Hodson { 0x83847650, 0x00, 0, "STAC9750/51", 0 }, 16319921b23SOrion Hodson { 0x83847652, 0x00, 0, "STAC9752/53", 0 }, 16419921b23SOrion Hodson { 0x83847656, 0x00, 0, "STAC9756/57", 0 }, 16519921b23SOrion Hodson { 0x83847658, 0x00, 0, "STAC9758/59", 0 }, 16619921b23SOrion Hodson { 0x83847660, 0x00, 0, "STAC9760/61", 0 }, /* Extrapolated */ 16719921b23SOrion Hodson { 0x83847662, 0x00, 0, "STAC9762/63", 0 }, /* Extrapolated */ 16819921b23SOrion Hodson { 0x53494c22, 0x00, 0, "Si3036", 0 }, 16919921b23SOrion Hodson { 0x53494c23, 0x00, 0, "Si3038", 0 }, 17019921b23SOrion Hodson { 0x54524103, 0x00, 0, "TR28023", 0 }, /* Extrapolated */ 17119921b23SOrion Hodson { 0x54524106, 0x00, 0, "TR28026", 0 }, 17219921b23SOrion Hodson { 0x54524108, 0x00, 0, "TR28028", 0 }, 17319921b23SOrion Hodson { 0x54524123, 0x00, 0, "TR28602", 0 }, 17419921b23SOrion Hodson { 0x56494161, 0x00, 0, "VIA1612A", 0 }, 17519921b23SOrion Hodson { 0x574d4c00, 0x00, 0, "WM9701A", 0 }, 17619921b23SOrion Hodson { 0x574d4c03, 0x00, 0, "WM9703/4/7/8", 0 }, 17719921b23SOrion Hodson { 0x574d4c04, 0x00, 0, "WM9704Q", 0 }, 17819921b23SOrion Hodson { 0x574d4c05, 0x00, 0, "WM9705/10", 0 }, 17919921b23SOrion Hodson { 0x594d4800, 0x00, 0, "YMF743", 0 }, 18019921b23SOrion Hodson { 0x594d4802, 0x00, 0, "YMF752", 0 }, 18119921b23SOrion Hodson { 0x594d4803, 0x00, 0, "YMF753", 0 }, 18219921b23SOrion Hodson { 0, 0, 0, NULL, 0 } 183987e5972SCameron Grant }; 184987e5972SCameron Grant 185987e5972SCameron Grant static char *ac97enhancement[] = { 18604553e63SCameron Grant "no 3D Stereo Enhancement", 187987e5972SCameron Grant "Analog Devices Phat Stereo", 188987e5972SCameron Grant "Creative Stereo Enhancement", 189987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 190987e5972SCameron Grant "Yamaha Ymersion", 191987e5972SCameron Grant "BBE 3D Stereo Enhancement", 192987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 193987e5972SCameron Grant "Qsound QXpander", 194987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 195987e5972SCameron Grant "SRS 3D Stereo Enhancement", 196987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 197987e5972SCameron Grant "AKM 3D Audio", 198987e5972SCameron Grant "Aureal Stereo Enhancement", 199987e5972SCameron Grant "Aztech 3D Enhancement", 200987e5972SCameron Grant "Binaura 3D Audio Enhancement", 201987e5972SCameron Grant "ESS Technology Stereo Enhancement", 202987e5972SCameron Grant "Harman International VMAx", 203987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 204987e5972SCameron Grant "Philips Incredible Sound", 205987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 206987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 207987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 208987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 209987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 210987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 211987e5972SCameron Grant "Delta Integration 3D Enhancement", 212987e5972SCameron Grant "SigmaTel 3D Enhancement", 213987e5972SCameron Grant "Reserved 27", 214987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 215987e5972SCameron Grant "Reserved 29", 216987e5972SCameron Grant "Reserved 30", 217987e5972SCameron Grant "Reserved 31" 218987e5972SCameron Grant }; 219987e5972SCameron Grant 220987e5972SCameron Grant static char *ac97feature[] = { 221987e5972SCameron Grant "mic channel", 222987e5972SCameron Grant "reserved", 223987e5972SCameron Grant "tone", 224987e5972SCameron Grant "simulated stereo", 225987e5972SCameron Grant "headphone", 226987e5972SCameron Grant "bass boost", 227987e5972SCameron Grant "18 bit DAC", 228987e5972SCameron Grant "20 bit DAC", 229987e5972SCameron Grant "18 bit ADC", 230987e5972SCameron Grant "20 bit ADC" 231987e5972SCameron Grant }; 232987e5972SCameron Grant 23339004e69SCameron Grant static char *ac97extfeature[] = { 23439004e69SCameron Grant "variable rate PCM", 23539004e69SCameron Grant "double rate PCM", 23639004e69SCameron Grant "reserved 1", 23739004e69SCameron Grant "variable rate mic", 23839004e69SCameron Grant "reserved 2", 23939004e69SCameron Grant "reserved 3", 24039004e69SCameron Grant "center DAC", 24139004e69SCameron Grant "surround DAC", 24239004e69SCameron Grant "LFE DAC", 24339004e69SCameron Grant "AMAP", 24439004e69SCameron Grant "reserved 4", 24539004e69SCameron Grant "reserved 5", 24639004e69SCameron Grant "reserved 6", 24739004e69SCameron Grant "reserved 7", 24839004e69SCameron Grant }; 24939004e69SCameron Grant 250f9eb1409SOrion Hodson u_int16_t 251f9eb1409SOrion Hodson ac97_rdcd(struct ac97_info *codec, int reg) 25239004e69SCameron Grant { 2530f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 25439004e69SCameron Grant } 25539004e69SCameron Grant 256f9eb1409SOrion Hodson void 257f9eb1409SOrion Hodson ac97_wrcd(struct ac97_info *codec, int reg, u_int16_t val) 25839004e69SCameron Grant { 2590f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 26039004e69SCameron Grant } 26139004e69SCameron Grant 262c6d4b83aSOrion Hodson static void 263c6d4b83aSOrion Hodson ac97_reset(struct ac97_info *codec) 264c6d4b83aSOrion Hodson { 265c6d4b83aSOrion Hodson u_int32_t i, ps; 266f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RESET, 0); 267c6d4b83aSOrion Hodson for (i = 0; i < 500; i++) { 268f9eb1409SOrion Hodson ps = ac97_rdcd(codec, AC97_REG_POWER) & AC97_POWER_STATUS; 269c6d4b83aSOrion Hodson if (ps == AC97_POWER_STATUS) 270c6d4b83aSOrion Hodson return; 271c6d4b83aSOrion Hodson DELAY(1000); 272c6d4b83aSOrion Hodson } 273a825c6e5SOrion Hodson device_printf(codec->dev, "AC97 reset timed out.\n"); 274c6d4b83aSOrion Hodson } 275c6d4b83aSOrion Hodson 27639004e69SCameron Grant int 27739004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 27839004e69SCameron Grant { 27939004e69SCameron Grant u_int16_t v; 28039004e69SCameron Grant 28139004e69SCameron Grant switch(which) { 28239004e69SCameron Grant case AC97_REGEXT_FDACRATE: 28339004e69SCameron Grant case AC97_REGEXT_SDACRATE: 28439004e69SCameron Grant case AC97_REGEXT_LDACRATE: 28539004e69SCameron Grant case AC97_REGEXT_LADCRATE: 28639004e69SCameron Grant case AC97_REGEXT_MADCRATE: 28739004e69SCameron Grant break; 28839004e69SCameron Grant 28939004e69SCameron Grant default: 29039004e69SCameron Grant return -1; 29139004e69SCameron Grant } 29239004e69SCameron Grant 29366ef8af5SCameron Grant snd_mtxlock(codec->lock); 29439004e69SCameron Grant if (rate != 0) { 29539004e69SCameron Grant v = rate; 29639004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 29739004e69SCameron Grant v >>= 1; 298f9eb1409SOrion Hodson ac97_wrcd(codec, which, v); 29939004e69SCameron Grant } 300f9eb1409SOrion Hodson v = ac97_rdcd(codec, which); 30139004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 30239004e69SCameron Grant v <<= 1; 30366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 30439004e69SCameron Grant return v; 30539004e69SCameron Grant } 30639004e69SCameron Grant 30739004e69SCameron Grant int 30839004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 30939004e69SCameron Grant { 31039004e69SCameron Grant mode &= AC97_EXTCAPS; 311647fbfebSOrion Hodson if ((mode & ~codec->extcaps) != 0) { 312647fbfebSOrion Hodson device_printf(codec->dev, "ac97 invalid mode set 0x%04x\n", 313647fbfebSOrion Hodson mode); 31439004e69SCameron Grant return -1; 315647fbfebSOrion Hodson } 31666ef8af5SCameron Grant snd_mtxlock(codec->lock); 317f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, mode); 318f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 31966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 32039004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 32139004e69SCameron Grant } 32239004e69SCameron Grant 3239ec437a3SCameron Grant u_int16_t 3249ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 3259ec437a3SCameron Grant { 3269ec437a3SCameron Grant return codec->extstat; 3279ec437a3SCameron Grant } 3289ec437a3SCameron Grant 3299ec437a3SCameron Grant u_int16_t 3309ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 3319ec437a3SCameron Grant { 3329ec437a3SCameron Grant return codec->extcaps; 3339ec437a3SCameron Grant } 3349ec437a3SCameron Grant 3355d91ad67SCameron Grant u_int16_t 3365d91ad67SCameron Grant ac97_getcaps(struct ac97_info *codec) 3375d91ad67SCameron Grant { 3385d91ad67SCameron Grant return codec->caps; 3395d91ad67SCameron Grant } 3405d91ad67SCameron Grant 341987e5972SCameron Grant static int 342987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 343987e5972SCameron Grant { 344987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 345341f16ccSCameron Grant 346987e5972SCameron Grant if (e->recidx > 0) { 347987e5972SCameron Grant int val = e->recidx - 1; 348987e5972SCameron Grant val |= val << 8; 34966ef8af5SCameron Grant snd_mtxlock(codec->lock); 350f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_RECSEL, val); 35166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 352987e5972SCameron Grant return 0; 35339004e69SCameron Grant } else 35439004e69SCameron Grant return -1; 355987e5972SCameron Grant } 356987e5972SCameron Grant 357987e5972SCameron Grant static int 358987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 359987e5972SCameron Grant { 360987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 361341f16ccSCameron Grant 362341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 363e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 364987e5972SCameron Grant 36539004e69SCameron Grant if (!e->stereo) 36639004e69SCameron Grant right = left; 367987e5972SCameron Grant if (e->reg > 0) { 368987e5972SCameron Grant left = 100 - left; 369987e5972SCameron Grant right = 100 - right; 370987e5972SCameron Grant } 371987e5972SCameron Grant 372987e5972SCameron Grant max = (1 << e->bits) - 1; 373987e5972SCameron Grant left = (left * max) / 100; 374987e5972SCameron Grant right = (right * max) / 100; 375987e5972SCameron Grant 376987e5972SCameron Grant val = (left << 8) | right; 377987e5972SCameron Grant 378987e5972SCameron Grant left = (left * 100) / max; 379987e5972SCameron Grant right = (right * 100) / max; 380987e5972SCameron Grant 381987e5972SCameron Grant if (e->reg > 0) { 382987e5972SCameron Grant left = 100 - left; 383987e5972SCameron Grant right = 100 - right; 384987e5972SCameron Grant } 385987e5972SCameron Grant 386987e5972SCameron Grant if (!e->stereo) { 387987e5972SCameron Grant val &= max; 388987e5972SCameron Grant val <<= e->ofs; 389987e5972SCameron Grant if (e->mask) { 390f9eb1409SOrion Hodson int cur = ac97_rdcd(codec, e->reg); 391987e5972SCameron Grant val |= cur & ~(max << e->ofs); 392987e5972SCameron Grant } 393987e5972SCameron Grant } 39439004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 39539004e69SCameron Grant val = AC97_MUTE; 39666ef8af5SCameron Grant snd_mtxlock(codec->lock); 397f9eb1409SOrion Hodson ac97_wrcd(codec, reg, val); 39866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 399987e5972SCameron Grant return left | (right << 8); 400341f16ccSCameron Grant } else { 401341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 40239004e69SCameron Grant return -1; 403987e5972SCameron Grant } 404341f16ccSCameron Grant } 405987e5972SCameron Grant 406987e5972SCameron Grant #if 0 407987e5972SCameron Grant static int 408987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 409987e5972SCameron Grant { 410987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 411987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 412987e5972SCameron Grant int max, val, volume; 413987e5972SCameron Grant 414987e5972SCameron Grant max = (1 << e->bits) - 1; 415f9eb1409SOrion Hodson val = ac97_rdcd(code, e->reg); 41639004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 41739004e69SCameron Grant volume = 0; 418987e5972SCameron Grant else { 419987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 420987e5972SCameron Grant val &= max; 421987e5972SCameron Grant volume = (val * 100) / max; 422987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 423987e5972SCameron Grant } 424987e5972SCameron Grant return volume; 42539004e69SCameron Grant } else 42639004e69SCameron Grant return -1; 427987e5972SCameron Grant } 428987e5972SCameron Grant #endif 429987e5972SCameron Grant 430108082c4SOrion Hodson static void 431108082c4SOrion Hodson ac97_fix_auxout(struct ac97_info *codec) 432108082c4SOrion Hodson { 433108082c4SOrion Hodson /* Determine what AUXOUT really means, it can be: 434108082c4SOrion Hodson * 435108082c4SOrion Hodson * 1. Headphone out. 436108082c4SOrion Hodson * 2. 4-Channel Out 437108082c4SOrion Hodson * 3. True line level out (effectively master volume). 438108082c4SOrion Hodson * 439108082c4SOrion Hodson * See Sections 5.2.1 and 5.27 for AUX_OUT Options in AC97r2.{2,3}. 440108082c4SOrion Hodson */ 441108082c4SOrion Hodson if (codec->caps & AC97_CAP_HEADPHONE) { 442108082c4SOrion Hodson /* XXX We should probably check the AUX_OUT initial value. 443108082c4SOrion Hodson * Leave AC97_MIX_AUXOUT - SOUND_MIXER_MONITOR relationship */ 444108082c4SOrion Hodson return; 445108082c4SOrion Hodson } else if (codec->extcaps & AC97_EXTCAP_SDAC && 446f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_MIXEXT_SURROUND) == 0x8080) { 447108082c4SOrion Hodson /* 4-Channel Out, add an additional gain setting. */ 448108082c4SOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 449108082c4SOrion Hodson } else { 450108082c4SOrion Hodson /* Master volume is/maybe fixed in h/w, not sufficiently 451108082c4SOrion Hodson * clear in spec to blat SOUND_MIXER_MASTER. */ 452108082c4SOrion Hodson codec->mix[SOUND_MIXER_OGAIN] = codec->mix[SOUND_MIXER_MONITOR]; 453108082c4SOrion Hodson } 454108082c4SOrion Hodson /* Blat monitor, inappropriate label if we get here */ 455108082c4SOrion Hodson bzero(&codec->mix[SOUND_MIXER_MONITOR], 456108082c4SOrion Hodson sizeof(codec->mix[SOUND_MIXER_MONITOR])); 457108082c4SOrion Hodson } 458108082c4SOrion Hodson 45919921b23SOrion Hodson static const char* 46019921b23SOrion Hodson ac97_hw_desc(u_int32_t id, const char* vname, const char* cname, char* buf) 46119921b23SOrion Hodson { 46219921b23SOrion Hodson if (cname == NULL) { 46319921b23SOrion Hodson sprintf(buf, "Unknown AC97 Codec (id = 0x%08x)", id); 46419921b23SOrion Hodson return buf; 46519921b23SOrion Hodson } 46619921b23SOrion Hodson 46719921b23SOrion Hodson if (vname == NULL) vname = "Unknown"; 46819921b23SOrion Hodson 46919921b23SOrion Hodson if (bootverbose) { 47019921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec (id = 0x%08x)", vname, cname, id); 47119921b23SOrion Hodson } else { 47219921b23SOrion Hodson sprintf(buf, "%s %s AC97 Codec", vname, cname); 47319921b23SOrion Hodson } 47419921b23SOrion Hodson return buf; 47519921b23SOrion Hodson } 47619921b23SOrion Hodson 477987e5972SCameron Grant static unsigned 47839004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 479987e5972SCameron Grant { 480f9eb1409SOrion Hodson ac97_patch codec_patch; 48119921b23SOrion Hodson const char *cname, *vname; 48219921b23SOrion Hodson char desc[80]; 48319921b23SOrion Hodson u_int8_t model, step; 484341f16ccSCameron Grant unsigned i, j, k, old; 485987e5972SCameron Grant u_int32_t id; 486987e5972SCameron Grant 48766ef8af5SCameron Grant snd_mtxlock(codec->lock); 4880f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 489cd2c103aSCameron Grant if (codec->count == 0) { 49004553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 49166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 49204553e63SCameron Grant return ENODEV; 49304553e63SCameron Grant } 4949ec437a3SCameron Grant 495f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 496c6d4b83aSOrion Hodson ac97_reset(codec); 497f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 498987e5972SCameron Grant 499f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REG_RESET); 500987e5972SCameron Grant codec->caps = i & 0x03ff; 501987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 502987e5972SCameron Grant 503f9eb1409SOrion Hodson id = (ac97_rdcd(codec, AC97_REG_ID1) << 16) | ac97_rdcd(codec, AC97_REG_ID2); 504e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 505e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 50666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 507e620d959SCameron Grant return ENODEV; 508e620d959SCameron Grant } 5096b4b88f7SCameron Grant 51019921b23SOrion Hodson codec->id = id; 511cd2c103aSCameron Grant codec->noext = 0; 512f9eb1409SOrion Hodson codec_patch = NULL; 51319921b23SOrion Hodson 51419921b23SOrion Hodson cname = NULL; 51519921b23SOrion Hodson model = step = 0; 516cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 51719921b23SOrion Hodson u_int32_t modelmask = 0xffffffff ^ ac97codecid[i].stepmask; 51819921b23SOrion Hodson if ((ac97codecid[i].id & modelmask) == (id & modelmask)) { 519cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 520f9eb1409SOrion Hodson codec_patch = ac97codecid[i].patch; 52119921b23SOrion Hodson cname = ac97codecid[i].name; 52219921b23SOrion Hodson model = (id & modelmask) & 0xff; 52319921b23SOrion Hodson step = (id & ~modelmask) & 0xff; 52419921b23SOrion Hodson break; 52519921b23SOrion Hodson } 52619921b23SOrion Hodson } 52719921b23SOrion Hodson 52819921b23SOrion Hodson vname = NULL; 52919921b23SOrion Hodson for (i = 0; ac97vendorid[i].id; i++) { 53019921b23SOrion Hodson if (ac97vendorid[i].id == (id & 0xffffff00)) { 53119921b23SOrion Hodson vname = ac97vendorid[i].name; 53219921b23SOrion Hodson break; 533cd2c103aSCameron Grant } 534cd2c103aSCameron Grant } 5356b4b88f7SCameron Grant 536cd2c103aSCameron Grant codec->extcaps = 0; 537cd2c103aSCameron Grant codec->extid = 0; 538cd2c103aSCameron Grant codec->extstat = 0; 5396a6ee5bbSCameron Grant if (!codec->noext) { 540f9eb1409SOrion Hodson i = ac97_rdcd(codec, AC97_REGEXT_ID); 5416a6ee5bbSCameron Grant if (i != 0xffff) { 54239004e69SCameron Grant codec->extcaps = i & 0x3fff; 54339004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 544f9eb1409SOrion Hodson codec->extstat = ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 5456b4b88f7SCameron Grant } 5466a6ee5bbSCameron Grant } 547987e5972SCameron Grant 548341f16ccSCameron Grant for (i = 0; i < 32; i++) { 549108082c4SOrion Hodson codec->mix[i] = ac97mixtable_default[i]; 550108082c4SOrion Hodson } 551108082c4SOrion Hodson ac97_fix_auxout(codec); 552f9eb1409SOrion Hodson if (codec_patch) 553f9eb1409SOrion Hodson codec_patch(codec); 554108082c4SOrion Hodson 555108082c4SOrion Hodson for (i = 0; i < 32; i++) { 55633c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 55733c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 558f9eb1409SOrion Hodson old = ac97_rdcd(codec, codec->mix[i].reg); 559f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, 0x3f); 560f9eb1409SOrion Hodson j = ac97_rdcd(codec, codec->mix[i].reg); 561f9eb1409SOrion Hodson ac97_wrcd(codec, codec->mix[i].reg, old); 5626a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 563341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 564341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 565341f16ccSCameron Grant } 566341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 567341f16ccSCameron Grant } 568987e5972SCameron Grant 56919921b23SOrion Hodson device_printf(codec->dev, "<%s>\n", 57019921b23SOrion Hodson ac97_hw_desc(codec->id, vname, cname, desc)); 571a825c6e5SOrion Hodson 572987e5972SCameron Grant if (bootverbose) { 57319921b23SOrion Hodson device_printf(codec->dev, "Codec features "); 57439004e69SCameron Grant for (i = j = 0; i < 10; i++) 57539004e69SCameron Grant if (codec->caps & (1 << i)) 57639004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 57739004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 578987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 57939004e69SCameron Grant 58039004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 58119921b23SOrion Hodson device_printf(codec->dev, "%s codec", 58219921b23SOrion Hodson codec->extid? "Secondary" : "Primary"); 58339004e69SCameron Grant if (codec->extcaps) 58439004e69SCameron Grant printf(" extended features "); 58539004e69SCameron Grant for (i = j = 0; i < 14; i++) 58639004e69SCameron Grant if (codec->extcaps & (1 << i)) 58739004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 58839004e69SCameron Grant printf("\n"); 58939004e69SCameron Grant } 590987e5972SCameron Grant } 591987e5972SCameron Grant 592f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 59303a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 59466ef8af5SCameron Grant snd_mtxunlock(codec->lock); 595987e5972SCameron Grant return 0; 596987e5972SCameron Grant } 597987e5972SCameron Grant 5989ec437a3SCameron Grant static unsigned 5999ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 6009ec437a3SCameron Grant { 60166ef8af5SCameron Grant snd_mtxlock(codec->lock); 6020f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 6039ec437a3SCameron Grant if (codec->count == 0) { 6049ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 60566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6069ec437a3SCameron Grant return ENODEV; 6079ec437a3SCameron Grant } 6089ec437a3SCameron Grant 609f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 610c6d4b83aSOrion Hodson ac97_reset(codec); 611f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 6129ec437a3SCameron Grant 6139ec437a3SCameron Grant if (!codec->noext) { 614f9eb1409SOrion Hodson ac97_wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 615f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS) 616b60e55dbSGuido van Rooij != codec->extstat) 6179ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 618b60e55dbSGuido van Rooij codec->extstat, 619f9eb1409SOrion Hodson ac97_rdcd(codec, AC97_REGEXT_STAT) & 620b60e55dbSGuido van Rooij AC97_EXTCAPS); 6219ec437a3SCameron Grant } 6229ec437a3SCameron Grant 623f9eb1409SOrion Hodson if ((ac97_rdcd(codec, AC97_REG_POWER) & 2) == 0) 6249ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 62566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 6269ec437a3SCameron Grant return 0; 6279ec437a3SCameron Grant } 6289ec437a3SCameron Grant 629987e5972SCameron Grant struct ac97_info * 6300f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 631987e5972SCameron Grant { 632987e5972SCameron Grant struct ac97_info *codec; 633987e5972SCameron Grant 6340f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 6350f55ac6cSCameron Grant if (codec == NULL) 6360f55ac6cSCameron Grant return NULL; 6370f55ac6cSCameron Grant 63866ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 639489c22ebSJohn Baldwin codec->lock = snd_mtxcreate(codec->name, "ac97 codec"); 640a163d034SWarner Losh codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 6410f55ac6cSCameron Grant if (codec->methods == NULL) { 64266ef8af5SCameron Grant snd_mtxlock(codec->lock); 64366ef8af5SCameron Grant snd_mtxfree(codec->lock); 6440f55ac6cSCameron Grant free(codec, M_AC97); 6450f55ac6cSCameron Grant return NULL; 646987e5972SCameron Grant } 6470f55ac6cSCameron Grant 6480f55ac6cSCameron Grant codec->dev = dev; 6490f55ac6cSCameron Grant codec->devinfo = devinfo; 65079bb7d52SCameron Grant codec->flags = 0; 651987e5972SCameron Grant return codec; 652987e5972SCameron Grant } 653987e5972SCameron Grant 65433dbf14aSCameron Grant void 65533dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 65633dbf14aSCameron Grant { 65766ef8af5SCameron Grant snd_mtxlock(codec->lock); 6580f55ac6cSCameron Grant if (codec->methods != NULL) 6590f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 66066ef8af5SCameron Grant snd_mtxfree(codec->lock); 6610f55ac6cSCameron Grant free(codec, M_AC97); 66233dbf14aSCameron Grant } 66333dbf14aSCameron Grant 66479bb7d52SCameron Grant void 66579bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 66679bb7d52SCameron Grant { 66779bb7d52SCameron Grant codec->flags = val; 66879bb7d52SCameron Grant } 66979bb7d52SCameron Grant 67079bb7d52SCameron Grant u_int32_t 67179bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 67279bb7d52SCameron Grant { 67379bb7d52SCameron Grant return codec->flags; 67479bb7d52SCameron Grant } 67579bb7d52SCameron Grant 6760f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 6770f55ac6cSCameron Grant 678987e5972SCameron Grant static int 67966ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 680987e5972SCameron Grant { 681987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 682341f16ccSCameron Grant u_int32_t i, mask; 683341f16ccSCameron Grant 68439004e69SCameron Grant if (codec == NULL) 68539004e69SCameron Grant return -1; 686341f16ccSCameron Grant 687e620d959SCameron Grant if (ac97_initmixer(codec)) 688e620d959SCameron Grant return -1; 689341f16ccSCameron Grant 690341f16ccSCameron Grant mask = 0; 691341f16ccSCameron Grant for (i = 0; i < 32; i++) 692341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 693341f16ccSCameron Grant mix_setdevs(m, mask); 694341f16ccSCameron Grant 695341f16ccSCameron Grant mask = 0; 696341f16ccSCameron Grant for (i = 0; i < 32; i++) 697341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 698341f16ccSCameron Grant mix_setrecdevs(m, mask); 699987e5972SCameron Grant return 0; 700987e5972SCameron Grant } 701987e5972SCameron Grant 702987e5972SCameron Grant static int 70366ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 70433dbf14aSCameron Grant { 70533dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 706341f16ccSCameron Grant 70733dbf14aSCameron Grant if (codec == NULL) 70833dbf14aSCameron Grant return -1; 70933dbf14aSCameron Grant /* 71033dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 71133dbf14aSCameron Grant return -1; 71233dbf14aSCameron Grant */ 71333dbf14aSCameron Grant ac97_destroy(codec); 71433dbf14aSCameron Grant return 0; 71533dbf14aSCameron Grant } 71633dbf14aSCameron Grant 71733dbf14aSCameron Grant static int 71866ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 7199ec437a3SCameron Grant { 7209ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 7219ec437a3SCameron Grant 7229ec437a3SCameron Grant if (codec == NULL) 7239ec437a3SCameron Grant return -1; 7249ec437a3SCameron Grant return ac97_reinitmixer(codec); 7259ec437a3SCameron Grant } 7269ec437a3SCameron Grant 7279ec437a3SCameron Grant static int 72866ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 729987e5972SCameron Grant { 730987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 731341f16ccSCameron Grant 73239004e69SCameron Grant if (codec == NULL) 73339004e69SCameron Grant return -1; 734987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 735987e5972SCameron Grant } 736987e5972SCameron Grant 737987e5972SCameron Grant static int 73866ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 739987e5972SCameron Grant { 740987e5972SCameron Grant int i; 741987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 742341f16ccSCameron Grant 74339004e69SCameron Grant if (codec == NULL) 74439004e69SCameron Grant return -1; 745987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 74639004e69SCameron Grant if ((src & (1 << i)) != 0) 74739004e69SCameron Grant break; 748987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 749987e5972SCameron Grant } 750987e5972SCameron Grant 7510f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 7520f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 7530f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 7540f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 7550f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 7560f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 7570f55ac6cSCameron Grant { 0, 0 } 758987e5972SCameron Grant }; 7590f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 7600f55ac6cSCameron Grant 7610f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 7620f55ac6cSCameron Grant 7630f55ac6cSCameron Grant kobj_class_t 7640f55ac6cSCameron Grant ac97_getmixerclass(void) 7650f55ac6cSCameron Grant { 7660f55ac6cSCameron Grant return &ac97mixer_class; 7670f55ac6cSCameron Grant } 7680f55ac6cSCameron Grant 769987e5972SCameron Grant 770