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