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/* mute is special -- when it's 0x01, it means enable... */
48mixer_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
70static int
71map_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
80static status_t
81mixer_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
109static status_t
110mixer_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
121static status_t
122mixer_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
134static int
135get_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
165static int
166gather_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
189static status_t
190set_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
225static int
226disperse_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
249static status_t
250mixer_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
284static status_t
285mixer_read(
286	void * cookie,
287	off_t pos,
288	void * data,
289	size_t * nread)
290{
291	return EPERM;
292}
293
294
295static status_t
296mixer_write(
297	void * cookie,
298	off_t pos,
299	const void * data,
300	size_t * nwritten)
301{
302	return EPERM;
303}
304
305