ac97.c revision 53465
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 53465 1999-11-20 16:50:33Z cg $ 2750724Scg */ 2850724Scg 2953465Scg#include <dev/sound/pcm/sound.h> 3053465Scg#include <dev/sound/pcm/ac97.h> 3150724Scg 3250724Scg#define AC97_MUTE 0x8000 3350724Scg 3450724Scg#define AC97_REG_RESET 0x00 3550724Scg#define AC97_MIX_MASTER 0x02 3650724Scg#define AC97_MIX_PHONES 0x04 3750724Scg#define AC97_MIX_MONO 0x06 3850724Scg#define AC97_MIX_TONE 0x08 3950724Scg#define AC97_MIX_BEEP 0x0a 4050724Scg#define AC97_MIX_PHONE 0x0c 4150724Scg#define AC97_MIX_MIC 0x0e 4250724Scg#define AC97_MIX_LINE 0x10 4350724Scg#define AC97_MIX_CD 0x12 4450724Scg#define AC97_MIX_VIDEO 0x14 4550724Scg#define AC97_MIX_AUX 0x16 4650724Scg#define AC97_MIX_PCM 0x18 4750724Scg#define AC97_REG_RECSEL 0x1a 4850724Scg#define AC97_MIX_RGAIN 0x1c 4950724Scg#define AC97_MIX_MGAIN 0x1e 5050724Scg#define AC97_REG_GEN 0x20 5150724Scg#define AC97_REG_3D 0x22 5250724Scg#define AC97_REG_POWER 0x26 5350724Scg#define AC97_REG_ID1 0x7c 5450724Scg#define AC97_REG_ID2 0x7e 5550724Scg 5650724Scgstruct ac97mixtable_entry { 5750724Scg int reg:8; 5850724Scg unsigned bits:4; 5950724Scg unsigned ofs:4; 6050724Scg unsigned stereo:1; 6150724Scg unsigned mute:1; 6250724Scg unsigned recidx:4; 6350724Scg unsigned mask:1; 6450724Scg}; 6550724Scg 6650724Scgstruct ac97_info { 6750724Scg ac97_read *read; 6850724Scg ac97_write *write; 6950724Scg void *devinfo; 7050724Scg char id[4]; 7150724Scg char rev; 7250724Scg unsigned caps, se; 7350724Scg struct ac97mixtable_entry mix[32]; 7450724Scg}; 7550724Scg 7650724Scgstruct ac97_codecid { 7750724Scg u_int32_t id; 7850724Scg char *name; 7950724Scg}; 8050724Scg 8150724Scgstatic const struct ac97mixtable_entry ac97mixtable_default[32] = { 8250724Scg [SOUND_MIXER_VOLUME] = { AC97_MIX_MASTER, 5, 0, 1, 1, 6, 0 }, 8350724Scg [SOUND_MIXER_BASS] = { AC97_MIX_TONE, 4, 8, 0, 0, 0, 1 }, 8450724Scg [SOUND_MIXER_TREBLE] = { AC97_MIX_TONE, 4, 0, 0, 0, 0, 1 }, 8550724Scg [SOUND_MIXER_PCM] = { AC97_MIX_PCM, 5, 0, 1, 1, 0, 0 }, 8650724Scg [SOUND_MIXER_SPEAKER] = { AC97_MIX_BEEP, 4, 1, 0, 1, 0, 0 }, 8750724Scg [SOUND_MIXER_LINE] = { AC97_MIX_LINE, 5, 0, 1, 1, 5, 0 }, 8850724Scg [SOUND_MIXER_MIC] = { AC97_MIX_MIC, 5, 0, 0, 1, 1, 0 }, 8950724Scg [SOUND_MIXER_CD] = { AC97_MIX_CD, 5, 0, 1, 1, 2, 0 }, 9050724Scg [SOUND_MIXER_LINE1] = { AC97_MIX_AUX, 5, 0, 1, 1, 4, 0 }, 9150724Scg [SOUND_MIXER_VIDEO] = { AC97_MIX_VIDEO, 5, 0, 1, 1, 3, 0 }, 9250724Scg [SOUND_MIXER_RECLEV] = { -AC97_MIX_RGAIN, 4, 0, 1, 1, 0, 0 } 9350724Scg}; 9450724Scg 9550724Scgstatic const unsigned ac97mixdevs = 9650724Scg SOUND_MASK_VOLUME | 9750724Scg SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | 9850724Scg SOUND_MASK_MIC | SOUND_MASK_CD | SOUND_MASK_LINE1 | 9950724Scg SOUND_MASK_VIDEO | SOUND_MASK_RECLEV; 10050724Scg 10150724Scgstatic const unsigned ac97recdevs = 10250724Scg SOUND_MASK_VOLUME | SOUND_MASK_LINE | SOUND_MASK_MIC | 10350724Scg SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_VIDEO; 10450724Scg 10550724Scgstatic struct ac97_codecid ac97codecid[] = { 10650724Scg { 0x414B4D00, "Asahi Kasei AK4540" }, 10750724Scg { 0x43525900, "Cirrus Logic CS4297" }, 10850724Scg { 0x83847600, "SigmaTel STAC????" }, 10950724Scg { 0x83847604, "SigmaTel STAC9701/3/4/5" }, 11050724Scg { 0x83847605, "SigmaTel STAC9704" }, 11150724Scg { 0x83847608, "SigmaTel STAC9708" }, 11250724Scg { 0x83847609, "SigmaTel STAC9721" }, 11350724Scg { 0, NULL } 11450724Scg}; 11550724Scg 11650724Scgstatic char *ac97enhancement[] = { 11750724Scg "", 11850724Scg "Analog Devices Phat Stereo", 11950724Scg "Creative Stereo Enhancement", 12050724Scg "National Semi 3D Stereo Enhancement", 12150724Scg "Yamaha Ymersion", 12250724Scg "BBE 3D Stereo Enhancement", 12350724Scg "Crystal Semi 3D Stereo Enhancement", 12450724Scg "Qsound QXpander", 12550724Scg "Spatializer 3D Stereo Enhancement", 12650724Scg "SRS 3D Stereo Enhancement", 12750724Scg "Platform Tech 3D Stereo Enhancement", 12850724Scg "AKM 3D Audio", 12950724Scg "Aureal Stereo Enhancement", 13050724Scg "Aztech 3D Enhancement", 13150724Scg "Binaura 3D Audio Enhancement", 13250724Scg "ESS Technology Stereo Enhancement", 13350724Scg "Harman International VMAx", 13450724Scg "Nvidea 3D Stereo Enhancement", 13550724Scg "Philips Incredible Sound", 13650724Scg "Texas Instruments 3D Stereo Enhancement", 13750724Scg "VLSI Technology 3D Stereo Enhancement", 13850724Scg "TriTech 3D Stereo Enhancement", 13950724Scg "Realtek 3D Stereo Enhancement", 14050724Scg "Samsung 3D Stereo Enhancement", 14150724Scg "Wolfson Microelectronics 3D Enhancement", 14250724Scg "Delta Integration 3D Enhancement", 14350724Scg "SigmaTel 3D Enhancement", 14450724Scg "Reserved 27", 14550724Scg "Rockwell 3D Stereo Enhancement", 14650724Scg "Reserved 29", 14750724Scg "Reserved 30", 14850724Scg "Reserved 31" 14950724Scg}; 15050724Scg 15150724Scgstatic char *ac97feature[] = { 15250724Scg "mic channel", 15350724Scg "reserved", 15450724Scg "tone", 15550724Scg "simulated stereo", 15650724Scg "headphone", 15750724Scg "bass boost", 15850724Scg "18 bit DAC", 15950724Scg "20 bit DAC", 16050724Scg "18 bit ADC", 16150724Scg "20 bit ADC" 16250724Scg}; 16350724Scg 16450724Scgstatic int 16550724Scgac97_setrecsrc(struct ac97_info *codec, int channel) 16650724Scg{ 16750724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 16850724Scg if (e->recidx > 0) { 16950724Scg int val = e->recidx - 1; 17050724Scg val |= val << 8; 17150724Scg codec->write(codec->devinfo, AC97_REG_RECSEL, val); 17250724Scg return 0; 17350724Scg } else return -1; 17450724Scg} 17550724Scg 17650724Scgstatic int 17750724Scgac97_setmixer(struct ac97_info *codec, unsigned channel, unsigned left, unsigned right) 17850724Scg{ 17950724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 18050724Scg if (e->reg != 0) { 18150724Scg int max, val; 18250724Scg 18350724Scg if (!e->stereo) right = left; 18450724Scg if (e->reg > 0) { 18550724Scg left = 100 - left; 18650724Scg right = 100 - right; 18750724Scg } 18850724Scg 18950724Scg max = (1 << e->bits) - 1; 19050724Scg left = (left * max) / 100; 19150724Scg right = (right * max) / 100; 19250724Scg 19350724Scg val = (left << 8) | right; 19450724Scg 19550724Scg left = (left * 100) / max; 19650724Scg right = (right * 100) / max; 19750724Scg 19850724Scg if (e->reg > 0) { 19950724Scg left = 100 - left; 20050724Scg right = 100 - right; 20150724Scg } 20250724Scg 20350724Scg if (!e->stereo) { 20450724Scg val &= max; 20550724Scg val <<= e->ofs; 20650724Scg if (e->mask) { 20750724Scg int cur = codec->read(codec->devinfo, e->reg); 20850724Scg val |= cur & ~(max << e->ofs); 20950724Scg } 21050724Scg } 21150724Scg if (left == 0 && right == 0 && e->mute == 1) val = AC97_MUTE; 21250724Scg codec->write(codec->devinfo, abs(e->reg), val); 21350724Scg return left | (right << 8); 21450724Scg } else return -1; 21550724Scg} 21650724Scg 21750724Scg#if 0 21850724Scgstatic int 21950724Scgac97_getmixer(struct ac97_info *codec, int channel) 22050724Scg{ 22150724Scg struct ac97mixtable_entry *e = &codec->mix[channel]; 22250724Scg if (channel < SOUND_MIXER_NRDEVICES && e->reg != 0) { 22350724Scg int max, val, volume; 22450724Scg 22550724Scg max = (1 << e->bits) - 1; 22650724Scg val = codec->read(codec->devinfo, e->reg); 22750724Scg if (val == AC97_MUTE && e->mute == 1) volume = 0; 22850724Scg else { 22950724Scg if (e->stereo == 0) val >>= e->ofs; 23050724Scg val &= max; 23150724Scg volume = (val * 100) / max; 23250724Scg if (e->reg > 0) volume = 100 - volume; 23350724Scg } 23450724Scg return volume; 23550724Scg } else return -1; 23650724Scg} 23750724Scg#endif 23850724Scg 23950724Scgstatic unsigned 24050724Scgac97_init(struct ac97_info *codec) 24150724Scg{ 24250724Scg unsigned i, j; 24350724Scg u_int32_t id; 24450724Scg 24550724Scg for (i = 0; i < 32; i++) codec->mix[i] = ac97mixtable_default[i]; 24650724Scg 24750724Scg codec->write(codec->devinfo, AC97_REG_POWER, 0); 24850724Scg codec->write(codec->devinfo, AC97_REG_RESET, 0); 24950724Scg DELAY(10000); 25050724Scg 25150724Scg i = codec->read(codec->devinfo, AC97_REG_RESET); 25250724Scg codec->caps = i & 0x03ff; 25350724Scg codec->se = (i & 0x7c00) >> 10; 25450724Scg 25550724Scg id = (codec->read(codec->devinfo, AC97_REG_ID1) << 16) | 25650724Scg codec->read(codec->devinfo, AC97_REG_ID2); 25750724Scg codec->rev = id & 0x000000ff; 25850724Scg 25950724Scg codec->write(codec->devinfo, AC97_MIX_MASTER, 0x20); 26050724Scg if ((codec->read(codec->devinfo, AC97_MIX_MASTER) & 0x20) == 0x20) 26150724Scg codec->mix[SOUND_MIXER_VOLUME].bits++; 26250724Scg codec->write(codec->devinfo, AC97_MIX_MASTER, 0x00); 26350724Scg 26450724Scg if (bootverbose) { 26550724Scg printf("ac97: codec id 0x%8x", id); 26650724Scg for (i = 0; ac97codecid[i].id; i++) { 26750724Scg if (ac97codecid[i].id == id) printf(" (%s)", ac97codecid[i].name); 26850724Scg } 26950724Scg printf("\nac97: codec features "); 27050724Scg for (i = j = 0; i < 10; i++) { 27150724Scg if (codec->caps & (1 << i)) { 27250724Scg printf("%s%s", j? ", " : "", ac97feature[i]); 27350724Scg j++; 27450724Scg } 27550724Scg } 27650724Scg printf("%s%d bit master volume", j? ", " : "", codec->mix[SOUND_MIXER_VOLUME].bits); 27750724Scg printf("%s%s\n", j? ", " : "", ac97enhancement[codec->se]); 27850724Scg } 27950724Scg 28050724Scg if ((codec->read(codec->devinfo, AC97_REG_POWER) & 2) == 0) 28150724Scg printf("ac97: dac not ready\n"); 28250724Scg return 0; 28350724Scg} 28450724Scg 28550724Scgstruct ac97_info * 28650724Scgac97_create(void *devinfo, ac97_read *rd, ac97_write *wr) 28750724Scg{ 28850724Scg struct ac97_info *codec; 28950724Scg 29050724Scg codec = (struct ac97_info *)malloc(sizeof *codec, M_DEVBUF, M_NOWAIT); 29150724Scg if (codec != NULL) { 29250724Scg codec->read = rd; 29350724Scg codec->write = wr; 29450724Scg codec->devinfo = devinfo; 29550724Scg } 29650724Scg return codec; 29750724Scg} 29850724Scg 29950724Scgstatic int 30050724Scgac97mix_init(snd_mixer *m) 30150724Scg{ 30250724Scg struct ac97_info *codec = mix_getdevinfo(m); 30350724Scg if (codec == NULL) return -1; 30450724Scg ac97_init(codec); 30550724Scg mix_setdevs(m, ac97mixdevs | ((codec->caps & 4)? SOUND_MASK_BASS | SOUND_MASK_TREBLE : 0)); 30650724Scg mix_setrecdevs(m, ac97recdevs); 30750724Scg return 0; 30850724Scg} 30950724Scg 31050724Scgstatic int 31150724Scgac97mix_set(snd_mixer *m, unsigned dev, unsigned left, unsigned right) 31250724Scg{ 31350724Scg struct ac97_info *codec = mix_getdevinfo(m); 31450724Scg if (codec == NULL) return -1; 31550724Scg return ac97_setmixer(codec, dev, left, right); 31650724Scg} 31750724Scg 31850724Scgstatic int 31950724Scgac97mix_setrecsrc(snd_mixer *m, u_int32_t src) 32050724Scg{ 32150724Scg int i; 32250724Scg struct ac97_info *codec = mix_getdevinfo(m); 32350724Scg if (codec == NULL) return -1; 32450724Scg for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) 32550724Scg if ((src & (1 << i)) != 0) break; 32650724Scg return (ac97_setrecsrc(codec, i) == 0)? 1 << i : -1; 32750724Scg} 32850724Scg 32950724Scgsnd_mixer ac97_mixer = { 33050724Scg "AC97 mixer", 33150724Scg ac97mix_init, 33250724Scg ac97mix_set, 33350724Scg ac97mix_setrecsrc, 33450724Scg}; 33550724Scg 336