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
14static status_t mixer_open(const char *name, uint32 flags, void **cookie);
15static status_t mixer_close(void *cookie);
16static status_t mixer_free(void *cookie);
17static status_t mixer_control(void *cookie, uint32 op, void *data, size_t len);
18static status_t mixer_read(void *cookie, off_t pos, void *data, size_t *len);
19static status_t mixer_write(void *cookie, off_t pos, const void *data, size_t *len);
20
21device_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
35typedef 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... */
49mixer_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
74static int
75map_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
85static status_t
86mixer_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
114static status_t
115mixer_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
126static status_t
127mixer_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
139static int
140get_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
170static int
171gather_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
194static status_t
195set_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
230static int
231disperse_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
254static status_t
255mixer_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
289static status_t
290mixer_read(
291	void * cookie,
292	off_t pos,
293	void * data,
294	size_t * nread)
295{
296	return EPERM;
297}
298
299
300static status_t
301mixer_write(
302	void * cookie,
303	off_t pos,
304	const void * data,
305	size_t * nwritten)
306{
307	return EPERM;
308}
309
310