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; 40987e5972SCameron Grant }; 41987e5972SCameron Grant 42987e5972SCameron Grant struct ac97_info { 4303a00905SCameron Grant device_t dev; 4439004e69SCameron Grant ac97_init *init; 45987e5972SCameron Grant ac97_read *read; 46987e5972SCameron Grant ac97_write *write; 47987e5972SCameron Grant void *devinfo; 48987e5972SCameron Grant char id[4]; 49987e5972SCameron Grant char rev; 506b4b88f7SCameron Grant unsigned caps, se, extcaps, extid, extstat, noext:1; 51987e5972SCameron Grant struct ac97mixtable_entry mix[32]; 52987e5972SCameron Grant }; 53987e5972SCameron Grant 54987e5972SCameron Grant struct ac97_codecid { 556b4b88f7SCameron Grant u_int32_t id, noext:1; 56987e5972SCameron Grant char *name; 57987e5972SCameron Grant }; 58987e5972SCameron Grant 59987e5972SCameron Grant static const struct ac97mixtable_entry ac97mixtable_default[32] = { 60987e5972SCameron Grant [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 61987e5972SCameron Grant [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 62987e5972SCameron Grant [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 63987e5972SCameron Grant [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 64987e5972SCameron Grant [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 65987e5972SCameron Grant [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 66987e5972SCameron Grant [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 67987e5972SCameron Grant [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 68987e5972SCameron Grant [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 69987e5972SCameron Grant [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 70987e5972SCameron Grant [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 71987e5972SCameron Grant }; 72987e5972SCameron Grant 73987e5972SCameron Grant static const unsigned ac97mixdevs = 74987e5972SCameron Grant SOUND_MASK_VOLUME | 75987e5972SCameron Grant SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 76987e5972SCameron Grant SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 77987e5972SCameron Grant SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 78987e5972SCameron Grant 79987e5972SCameron Grant static const unsigned ac97recdevs = 80987e5972SCameron Grant SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 81987e5972SCameron Grant SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 82987e5972SCameron Grant 83987e5972SCameron Grant static struct ac97_codecid ac97codecid[] = { 846b4b88f7SCameron Grant { 0x414b4d00, 1, "Asahi Kasei AK4540 rev 0" }, 856b4b88f7SCameron Grant { 0x43525900, 0, "Cirrus Logic CS4297" }, 866b4b88f7SCameron Grant { 0x83847600, 0, "SigmaTel STAC????" }, 876b4b88f7SCameron Grant { 0x83847604, 0, "SigmaTel STAC9701/3/4/5" }, 886b4b88f7SCameron Grant { 0x83847605, 0, "SigmaTel STAC9704" }, 896b4b88f7SCameron Grant { 0x83847608, 0, "SigmaTel STAC9708" }, 906b4b88f7SCameron Grant { 0x83847609, 0, "SigmaTel STAC9721" }, 916b4b88f7SCameron Grant { 0x414b4d01, 1, "Asahi Kasei AK4540 rev 1" }, 926b4b88f7SCameron Grant { 0, 0, NULL } 93987e5972SCameron Grant }; 94987e5972SCameron Grant 95987e5972SCameron Grant static char *ac97enhancement[] = { 9604553e63SCameron Grant "no 3D Stereo Enhancement", 97987e5972SCameron Grant "Analog Devices Phat Stereo", 98987e5972SCameron Grant "Creative Stereo Enhancement", 99987e5972SCameron Grant "National Semi 3D Stereo Enhancement", 100987e5972SCameron Grant "Yamaha Ymersion", 101987e5972SCameron Grant "BBE 3D Stereo Enhancement", 102987e5972SCameron Grant "Crystal Semi 3D Stereo Enhancement", 103987e5972SCameron Grant "Qsound QXpander", 104987e5972SCameron Grant "Spatializer 3D Stereo Enhancement", 105987e5972SCameron Grant "SRS 3D Stereo Enhancement", 106987e5972SCameron Grant "Platform Tech 3D Stereo Enhancement", 107987e5972SCameron Grant "AKM 3D Audio", 108987e5972SCameron Grant "Aureal Stereo Enhancement", 109987e5972SCameron Grant "Aztech 3D Enhancement", 110987e5972SCameron Grant "Binaura 3D Audio Enhancement", 111987e5972SCameron Grant "ESS Technology Stereo Enhancement", 112987e5972SCameron Grant "Harman International VMAx", 113987e5972SCameron Grant "Nvidea 3D Stereo Enhancement", 114987e5972SCameron Grant "Philips Incredible Sound", 115987e5972SCameron Grant "Texas Instruments 3D Stereo Enhancement", 116987e5972SCameron Grant "VLSI Technology 3D Stereo Enhancement", 117987e5972SCameron Grant "TriTech 3D Stereo Enhancement", 118987e5972SCameron Grant "Realtek 3D Stereo Enhancement", 119987e5972SCameron Grant "Samsung 3D Stereo Enhancement", 120987e5972SCameron Grant "Wolfson Microelectronics 3D Enhancement", 121987e5972SCameron Grant "Delta Integration 3D Enhancement", 122987e5972SCameron Grant "SigmaTel 3D Enhancement", 123987e5972SCameron Grant "Reserved 27", 124987e5972SCameron Grant "Rockwell 3D Stereo Enhancement", 125987e5972SCameron Grant "Reserved 29", 126987e5972SCameron Grant "Reserved 30", 127987e5972SCameron Grant "Reserved 31" 128987e5972SCameron Grant }; 129987e5972SCameron Grant 130987e5972SCameron Grant static char *ac97feature[] = { 131987e5972SCameron Grant "mic channel", 132987e5972SCameron Grant "reserved", 133987e5972SCameron Grant "tone", 134987e5972SCameron Grant "simulated stereo", 135987e5972SCameron Grant "headphone", 136987e5972SCameron Grant "bass boost", 137987e5972SCameron Grant "18 bit DAC", 138987e5972SCameron Grant "20 bit DAC", 139987e5972SCameron Grant "18 bit ADC", 140987e5972SCameron Grant "20 bit ADC" 141987e5972SCameron Grant }; 142987e5972SCameron Grant 14339004e69SCameron Grant static char *ac97extfeature[] = { 14439004e69SCameron Grant "variable rate PCM", 14539004e69SCameron Grant "double rate PCM", 14639004e69SCameron Grant "reserved 1", 14739004e69SCameron Grant "variable rate mic", 14839004e69SCameron Grant "reserved 2", 14939004e69SCameron Grant "reserved 3", 15039004e69SCameron Grant "center DAC", 15139004e69SCameron Grant "surround DAC", 15239004e69SCameron Grant "LFE DAC", 15339004e69SCameron Grant "AMAP", 15439004e69SCameron Grant "reserved 4", 15539004e69SCameron Grant "reserved 5", 15639004e69SCameron Grant "reserved 6", 15739004e69SCameron Grant "reserved 7", 15839004e69SCameron Grant }; 15939004e69SCameron Grant 16039004e69SCameron Grant static u_int16_t 16139004e69SCameron Grant rdcd(struct ac97_info *codec, int reg) 16239004e69SCameron Grant { 16339004e69SCameron Grant return codec->read(codec->devinfo, reg); 16439004e69SCameron Grant } 16539004e69SCameron Grant 16639004e69SCameron Grant static void 16739004e69SCameron Grant wrcd(struct ac97_info *codec, int reg, u_int16_t val) 16839004e69SCameron Grant { 16939004e69SCameron Grant codec->write(codec->devinfo, reg, val); 17039004e69SCameron Grant } 17139004e69SCameron Grant 17239004e69SCameron Grant int 17339004e69SCameron Grant ac97_setrate(struct ac97_info *codec, int which, int rate) 17439004e69SCameron Grant { 17539004e69SCameron Grant u_int16_t v; 17639004e69SCameron Grant 17739004e69SCameron Grant switch(which) { 17839004e69SCameron Grant case AC97_REGEXT_FDACRATE: 17939004e69SCameron Grant case AC97_REGEXT_SDACRATE: 18039004e69SCameron Grant case AC97_REGEXT_LDACRATE: 18139004e69SCameron Grant case AC97_REGEXT_LADCRATE: 18239004e69SCameron Grant case AC97_REGEXT_MADCRATE: 18339004e69SCameron Grant break; 18439004e69SCameron Grant 18539004e69SCameron Grant default: 18639004e69SCameron Grant return -1; 18739004e69SCameron Grant } 18839004e69SCameron Grant 18939004e69SCameron Grant if (rate != 0) { 19039004e69SCameron Grant v = rate; 19139004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 19239004e69SCameron Grant v >>= 1; 19339004e69SCameron Grant wrcd(codec, which, v); 19439004e69SCameron Grant } 19539004e69SCameron Grant v = rdcd(codec, which); 19639004e69SCameron Grant if (codec->extstat & AC97_EXTCAP_DRA) 19739004e69SCameron Grant v <<= 1; 19839004e69SCameron Grant return v; 19939004e69SCameron Grant } 20039004e69SCameron Grant 20139004e69SCameron Grant int 20239004e69SCameron Grant ac97_setextmode(struct ac97_info *codec, u_int16_t mode) 20339004e69SCameron Grant { 20439004e69SCameron Grant mode &= AC97_EXTCAPS; 20539004e69SCameron Grant if ((mode & ~codec->extcaps) != 0) 20639004e69SCameron Grant return -1; 20739004e69SCameron Grant wrcd(codec, AC97_REGEXT_STAT, mode); 20839004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 20939004e69SCameron Grant return (mode == codec->extstat)? 0 : -1; 21039004e69SCameron Grant } 21139004e69SCameron Grant 212987e5972SCameron Grant static int 213987e5972SCameron Grant ac97_setrecsrc(struct ac97_info *codec, int channel) 214987e5972SCameron Grant { 215987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 216987e5972SCameron Grant if (e->recidx > 0) { 217987e5972SCameron Grant int val = e->recidx - 1; 218987e5972SCameron Grant val |= val << 8; 21939004e69SCameron Grant wrcd(codec, AC97_REG_RECSEL, val); 220987e5972SCameron Grant return 0; 22139004e69SCameron Grant } else 22239004e69SCameron Grant return -1; 223987e5972SCameron Grant } 224987e5972SCameron Grant 225987e5972SCameron Grant static int 226987e5972SCameron Grant ac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 227987e5972SCameron Grant { 228987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 229987e5972SCameron Grant if (e->reg != 0) { 230e479a8afSCameron Grant int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 231987e5972SCameron Grant 23239004e69SCameron Grant if (!e->stereo) 23339004e69SCameron Grant right = left; 234987e5972SCameron Grant if (e->reg > 0) { 235987e5972SCameron Grant left = 100 - left; 236987e5972SCameron Grant right = 100 - right; 237987e5972SCameron Grant } 238987e5972SCameron Grant 239987e5972SCameron Grant max = (1 << e->bits) - 1; 240987e5972SCameron Grant left = (left * max) / 100; 241987e5972SCameron Grant right = (right * max) / 100; 242987e5972SCameron Grant 243987e5972SCameron Grant val = (left << 8) | right; 244987e5972SCameron Grant 245987e5972SCameron Grant left = (left * 100) / max; 246987e5972SCameron Grant right = (right * 100) / max; 247987e5972SCameron Grant 248987e5972SCameron Grant if (e->reg > 0) { 249987e5972SCameron Grant left = 100 - left; 250987e5972SCameron Grant right = 100 - right; 251987e5972SCameron Grant } 252987e5972SCameron Grant 253987e5972SCameron Grant if (!e->stereo) { 254987e5972SCameron Grant val &= max; 255987e5972SCameron Grant val <<= e->ofs; 256987e5972SCameron Grant if (e->mask) { 25739004e69SCameron Grant int cur = rdcd(codec, e->reg); 258987e5972SCameron Grant val |= cur & ~(max << e->ofs); 259987e5972SCameron Grant } 260987e5972SCameron Grant } 26139004e69SCameron Grant if (left == 0 && right == 0 && e->mute == 1) 26239004e69SCameron Grant val = AC97_MUTE; 26339004e69SCameron Grant wrcd(codec, reg, val); 264987e5972SCameron Grant return left | (right << 8); 26539004e69SCameron Grant } else 26639004e69SCameron Grant return -1; 267987e5972SCameron Grant } 268987e5972SCameron Grant 269987e5972SCameron Grant #if 0 270987e5972SCameron Grant static int 271987e5972SCameron Grant ac97_getmixer(struct ac97_info *codec, int channel) 272987e5972SCameron Grant { 273987e5972SCameron Grant struct ac97mixtable_entry *e = &codec->mix[channel]; 274987e5972SCameron Grant if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 275987e5972SCameron Grant int max, val, volume; 276987e5972SCameron Grant 277987e5972SCameron Grant max = (1 << e->bits) - 1; 27839004e69SCameron Grant val = rdcd(code, e->reg); 27939004e69SCameron Grant if (val == AC97_MUTE && e->mute == 1) 28039004e69SCameron Grant volume = 0; 281987e5972SCameron Grant else { 282987e5972SCameron Grant if (e->stereo == 0) val >>= e->ofs; 283987e5972SCameron Grant val &= max; 284987e5972SCameron Grant volume = (val * 100) / max; 285987e5972SCameron Grant if (e->reg > 0) volume = 100 - volume; 286987e5972SCameron Grant } 287987e5972SCameron Grant return volume; 28839004e69SCameron Grant } else 28939004e69SCameron Grant return -1; 290987e5972SCameron Grant } 291987e5972SCameron Grant #endif 292987e5972SCameron Grant 293987e5972SCameron Grant static unsigned 29439004e69SCameron Grant ac97_initmixer(struct ac97_info *codec) 295987e5972SCameron Grant { 296987e5972SCameron Grant unsigned i, j; 297987e5972SCameron Grant u_int32_t id; 298987e5972SCameron Grant 29939004e69SCameron Grant for (i = 0; i < 32; i++) 30039004e69SCameron Grant codec->mix[i] = ac97mixtable_default[i]; 301987e5972SCameron Grant 30204553e63SCameron Grant if (codec->init) { 30304553e63SCameron Grant if (codec->init(codec->devinfo)) { 30404553e63SCameron Grant device_printf(codec->dev, "ac97 codec init failed\n"); 30504553e63SCameron Grant return ENODEV; 30604553e63SCameron Grant } 30704553e63SCameron Grant } 30839004e69SCameron Grant wrcd(codec, AC97_REG_POWER, 0); 30939004e69SCameron Grant wrcd(codec, AC97_REG_RESET, 0); 310e620d959SCameron Grant DELAY(100000); 311987e5972SCameron Grant 31239004e69SCameron Grant i = rdcd(codec, AC97_REG_RESET); 313987e5972SCameron Grant codec->caps = i & 0x03ff; 314987e5972SCameron Grant codec->se = (i & 0x7c00) >> 10; 315987e5972SCameron Grant 3166b4b88f7SCameron Grant id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 3176b4b88f7SCameron Grant codec->rev = id & 0x000000ff; 318e620d959SCameron Grant if (id == 0 || id == 0xffffffff) { 319e620d959SCameron Grant device_printf(codec->dev, "ac97 codec invalid or not present (id == %x)\n", id); 320e620d959SCameron Grant return ENODEV; 321e620d959SCameron Grant } 3226b4b88f7SCameron Grant 3236b4b88f7SCameron Grant for (i = 0; ac97codecid[i].id; i++) 3246b4b88f7SCameron Grant if (ac97codecid[i].id == id) 3256b4b88f7SCameron Grant codec->noext = 1; 3266b4b88f7SCameron Grant 3276b4b88f7SCameron Grant if (!codec->noext) { 32839004e69SCameron Grant i = rdcd(codec, AC97_REGEXT_ID); 32939004e69SCameron Grant codec->extcaps = i & 0x3fff; 33039004e69SCameron Grant codec->extid = (i & 0xc000) >> 14; 33139004e69SCameron Grant codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 3326b4b88f7SCameron Grant } else { 3336b4b88f7SCameron Grant codec->extcaps = 0; 3346b4b88f7SCameron Grant codec->extid = 0; 3356b4b88f7SCameron Grant codec->extstat = 0; 3366b4b88f7SCameron Grant } 337987e5972SCameron Grant 33839004e69SCameron Grant wrcd(codec, AC97_MIX_MASTER, 0x20); 33939004e69SCameron Grant if ((rdcd(codec, AC97_MIX_MASTER) & 0x20) == 0x20) 340987e5972SCameron Grant codec->mix[SOUND_MIXER_VOLUME].bits++; 34139004e69SCameron Grant wrcd(codec, AC97_MIX_MASTER, 0x00); 342987e5972SCameron Grant 343987e5972SCameron Grant if (bootverbose) { 344e620d959SCameron Grant device_printf(codec->dev, "ac97 codec id 0x%08x", id); 34539004e69SCameron Grant for (i = 0; ac97codecid[i].id; i++) 34639004e69SCameron Grant if (ac97codecid[i].id == id) 34739004e69SCameron Grant printf(" (%s)", ac97codecid[i].name); 34803a00905SCameron Grant printf("\n"); 34903a00905SCameron Grant device_printf(codec->dev, "ac97 codec features "); 35039004e69SCameron Grant for (i = j = 0; i < 10; i++) 35139004e69SCameron Grant if (codec->caps & (1 << i)) 35239004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97feature[i]); 35339004e69SCameron Grant printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 354987e5972SCameron Grant printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 35539004e69SCameron Grant 35639004e69SCameron Grant if (codec->extcaps != 0 || codec->extid) { 35739004e69SCameron Grant device_printf(codec->dev, "ac97 %s codec", 35839004e69SCameron Grant codec->extid? "secondary" : "primary"); 35939004e69SCameron Grant if (codec->extcaps) 36039004e69SCameron Grant printf(" extended features "); 36139004e69SCameron Grant for (i = j = 0; i < 14; i++) 36239004e69SCameron Grant if (codec->extcaps & (1 << i)) 36339004e69SCameron Grant printf("%s%s", j++? ", " : "", ac97extfeature[i]); 36439004e69SCameron Grant printf("\n"); 36539004e69SCameron Grant } 366987e5972SCameron Grant } 367987e5972SCameron Grant 36839004e69SCameron Grant if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 36903a00905SCameron Grant device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 370987e5972SCameron Grant return 0; 371987e5972SCameron Grant } 372987e5972SCameron Grant 373987e5972SCameron Grant struct ac97_info * 37439004e69SCameron Grant ac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr) 375987e5972SCameron Grant { 376987e5972SCameron Grant struct ac97_info *codec; 377987e5972SCameron Grant 378987e5972SCameron Grant codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 379987e5972SCameron Grant if (codec != NULL) { 38003a00905SCameron Grant codec->dev = dev; 38139004e69SCameron Grant codec->init = init; 382987e5972SCameron Grant codec->read = rd; 383987e5972SCameron Grant codec->write = wr; 384987e5972SCameron Grant codec->devinfo = devinfo; 385987e5972SCameron Grant } 386987e5972SCameron Grant return codec; 387987e5972SCameron Grant } 388987e5972SCameron Grant 38933dbf14aSCameron Grant void 39033dbf14aSCameron Grant ac97_destroy(struct ac97_info *codec) 39133dbf14aSCameron Grant { 39233dbf14aSCameron Grant free(codec, M_DEVBUF); 39333dbf14aSCameron Grant } 39433dbf14aSCameron Grant 395987e5972SCameron Grant static int 396987e5972SCameron Grant ac97mix_init(snd_mixer *m) 397987e5972SCameron Grant { 398987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 39939004e69SCameron Grant if (codec == NULL) 40039004e69SCameron Grant return -1; 401e620d959SCameron Grant if (ac97_initmixer(codec)) 402e620d959SCameron Grant return -1; 403987e5972SCameron Grant mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 404987e5972SCameron Grant mix_setrecdevs(m, ac97recdevs); 405987e5972SCameron Grant return 0; 406987e5972SCameron Grant } 407987e5972SCameron Grant 408987e5972SCameron Grant static int 40933dbf14aSCameron Grant ac97mix_uninit(snd_mixer *m) 41033dbf14aSCameron Grant { 41133dbf14aSCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 41233dbf14aSCameron Grant if (codec == NULL) 41333dbf14aSCameron Grant return -1; 41433dbf14aSCameron Grant /* 41533dbf14aSCameron Grant if (ac97_uninitmixer(codec)) 41633dbf14aSCameron Grant return -1; 41733dbf14aSCameron Grant */ 41833dbf14aSCameron Grant ac97_destroy(codec); 41933dbf14aSCameron Grant return 0; 42033dbf14aSCameron Grant } 42133dbf14aSCameron Grant 42233dbf14aSCameron Grant static int 423987e5972SCameron Grant ac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 424987e5972SCameron Grant { 425987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 42639004e69SCameron Grant if (codec == NULL) 42739004e69SCameron Grant return -1; 428987e5972SCameron Grant return ac97_setmixer(codec, dev, left, right); 429987e5972SCameron Grant } 430987e5972SCameron Grant 431987e5972SCameron Grant static int 432987e5972SCameron Grant ac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 433987e5972SCameron Grant { 434987e5972SCameron Grant int i; 435987e5972SCameron Grant struct ac97_info *codec = mix_getdevinfo(m); 43639004e69SCameron Grant if (codec == NULL) 43739004e69SCameron Grant return -1; 438987e5972SCameron Grant for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 43939004e69SCameron Grant if ((src & (1 << i)) != 0) 44039004e69SCameron Grant break; 441987e5972SCameron Grant return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 442987e5972SCameron Grant } 443987e5972SCameron Grant 444987e5972SCameron Grant snd_mixer ac97_mixer = { 445987e5972SCameron Grant "AC97 mixer", 446987e5972SCameron Grant ac97mix_init, 44733dbf14aSCameron Grant ac97mix_uninit, 448987e5972SCameron Grant ac97mix_set, 449987e5972SCameron Grant ac97mix_setrecsrc, 450987e5972SCameron Grant }; 451987e5972SCameron Grant 452