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 32987e5972SCameron Grant struct ac97mixtable_entry { 33987e5972SCameron Grant int reg:8; 34987e5972SCameron Grant unsigned bits:4; 35987e5972SCameron Grant unsigned ofs:4; 36987e5972SCameron Grant unsigned stereo:1; 37987e5972SCameron Grant unsigned mute:1; 38987e5972SCameron Grant unsigned recidx:4; 39987e5972SCameron Grant unsigned mask:1; 40341f16ccSCameron Grant unsigned enable:1; 41987e5972SCameron Grant }; 42987e5972SCameron Grant 43987e5972SCameron Grant struct ac97_info { 4403a00905SCameron Grant device_t dev; 4539004e69SCameron Grant ac97_init *init; 46987e5972SCameron Grant ac97_read *read; 47987e5972SCameron Grant ac97_write *write; 48987e5972SCameron Grant void *devinfo; 49cd2c103aSCameron Grant char *name; 50987e5972SCameron Grant char rev; 51cd2c103aSCameron Grant unsigned count, caps, se, extcaps, extid, extstat, noext:1; 52987e5972SCameron Grant struct ac97mixtable_entry mix[32]; 53987e5972SCameron Grant }; 54987e5972SCameron Grant 55987e5972SCameron Grant struct ac97_codecid { 566b4b88f7SCameron Grant u_int32_t id, noext:1; 57987e5972SCameron Grant char *name; 58987e5972SCameron Grant }; 59987e5972SCameron Grant 60987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 61341f16ccSCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0, 1 }, 62341f16ccSCameron Grant [SOUND_MIXER_MONITOR] = { AC97_MIX_PHONES, 5, 0, 1, 1, 0, 0, 0 }, 63341f16ccSCameron Grant [SOUND_MIXER_PHONEOUT] = { AC97_MIX_MONO, 5, 0, 0, 1, 7, 0, 0 }, 64341f16ccSCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1, 0 }, 65341f16ccSCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1, 0 }, 66341f16ccSCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0, 1 }, 67341f16ccSCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0, 0 }, 68341f16ccSCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0, 1 }, 69341f16ccSCameron Grant [SOUND_MIXER_PHONEIN] = { AC97_MIX_PHONE, 5, 0, 0, 1, 8, 0, 0 }, 70341f16ccSCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0, 1 }, 71341f16ccSCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0, 1 }, 72341f16ccSCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0, 0 }, 73341f16ccSCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0, 0 }, 74341f16ccSCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0, 1 } 75987e5972SCameron Grant }; 76987e5972SCameron Grant 77987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 786b4b88f7SCameron Grant { 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" }, 79cd2c103aSCameron Grant { 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" }, 806b4b88f7SCameron Grant { 0x43525900, 0, "Cirrus Logic CS4297" }, 816b4b88f7SCameron Grant { 0x83847600, 0, "SigmaTel STAC????" }, 826b4b88f7SCameron Grant { 0x83847604, 0, "SigmaTel STAC9701/3/4/5" }, 836b4b88f7SCameron Grant { 0x83847605, 0, "SigmaTel STAC9704" }, 846b4b88f7SCameron Grant { 0x83847608, 0, "SigmaTel STAC9708" }, 856b4b88f7SCameron Grant { 0x83847609, 0, "SigmaTel STAC9721" }, 866b4b88f7SCameron Grant { 0, 0, NULL } 87987e5972SCameron Grant }; 88987e5972SCameron Grant 89987e5972SCameron Grant static char *ac97enhancement[] = { 9004553e63SCameron Grant "no 3D Stereo Enhancement", 91987e5972SCameron Grant "Analog Devices Phat Stereo", 92987e5972SCameron Grant "Creative Stereo Enhancement", 93987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 94987e5972SCameron Grant "Yamaha Ymersion", 95987e5972SCameron Grant "BBE 3D Stereo Enhancement", 96987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 97987e5972SCameron Grant "Qsound QXpander", 98987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 99987e5972SCameron Grant "SRS 3D Stereo Enhancement", 100987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 101987e5972SCameron Grant "AKM 3D Audio", 102987e5972SCameron Grant "Aureal Stereo Enhancement", 103987e5972SCameron Grant "Aztech 3D Enhancement", 104987e5972SCameron Grant "Binaura 3D Audio Enhancement", 105987e5972SCameron Grant "ESS Technology Stereo Enhancement", 106987e5972SCameron Grant "Harman International VMAx", 107987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 108987e5972SCameron Grant "Philips Incredible Sound", 109987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 110987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 111987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 112987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 113987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 114987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 115987e5972SCameron Grant "Delta Integration 3D Enhancement", 116987e5972SCameron Grant "SigmaTel 3D Enhancement", 117987e5972SCameron Grant "Reserved 27", 118987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 119987e5972SCameron Grant "Reserved 29", 120987e5972SCameron Grant "Reserved 30", 121987e5972SCameron Grant "Reserved 31" 122987e5972SCameron Grant }; 123987e5972SCameron Grant 124987e5972SCameron Grant static char *ac97feature[] = { 125987e5972SCameron Grant "mic channel", 126987e5972SCameron Grant "reserved", 127987e5972SCameron Grant "tone", 128987e5972SCameron Grant "simulated stereo", 129987e5972SCameron Grant "headphone", 130987e5972SCameron Grant "bass boost", 131987e5972SCameron Grant "18 bit DAC", 132987e5972SCameron Grant "20 bit DAC", 133987e5972SCameron Grant "18 bit ADC", 134987e5972SCameron Grant "20 bit ADC" 135987e5972SCameron Grant }; 136987e5972SCameron Grant 13739004e69SCameron Grant static char *ac97extfeature[] = { 13839004e69SCameron Grant "variable rate PCM", 13939004e69SCameron Grant "double rate PCM", 14039004e69SCameron Grant "reserved 1", 14139004e69SCameron Grant "variable rate mic", 14239004e69SCameron Grant "reserved 2", 14339004e69SCameron Grant "reserved 3", 14439004e69SCameron Grant "center DAC", 14539004e69SCameron Grant "surround DAC", 14639004e69SCameron Grant "LFE DAC", 14739004e69SCameron Grant "AMAP", 14839004e69SCameron Grant "reserved 4", 14939004e69SCameron Grant "reserved 5", 15039004e69SCameron Grant "reserved 6", 15139004e69SCameron Grant "reserved 7", 15239004e69SCameron Grant }; 15339004e69SCameron Grant 15439004e69SCameron Grant static u_int16_t 15539004e69SCameron Grant rdcd(struct ac97_info *codec, int reg) 15639004e69SCameron Grant { 15739004e69SCameron Grant return codec->read(codec->devinfo, reg); 15839004e69SCameron Grant } 15939004e69SCameron Grant 16039004e69SCameron Grant static void 16139004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val) 16239004e69SCameron Grant { 16339004e69SCameron Grant codec->write(codec->devinfo, reg, val); 16439004e69SCameron Grant } 16539004e69SCameron Grant 16639004e69SCameron Grant int 16739004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 16839004e69SCameron Grant { 16939004e69SCameron Grant u_int16_t v; 17039004e69SCameron Grant 17139004e69SCameron Grant switch(which) { 17239004e69SCameron Grant case AC97_REGEXT_FDACRATE: 17339004e69SCameron Grant case AC97_REGEXT_SDACRATE: 17439004e69SCameron Grant case AC97_REGEXT_LDACRATE: 17539004e69SCameron Grant case AC97_REGEXT_LADCRATE: 17639004e69SCameron Grant case AC97_REGEXT_MADCRATE: 17739004e69SCameron Grant break; 17839004e69SCameron Grant 17939004e69SCameron Grant default: 18039004e69SCameron Grant return -1; 18139004e69SCameron Grant } 18239004e69SCameron Grant 18339004e69SCameron Grant if (rate != 0) { 18439004e69SCameron Grant v = rate; 18539004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 18639004e69SCameron Grant v >>= 1; 18739004e69SCameron Grant wrcd(codec, which, v); 18839004e69SCameron Grant } 18939004e69SCameron Grant v = rdcd(codec, which); 19039004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 19139004e69SCameron Grant v <<= 1; 19239004e69SCameron Grant return v; 19339004e69SCameron Grant } 19439004e69SCameron Grant 19539004e69SCameron Grant int 19639004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 19739004e69SCameron Grant { 19839004e69SCameron Grant mode &= AC97_EXTCAPS; 19939004e69SCameron Grant if ((mode & ~codec->extcaps) != 0) 20039004e69SCameron Grant return -1; 20139004e69SCameron Grant wrcd(codec, AC97_REGEXT_STAT, mode); 20239004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 20339004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 20439004e69SCameron Grant } 20539004e69SCameron Grant 2069ec437a3SCameron Grant u_int16_t 2079ec437a3SCameron Grant ac97_getextmode(struct ac97_info *codec) 2089ec437a3SCameron Grant { 2099ec437a3SCameron Grant return codec->extstat; 2109ec437a3SCameron Grant } 2119ec437a3SCameron Grant 2129ec437a3SCameron Grant u_int16_t 2139ec437a3SCameron Grant ac97_getextcaps(struct ac97_info *codec) 2149ec437a3SCameron Grant { 2159ec437a3SCameron Grant return codec->extcaps; 2169ec437a3SCameron Grant } 2179ec437a3SCameron Grant 218987e5972SCameron Grant static int 219987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 220987e5972SCameron Grant { 221987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 222341f16ccSCameron Grant 223987e5972SCameron Grant if (e->recidx > 0) { 224987e5972SCameron Grant int val = e->recidx - 1; 225987e5972SCameron Grant val |= val << 8; 22639004e69SCameron Grant wrcd(codec, AC97_REG_RECSEL, val); 227987e5972SCameron Grant return 0; 22839004e69SCameron Grant } else 22939004e69SCameron Grant return -1; 230987e5972SCameron Grant } 231987e5972SCameron Grant 232987e5972SCameron Grant static int 233987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 234987e5972SCameron Grant { 235987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 236341f16ccSCameron Grant 237341f16ccSCameron Grant if (e->reg && e->enable && e->bits) { 238e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 239987e5972SCameron Grant 24039004e69SCameron Grant if (!e->stereo) 24139004e69SCameron Grant right = left; 242987e5972SCameron Grant if (e->reg > 0) { 243987e5972SCameron Grant left = 100 - left; 244987e5972SCameron Grant right = 100 - right; 245987e5972SCameron Grant } 246987e5972SCameron Grant 247987e5972SCameron Grant max = (1 << e->bits) - 1; 248987e5972SCameron Grant left = (left * max) / 100; 249987e5972SCameron Grant right = (right * max) / 100; 250987e5972SCameron Grant 251987e5972SCameron Grant val = (left << 8) | right; 252987e5972SCameron Grant 253987e5972SCameron Grant left = (left * 100) / max; 254987e5972SCameron Grant right = (right * 100) / max; 255987e5972SCameron Grant 256987e5972SCameron Grant if (e->reg > 0) { 257987e5972SCameron Grant left = 100 - left; 258987e5972SCameron Grant right = 100 - right; 259987e5972SCameron Grant } 260987e5972SCameron Grant 261987e5972SCameron Grant if (!e->stereo) { 262987e5972SCameron Grant val &= max; 263987e5972SCameron Grant val <<= e->ofs; 264987e5972SCameron Grant if (e->mask) { 26539004e69SCameron Grant int cur = rdcd(codec, e->reg); 266987e5972SCameron Grant val |= cur & ~(max << e->ofs); 267987e5972SCameron Grant } 268987e5972SCameron Grant } 26939004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 27039004e69SCameron Grant val = AC97_MUTE; 27139004e69SCameron Grant wrcd(codec, reg, val); 272987e5972SCameron Grant return left | (right << 8); 273341f16ccSCameron Grant } else { 274341f16ccSCameron Grant /* printf("ac97_setmixer: reg=%d, bits=%d, enable=%d\n", e->reg, e->bits, e->enable); */ 27539004e69SCameron Grant return -1; 276987e5972SCameron Grant } 277341f16ccSCameron Grant } 278987e5972SCameron Grant 279987e5972SCameron Grant #if 0 280987e5972SCameron Grant static int 281987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 282987e5972SCameron Grant { 283987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 284987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 285987e5972SCameron Grant int max, val, volume; 286987e5972SCameron Grant 287987e5972SCameron Grant max = (1 << e->bits) - 1; 28839004e69SCameron Grant val = rdcd(code, e->reg); 28939004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 29039004e69SCameron Grant volume = 0; 291987e5972SCameron Grant else { 292987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 293987e5972SCameron Grant val &= max; 294987e5972SCameron Grant volume = (val * 100) / max; 295987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 296987e5972SCameron Grant } 297987e5972SCameron Grant return volume; 29839004e69SCameron Grant } else 29939004e69SCameron Grant return -1; 300987e5972SCameron Grant } 301987e5972SCameron Grant #endif 302987e5972SCameron Grant 303987e5972SCameron Grant static unsigned 30439004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 305987e5972SCameron Grant { 306341f16ccSCameron Grant unsigned i, j, k, old; 307987e5972SCameron Grant u_int32_t id; 308987e5972SCameron Grant 30939004e69SCameron Grant for (i = 0; i < 32; i++) 31039004e69SCameron Grant codec->mix[i] = ac97mixtable_default[i]; 311987e5972SCameron Grant 31204553e63SCameron Grant if (codec->init) { 313cd2c103aSCameron Grant codec->count = codec->init(codec->devinfo); 314cd2c103aSCameron Grant if (codec->count == 0) { 31504553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 31604553e63SCameron Grant return ENODEV; 31704553e63SCameron Grant } 318cd2c103aSCameron Grant } else 319cd2c103aSCameron Grant codec->count = 1; 3209ec437a3SCameron Grant 32139004e69SCameron Grant wrcd(codec, AC97_REG_POWER, 0); 32239004e69SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 323e620d959SCameron Grant DELAY(100000); 324987e5972SCameron Grant 32539004e69SCameron Grant i = rdcd(codec, AC97_REG_RESET); 326987e5972SCameron Grant codec->caps = i & 0x03ff; 327987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 328987e5972SCameron Grant 3296b4b88f7SCameron Grant id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 3306b4b88f7SCameron Grant codec->rev = id & 0x000000ff; 331e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 332e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 333e620d959SCameron Grant return ENODEV; 334e620d959SCameron Grant } 3356b4b88f7SCameron Grant 336cd2c103aSCameron Grant codec->noext = 0; 337cd2c103aSCameron Grant codec->name = NULL; 338cd2c103aSCameron Grant for (i = 0; ac97codecid[i].id; i++) { 339cd2c103aSCameron Grant if (ac97codecid[i].id == id) { 340cd2c103aSCameron Grant codec->name = ac97codecid[i].name; 341cd2c103aSCameron Grant codec->noext = ac97codecid[i].noext; 342cd2c103aSCameron Grant } 343cd2c103aSCameron Grant } 3446b4b88f7SCameron Grant 345cd2c103aSCameron Grant if (codec->noext) { 346cd2c103aSCameron Grant codec->extcaps = 0; 347cd2c103aSCameron Grant codec->extid = 0; 348cd2c103aSCameron Grant codec->extstat = 0; 349cd2c103aSCameron Grant } else { 35039004e69SCameron Grant i = rdcd(codec, AC97_REGEXT_ID); 35139004e69SCameron Grant codec->extcaps = i & 0x3fff; 35239004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 35339004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 3546b4b88f7SCameron Grant } 355987e5972SCameron Grant 356341f16ccSCameron Grant for (i = 0; i < 32; i++) { 35733c878f0SCameron Grant k = codec->noext? codec->mix[i].enable : 1; 35833c878f0SCameron Grant if (k && (codec->mix[i].reg > 0)) { 359341f16ccSCameron Grant old = rdcd(codec, codec->mix[i].reg); 360341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, 0x3f); 361341f16ccSCameron Grant j = rdcd(codec, codec->mix[i].reg); 362341f16ccSCameron Grant wrcd(codec, codec->mix[i].reg, old); 363341f16ccSCameron Grant codec->mix[i].enable = j? 1 : 0; 364341f16ccSCameron Grant for (k = 1; j & (1 << k); k++); 365341f16ccSCameron Grant codec->mix[i].bits = j? k - codec->mix[i].ofs : 0; 366341f16ccSCameron Grant } 367341f16ccSCameron Grant /* printf("mixch %d, en=%d, b=%d\n", i, codec->mix[i].enable, codec->mix[i].bits); */ 368341f16ccSCameron Grant } 369987e5972SCameron Grant 370987e5972SCameron Grant if (bootverbose) { 371e620d959SCameron Grant device_printf(codec->dev, "ac97 codec id 0x%08x", id); 372cd2c103aSCameron Grant if (codec->name) 373cd2c103aSCameron Grant printf(" (%s)", codec->name); 37403a00905SCameron Grant printf("\n"); 37503a00905SCameron Grant device_printf(codec->dev, "ac97 codec features "); 37639004e69SCameron Grant for (i = j = 0; i < 10; i++) 37739004e69SCameron Grant if (codec->caps & (1 << i)) 37839004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 37939004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 380987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 38139004e69SCameron Grant 38239004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 38339004e69SCameron Grant device_printf(codec->dev, "ac97 %s codec", 38439004e69SCameron Grant codec->extid? "secondary" : "primary"); 38539004e69SCameron Grant if (codec->extcaps) 38639004e69SCameron Grant printf(" extended features "); 38739004e69SCameron Grant for (i = j = 0; i < 14; i++) 38839004e69SCameron Grant if (codec->extcaps & (1 << i)) 38939004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 39039004e69SCameron Grant printf("\n"); 39139004e69SCameron Grant } 392987e5972SCameron Grant } 393987e5972SCameron Grant 39439004e69SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 39503a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 396987e5972SCameron Grant return 0; 397987e5972SCameron Grant } 398987e5972SCameron Grant 3999ec437a3SCameron Grant static unsigned 4009ec437a3SCameron Grant ac97_reinitmixer(struct ac97_info *codec) 4019ec437a3SCameron Grant { 4029ec437a3SCameron Grant unsigned i; 4039ec437a3SCameron Grant 4049ec437a3SCameron Grant if (codec->init) { 4059ec437a3SCameron Grant codec->count = codec->init(codec->devinfo); 4069ec437a3SCameron Grant if (codec->count == 0) { 4079ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 4089ec437a3SCameron Grant return ENODEV; 4099ec437a3SCameron Grant } 4109ec437a3SCameron Grant } else 4119ec437a3SCameron Grant codec->count = 1; 4129ec437a3SCameron Grant 4139ec437a3SCameron Grant wrcd(codec, AC97_REG_POWER, 0); 4149ec437a3SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 4159ec437a3SCameron Grant DELAY(100000); 4169ec437a3SCameron Grant i = rdcd(codec, AC97_REG_RESET); 4179ec437a3SCameron Grant 4189ec437a3SCameron Grant if (!codec->noext) { 4199ec437a3SCameron Grant wrcd(codec, AC97_REGEXT_STAT, codec->extstat); 4209ec437a3SCameron Grant if (rdcd(codec, AC97_REGEXT_STAT) != codec->extstat) 4219ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec failed to reset extended mode (%x, got %x)\n", 4229ec437a3SCameron Grant codec->extstat, rdcd(codec, AC97_REGEXT_STAT)); 4239ec437a3SCameron Grant } 4249ec437a3SCameron Grant 4259ec437a3SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 4269ec437a3SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 4279ec437a3SCameron Grant return 0; 4289ec437a3SCameron Grant } 4299ec437a3SCameron Grant 430987e5972SCameron Grant struct ac97_info * 43139004e69SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr) 432987e5972SCameron Grant { 433987e5972SCameron Grant struct ac97_info *codec; 434987e5972SCameron Grant 435987e5972SCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 436987e5972SCameron Grant if (codec != NULL) { 43703a00905SCameron Grant codec->dev = dev; 43839004e69SCameron Grant codec->init = init; 439987e5972SCameron Grant codec->read = rd; 440987e5972SCameron Grant codec->write = wr; 441987e5972SCameron Grant codec->devinfo = devinfo; 442987e5972SCameron Grant } 443987e5972SCameron Grant return codec; 444987e5972SCameron Grant } 445987e5972SCameron Grant 44633dbf14aSCameron Grant void 44733dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 44833dbf14aSCameron Grant { 44933dbf14aSCameron Grant free(codec, M_DEVBUF); 45033dbf14aSCameron Grant } 45133dbf14aSCameron Grant 452987e5972SCameron Grant static int 453987e5972SCameron Grant ac97mix_init(snd_mixer *m) 454987e5972SCameron Grant { 455987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 456341f16ccSCameron Grant u_int32_t i, mask; 457341f16ccSCameron Grant 45839004e69SCameron Grant if (codec == NULL) 45939004e69SCameron Grant return -1; 460341f16ccSCameron Grant 461e620d959SCameron Grant if (ac97_initmixer(codec)) 462e620d959SCameron Grant return -1; 463341f16ccSCameron Grant 464341f16ccSCameron Grant mask = 0; 465341f16ccSCameron Grant for (i = 0; i < 32; i++) 466341f16ccSCameron Grant mask |= codec->mix[i].enable? 1 << i : 0; 467341f16ccSCameron Grant mix_setdevs(m, mask); 468341f16ccSCameron Grant 469341f16ccSCameron Grant mask = 0; 470341f16ccSCameron Grant for (i = 0; i < 32; i++) 471341f16ccSCameron Grant mask |= codec->mix[i].recidx? 1 << i : 0; 472341f16ccSCameron Grant mix_setrecdevs(m, mask); 473987e5972SCameron Grant return 0; 474987e5972SCameron Grant } 475987e5972SCameron Grant 476987e5972SCameron Grant static int 47733dbf14aSCameron Grant ac97mix_uninit(snd_mixer *m) 47833dbf14aSCameron Grant { 47933dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 480341f16ccSCameron Grant 48133dbf14aSCameron Grant if (codec == NULL) 48233dbf14aSCameron Grant return -1; 48333dbf14aSCameron Grant /* 48433dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 48533dbf14aSCameron Grant return -1; 48633dbf14aSCameron Grant */ 48733dbf14aSCameron Grant ac97_destroy(codec); 48833dbf14aSCameron Grant return 0; 48933dbf14aSCameron Grant } 49033dbf14aSCameron Grant 49133dbf14aSCameron Grant static int 4929ec437a3SCameron Grant ac97mix_reinit(snd_mixer *m) 4939ec437a3SCameron Grant { 4949ec437a3SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 4959ec437a3SCameron Grant 4969ec437a3SCameron Grant if (codec == NULL) 4979ec437a3SCameron Grant return -1; 4989ec437a3SCameron Grant return ac97_reinitmixer(codec); 4999ec437a3SCameron Grant } 5009ec437a3SCameron Grant 5019ec437a3SCameron Grant static int 502987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 503987e5972SCameron Grant { 504987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 505341f16ccSCameron Grant 50639004e69SCameron Grant if (codec == NULL) 50739004e69SCameron Grant return -1; 508987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 509987e5972SCameron Grant } 510987e5972SCameron Grant 511987e5972SCameron Grant static int 512987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 513987e5972SCameron Grant { 514987e5972SCameron Grant int i; 515987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 516341f16ccSCameron Grant 51739004e69SCameron Grant if (codec == NULL) 51839004e69SCameron Grant return -1; 519987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 52039004e69SCameron Grant if ((src & (1 << i)) != 0) 52139004e69SCameron Grant break; 522987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 523987e5972SCameron Grant } 524987e5972SCameron Grant 525987e5972SCameron Grant snd_mixer ac97_mixer = { 526987e5972SCameron Grant "AC97 mixer", 527987e5972SCameron Grant ac97mix_init, 52833dbf14aSCameron Grant ac97mix_uninit, 5299ec437a3SCameron Grant ac97mix_reinit, 530987e5972SCameron Grant ac97mix_set, 531987e5972SCameron Grant ac97mix_setrecsrc, 532987e5972SCameron Grant }; 533987e5972SCameron Grant 534