xref: /freebsd/sys/dev/sound/pcm/mixer.c (revision b601c69bdbe8755d26570261d7fd4c02ee4eff74)
1 /*
2  * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <dev/sound/pcm/sound.h>
30 
31 static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
32 	[SOUND_MIXER_VOLUME]	= 75,
33 	[SOUND_MIXER_BASS]	= 50,
34 	[SOUND_MIXER_TREBLE]	= 50,
35 	[SOUND_MIXER_SYNTH]	= 75,
36 	[SOUND_MIXER_PCM]	= 75,
37 	[SOUND_MIXER_SPEAKER]	= 75,
38 	[SOUND_MIXER_LINE]	= 75,
39 	[SOUND_MIXER_MIC] 	= 0,
40 	[SOUND_MIXER_CD]	= 75,
41 	[SOUND_MIXER_LINE1]	= 75,
42 	[SOUND_MIXER_VIDEO]	= 75,
43 	[SOUND_MIXER_RECLEV]	= 0,
44 	[SOUND_MIXER_OGAIN]	= 50,
45 };
46 
47 int
48 mixer_init(snddev_info *d, snd_mixer *m, void *devinfo)
49 {
50 	if (d == NULL) return -1;
51 	d->mixer = *m;
52 	d->mixer.devinfo = devinfo;
53 	bzero(&d->mixer.level, sizeof d->mixer.level);
54 	if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) {
55 		int i;
56 		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
57 			u_int16_t v = snd_mixerdefaults[i];
58 			mixer_set(d, i, v | (v << 8));
59 		}
60 		mixer_setrecsrc(d, SOUND_MASK_MIC);
61 		return 0;
62 	} else return -1;
63 }
64 
65 int
66 mixer_reinit(snddev_info *d)
67 {
68 	int i;
69 	if (d == NULL) return -1;
70 	if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) {
71 		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
72 			mixer_set(d, i, d->mixer.level[i]);
73 		mixer_setrecsrc(d, d->mixer.recsrc);
74 		return 0;
75 	} else return -1;
76 }
77 
78 int
79 mixer_set(snddev_info *d, unsigned dev, unsigned lev)
80 {
81 	if (d == NULL || d->mixer.set == NULL) return -1;
82 	if ((dev < SOUND_MIXER_NRDEVICES) && (d->mixer.devs & (1 << dev))) {
83 		unsigned l = min((lev & 0x00ff), 100);
84 		unsigned r = min(((lev & 0xff00) >> 8), 100);
85 		int v = d->mixer.set(&d->mixer, dev, l, r);
86 		if (v >= 0) d->mixer.level[dev] = l | (r << 8);
87 		return 0;
88 	} else return -1;
89 }
90 
91 int
92 mixer_get(snddev_info *d, int dev)
93 {
94 	if (d == NULL) return -1;
95 	if (dev < SOUND_MIXER_NRDEVICES && (d->mixer.devs & (1 << dev)))
96 		return d->mixer.level[dev];
97 	else return -1;
98 }
99 
100 int
101 mixer_setrecsrc(snddev_info *d, u_int32_t src)
102 {
103 	if (d == NULL || d->mixer.setrecsrc == NULL) return -1;
104 	src &= d->mixer.recdevs;
105 	if (src == 0) src = SOUND_MASK_MIC;
106 	d->mixer.recsrc = d->mixer.setrecsrc(&d->mixer, src);
107 	return 0;
108 }
109 
110 int
111 mixer_getrecsrc(snddev_info *d)
112 {
113 	if (d == NULL) return -1;
114 	return d->mixer.recsrc;
115 }
116 
117 int
118 mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
119 {
120 	int ret, *arg_i = (int *)arg;
121 
122 	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
123 		int j = cmd & 0xff;
124 
125 		if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(d, *arg_i);
126 		else ret = mixer_set(d, j, *arg_i);
127 		return (ret == 0)? 0 : ENXIO;
128 	}
129 
130     	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
131 		int v = -1, j = cmd & 0xff;
132 
133 		switch (j) {
134     		case SOUND_MIXER_DEVMASK:
135     		case SOUND_MIXER_CAPS:
136     		case SOUND_MIXER_STEREODEVS:
137 			v = d->mixer.devs;
138 			break;
139 
140     		case SOUND_MIXER_RECMASK:
141 			v = d->mixer.recdevs;
142 			break;
143 
144     		case SOUND_MIXER_RECSRC:
145 			v = mixer_getrecsrc(d);
146 			break;
147 
148 		default:
149 			v = mixer_get(d, j);
150 		}
151 		*arg_i = v;
152 		return (v != -1)? 0 : ENXIO;
153 	}
154 	return ENXIO;
155 }
156 
157 void
158 mix_setdevs(snd_mixer *m, u_int32_t v)
159 {
160 	m->devs = v;
161 }
162 
163 void
164 mix_setrecdevs(snd_mixer *m, u_int32_t v)
165 {
166 	m->recdevs = v;
167 }
168 
169 u_int32_t
170 mix_getdevs(snd_mixer *m)
171 {
172 	return m->devs;
173 }
174 
175 u_int32_t
176 mix_getrecdevs(snd_mixer *m)
177 {
178 	return m->recdevs;
179 }
180 
181 void *
182 mix_getdevinfo(snd_mixer *m)
183 {
184 	return m->devinfo;
185 }
186 
187 /*
188  * The various mixers use a variety of bitmasks etc. The Voxware
189  * driver had a very nice technique to describe a mixer and interface
190  * to it. A table defines, for each channel, which register, bits,
191  * offset, polarity to use. This procedure creates the new value
192  * using the table and the old value.
193  */
194 
195 void
196 change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
197 {
198     	u_char mask;
199     	int shift;
200 
201     	DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
202 		"r %d p %d bit %d off %d\n",
203 		dev, chn, newval, *regval,
204 		(*t)[dev][chn].regno, (*t)[dev][chn].polarity,
205 		(*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
206 
207     	if ( (*t)[dev][chn].polarity == 1)	/* reverse */
208 		newval = 100 - newval ;
209 
210     	mask = (1 << (*t)[dev][chn].nbits) - 1;
211     	newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
212     	shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
213 
214     	*regval &= ~(mask << shift);        /* Filter out the previous value */
215     	*regval |= (newval & mask) << shift;        /* Set the new value */
216 }
217 
218