ac97.c revision 58384
150724Scg/* 250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 350724Scg * All rights reserved. 450724Scg * 550724Scg * Redistribution and use in source and binary forms, with or without 650724Scg * modification, are permitted provided that the following conditions 750724Scg * are met: 850724Scg * 1. Redistributions of source code must retain the above copyright 950724Scg * notice, this list of conditions and the following disclaimer. 1050724Scg * 2. Redistributions in binary form must reproduce the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer in the 1250724Scg * documentation and/or other materials provided with the distribution. 1350724Scg * 1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450724Scg * SUCH DAMAGE. 2550724Scg * 2650733Speter * $FreeBSD: head/sys/dev/sound/pcm/ac97.c 58384 2000-03-20 15:30:50Z cg $ 2750724Scg */ 2850724Scg 2953465Scg#include <dev/sound/pcm/sound.h> 3053465Scg#include <dev/sound/pcm/ac97.h> 3150724Scg 3250724Scgstruct ac97mixtable_entry { 3350724Scg int reg:8; 3450724Scg unsigned bits:4; 3550724Scg unsigned ofs:4; 3650724Scg unsigned stereo:1; 3750724Scg unsigned mute:1; 3850724Scg unsigned recidx:4; 3950724Scg unsigned mask:1; 4050724Scg}; 4150724Scg 4250724Scgstruct ac97_info { 4356249Scg device_t dev; 4458384Scg ac97_init *init; 4550724Scg ac97_read *read; 4650724Scg ac97_write *write; 4750724Scg void *devinfo; 4850724Scg char id[4]; 4950724Scg char rev; 5058384Scg unsigned caps, se, extcaps, extid, extstat; 5150724Scg struct ac97mixtable_entry mix[32]; 5250724Scg}; 5350724Scg 5450724Scgstruct ac97_codecid { 5550724Scg u_int32_t id; 5650724Scg char *name; 5750724Scg}; 5850724Scg 5950724Scgstatic const struct ac97mixtable_entry ac97mixtable_default[32] = { 6050724Scg [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 6150724Scg [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 6250724Scg [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 6350724Scg [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 6450724Scg [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 6550724Scg [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 6650724Scg [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 6750724Scg [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 6850724Scg [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 6950724Scg [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 7050724Scg [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 7150724Scg}; 7250724Scg 7350724Scgstatic const unsigned ac97mixdevs = 7450724Scg SOUND_MASK_VOLUME | 7550724Scg SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 7650724Scg SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 7750724Scg SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 7850724Scg 7950724Scgstatic const unsigned ac97recdevs = 8050724Scg SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 8150724Scg SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 8250724Scg 8350724Scgstatic struct ac97_codecid ac97codecid[] = { 8450724Scg { 0x414B4D00, "Asahi Kasei AK4540" }, 8550724Scg { 0x43525900, "Cirrus Logic CS4297" }, 8650724Scg { 0x83847600, "SigmaTel STAC????" }, 8750724Scg { 0x83847604, "SigmaTel STAC9701/3/4/5" }, 8850724Scg { 0x83847605, "SigmaTel STAC9704" }, 8950724Scg { 0x83847608, "SigmaTel STAC9708" }, 9050724Scg { 0x83847609, "SigmaTel STAC9721" }, 9150724Scg { 0, NULL } 9250724Scg}; 9350724Scg 9450724Scgstatic char *ac97enhancement[] = { 9550724Scg "", 9650724Scg "Analog Devices Phat Stereo", 9750724Scg "Creative Stereo Enhancement", 9850724Scg "National Semi 3D Stereo Enhancement", 9950724Scg "Yamaha Ymersion", 10050724Scg "BBE 3D Stereo Enhancement", 10150724Scg "Crystal Semi 3D Stereo Enhancement", 10250724Scg "Qsound QXpander", 10350724Scg "Spatializer 3D Stereo Enhancement", 10450724Scg "SRS 3D Stereo Enhancement", 10550724Scg "Platform Tech 3D Stereo Enhancement", 10650724Scg "AKM 3D Audio", 10750724Scg "Aureal Stereo Enhancement", 10850724Scg "Aztech 3D Enhancement", 10950724Scg "Binaura 3D Audio Enhancement", 11050724Scg "ESS Technology Stereo Enhancement", 11150724Scg "Harman International VMAx", 11250724Scg "Nvidea 3D Stereo Enhancement", 11350724Scg "Philips Incredible Sound", 11450724Scg "Texas Instruments 3D Stereo Enhancement", 11550724Scg "VLSI Technology 3D Stereo Enhancement", 11650724Scg "TriTech 3D Stereo Enhancement", 11750724Scg "Realtek 3D Stereo Enhancement", 11850724Scg "Samsung 3D Stereo Enhancement", 11950724Scg "Wolfson Microelectronics 3D Enhancement", 12050724Scg "Delta Integration 3D Enhancement", 12150724Scg "SigmaTel 3D Enhancement", 12250724Scg "Reserved 27", 12350724Scg "Rockwell 3D Stereo Enhancement", 12450724Scg "Reserved 29", 12550724Scg "Reserved 30", 12650724Scg "Reserved 31" 12750724Scg}; 12850724Scg 12950724Scgstatic char *ac97feature[] = { 13050724Scg "mic channel", 13150724Scg "reserved", 13250724Scg "tone", 13350724Scg "simulated stereo", 13450724Scg "headphone", 13550724Scg "bass boost", 13650724Scg "18 bit DAC", 13750724Scg "20 bit DAC", 13850724Scg "18 bit ADC", 13950724Scg "20 bit ADC" 14050724Scg}; 14150724Scg 14258384Scgstatic char *ac97extfeature[] = { 14358384Scg "variable rate PCM", 14458384Scg "double rate PCM", 14558384Scg "reserved 1", 14658384Scg "variable rate mic", 14758384Scg "reserved 2", 14858384Scg "reserved 3", 14958384Scg "center DAC", 15058384Scg "surround DAC", 15158384Scg "LFE DAC", 15258384Scg "AMAP", 15358384Scg "reserved 4", 15458384Scg "reserved 5", 15558384Scg "reserved 6", 15658384Scg "reserved 7", 15758384Scg}; 15858384Scg 15958384Scgstatic u_int16_t 16058384Scgrdcd(struct ac97_info *codec, int reg) 16158384Scg{ 16258384Scg return codec->read(codec->devinfo, reg); 16358384Scg} 16458384Scg 16558384Scgstatic void 16658384Scgwrcd(struct ac97_info *codec, int reg, u_int16_t val) 16758384Scg{ 16858384Scg codec->write(codec->devinfo, reg, val); 16958384Scg} 17058384Scg 17158384Scgint 17258384Scgac97_setrate(struct ac97_info *codec, int which, int rate) 17358384Scg{ 17458384Scg u_int16_t v; 17558384Scg 17658384Scg switch(which) { 17758384Scg case AC97_REGEXT_FDACRATE: 17858384Scg case AC97_REGEXT_SDACRATE: 17958384Scg case AC97_REGEXT_LDACRATE: 18058384Scg case AC97_REGEXT_LADCRATE: 18158384Scg case AC97_REGEXT_MADCRATE: 18258384Scg break; 18358384Scg 18458384Scg default: 18558384Scg return -1; 18658384Scg } 18758384Scg 18858384Scg if (rate != 0) { 18958384Scg v = rate; 19058384Scg if (codec->extstat & AC97_EXTCAP_DRA) 19158384Scg v >>= 1; 19258384Scg wrcd(codec, which, v); 19358384Scg } 19458384Scg v = rdcd(codec, which); 19558384Scg if (codec->extstat & AC97_EXTCAP_DRA) 19658384Scg v <<= 1; 19758384Scg return v; 19858384Scg} 19958384Scg 20058384Scgint 20158384Scgac97_setextmode(struct ac97_info *codec, u_int16_t mode) 20258384Scg{ 20358384Scg mode &= AC97_EXTCAPS; 20458384Scg if ((mode & ~codec->extcaps) != 0) 20558384Scg return -1; 20658384Scg wrcd(codec, AC97_REGEXT_STAT, mode); 20758384Scg codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 20858384Scg return (mode == codec->extstat)? 0 : -1; 20958384Scg} 21058384Scg 21150724Scgstatic int 21250724Scgac97_setrecsrc(struct ac97_info *codec, int channel) 21350724Scg{ 21450724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 21550724Scg if (e->recidx > 0) { 21650724Scg int val = e->recidx - 1; 21750724Scg val |= val << 8; 21858384Scg wrcd(codec, AC97_REG_RECSEL, val); 21950724Scg return 0; 22058384Scg } else 22158384Scg return -1; 22250724Scg} 22350724Scg 22450724Scgstatic int 22550724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 22650724Scg{ 22750724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 22850724Scg if (e->reg != 0) { 22953512Scg int max, val, reg = (e->reg >= 0)? e->reg : -e->reg; 23050724Scg 23158384Scg if (!e->stereo) 23258384Scg right = left; 23350724Scg if (e->reg > 0) { 23450724Scg left = 100 - left; 23550724Scg right = 100 - right; 23650724Scg } 23750724Scg 23850724Scg max = (1 << e->bits) - 1; 23950724Scg left = (left * max) / 100; 24050724Scg right = (right * max) / 100; 24150724Scg 24250724Scg val = (left << 8) | right; 24350724Scg 24450724Scg left = (left * 100) / max; 24550724Scg right = (right * 100) / max; 24650724Scg 24750724Scg if (e->reg > 0) { 24850724Scg left = 100 - left; 24950724Scg right = 100 - right; 25050724Scg } 25150724Scg 25250724Scg if (!e->stereo) { 25350724Scg val &= max; 25450724Scg val <<= e->ofs; 25550724Scg if (e->mask) { 25658384Scg int cur = rdcd(codec, e->reg); 25750724Scg val |= cur & ~(max << e->ofs); 25850724Scg } 25950724Scg } 26058384Scg if (left == 0 && right == 0 && e->mute == 1) 26158384Scg val = AC97_MUTE; 26258384Scg wrcd(codec, reg, val); 26350724Scg return left | (right << 8); 26458384Scg } else 26558384Scg return -1; 26650724Scg} 26750724Scg 26850724Scg#if 0 26950724Scgstatic int 27050724Scgac97_getmixer(struct ac97_info *codec, int channel) 27150724Scg{ 27250724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 27350724Scg if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 27450724Scg int max, val, volume; 27550724Scg 27650724Scg max = (1 << e->bits) - 1; 27758384Scg val = rdcd(code, e->reg); 27858384Scg if (val == AC97_MUTE && e->mute == 1) 27958384Scg volume = 0; 28050724Scg else { 28150724Scg if (e->stereo == 0) val >>= e->ofs; 28250724Scg val &= max; 28350724Scg volume = (val * 100) / max; 28450724Scg if (e->reg > 0) volume = 100 - volume; 28550724Scg } 28650724Scg return volume; 28758384Scg } else 28858384Scg return -1; 28950724Scg} 29050724Scg#endif 29150724Scg 29250724Scgstatic unsigned 29358384Scgac97_initmixer(struct ac97_info *codec) 29450724Scg{ 29550724Scg unsigned i, j; 29650724Scg u_int32_t id; 29750724Scg 29858384Scg for (i = 0; i < 32; i++) 29958384Scg codec->mix[i] = ac97mixtable_default[i]; 30050724Scg 30158384Scg if (codec->init) 30258384Scg codec->init(codec->devinfo); 30358384Scg wrcd(codec, AC97_REG_POWER, 0); 30458384Scg wrcd(codec, AC97_REG_RESET, 0); 30550724Scg DELAY(10000); 30650724Scg 30758384Scg i = rdcd(codec, AC97_REG_RESET); 30850724Scg codec->caps = i & 0x03ff; 30950724Scg codec->se = (i & 0x7c00) >> 10; 31050724Scg 31158384Scg i = rdcd(codec, AC97_REGEXT_ID); 31258384Scg codec->extcaps = i & 0x3fff; 31358384Scg codec->extid = (i & 0xc000) >> 14; 31458384Scg 31558384Scg codec->extstat = rdcd(codec, AC97_REGEXT_STAT) & AC97_EXTCAPS; 31658384Scg 31758384Scg id = (rdcd(codec, AC97_REG_ID1) << 16) | rdcd(codec, AC97_REG_ID2); 31850724Scg codec->rev = id & 0x000000ff; 31950724Scg 32058384Scg wrcd(codec, AC97_MIX_MASTER, 0x20); 32158384Scg if ((rdcd(codec, AC97_MIX_MASTER) & 0x20) == 0x20) 32250724Scg codec->mix[SOUND_MIXER_VOLUME].bits++; 32358384Scg wrcd(codec, AC97_MIX_MASTER, 0x00); 32450724Scg 32550724Scg if (bootverbose) { 32656249Scg device_printf(codec->dev, "ac97 codec id 0x%8x", id); 32758384Scg for (i = 0; ac97codecid[i].id; i++) 32858384Scg if (ac97codecid[i].id == id) 32958384Scg printf(" (%s)", ac97codecid[i].name); 33056249Scg printf("\n"); 33156249Scg device_printf(codec->dev, "ac97 codec features "); 33258384Scg for (i = j = 0; i < 10; i++) 33358384Scg if (codec->caps & (1 << i)) 33458384Scg printf("%s%s", j++? ", " : "", ac97feature[i]); 33558384Scg printf("%s%d bit master volume", j++? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 33658384Scg printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 33758384Scg 33858384Scg if (codec->extcaps != 0 || codec->extid) { 33958384Scg device_printf(codec->dev, "ac97 %s codec", 34058384Scg codec->extid? "secondary" : "primary"); 34158384Scg if (codec->extcaps) 34258384Scg printf(" extended features "); 34358384Scg for (i = j = 0; i < 14; i++) 34458384Scg if (codec->extcaps & (1 << i)) 34558384Scg printf("%s%s", j++? ", " : "", ac97extfeature[i]); 34658384Scg printf("\n"); 34750724Scg } 34850724Scg } 34950724Scg 35058384Scg if ((rdcd(codec, AC97_REG_POWER) & 2) == 0) 35156249Scg device_printf(codec->dev, "ac97 codec reports dac not ready\n"); 35250724Scg return 0; 35350724Scg} 35450724Scg 35550724Scgstruct ac97_info * 35658384Scgac97_create(device_t dev, void *devinfo, ac97_init *init, ac97_read *rd, ac97_write *wr) 35750724Scg{ 35850724Scg struct ac97_info *codec; 35950724Scg 36050724Scg codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 36150724Scg if (codec != NULL) { 36256249Scg codec->dev = dev; 36358384Scg codec->init = init; 36450724Scg codec->read = rd; 36550724Scg codec->write = wr; 36650724Scg codec->devinfo = devinfo; 36750724Scg } 36850724Scg return codec; 36950724Scg} 37050724Scg 37150724Scgstatic int 37250724Scgac97mix_init(snd_mixer *m) 37350724Scg{ 37450724Scg struct ac97_info *codec = mix_getdevinfo(m); 37558384Scg if (codec == NULL) 37658384Scg return -1; 37758384Scg ac97_initmixer(codec); 37850724Scg mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 37950724Scg mix_setrecdevs(m, ac97recdevs); 38050724Scg return 0; 38150724Scg} 38250724Scg 38350724Scgstatic int 38450724Scgac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 38550724Scg{ 38650724Scg struct ac97_info *codec = mix_getdevinfo(m); 38758384Scg if (codec == NULL) 38858384Scg return -1; 38950724Scg return ac97_setmixer(codec, dev, left, right); 39050724Scg} 39150724Scg 39250724Scgstatic int 39350724Scgac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 39450724Scg{ 39550724Scg int i; 39650724Scg struct ac97_info *codec = mix_getdevinfo(m); 39758384Scg if (codec == NULL) 39858384Scg return -1; 39950724Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 40058384Scg if ((src & (1 << i)) != 0) 40158384Scg break; 40250724Scg return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 40350724Scg} 40450724Scg 40550724Scgsnd_mixer ac97_mixer = { 40650724Scg "AC97 mixer", 40750724Scg ac97mix_init, 40850724Scg ac97mix_set, 40950724Scg ac97mix_setrecsrc, 41050724Scg}; 41150724Scg 412