/* * Copyright (c) 2002 Orion Hodson * Copyright (c) 1999,2002 Cameron Grant * Copyright 1997,1998 Luigi Rizzo. * * Derived from files in the Voxware 3.5 distribution, * Copyright by Hannu Savolainen 1994, under the same copyright * conditions. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include "mixer_if.h" SND_DECLARE_FILE("$FreeBSD$"); MALLOC_DEFINE(M_SB16MIX, "sb16mix", "sb16 mixer"); static const char *sb16_mixer_5bit[] = { "-62", "-60", "-58", "-56", "-54", "-52", "-50", "-48", "-46", "-44", "-42", "-40", "-38", "-36", "-34", "-32", "-30", "-28", "-26", "-24", "-22", "-20", "-18", "-16", "-14", "-12", "-10", "-8", "-6", "-4", "-2", "0", }; static const char *sb16_mixer_tone[] = { "-14", "-12", "-10", "-8", "-6", "-4", "-2", NULL, "0", "+2", "+4", "+6", "+8", "+10", "+12", "+14", }; static const char *sb16_mixer_speaker[] = { "-18", "-12", "-6", "0" }; static const char *sb16_mixer_gain[] = { "0", "+6", "+12", "+18" }; static const char *sb16_mixer_onoff[] = { "off", "on" }; static const char *sb16_mixer_mixer[] = { "mic", "cd-right", "cd-left", "line-right", "line-left", "synth-right", "synth-left", }; static int sb16_oss[] = { SOUND_MASK_MIC, SOUND_MASK_CD, SOUND_MASK_CD, SOUND_MASK_LINE, SOUND_MASK_LINE, SOUND_MASK_SYNTH, SOUND_MASK_SYNTH, }; struct sb16_mixer_info; struct sb16_mixer_control { const char *name; int reg; int bits; int offset; int stereo; int *bitfield; int oss_channel; const char **values; const char *postfix; const char *defaultvalue; const char *zerovalue; struct sb16_mixer_info *sbi; }; static const struct sb16_mixer_control sb16_mixer_controls[] = { { "out.master", 0x30, 5, 3, 1, NULL, SOUND_MIXER_VOLUME, sb16_mixer_5bit, "dB", "-14", "0" }, { "out.mixer", 0x3c, 5, 0, 0, sb16_oss, -1, sb16_mixer_mixer, NULL, "line", NULL }, { "out.gain", 0x41, 2, 6, 1, NULL, SOUND_MIXER_OGAIN, sb16_mixer_gain, "dB", "-14", "0" }, { "out.treble", 0x44, 4, 4, 1, NULL, SOUND_MIXER_TREBLE, sb16_mixer_tone, "dB", "-14", "0" }, { "out.bass", 0x46, 4, 4, 1, NULL, SOUND_MIXER_BASS, sb16_mixer_tone, "dB", "-14", "0" }, { "pcm", 0x32, 5, 3, 1, NULL, SOUND_MIXER_PCM, sb16_mixer_5bit, "dB", "-14", "0" }, { "synth", 0x34, 5, 3, 1, NULL, SOUND_MIXER_SYNTH, sb16_mixer_5bit, "dB", "-14", "0" }, { "cd", 0x36, 5, 3, 1, NULL, SOUND_MIXER_CD, sb16_mixer_5bit, "dB", "0", "0" }, { "line", 0x38, 5, 3, 1, NULL, SOUND_MIXER_LINE, sb16_mixer_5bit, "dB", "0", "0" }, { "mic", 0x3a, 5, 3, 0, NULL, SOUND_MIXER_MIC, sb16_mixer_5bit, "dB", "0", "0" }, { "beep", 0x3a, 2, 6, 0, NULL, SOUND_MIXER_SPEAKER, sb16_mixer_speaker, "dB", "0", "0" }, { "rec.mixer", 0x3c, 7, 0, 1, sb16_oss, SOUND_MIXER_RECSRC, sb16_mixer_mixer, NULL, "mic", NULL }, { "rec.gain", 0x3f, 2, 6, 1, NULL, SOUND_MIXER_IGAIN, sb16_mixer_gain, "dB", "-14", "0" }, { "rec.agc", 0x43, 1, 0, 0, NULL, -1, sb16_mixer_onoff, NULL, "off", NULL }, }; #define SB16_NUMCONTROLS (sizeof(sb16_mixer_controls) / sizeof(*sb16_mixer_controls)) struct sb16_mixer_info { KOBJ_FIELDS; /* sb16_mixer_class methods placeholder */ kobj_t obj_sb16; /* parent object that must implement read/write methods */ device_t dev; /* parent device */ /* codec read/write methods should use card local mutex */ struct sb16_mixer_control control[SB16_NUMCONTROLS]; }; static u_int8_t sb16_mixer_read(struct sb16_mixer_info *sbi, u_int8_t reg) { return SB16MIXER_READ(sbi->obj_sb16, reg); } static void sb16_mixer_write(struct sb16_mixer_info *sbi, u_int8_t reg, u_int8_t val) { SB16MIXER_WRITE(sbi->obj_sb16, reg, val); } /* ------------------------------------------------------------------------- */ /* Mixer construction and configuration methods */ static int sb16_mixer_set_left(void *ctrlinfo, int value) { struct sb16_mixer_control *chn = ctrlinfo; sb16_mixer_write(chn->sbi, chn->reg, value << chn->offset); return 0; } static int sb16_mixer_set_right(void *ctrlinfo, int value) { struct sb16_mixer_control *chn = ctrlinfo; sb16_mixer_write(chn->sbi, chn->reg + 1, value << chn->offset); return 0; } static int sb16_mixer_addchannel(struct mixer *mixer, struct sb16_mixer_control *chn) { enum mixercontrol_type type; struct mixercontrol *l, *r; struct mixergroup *g; int i, max, o; l = NULL; r = NULL; type = chn->bitfield? MIXCTRL_MULTI : MIXCTRL_SINGLE; if (chn->stereo) { g = mixer_group_new(mixer, chn->name, chn->oss_channel); if (g == NULL) return -1; l = mixer_control_new(mixer, g, "left", type, sb16_mixer_set_left, chn, chn->oss_channel); if (l == NULL) goto fail; r = mixer_control_new(mixer, g, "right", type, sb16_mixer_set_right, chn, chn->oss_channel); if (r == NULL) goto fail; } else { l = mixer_control_new(mixer, NULL, chn->name, type, sb16_mixer_set_left, chn, chn->oss_channel); if (l == NULL) goto fail; } max = chn->bitfield? chn->bits : (1 << chn->bits) - 1; for (i = 0; i <= max; i++) { if (chn->values[i] == NULL) continue; o = chn->bitfield? chn->bitfield[i] : i * 100 / max; if (mixer_control_addsetting(l, chn->values[i], chn->bitfield? 1 << i : i, o)) goto fail; if (r && mixer_control_addsetting(r, chn->values[i], chn->bitfield? 1 << i : i, o)) goto fail; } if (chn->defaultvalue != NULL) { if (mixer_control_setdefault(l, chn->defaultvalue)) goto fail; if (r && mixer_control_setdefault(r, chn->defaultvalue)) goto fail; } if (chn->zerovalue != NULL) { if (mixer_control_setzero(l, chn->zerovalue)) goto fail; if (r && mixer_control_setzero(r, chn->zerovalue)) goto fail; } if (chn->postfix != NULL) { if (mixer_control_setpostfix(l, chn->postfix)) goto fail; if (r && mixer_control_setpostfix(r, chn->postfix)) goto fail; } return 0; fail: return -1; } /* ------------------------------------------------------------------------- */ /* Exported mixer interface */ static int sb16_mixer_attach(struct sb16_mixer_info *sbi) { int i; for (i = 0; i < SB16_NUMCONTROLS; i++) { sbi->control[i] = sb16_mixer_controls[i]; sbi->control[i].sbi = sbi; } sb16_mixer_write(sbi, 0, 1); /* reset mixer */ return 0; } static int sb16_mixer_build(struct sb16_mixer_info *sbi, struct mixer *mixer) { struct sb16_mixer_control *chn; int i, err; for (i = err = 0; i < SB16_NUMCONTROLS; i++) { chn = &sbi->control[i]; err = sb16_mixer_addchannel(mixer, chn); if (err) { printf("sb16_mixer_addchannel(%s): %d\n", chn->name, err); break; } } return err; } static kobj_method_t sb16_mixer_methods[] = { KOBJMETHOD(mixer_attach, sb16_mixer_attach), KOBJMETHOD(mixer_build, sb16_mixer_build), { 0, 0 }, }; static DEFINE_CLASS(sb16_mixer, sb16_mixer_methods, sizeof(struct sb16_mixer_info)); struct mixer * sb16_createmixer(kobj_t obj_sb16, device_t dev) { struct sb16_mixer_info *sbi; struct mixer *mixer; sbi = (struct sb16_mixer_info *)kobj_create(&sb16_mixer_class, M_SB16MIX, M_WAITOK | M_ZERO); if (sbi == NULL) return NULL; sbi->obj_sb16 = obj_sb16; sbi->dev = dev; mixer = mixer_attach((kobj_t)sbi, dev); if (mixer == NULL) kobj_delete((kobj_t)sbi, M_SB16MIX); return mixer; }