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 * 2653c5a968SPeter Wemm * $FreeBSD$ 27987e5972SCameron Grant */ 28987e5972SCameron Grant 29ef9308b1SCameron Grant #include <dev/sound/pcm/sound.h> 30ef9308b1SCameron Grant #include <dev/sound/pcm/ac97.h> 31987e5972SCameron Grant 320f55ac6cSCameron Grant #include "mixer_if.h" 33987e5972SCameron Grant 340f55ac6cSCameron Grant MALLOC_DEFINE(M_AC97, "ac97", "ac97 codec"); 35987e5972SCameron Grant 3679bb7d52SCameron Grant struct ac97mixtable_entry { 3779bb7d52SCameron Grant int reg:8; 3879bb7d52SCameron Grant unsigned bits:4; 3979bb7d52SCameron Grant unsigned ofs:4; 4079bb7d52SCameron Grant unsigned stereo:1; 4179bb7d52SCameron Grant unsigned mute:1; 4279bb7d52SCameron Grant unsigned recidx:4; 4379bb7d52SCameron Grant unsigned mask:1; 4479bb7d52SCameron Grant unsigned enable:1; 4579bb7d52SCameron Grant }; 4679bb7d52SCameron Grant 4766ef8af5SCameron Grant #define AC97_NAMELEN 16 4866ef8af5SCameron Grant struct ac97_info { 4966ef8af5SCameron Grant kobj_t methods; 5066ef8af5SCameron Grant device_t dev; 5166ef8af5SCameron Grant void *devinfo; 5266ef8af5SCameron Grant char *id; 5366ef8af5SCameron Grant char rev; 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]; 5866ef8af5SCameron Grant void *lock; 5966ef8af5SCameron Grant }; 6066ef8af5SCameron Grant 61987e5972SCameron Grant struct ac97_codecid { 626b4b88f7SCameron Grant u_int32_t id, noext:1; 63987e5972SCameron Grant char *name; 64987e5972SCameron Grant }; 65987e5972SCameron Grant 66987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 67341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 68341f16ccSCameron Grant [SOUND_MIXER_MONITOR] = { AC97_MIX_PHONES, 5, 0, 1, 1, 0, 0, 0 }, 69341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 70341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 71341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 72341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 73341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 74341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 75341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 76341f16ccSCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 77341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 78341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 79341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 80341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 81987e5972SCameron Grant }; 82987e5972SCameron Grant 83987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 8473770282SCameron Grant { 0x41445303, 0, "Analog Devices AD1819" }, 8573770282SCameron Grant { 0x41445340, 0, "Analog Devices AD1881" }, 8673770282SCameron Grant { 0x41445348, 0, "Analog Devices AD1881A" }, 8773770282SCameron Grant { 0x41445360, 0, "Analog Devices AD1885" }, 8873770282SCameron Grant { 0x414b4d00, 1, "Asahi Kasei AK4540" }, 8973770282SCameron Grant { 0x414b4d01, 1, "Asahi Kasei AK4542" }, 9073770282SCameron Grant { 0x414b4d02, 1, "Asahi Kasei AK4543" }, 9173770282SCameron Grant { 0x414c4710, 0, "Avance Logic ALC200/200P" }, 926b4b88f7SCameron Grant { 0x43525900, 0, "Cirrus Logic CS4297" }, 9373770282SCameron Grant { 0x43525903, 0, "Cirrus Logic CS4297" }, 9473770282SCameron Grant { 0x43525913, 0, "Cirrus Logic CS4297A" }, 9573770282SCameron Grant { 0x43525914, 0, "Cirrus Logic CS4297B" }, 9673770282SCameron Grant { 0x43525923, 0, "Cirrus Logic CS4294C" }, 9773770282SCameron Grant { 0x4352592b, 0, "Cirrus Logic CS4298C" }, 9873770282SCameron Grant { 0x43525931, 0, "Cirrus Logic CS4299A" }, 9973770282SCameron Grant { 0x43525933, 0, "Cirrus Logic CS4299C" }, 10073770282SCameron Grant { 0x43525934, 0, "Cirrus Logic CS4299D" }, 10173770282SCameron Grant { 0x43525941, 0, "Cirrus Logic CS4201A" }, 10273770282SCameron Grant { 0x43525951, 0, "Cirrus Logic CS4205A" }, 10373770282SCameron Grant { 0x43525961, 0, "Cirrus Logic CS4291A" }, 10473770282SCameron Grant { 0x45838308, 0, "ESS Technology ES1921" }, 10573770282SCameron Grant { 0x49434511, 0, "ICEnsemble ICE1232" }, 10673770282SCameron Grant { 0x4e534331, 0, "National Semiconductor LM4549" }, 10773770282SCameron Grant { 0x83847600, 0, "SigmaTel STAC9700/9783/9784" }, 10873770282SCameron Grant { 0x83847604, 0, "SigmaTel STAC9701/9703/9704/9705" }, 1096b4b88f7SCameron Grant { 0x83847605, 0, "SigmaTel STAC9704" }, 11073770282SCameron Grant { 0x83847608, 0, "SigmaTel STAC9708/9711" }, 11173770282SCameron Grant { 0x83847609, 0, "SigmaTel STAC9721/9723" }, 11273770282SCameron Grant { 0x83847644, 0, "SigmaTel STAC9744" }, 11373770282SCameron Grant { 0x83847656, 0, "SigmaTel STAC9756/9757" }, 11473770282SCameron Grant { 0x53494c22, 0, "Silicon Laboratory Si3036" }, 11573770282SCameron Grant { 0x53494c23, 0, "Silicon Laboratory Si3038" }, 11673770282SCameron Grant { 0x54524103, 0, "TriTech TR?????" }, 11773770282SCameron Grant { 0x54524106, 0, "TriTech TR28026" }, 11873770282SCameron Grant { 0x54524108, 0, "TriTech TR28028" }, 11973770282SCameron Grant { 0x54524123, 0, "TriTech TR28602" }, 12073770282SCameron Grant { 0x574d4c00, 0, "Wolfson WM9701A" }, 12173770282SCameron Grant { 0x574d4c03, 0, "Wolfson WM9703/9704" }, 12273770282SCameron Grant { 0x574d4c04, 0, "Wolfson WM9704 (quad)" }, 1236b4b88f7SCameron Grant { 0, 0, NULL } 124987e5972SCameron Grant }; 125987e5972SCameron Grant 126987e5972SCameron Grant static char *ac97enhancement[] = { 12704553e63SCameron Grant "no 3D Stereo Enhancement", 128987e5972SCameron Grant "Analog Devices Phat Stereo", 129987e5972SCameron Grant "Creative Stereo Enhancement", 130987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 131987e5972SCameron Grant "Yamaha Ymersion", 132987e5972SCameron Grant "BBE 3D Stereo Enhancement", 133987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 134987e5972SCameron Grant "Qsound QXpander", 135987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 136987e5972SCameron Grant "SRS 3D Stereo Enhancement", 137987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 138987e5972SCameron Grant "AKM 3D Audio", 139987e5972SCameron Grant "Aureal Stereo Enhancement", 140987e5972SCameron Grant "Aztech 3D Enhancement", 141987e5972SCameron Grant "Binaura 3D Audio Enhancement", 142987e5972SCameron Grant "ESS Technology Stereo Enhancement", 143987e5972SCameron Grant "Harman International VMAx", 144987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 145987e5972SCameron Grant "Philips Incredible Sound", 146987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 147987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 148987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 149987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 150987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 151987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 152987e5972SCameron Grant "Delta Integration 3D Enhancement", 153987e5972SCameron Grant "SigmaTel 3D Enhancement", 154987e5972SCameron Grant "Reserved 27", 155987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 156987e5972SCameron Grant "Reserved 29", 157987e5972SCameron Grant "Reserved 30", 158987e5972SCameron Grant "Reserved 31" 159987e5972SCameron Grant }; 160987e5972SCameron Grant 161987e5972SCameron Grant static char *ac97feature[] = { 162987e5972SCameron Grant "mic channel", 163987e5972SCameron Grant "reserved", 164987e5972SCameron Grant "tone", 165987e5972SCameron Grant "simulated stereo", 166987e5972SCameron Grant "headphone", 167987e5972SCameron Grant "bass boost", 168987e5972SCameron Grant "18 bit DAC", 169987e5972SCameron Grant "20 bit DAC", 170987e5972SCameron Grant "18 bit ADC", 171987e5972SCameron Grant "20 bit ADC" 172987e5972SCameron Grant }; 173987e5972SCameron Grant 17439004e69SCameron Grant static char *ac97extfeature[] = { 17539004e69SCameron Grant "variable rate PCM", 17639004e69SCameron Grant "double rate PCM", 17739004e69SCameron Grant "reserved 1", 17839004e69SCameron Grant "variable rate mic", 17939004e69SCameron Grant "reserved 2", 18039004e69SCameron Grant "reserved 3", 18139004e69SCameron Grant "center DAC", 18239004e69SCameron Grant "surround DAC", 18339004e69SCameron Grant "LFE DAC", 18439004e69SCameron Grant "AMAP", 18539004e69SCameron Grant "reserved 4", 18639004e69SCameron Grant "reserved 5", 18739004e69SCameron Grant "reserved 6", 18839004e69SCameron Grant "reserved 7", 18939004e69SCameron Grant }; 19039004e69SCameron Grant 19139004e69SCameron Grant static u_int16_t 19239004e69SCameron Grant rdcd(struct ac97_info *codec, int reg) 19339004e69SCameron Grant { 1940f55ac6cSCameron Grant return AC97_READ(codec->methods, codec->devinfo, reg); 19539004e69SCameron Grant } 19639004e69SCameron Grant 19739004e69SCameron Grant static void 19839004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val) 19939004e69SCameron Grant { 2000f55ac6cSCameron Grant AC97_WRITE(codec->methods, codec->devinfo, reg, val); 20139004e69SCameron Grant } 20239004e69SCameron Grant 20339004e69SCameron Grant int 20439004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 20539004e69SCameron Grant { 20639004e69SCameron Grant u_int16_t v; 20739004e69SCameron Grant 20839004e69SCameron Grant switch(which) { 20939004e69SCameron Grant case AC97_REGEXT_FDACRATE: 21039004e69SCameron Grant case AC97_REGEXT_SDACRATE: 21139004e69SCameron Grant case AC97_REGEXT_LDACRATE: 21239004e69SCameron Grant case AC97_REGEXT_LADCRATE: 21339004e69SCameron Grant case AC97_REGEXT_MADCRATE: 21439004e69SCameron Grant break; 21539004e69SCameron Grant 21639004e69SCameron Grant default: 21739004e69SCameron Grant return -1; 21839004e69SCameron Grant } 21939004e69SCameron Grant 22066ef8af5SCameron Grant snd_mtxlock(codec->lock); 22139004e69SCameron Grant if (rate != 0) { 22239004e69SCameron Grant v = rate; 22339004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 22439004e69SCameron Grant v >>= 1; 22539004e69SCameron Grant wrcd(codec, which, v); 22639004e69SCameron Grant } 22739004e69SCameron Grant v = rdcd(codec, which); 22839004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 22939004e69SCameron Grant v <<= 1; 23066ef8af5SCameron Grant snd_mtxunlock(codec->lock); 23139004e69SCameron Grant return v; 23239004e69SCameron Grant } 23339004e69SCameron Grant 23439004e69SCameron Grant int 23539004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 23639004e69SCameron Grant { 23739004e69SCameron Grant mode &= AC97_EXTCAPS; 23839004e69SCameron Grant if ((mode & ~codec->extcaps) != 0) 23939004e69SCameron Grant return -1; 24066ef8af5SCameron Grant snd_mtxlock(codec->lock); 24139004e69SCameron Grant wrcd(codec, AC97_REGEXT_STAT, mode); 24239004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 24366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 24439004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 24539004e69SCameron Grant } 24639004e69SCameron Grant 2479ec437a3SCameron Grant u_int16_t 2489ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 2499ec437a3SCameron Grant { 2509ec437a3SCameron Grant return codec->extstat; 2519ec437a3SCameron Grant } 2529ec437a3SCameron Grant 2539ec437a3SCameron Grant u_int16_t 2549ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 2559ec437a3SCameron Grant { 2569ec437a3SCameron Grant return codec->extcaps; 2579ec437a3SCameron Grant } 2589ec437a3SCameron Grant 259987e5972SCameron Grant static int 260987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 261987e5972SCameron Grant { 262987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 263341f16ccSCameron Grant 264987e5972SCameron Grant if (e->recidx > 0) { 265987e5972SCameron Grant int val = e->recidx - 1; 266987e5972SCameron Grant val |= val << 8; 26766ef8af5SCameron Grant snd_mtxlock(codec->lock); 26839004e69SCameron Grant wrcd(codec, AC97_REG_RECSEL, val); 26966ef8af5SCameron Grant snd_mtxunlock(codec->lock); 270987e5972SCameron Grant return 0; 27139004e69SCameron Grant } else 27239004e69SCameron Grant return -1; 273987e5972SCameron Grant } 274987e5972SCameron Grant 275987e5972SCameron Grant static int 276987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 277987e5972SCameron Grant { 278987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 279341f16ccSCameron Grant 280341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 281e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 282987e5972SCameron Grant 28339004e69SCameron Grant if (!e->stereo) 28439004e69SCameron Grant right = left; 285987e5972SCameron Grant if (e->reg > 0) { 286987e5972SCameron Grant left = 100 - left; 287987e5972SCameron Grant right = 100 - right; 288987e5972SCameron Grant } 289987e5972SCameron Grant 290987e5972SCameron Grant max = (1 << e->bits) - 1; 291987e5972SCameron Grant left = (left * max) / 100; 292987e5972SCameron Grant right = (right * max) / 100; 293987e5972SCameron Grant 294987e5972SCameron Grant val = (left << 8) | right; 295987e5972SCameron Grant 296987e5972SCameron Grant left = (left * 100) / max; 297987e5972SCameron Grant right = (right * 100) / max; 298987e5972SCameron Grant 299987e5972SCameron Grant if (e->reg > 0) { 300987e5972SCameron Grant left = 100 - left; 301987e5972SCameron Grant right = 100 - right; 302987e5972SCameron Grant } 303987e5972SCameron Grant 304987e5972SCameron Grant if (!e->stereo) { 305987e5972SCameron Grant val &= max; 306987e5972SCameron Grant val <<= e->ofs; 307987e5972SCameron Grant if (e->mask) { 30839004e69SCameron Grant int cur = rdcd(codec, e->reg); 309987e5972SCameron Grant val |= cur & ~(max << e->ofs); 310987e5972SCameron Grant } 311987e5972SCameron Grant } 31239004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 31339004e69SCameron Grant val = AC97_MUTE; 31466ef8af5SCameron Grant snd_mtxlock(codec->lock); 31539004e69SCameron Grant wrcd(codec, reg, val); 31666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 317987e5972SCameron Grant return left | (right << 8); 318341f16ccSCameron Grant } else { 319341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 32039004e69SCameron Grant return -1; 321987e5972SCameron Grant } 322341f16ccSCameron Grant } 323987e5972SCameron Grant 324987e5972SCameron Grant #if 0 325987e5972SCameron Grant static int 326987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 327987e5972SCameron Grant { 328987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 329987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 330987e5972SCameron Grant int max, val, volume; 331987e5972SCameron Grant 332987e5972SCameron Grant max = (1 << e->bits) - 1; 33339004e69SCameron Grant val = rdcd(code, e->reg); 33439004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 33539004e69SCameron Grant volume = 0; 336987e5972SCameron Grant else { 337987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 338987e5972SCameron Grant val &= max; 339987e5972SCameron Grant volume = (val * 100) / max; 340987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 341987e5972SCameron Grant } 342987e5972SCameron Grant return volume; 34339004e69SCameron Grant } else 34439004e69SCameron Grant return -1; 345987e5972SCameron Grant } 346987e5972SCameron Grant #endif 347987e5972SCameron Grant 348987e5972SCameron Grant static unsigned 34939004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 350987e5972SCameron Grant { 351341f16ccSCameron Grant unsigned i, j, k, old; 352987e5972SCameron Grant u_int32_t id; 353987e5972SCameron Grant 35466ef8af5SCameron Grant snd_mtxlock(codec->lock); 35539004e69SCameron Grant for (i = 0; i < 32; i++) 35639004e69SCameron Grant codec->mix[i] = ac97mixtable_default[i]; 357987e5972SCameron Grant 3580f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 359cd2c103aSCameron Grant if (codec->count == 0) { 36004553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 36166ef8af5SCameron Grant snd_mtxunlock(codec->lock); 36204553e63SCameron Grant return ENODEV; 36304553e63SCameron Grant } 3649ec437a3SCameron Grant 36579bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 36639004e69SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 367e620d959SCameron Grant DELAY(100000); 36879bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 369987e5972SCameron Grant 37039004e69SCameron Grant i = rdcd(codec, AC97_REG_RESET); 371987e5972SCameron Grant codec->caps = i & 0x03ff; 372987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 373987e5972SCameron Grant 3746b4b88f7SCameron Grant id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 3756b4b88f7SCameron Grant codec->rev = id & 0x000000ff; 376e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 377e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 37866ef8af5SCameron Grant snd_mtxunlock(codec->lock); 379e620d959SCameron Grant return ENODEV; 380e620d959SCameron Grant } 3816b4b88f7SCameron Grant 382cd2c103aSCameron Grant codec->noext = 0; 38366ef8af5SCameron Grant codec->id = NULL; 384cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 385cd2c103aSCameron Grant if (ac97codecid[i].id == id) { 38666ef8af5SCameron Grant codec->id = ac97codecid[i].name; 387cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 388cd2c103aSCameron Grant } 389cd2c103aSCameron Grant } 3906b4b88f7SCameron Grant 391cd2c103aSCameron Grant codec->extcaps = 0; 392cd2c103aSCameron Grant codec->extid = 0; 393cd2c103aSCameron Grant codec->extstat = 0; 3946a6ee5bbSCameron Grant if (!codec->noext) { 39539004e69SCameron Grant i = rdcd(codec, AC97_REGEXT_ID); 3966a6ee5bbSCameron Grant if (i != 0xffff) { 39739004e69SCameron Grant codec->extcaps = i & 0x3fff; 39839004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 39939004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 4006b4b88f7SCameron Grant } 4016a6ee5bbSCameron Grant } 402987e5972SCameron Grant 403341f16ccSCameron Grant for (i = 0; i < 32; i++) { 40433c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 40533c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 406341f16ccSCameron Grant old = rdcd(codec, codec->mix[i].reg); 407341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, 0x3f); 408341f16ccSCameron Grant j = rdcd(codec, codec->mix[i].reg); 409341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, old); 4106a6ee5bbSCameron Grant codec->mix[i].enable = (j != 0 && j != old)? 1 : 0; 411341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 412341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 413341f16ccSCameron Grant } 414341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 415341f16ccSCameron Grant } 416987e5972SCameron Grant 417987e5972SCameron Grant if (bootverbose) { 418e620d959SCameron Grant device_printf(codec->dev, "ac97 codec id 0x%08x", id); 41966ef8af5SCameron Grant if (codec->id) 42066ef8af5SCameron Grant printf(" (%s)", codec->id); 42103a00905SCameron Grant printf("\n"); 42203a00905SCameron Grant device_printf(codec->dev, "ac97 codec features "); 42339004e69SCameron Grant for (i = j = 0; i < 10; i++) 42439004e69SCameron Grant if (codec->caps & (1 << i)) 42539004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 42639004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 427987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 42839004e69SCameron Grant 42939004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 43039004e69SCameron Grant device_printf(codec->dev, "ac97 %s codec", 43139004e69SCameron Grant codec->extid? "secondary" : "primary"); 43239004e69SCameron Grant if (codec->extcaps) 43339004e69SCameron Grant printf(" extended features "); 43439004e69SCameron Grant for (i = j = 0; i < 14; i++) 43539004e69SCameron Grant if (codec->extcaps & (1 << i)) 43639004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 43739004e69SCameron Grant printf("\n"); 43839004e69SCameron Grant } 439987e5972SCameron Grant } 440987e5972SCameron Grant 44139004e69SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 44203a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 44366ef8af5SCameron Grant snd_mtxunlock(codec->lock); 444987e5972SCameron Grant return 0; 445987e5972SCameron Grant } 446987e5972SCameron Grant 4479ec437a3SCameron Grant static unsigned 4489ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 4499ec437a3SCameron Grant { 4509ec437a3SCameron Grant unsigned i; 4519ec437a3SCameron Grant 45266ef8af5SCameron Grant snd_mtxlock(codec->lock); 4530f55ac6cSCameron Grant codec->count = AC97_INIT(codec->methods, codec->devinfo); 4549ec437a3SCameron Grant if (codec->count == 0) { 4559ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 45666ef8af5SCameron Grant snd_mtxunlock(codec->lock); 4579ec437a3SCameron Grant return ENODEV; 4589ec437a3SCameron Grant } 4599ec437a3SCameron Grant 46079bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 4619ec437a3SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 4629ec437a3SCameron Grant DELAY(100000); 46379bb7d52SCameron Grant wrcd(codec, AC97_REG_POWER, (codec->flags & AC97_F_EAPD_INV)? 0x8000 : 0x0000); 4649ec437a3SCameron Grant i = rdcd(codec, AC97_REG_RESET); 4659ec437a3SCameron Grant 4669ec437a3SCameron Grant if (!codec->noext) { 4679ec437a3SCameron Grant wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 4689ec437a3SCameron Grant if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) 4699ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 4709ec437a3SCameron Grant codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); 4719ec437a3SCameron Grant } 4729ec437a3SCameron Grant 4739ec437a3SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 4749ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 47566ef8af5SCameron Grant snd_mtxunlock(codec->lock); 4769ec437a3SCameron Grant return 0; 4779ec437a3SCameron Grant } 4789ec437a3SCameron Grant 479987e5972SCameron Grant struct ac97_info * 4800f55ac6cSCameron Grant ac97_create(device_t dev, void *devinfo, kobj_class_t cls) 481987e5972SCameron Grant { 482987e5972SCameron Grant struct ac97_info *codec; 483987e5972SCameron Grant 4840f55ac6cSCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_AC97, M_NOWAIT); 4850f55ac6cSCameron Grant if (codec == NULL) 4860f55ac6cSCameron Grant return NULL; 4870f55ac6cSCameron Grant 48866ef8af5SCameron Grant snprintf(codec->name, AC97_NAMELEN, "%s:ac97", device_get_nameunit(dev)); 48966ef8af5SCameron Grant codec->lock = snd_mtxcreate(codec->name); 4900f55ac6cSCameron Grant codec->methods = kobj_create(cls, M_AC97, M_WAITOK); 4910f55ac6cSCameron Grant if (codec->methods == NULL) { 49266ef8af5SCameron Grant snd_mtxlock(codec->lock); 49366ef8af5SCameron Grant snd_mtxfree(codec->lock); 4940f55ac6cSCameron Grant free(codec, M_AC97); 4950f55ac6cSCameron Grant return NULL; 496987e5972SCameron Grant } 4970f55ac6cSCameron Grant 4980f55ac6cSCameron Grant codec->dev = dev; 4990f55ac6cSCameron Grant codec->devinfo = devinfo; 50079bb7d52SCameron Grant codec->flags = 0; 501987e5972SCameron Grant return codec; 502987e5972SCameron Grant } 503987e5972SCameron Grant 50433dbf14aSCameron Grant void 50533dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 50633dbf14aSCameron Grant { 50766ef8af5SCameron Grant snd_mtxlock(codec->lock); 5080f55ac6cSCameron Grant if (codec->methods != NULL) 5090f55ac6cSCameron Grant kobj_delete(codec->methods, M_AC97); 51066ef8af5SCameron Grant snd_mtxfree(codec->lock); 5110f55ac6cSCameron Grant free(codec, M_AC97); 51233dbf14aSCameron Grant } 51333dbf14aSCameron Grant 51479bb7d52SCameron Grant void 51579bb7d52SCameron Grant ac97_setflags(struct ac97_info *codec, u_int32_t val) 51679bb7d52SCameron Grant { 51779bb7d52SCameron Grant codec->flags = val; 51879bb7d52SCameron Grant } 51979bb7d52SCameron Grant 52079bb7d52SCameron Grant u_int32_t 52179bb7d52SCameron Grant ac97_getflags(struct ac97_info *codec) 52279bb7d52SCameron Grant { 52379bb7d52SCameron Grant return codec->flags; 52479bb7d52SCameron Grant } 52579bb7d52SCameron Grant 5260f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 5270f55ac6cSCameron Grant 528987e5972SCameron Grant static int 52966ef8af5SCameron Grant ac97mix_init(struct snd_mixer *m) 530987e5972SCameron Grant { 531987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 532341f16ccSCameron Grant u_int32_t i, mask; 533341f16ccSCameron Grant 53439004e69SCameron Grant if (codec == NULL) 53539004e69SCameron Grant return -1; 536341f16ccSCameron Grant 537e620d959SCameron Grant if (ac97_initmixer(codec)) 538e620d959SCameron Grant return -1; 539341f16ccSCameron Grant 540341f16ccSCameron Grant mask = 0; 541341f16ccSCameron Grant for (i = 0; i < 32; i++) 542341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 543341f16ccSCameron Grant mix_setdevs(m, mask); 544341f16ccSCameron Grant 545341f16ccSCameron Grant mask = 0; 546341f16ccSCameron Grant for (i = 0; i < 32; i++) 547341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 548341f16ccSCameron Grant mix_setrecdevs(m, mask); 549987e5972SCameron Grant return 0; 550987e5972SCameron Grant } 551987e5972SCameron Grant 552987e5972SCameron Grant static int 55366ef8af5SCameron Grant ac97mix_uninit(struct snd_mixer *m) 55433dbf14aSCameron Grant { 55533dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 556341f16ccSCameron Grant 55733dbf14aSCameron Grant if (codec == NULL) 55833dbf14aSCameron Grant return -1; 55933dbf14aSCameron Grant /* 56033dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 56133dbf14aSCameron Grant return -1; 56233dbf14aSCameron Grant */ 56333dbf14aSCameron Grant ac97_destroy(codec); 56433dbf14aSCameron Grant return 0; 56533dbf14aSCameron Grant } 56633dbf14aSCameron Grant 56733dbf14aSCameron Grant static int 56866ef8af5SCameron Grant ac97mix_reinit(struct snd_mixer *m) 5699ec437a3SCameron Grant { 5709ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 5719ec437a3SCameron Grant 5729ec437a3SCameron Grant if (codec == NULL) 5739ec437a3SCameron Grant return -1; 5749ec437a3SCameron Grant return ac97_reinitmixer(codec); 5759ec437a3SCameron Grant } 5769ec437a3SCameron Grant 5779ec437a3SCameron Grant static int 57866ef8af5SCameron Grant ac97mix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 579987e5972SCameron Grant { 580987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 581341f16ccSCameron Grant 58239004e69SCameron Grant if (codec == NULL) 58339004e69SCameron Grant return -1; 584987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 585987e5972SCameron Grant } 586987e5972SCameron Grant 587987e5972SCameron Grant static int 58866ef8af5SCameron Grant ac97mix_setrecsrc(struct snd_mixer *m, u_int32_t src) 589987e5972SCameron Grant { 590987e5972SCameron Grant int i; 591987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 592341f16ccSCameron Grant 59339004e69SCameron Grant if (codec == NULL) 59439004e69SCameron Grant return -1; 595987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 59639004e69SCameron Grant if ((src & (1 << i)) != 0) 59739004e69SCameron Grant break; 598987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 599987e5972SCameron Grant } 600987e5972SCameron Grant 6010f55ac6cSCameron Grant static kobj_method_t ac97mixer_methods[] = { 6020f55ac6cSCameron Grant KOBJMETHOD(mixer_init, ac97mix_init), 6030f55ac6cSCameron Grant KOBJMETHOD(mixer_uninit, ac97mix_uninit), 6040f55ac6cSCameron Grant KOBJMETHOD(mixer_reinit, ac97mix_reinit), 6050f55ac6cSCameron Grant KOBJMETHOD(mixer_set, ac97mix_set), 6060f55ac6cSCameron Grant KOBJMETHOD(mixer_setrecsrc, ac97mix_setrecsrc), 6070f55ac6cSCameron Grant { 0, 0 } 608987e5972SCameron Grant }; 6090f55ac6cSCameron Grant MIXER_DECLARE(ac97mixer); 6100f55ac6cSCameron Grant 6110f55ac6cSCameron Grant /* -------------------------------------------------------------------- */ 6120f55ac6cSCameron Grant 6130f55ac6cSCameron Grant kobj_class_t 6140f55ac6cSCameron Grant ac97_getmixerclass(void) 6150f55ac6cSCameron Grant { 6160f55ac6cSCameron Grant return &ac97mixer_class; 6170f55ac6cSCameron Grant } 6180f55ac6cSCameron Grant 619987e5972SCameron Grant 620