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