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