xref: /haiku/src/add-ons/kernel/drivers/audio/cmedia/mixer.c (revision 13581b3d2a71545960b98fefebc5225b5bf29072)
1 /*
2 	Copyright 1999, Be Incorporated.   All Rights Reserved.
3 	This file may be used under the terms of the Be Sample Code License.
4 */
5 
6 #include "cm_private.h"
7 #include <string.h>
8 
9 #if !defined(_KERNEL_EXPORT_H)
10 #include <KernelExport.h>
11 #endif /* _KERNEL_EXPORT_H */
12 
13 
14 static status_t mixer_open(const char *name, uint32 flags, void **cookie);
15 static status_t mixer_close(void *cookie);
16 static status_t mixer_free(void *cookie);
17 static status_t mixer_control(void *cookie, uint32 op, void *data, size_t len);
18 static status_t mixer_read(void *cookie, off_t pos, void *data, size_t *len);
19 static status_t mixer_write(void *cookie, off_t pos, const void *data, size_t *len);
20 
21 device_hooks mixer_hooks = {
22     &mixer_open,
23     &mixer_close,
24     &mixer_free,
25     &mixer_control,
26     &mixer_read,
27     &mixer_write,
28     NULL,		/* select */
29     NULL,		/* deselect */
30     NULL,		/* readv */
31     NULL		/* writev */
32 };
33 
34 
35 typedef struct {
36 	int selector;
37 	int port;
38 	float div;
39 	float sub;
40 	int minval;
41 	int maxval;
42 	int leftshift;
43 	int mask;
44 	int mutemask;
45 } mixer_info;
46 
47 
48 /* mute is special -- when it's 0x01, it means enable... */
49 mixer_info the_mixers[] = {
50   {CMEDIA_PCI_LEFT_ADC_INPUT_G,			0,	   1.5,  0.0, 0, 15, 0, 0x0f, 0x00},
51   {CMEDIA_PCI_RIGHT_ADC_INPUT_G,		1,	   1.5,  0.0, 0, 15, 0, 0x0f, 0x00},
52   {CMEDIA_PCI_LEFT_AUX1_LOOPBACK_GAM,	2,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
53   {CMEDIA_PCI_RIGHT_AUX1_LOOPBACK_GAM,	3,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
54   {CMEDIA_PCI_LEFT_CD_LOOPBACK_GAM,		4,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
55   {CMEDIA_PCI_RIGHT_CD_LOOPBACK_GAM,	5,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
56   {CMEDIA_PCI_LEFT_LINE_LOOPBACK_GAM,	6,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
57   {CMEDIA_PCI_RIGHT_LINE_LOOPBACK_GAM,	7,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
58   {CMEDIA_PCI_MIC_LOOPBACK_GAM,			8,	  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
59   {CMEDIA_PCI_LEFT_SYNTH_OUTPUT_GAM,	0xa,  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
60   {CMEDIA_PCI_RIGHT_SYNTH_OUTPUT_GAM,	0xb,  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
61   {CMEDIA_PCI_LEFT_AUX2_LOOPBACK_GAM,	0xc,  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
62   {CMEDIA_PCI_RIGHT_AUX2_LOOPBACK_GAM,	0xd,  -1.5, 12.0, 0, 31, 0, 0x1f, 0x80},
63   {CMEDIA_PCI_LEFT_MASTER_VOLUME_AM,	0xe,  -1.5,  0.0, 0, 31, 0, 0x1f, 0x80},
64   {CMEDIA_PCI_RIGHT_MASTER_VOLUME_AM,	0xf,  -1.5,  0.0, 0, 31, 0, 0x1f, 0x80},
65   {CMEDIA_PCI_LEFT_PCM_OUTPUT_GAM,		0x10, -1.5,  0.0, 0, 63, 0, 0x3f, 0x80},
66   {CMEDIA_PCI_RIGHT_PCM_OUTPUT_GAM,		0x11, -1.5,  0.0, 0, 63, 0, 0x3f, 0x80},
67   {CMEDIA_PCI_DIGITAL_LOOPBACK_AM,		0x16, -1.5,  0.0, 0, 63, 2, 0xfc, 0x01},
68 };
69 
70 
71 #define N_MIXERS (sizeof(the_mixers) / sizeof(the_mixers[0]))
72 
73 
74 static int
75 map_mixer(int selector) {
76 	uint32 i;
77 	for (i = 0; i < N_MIXERS; i++) {
78 		if (the_mixers[i].selector == selector)
79 		return i;
80 	}
81 	return -1;
82 }
83 
84 
85 static status_t
86 mixer_open(
87 	const char * name,
88 	uint32 flags,
89 	void ** cookie)
90 {
91 	int ix;
92 	/* mixer_dev * it = NULL; */
93 
94 	ddprintf(("cmedia_pci: mixer_open()\n"));
95 
96 	*cookie = NULL;
97 	for (ix=0; ix<num_cards; ix++) {
98 		if (!strcmp(name, cards[ix].mixer.name)) {
99 			break;
100 		}
101 	}
102 	if (ix == num_cards) {
103 		return ENODEV;
104 	}
105 
106 	atomic_add(&cards[ix].mixer.open_count, 1);
107 	cards[ix].mixer.card = &cards[ix];
108 	*cookie = &cards[ix].mixer;
109 
110 	return B_OK;
111 }
112 
113 
114 static status_t
115 mixer_close(
116 	void * cookie)
117 {
118 	mixer_dev * it = (mixer_dev *)cookie;
119 
120 	atomic_add(&it->open_count, -1);
121 
122 	return B_OK;
123 }
124 
125 
126 static status_t
127 mixer_free(
128 	void * cookie)
129 {
130 	ddprintf(("cmedia_pci: mixer_free()\n"));
131 
132 	if (((mixer_dev *)cookie)->open_count != 0) {
133 		dprintf("cmedia_pci: mixer open_count is bad in mixer_free()!\n");
134 	}
135 	return B_OK;	/* already done in close */
136 }
137 
138 
139 static int
140 get_mixer_value(
141 	cmedia_pci_dev * card,
142 	cmedia_pci_level * lev)
143 {
144 	int ix = map_mixer(lev->selector);
145 	uchar val;
146 	if (ix < 0) {
147 		return B_BAD_VALUE;
148 	}
149 	val = get_indirect(card, the_mixers[ix].port);
150 	lev->flags = 0;
151 	if (!the_mixers[ix].mutemask) {
152 		/* no change */
153 	}
154 	else if (the_mixers[ix].mutemask == 0x01) {
155 		if (!(val & 0x01)) {
156 			lev->flags |= CMEDIA_PCI_LEVEL_MUTED;
157 		}
158 	}
159 	else if (val & the_mixers[ix].mutemask) {
160 		lev->flags |= CMEDIA_PCI_LEVEL_MUTED;
161 	}
162 	val &= the_mixers[ix].mask;
163 	val >>= the_mixers[ix].leftshift;
164 	lev->value = ((float)val)*the_mixers[ix].div+the_mixers[ix].sub;
165 
166 	return B_OK;
167 }
168 
169 
170 static int
171 gather_info(
172 	mixer_dev * mixer,
173 	cmedia_pci_level * data,
174 	int count)
175 {
176 	int ix;
177 	cpu_status cp;
178 
179 	cp = disable_interrupts();
180 	acquire_spinlock(&mixer->card->hardware);
181 
182 	for (ix=0; ix<count; ix++) {
183 		if (get_mixer_value(mixer->card, &data[ix]) < B_OK)
184 			break;
185 	}
186 
187 	release_spinlock(&mixer->card->hardware);
188 	restore_interrupts(cp);
189 
190 	return ix;
191 }
192 
193 
194 static status_t
195 set_mixer_value(
196 	cmedia_pci_dev * card,
197 	cmedia_pci_level * lev)
198 {
199 	int selector = map_mixer(lev->selector);
200 	int value;
201 	int mask;
202 	if (selector < 0) {
203 		return EINVAL;
204 	}
205 	value = (lev->value-the_mixers[selector].sub)/the_mixers[selector].div;
206 	if (value < the_mixers[selector].minval) {
207 		value = the_mixers[selector].minval;
208 	}
209 	if (value > the_mixers[selector].maxval) {
210 		value = the_mixers[selector].maxval;
211 	}
212 	value <<= the_mixers[selector].leftshift;
213 	if (the_mixers[selector].mutemask) {
214 		if (the_mixers[selector].mutemask == 0x01) {
215 			if (!(lev->flags & CMEDIA_PCI_LEVEL_MUTED)) {
216 				value |= the_mixers[selector].mutemask;
217 			}
218 		} else {
219 			if (lev->flags & CMEDIA_PCI_LEVEL_MUTED) {
220 				value |= the_mixers[selector].mutemask;
221 			}
222 		}
223 	}
224 	mask = the_mixers[selector].mutemask | the_mixers[selector].mask;
225 	set_indirect(card, the_mixers[selector].port, value, mask);
226 	return B_OK;
227 }
228 
229 
230 static int
231 disperse_info(
232 	mixer_dev * mixer,
233 	cmedia_pci_level * data,
234 	int count)
235 {
236 	int ix;
237 	cpu_status cp;
238 
239 	cp = disable_interrupts();
240 	acquire_spinlock(&mixer->card->hardware);
241 
242 	for (ix=0; ix<count; ix++) {
243 		if (set_mixer_value(mixer->card, &data[ix]) < B_OK)
244 			break;
245 	}
246 
247 	release_spinlock(&mixer->card->hardware);
248 	restore_interrupts(cp);
249 
250 	return ix;
251 }
252 
253 
254 static status_t
255 mixer_control(
256 	void * cookie,
257 	uint32 iop,
258 	void * data,
259 	size_t len)
260 {
261 	mixer_dev * it = (mixer_dev *)cookie;
262 	status_t err = B_OK;
263 
264 	if (!data) {
265 		return B_BAD_VALUE;
266 	}
267 
268 	ddprintf(("cmedia_pci: mixer_control()\n")); /* slow printing */
269 
270 	switch (iop) {
271 	case B_MIXER_GET_VALUES:
272 		((cmedia_pci_level_cmd *)data)->count =
273 			gather_info(it, ((cmedia_pci_level_cmd *)data)->data,
274 				((cmedia_pci_level_cmd *)data)->count);
275 		break;
276 	case B_MIXER_SET_VALUES:
277 		((cmedia_pci_level_cmd *)data)->count =
278 			disperse_info(it, ((cmedia_pci_level_cmd *)data)->data,
279 				((cmedia_pci_level_cmd *)data)->count);
280 		break;
281 	default:
282 		err = B_BAD_VALUE;
283 		break;
284 	}
285 	return err;
286 }
287 
288 
289 static status_t
290 mixer_read(
291 	void * cookie,
292 	off_t pos,
293 	void * data,
294 	size_t * nread)
295 {
296 	return EPERM;
297 }
298 
299 
300 static status_t
301 mixer_write(
302 	void * cookie,
303 	off_t pos,
304 	const void * data,
305 	size_t * nwritten)
306 {
307 	return EPERM;
308 }
309 
310