xref: /freebsd/sys/dev/sound/pcm/mixer.c (revision 77a0943ded95b9e6438f7db70c4a28e4d93946d4)
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 static int
48 mixer_set(snddev_info *d, unsigned dev, unsigned lev)
49 {
50 	if (d == NULL || d->mixer.set == NULL) return -1;
51 	if ((dev < SOUND_MIXER_NRDEVICES) && (d->mixer.devs & (1 << dev))) {
52 		unsigned l = min((lev & 0x00ff), 100);
53 		unsigned r = min(((lev & 0xff00) >> 8), 100);
54 		int v = d->mixer.set(&d->mixer, dev, l, r);
55 		if (v >= 0) d->mixer.level[dev] = l | (r << 8);
56 		return 0;
57 	} else return -1;
58 }
59 
60 static int
61 mixer_get(snddev_info *d, int dev)
62 {
63 	if (d == NULL) return -1;
64 	if (dev < SOUND_MIXER_NRDEVICES && (d->mixer.devs & (1 << dev)))
65 		return d->mixer.level[dev];
66 	else return -1;
67 }
68 
69 static int
70 mixer_setrecsrc(snddev_info *d, u_int32_t src)
71 {
72 	if (d == NULL || d->mixer.setrecsrc == NULL) return -1;
73 	src &= d->mixer.recdevs;
74 	if (src == 0) src = SOUND_MASK_MIC;
75 	d->mixer.recsrc = d->mixer.setrecsrc(&d->mixer, src);
76 	return 0;
77 }
78 
79 static int
80 mixer_getrecsrc(snddev_info *d)
81 {
82 	if (d == NULL) return -1;
83 	return d->mixer.recsrc;
84 }
85 
86 int
87 mixer_init(device_t dev, snd_mixer *m, void *devinfo)
88 {
89     	snddev_info *d = device_get_softc(dev);
90 	if (d == NULL) return -1;
91 	d->mixer = *m;
92 	d->mixer.devinfo = devinfo;
93 	bzero(&d->mixer.level, sizeof d->mixer.level);
94 	if (d->mixer.init != NULL && d->mixer.init(&d->mixer) == 0) {
95 		int i;
96 		for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) {
97 			u_int16_t v = snd_mixerdefaults[i];
98 			mixer_set(d, i, v | (v << 8));
99 		}
100 		mixer_setrecsrc(d, SOUND_MASK_MIC);
101 		return 0;
102 	} else return -1;
103 }
104 
105 int
106 mixer_uninit(device_t dev)
107 {
108 	int i;
109     	snddev_info *d = device_get_softc(dev);
110 	if (d == NULL) return -1;
111 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
112 		mixer_set(d, i, 0);
113 	mixer_setrecsrc(d, SOUND_MASK_MIC);
114 	if (d->mixer.uninit != NULL) d->mixer.uninit(&d->mixer);
115 	return 0;
116 }
117 
118 int
119 mixer_reinit(device_t dev)
120 {
121 	int i;
122     	snddev_info *d = device_get_softc(dev);
123 	if (d == NULL) return -1;
124 
125 	if (d->mixer.reinit != NULL)
126 		i = d->mixer.reinit(&d->mixer);
127 	else if (d->mixer.init != NULL)
128 		i = d->mixer.init(&d->mixer);
129 	else
130 		i = 0;
131 	if (i)
132 		return i;
133 	for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
134 		mixer_set(d, i, d->mixer.level[i]);
135 	mixer_setrecsrc(d, d->mixer.recsrc);
136 	return 0;
137 }
138 
139 int
140 mixer_ioctl(snddev_info *d, u_long cmd, caddr_t arg)
141 {
142 	int ret, *arg_i = (int *)arg;
143 
144 	if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) {
145 		int j = cmd & 0xff;
146 
147 		if (j == SOUND_MIXER_RECSRC) ret = mixer_setrecsrc(d, *arg_i);
148 		else ret = mixer_set(d, j, *arg_i);
149 		return (ret == 0)? 0 : ENXIO;
150 	}
151 
152     	if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) {
153 		int v = -1, j = cmd & 0xff;
154 
155 		switch (j) {
156     		case SOUND_MIXER_DEVMASK:
157     		case SOUND_MIXER_CAPS:
158     		case SOUND_MIXER_STEREODEVS:
159 			v = d->mixer.devs;
160 			break;
161 
162     		case SOUND_MIXER_RECMASK:
163 			v = d->mixer.recdevs;
164 			break;
165 
166     		case SOUND_MIXER_RECSRC:
167 			v = mixer_getrecsrc(d);
168 			break;
169 
170 		default:
171 			v = mixer_get(d, j);
172 		}
173 		*arg_i = v;
174 		return (v != -1)? 0 : ENXIO;
175 	}
176 	return ENXIO;
177 }
178 
179 int
180 mixer_busy(snddev_info *d, int busy)
181 {
182 	if (d == NULL) return -1;
183 	d->mixer.busy = busy;
184 	return 0;
185 }
186 
187 int
188 mixer_isbusy(snddev_info *d)
189 {
190 	if (d == NULL) return -1;
191 	return d->mixer.busy;
192 }
193 
194 void
195 mix_setdevs(snd_mixer *m, u_int32_t v)
196 {
197 	m->devs = v;
198 }
199 
200 void
201 mix_setrecdevs(snd_mixer *m, u_int32_t v)
202 {
203 	m->recdevs = v;
204 }
205 
206 u_int32_t
207 mix_getdevs(snd_mixer *m)
208 {
209 	return m->devs;
210 }
211 
212 u_int32_t
213 mix_getrecdevs(snd_mixer *m)
214 {
215 	return m->recdevs;
216 }
217 
218 void *
219 mix_getdevinfo(snd_mixer *m)
220 {
221 	return m->devinfo;
222 }
223 
224 /*
225  * The various mixers use a variety of bitmasks etc. The Voxware
226  * driver had a very nice technique to describe a mixer and interface
227  * to it. A table defines, for each channel, which register, bits,
228  * offset, polarity to use. This procedure creates the new value
229  * using the table and the old value.
230  */
231 
232 void
233 change_bits(mixer_tab *t, u_char *regval, int dev, int chn, int newval)
234 {
235     	u_char mask;
236     	int shift;
237 
238     	DEB(printf("ch_bits dev %d ch %d val %d old 0x%02x "
239 		"r %d p %d bit %d off %d\n",
240 		dev, chn, newval, *regval,
241 		(*t)[dev][chn].regno, (*t)[dev][chn].polarity,
242 		(*t)[dev][chn].nbits, (*t)[dev][chn].bitoffs ) );
243 
244     	if ( (*t)[dev][chn].polarity == 1)	/* reverse */
245 		newval = 100 - newval ;
246 
247     	mask = (1 << (*t)[dev][chn].nbits) - 1;
248     	newval = (int) ((newval * mask) + 50) / 100; /* Scale it */
249     	shift = (*t)[dev][chn].bitoffs /*- (*t)[dev][LEFT_CHN].nbits + 1*/;
250 
251     	*regval &= ~(mask << shift);        /* Filter out the previous value */
252     	*regval |= (newval & mask) << shift;        /* Set the new value */
253 }
254 
255