3.1.7 branch.
[minix.git] / drivers / sb16 / mixer / sb16_mixer.c
blobe7583de6163bb26a700b03ba65745f1da19245c1
1 /* This file contains the driver for the mixer on
2 * a SoundBlaster 16 soundcard.
4 * The driver supports the following operations (using message format m2):
6 * m_type DEVICE IO_ENDPT COUNT POSITION ADRRESS
7 * ----------------------------------------------------------------
8 * | DEV_OPEN | device | proc nr | | | |
9 * |------------+---------+---------+---------+---------+---------|
10 * | DEV_CLOSE | device | proc nr | | | |
11 * |------------+---------+---------+---------+---------+---------|
12 * | DEV_IOCTL | device | proc nr |func code| | buf_ptr |
13 * ----------------------------------------------------------------
15 * The file contains one entry point:
17 * sb16mixer_task: main entry when system is brought up
19 * August 24 2005 Ported driver to user space (Peter Boonstoppel)
20 * May 20 1995 Author: Michel R. Prevenier
24 #include "sb16.h"
27 _PROTOTYPE(void main, (void));
28 FORWARD _PROTOTYPE( int mixer_init, (void));
29 FORWARD _PROTOTYPE( int mixer_open, (const message *m_ptr));
30 FORWARD _PROTOTYPE( int mixer_close, (const message *m_ptr));
31 FORWARD _PROTOTYPE( int mixer_ioctl, (const message *m_ptr));
32 FORWARD _PROTOTYPE( int mixer_get, (int reg));
33 FORWARD _PROTOTYPE( int get_set_volume, (const message *m_ptr, int flag));
34 FORWARD _PROTOTYPE( int get_set_input, (const message *m_ptr, int flag, int channel));
35 FORWARD _PROTOTYPE( int get_set_output, (const message *m_ptr, int flag));
38 PRIVATE int mixer_avail = 0; /* Mixer exists? */
41 #define dprint (void)
43 /* SEF functions and variables. */
44 FORWARD _PROTOTYPE( void sef_local_startup, (void) );
45 FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) );
47 /*===========================================================================*
48 * main
49 *===========================================================================*/
50 PUBLIC void main() {
51 message mess;
52 int ipc_status;
53 int err, caller, proc_nr;
55 /* SEF local startup. */
56 sef_local_startup();
58 /* Here is the main loop of the mixer task. It waits for a message, carries
59 * it out, and sends a reply.
61 while (TRUE) {
62 driver_receive(ANY, &mess, &ipc_status);
64 caller = mess.m_source;
65 proc_nr = mess.IO_ENDPT;
67 switch (caller) {
68 case HARDWARE: /* Leftover interrupt. */
69 continue;
70 case FS_PROC_NR: /* The only legitimate caller. */
71 break;
72 default:
73 dprint("sb16: got message from %d\n", caller);
74 continue;
77 /* Now carry out the work. */
78 switch(mess.m_type) {
79 case DEV_OPEN: err = mixer_open(&mess); break;
80 case DEV_CLOSE: err = mixer_close(&mess); break;
81 #ifdef DEV_IOCTL
82 case DEV_IOCTL: err = mixer_ioctl(&mess); break;
83 #endif
84 default: err = EINVAL; break;
87 /* Finally, prepare and send the reply message. */
88 mess.m_type = TASK_REPLY;
89 mess.REP_ENDPT = proc_nr;
91 dprint("%d %d", err, OK);
93 mess.REP_STATUS = err; /* error code */
94 send(caller, &mess); /* send reply to caller */
98 /*===========================================================================*
99 * sef_local_startup *
100 *===========================================================================*/
101 PRIVATE void sef_local_startup()
103 /* Register init callbacks. */
104 sef_setcb_init_fresh(sef_cb_init_fresh);
105 sef_setcb_init_lu(sef_cb_init_fresh);
106 sef_setcb_init_restart(sef_cb_init_fresh);
108 /* Register live update callbacks. */
109 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
110 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
112 /* Let SEF perform startup. */
113 sef_startup();
116 /*===========================================================================*
117 * sef_cb_init_fresh *
118 *===========================================================================*/
119 PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
121 /* Initialize the sb16 mixer driver. */
122 /* Announce we are up! */
123 driver_announce();
125 return(OK);
128 /*=========================================================================*
129 * mixer_open
130 *=========================================================================*/
131 PRIVATE int mixer_open(const message *m_ptr)
133 dprint("mixer_open\n");
135 /* try to detect the mixer type */
136 if (!mixer_avail && mixer_init() != OK) return EIO;
138 return OK;
142 /*=========================================================================*
143 * mixer_close
144 *=========================================================================*/
145 PRIVATE int mixer_close(const message *m_ptr)
147 dprint("mixer_close\n");
149 return OK;
153 /*=========================================================================*
154 * mixer_ioctl
155 *=========================================================================*/
156 PRIVATE int mixer_ioctl(const message *m_ptr)
158 int status;
160 dprint("mixer: got ioctl %d\n", m_ptr->REQUEST);
163 switch(m_ptr->REQUEST) {
164 case MIXIOGETVOLUME: status = get_set_volume(m_ptr, 0); break;
165 case MIXIOSETVOLUME: status = get_set_volume(m_ptr, 1); break;
166 case MIXIOGETINPUTLEFT: status = get_set_input(m_ptr, 0, 0); break;
167 case MIXIOGETINPUTRIGHT: status = get_set_input(m_ptr, 0, 1); break;
168 case MIXIOGETOUTPUT: status = get_set_output(m_ptr, 0); break;
169 case MIXIOSETINPUTLEFT: status = get_set_input(m_ptr, 1, 0); break;
170 case MIXIOSETINPUTRIGHT: status = get_set_input(m_ptr, 1, 1); break;
171 case MIXIOSETOUTPUT: status = get_set_output(m_ptr, 1); break;
172 default: status = ENOTTY;
175 return status;
179 /*=========================================================================*
180 * mixer_init
181 *=========================================================================*/
182 PRIVATE int mixer_init()
184 /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
185 * value written can be read back the mixer is there
188 mixer_set(MIXER_DAC_LEVEL, 0x10); /* write something to it */
189 if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
190 dprint("sb16: Mixer not detected\n");
191 return EIO;
194 /* Enable Automatic Gain Control */
195 mixer_set(MIXER_AGC, 0x01);
197 dprint("Mixer detected\n");
199 mixer_avail = 1;
200 return OK;
204 /*=========================================================================*
205 * mixer_get
206 *=========================================================================*/
207 PRIVATE int mixer_get(int reg)
209 int i;
211 sb16_outb(MIXER_REG, reg);
212 for(i = 0; i < 100; i++);
213 return sb16_inb(MIXER_DATA) & 0xff;
217 /*=========================================================================*
218 * get_set_volume *
219 *=========================================================================*/
220 PRIVATE int get_set_volume(const message *m_ptr, int flag)
221 /* flag 0 = get, 1 = set */
223 struct volume_level level;
224 int cmd_left, cmd_right, shift, max_level;
226 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&level, (phys_bytes)sizeof(level));
228 shift = 3;
229 max_level = 0x1F;
231 switch(level.device) {
232 case Master:
233 cmd_left = MIXER_MASTER_LEFT;
234 cmd_right = MIXER_MASTER_RIGHT;
235 break;
236 case Dac:
237 cmd_left = MIXER_DAC_LEFT;
238 cmd_right = MIXER_DAC_RIGHT;
239 break;
240 case Fm:
241 cmd_left = MIXER_FM_LEFT;
242 cmd_right = MIXER_FM_RIGHT;
243 break;
244 case Cd:
245 cmd_left = MIXER_CD_LEFT;
246 cmd_right = MIXER_CD_RIGHT;
247 break;
248 case Line:
249 cmd_left = MIXER_LINE_LEFT;
250 cmd_right = MIXER_LINE_RIGHT;
251 break;
252 case Mic:
253 cmd_left = cmd_right = MIXER_MIC_LEVEL;
254 break;
255 case Speaker:
256 cmd_left = cmd_right = MIXER_PC_LEVEL;
257 shift = 6;
258 max_level = 0x03;
259 break;
260 case Treble:
261 cmd_left = MIXER_TREBLE_LEFT;
262 cmd_right = MIXER_TREBLE_RIGHT;
263 shift = 4;
264 max_level = 0x0F;
265 break;
266 case Bass:
267 cmd_left = MIXER_BASS_LEFT;
268 cmd_right = MIXER_BASS_RIGHT;
269 shift = 4;
270 max_level = 0x0F;
271 break;
272 default:
273 return EINVAL;
276 if(flag) { /* Set volume level */
277 if(level.right < 0) level.right = 0;
278 else if(level.right > max_level) level.right = max_level;
279 if(level.left < 0) level.left = 0;
280 else if(level.left > max_level) level.left = max_level;
282 mixer_set(cmd_right, (level.right << shift));
283 mixer_set(cmd_left, (level.left << shift));
284 } else { /* Get volume level */
285 level.left = mixer_get(cmd_left);
286 level.right = mixer_get(cmd_right);
288 level.left >>= shift;
289 level.right >>= shift;
291 /* Copy back to user */
292 sys_datacopy(SELF, (vir_bytes)&level, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(level));
295 return OK;
299 /*=========================================================================*
300 * get_set_input *
301 *=========================================================================*/
302 PRIVATE int get_set_input(const message *m_ptr, int flag, int channel)
304 * flag 0 = get, 1 = set
305 * channel 0 = left, 1 = right
308 struct inout_ctrl input;
309 int input_cmd, input_mask, mask, del_mask, shift;
311 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&input, (phys_bytes)sizeof(input));
313 input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
315 mask = mixer_get(input_cmd);
317 switch (input.device) {
318 case Fm:
319 shift = 5;
320 del_mask = 0x1F;
321 break;
322 case Cd:
323 shift = 1;
324 del_mask = 0x79;
325 break;
326 case Line:
327 shift = 3;
328 del_mask = 0x67;
329 break;
330 case Mic:
331 shift = 0;
332 del_mask = 0x7E;
333 break;
334 default:
335 return EINVAL;
338 if (flag) { /* Set input */
339 input_mask = ((input.left == ON ? 1 : 0) << 1) | (input.right == ON ? 1 : 0);
341 if (shift > 0) input_mask <<= shift;
342 else input_mask >>= 1;
344 mask &= del_mask;
345 mask |= input_mask;
347 mixer_set(input_cmd, mask);
348 } else { /* Get input */
349 if (shift > 0) {
350 input.left = ((((mask >> (shift+1)) & 1) == 1) ? ON : OFF);
351 input.right = ((((mask >> shift) & 1) == 1) ? ON : OFF);
352 } else {
353 input.left = (((mask & 1) == 1) ? ON : OFF);
356 /* Copy back to user */
357 sys_datacopy(SELF, (vir_bytes)&input, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(input));
360 return OK;
364 /*=========================================================================*
365 * get_set_output *
366 *=========================================================================*/
367 PRIVATE int get_set_output(const message *m_ptr, int flag)
368 /* flag 0 = get, 1 = set */
370 struct inout_ctrl output;
371 int output_mask, mask, del_mask, shift;
373 sys_datacopy(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, SELF, (vir_bytes)&output, (phys_bytes)sizeof(output));
375 mask = mixer_get(MIXER_OUTPUT_CTRL);
377 switch (output.device) {
378 case Cd:
379 shift = 1;
380 del_mask = 0x79;
381 break;
382 case Line:
383 shift = 3;
384 del_mask = 0x67;
385 break;
386 case Mic:
387 shift = 0;
388 del_mask = 0x7E;
389 break;
390 default:
391 return EINVAL;
394 if (flag) { /* Set input */
395 output_mask = ((output.left == ON ? 1 : 0) << 1) | (output.right == ON ? 1 : 0);
397 if (shift > 0) output_mask <<= shift;
398 else output_mask >>= 1;
400 mask &= del_mask;
401 mask |= output_mask;
403 mixer_set(MIXER_OUTPUT_CTRL, mask);
404 } else { /* Get input */
405 if (shift > 0) {
406 output.left = ((((mask >> (shift+1)) & 1) == 1) ? ON : OFF);
407 output.right = ((((mask >> shift) & 1) == 1) ? ON : OFF);
408 } else {
409 output.left = (((mask & 1) == 1) ? ON : OFF);
412 /* Copy back to user */
413 sys_datacopy(SELF, (vir_bytes)&output, m_ptr->IO_ENDPT, (vir_bytes)m_ptr->ADDRESS, (phys_bytes)sizeof(output));
416 return OK;