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