xref: /freebsd/sys/dev/sound/pcm/mixer.c (revision 1d66272a85cde1c8a69c58f4b5dd649babd6eca6)
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 #include "mixer_if.h"
32 
33 MALLOC_DEFINE(M_MIXER, "mixer", "mixer");
34 
35 static u_int16_t snd_mixerdefaults[SOUND_MIXER_NRDEVICES] = {
36 	[SOUND_MIXER_VOLUME]	= 75,
37 	[SOUND_MIXER_BASS]	= 50,
38 	[SOUND_MIXER_TREBLE]	= 50,
39 	[SOUND_MIXER_SYNTH]	= 75,
40 	[SOUND_MIXER_PCM]	= 75,
41 	[SOUND_MIXER_SPEAKER]	= 75,
42 	[SOUND_MIXER_LINE]	= 75,
43 	[SOUND_MIXER_MIC] 	= 0,
44 	[SOUND_MIXER_CD]	= 75,
45 	[SOUND_MIXER_LINE1]	= 75,
46 	[SOUND_MIXER_VIDEO]	= 75,
47 	[SOUND_MIXER_RECLEV]	= 0,
48 	[SOUND_MIXER_OGAIN]	= 50,
49 };
50 
51 static char* snd_mixernames[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
52 
53 static int
54 mixer_lookup(char *devname)
55 {
56 	int i;
57 
58 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
59 		if (strncmp(devname, snd_mixernames[i],
60 		    strlen(snd_mixernames[i])) == 0)
61 			return i;
62 	return -1;
63 }
64 
65 static int
66 mixer_set(snd_mixer *mixer, unsigned dev, unsigned lev)
67 {
68 	unsigned l, r;
69 	int v;
70 
71 	if ((dev >= SOUND_MIXER_NRDEVICES) || (0 == (mixer->devs & (1 << dev))))
72 		return -1;
73 
74 	l = min((lev & 0x00ff), 100);
75 	r = min(((lev & 0xff00) >> 8), 100);
76 
77 	v = MIXER_SET(mixer, dev, l, r);
78 	if (v < 0)
79 		return -1;
80 
81 	mixer->level[dev] = l | (r << 8);
82 	return 0;
83 }
84 
85 static int
86 mixer_get(snd_mixer *mixer, int dev)
87 {
88 	if ((dev < SOUND_MIXER_NRDEVICES) && (mixer->devs & (1 << dev)))
89 		return mixer->level[dev];
90 	else return -1;
91 }
92 
93 static int
94 mixer_setrecsrc(snd_mixer *mixer, u_int32_t src)
95 {
96 	src &= mixer->recdevs;
97 	if (src == 0)
98 		src = SOUND_MASK_MIC;
99 	mixer->recsrc = MIXER_SETRECSRC(mixer, src);
100 	return 0;
101 }
102 
103 static int
104 mixer_getrecsrc(snd_mixer *mixer)
105 {
106 	return mixer->recsrc;
107 }
108 
109 void
110 mix_setdevs(snd_mixer *m, u_int32_t v)
111 {
112 	m->devs = v;
113 }
114 
115 void
116 mix_setrecdevs(snd_mixer *m, u_int32_t v)
117 {
118 	m->recdevs = v;
119 }
120 
121 u_int32_t
122 mix_getdevs(snd_mixer *m)
123 {
124 	return m->devs;
125 }
126 
127 u_int32_t
128 mix_getrecdevs(snd_mixer *m)
129 {
130 	return m->recdevs;
131 }
132 
133 void *
134 mix_getdevinfo(snd_mixer *m)
135 {
136 	return m->devinfo;
137 }
138 
139 int
140 mixer_busy(snd_mixer *m, int busy)
141 {
142 	m->busy = busy;
143 	return 0;
144 }
145 
146 int
147 mixer_isbusy(snd_mixer *m)
148 {
149 	return m->busy;
150 }
151 
152 int
153 mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
154 {
155     	snddev_info *d;
156 	snd_mixer *m;
157 	u_int16_t v;
158 	int i;
159 
160 	d = device_get_softc(dev);
161 	m = (snd_mixer *)kobj_create(cls, M_MIXER, M_WAITOK | M_ZERO);
162 
163 	m->name = cls->name;
164 	m->devinfo = devinfo;
165 
166 	if (MIXER_INIT(m))
167 		goto bad;
168 
169 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
170 		v = snd_mixerdefaults[i];
171 		mixer_set(m, i, v | (v << 8));
172 	}
173 
174 	mixer_setrecsrc(m, SOUND_MASK_MIC);
175 
176 	d->mixer = m;
177 
178 	return 0;
179 
180 bad:	kobj_delete((kobj_t)m, M_MIXER);
181 	return -1;
182 }
183 
184 int
185 mixer_uninit(device_t dev)
186 {
187 	int i;
188     	snddev_info *d;
189 	snd_mixer *m;
190 
191 	d = device_get_softc(dev);
192 	m = d->mixer;
193 
194 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
195 		mixer_set(m, i, 0);
196 
197 	mixer_setrecsrc(m, SOUND_MASK_MIC);
198 
199 	MIXER_UNINIT(m);
200 
201 	kobj_delete((kobj_t)m, M_MIXER);
202 	d->mixer = NULL;
203 
204 	return 0;
205 }
206 
207 int
208 mixer_reinit(device_t dev)
209 {
210 	int i;
211     	snddev_info *d;
212 	snd_mixer *m;
213 
214 	d = device_get_softc(dev);
215 	m = d->mixer;
216 
217 	i = MIXER_REINIT(m);
218 	if (i)
219 		return i;
220 
221 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
222 		mixer_set(m, i, m->level[i]);
223 
224 	mixer_setrecsrc(m, m->recsrc);
225 
226 	return 0;
227 }
228 
229 int
230 mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
231 {
232 	int ret, *arg_i = (int *)arg;
233 	int v = -1, j = cmd & 0xff;
234 	snd_mixer *m;
235 
236 	m = d->mixer;
237 
238 	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
239 		if (j == SOUND_MIXER_RECSRC)
240 			ret = mixer_setrecsrc(m, *arg_i);
241 		else
242 			ret = mixer_set(m, j, *arg_i);
243 		return (ret == 0)? 0 : ENXIO;
244 	}
245 
246     	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
247 		switch (j) {
248     		case SOUND_MIXER_DEVMASK:
249     		case SOUND_MIXER_CAPS:
250     		case SOUND_MIXER_STEREODEVS:
251 			v = mix_getdevs(m);
252 			break;
253 
254     		case SOUND_MIXER_RECMASK:
255 			v = mix_getrecdevs(m);
256 			break;
257 
258     		case SOUND_MIXER_RECSRC:
259 			v = mixer_getrecsrc(m);
260 			break;
261 
262 		default:
263 			v = mixer_get(m, j);
264 		}
265 		*arg_i = v;
266 		return (v != -1)? 0 : ENXIO;
267 	}
268 	return ENXIO;
269 }
270 
271 static int
272 sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS)
273 {
274 	char devname[32];
275 	int error, dev;
276 	snd_mixer *m;
277 
278 	m = oidp->oid_arg1;
279 	strncpy(devname, snd_mixernames[m->hwvol_mixer], sizeof(devname));
280 	error = sysctl_handle_string(oidp, &devname[0], sizeof(devname), req);
281 	if (error == 0 && req->newptr != NULL) {
282 		dev = mixer_lookup(devname);
283 		if (dev == -1)
284 			return EINVAL;
285 		else
286 			m->hwvol_mixer = dev;
287 	}
288 	return error;
289 }
290 
291 int
292 mixer_hwinit(device_t dev)
293 {
294     	snddev_info *d;
295 	snd_mixer *m;
296 
297 	d = device_get_softc(dev);
298 	m = d->mixer;
299 	m->hwvol_mixer = SOUND_MIXER_VOLUME;
300 	m->hwvol_step = 5;
301 	SYSCTL_ADD_INT(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
302             OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, "");
303 	SYSCTL_ADD_PROC(&d->sysctl_tree, SYSCTL_CHILDREN(d->sysctl_tree_top),
304             OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0,
305 	    sysctl_hw_snd_hwvol_mixer, "A", "")
306 	return 0;
307 }
308 
309 void
310 mixer_hwmute(device_t dev)
311 {
312     	snddev_info *d;
313 	snd_mixer *m;
314 
315 	d = device_get_softc(dev);
316 	m = d->mixer;
317 	if (m->muted) {
318 		m->muted = 0;
319 		mixer_set(m, m->hwvol_mixer, m->mute_level);
320 	} else {
321 		m->muted++;
322 		m->mute_level = mixer_get(m, m->hwvol_mixer);
323 		mixer_set(m, m->hwvol_mixer, 0);
324 	}
325 }
326 
327 void
328 mixer_hwstep(device_t dev, int left_step, int right_step)
329 {
330     	snddev_info *d;
331 	snd_mixer *m;
332 	int level, left, right;
333 
334 	d = device_get_softc(dev);
335 	m = d->mixer;
336 	level = mixer_get(m, m->hwvol_mixer);
337 	if (level != -1) {
338 		left = level & 0xff;
339 		right = level >> 8;
340 		left += left_step * m->hwvol_step;
341 		if (left < 0)
342 			left = 0;
343 		right += right_step * m->hwvol_step;
344 		if (right < 0)
345 			right = 0;
346 		mixer_set(m, m->hwvol_mixer, left | right << 8);
347 	}
348 }
349 
350 /*
351  * The various mixers use a variety of bitmasks etc. The Voxware
352  * driver had a very nice technique to describe a mixer and interface
353  * to it. A table defines, for each channel, which register, bits,
354  * offset, polarity to use. This procedure creates the new value
355  * using the table and the old value.
356  */
357 
358 void
359 change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
360 {
361     	u_char mask;
362     	int shift;
363 
364     	DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
365 		"r %d p %d bit %d off %d\n",
366 		dev, chn, newval, *regval,
367 		(*t)[dev][chn].regno, (*t)[dev][chn].polarity,
368 		(*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
369 
370     	if ( (*t)[dev][chn].polarity == 1)	/* reverse */
371 		newval = 100 - newval ;
372 
373     	mask = (1 << (*t)[dev][chn].nbits) - 1;
374     	newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
375     	shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
376 
377     	*regval &= ~(mask << shift);        /* Filter out the previous value */
378     	*regval |= (newval & mask) << shift;        /* Set the new value */
379 }
380 
381