minix/drivers/audio/sb16/mixer.c
2009-11-09 10:26:00 +00:00

253 lines
No EOL
6 KiB
C

#include "sb16.h"
#include "mixer.h"
FORWARD _PROTOTYPE( int get_set_volume, (struct volume_level *level, int flag));
FORWARD _PROTOTYPE( int get_set_input, (struct inout_ctrl *input, int flag, int channel));
FORWARD _PROTOTYPE( int get_set_output, (struct inout_ctrl *output, int flag));
/*=========================================================================*
* mixer_ioctl
*=========================================================================*/
PUBLIC int mixer_ioctl(int request, void *val, int *len) {
int status;
switch(request) {
case MIXIOGETVOLUME: status = get_set_volume(val, 0); break;
case MIXIOSETVOLUME: status = get_set_volume(val, 1); break;
case MIXIOGETINPUTLEFT: status = get_set_input(val, 0, 0); break;
case MIXIOGETINPUTRIGHT: status = get_set_input(val, 0, 1); break;
case MIXIOGETOUTPUT: status = get_set_output(val, 0); break;
case MIXIOSETINPUTLEFT: status = get_set_input(val, 1, 0); break;
case MIXIOSETINPUTRIGHT: status = get_set_input(val, 1, 1); break;
case MIXIOSETOUTPUT: status = get_set_output(val, 1); break;
default: status = ENOTTY;
}
return status;
}
/*=========================================================================*
* mixer_init
*=========================================================================*/
PUBLIC int mixer_init() {
/* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
* value written can be read back the mixer is there
*/
mixer_set(MIXER_DAC_LEVEL, 0x10); /* write something to it */
if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
dprint("sb16: Mixer not detected\n");
return EIO;
}
/* Enable Automatic Gain Control */
mixer_set(MIXER_AGC, 0x01);
dprint("Mixer detected\n");
return OK;
}
/*=========================================================================*
* get_set_volume *
*=========================================================================*/
PRIVATE int get_set_volume(struct volume_level *level, int flag) {
int cmd_left, cmd_right, shift, max_level;
shift = 3;
max_level = 0x1F;
switch(level->device) {
case Master:
cmd_left = MIXER_MASTER_LEFT;
cmd_right = MIXER_MASTER_RIGHT;
break;
case Dac:
cmd_left = MIXER_DAC_LEFT;
cmd_right = MIXER_DAC_RIGHT;
break;
case Fm:
cmd_left = MIXER_FM_LEFT;
cmd_right = MIXER_FM_RIGHT;
break;
case Cd:
cmd_left = MIXER_CD_LEFT;
cmd_right = MIXER_CD_RIGHT;
break;
case Line:
cmd_left = MIXER_LINE_LEFT;
cmd_right = MIXER_LINE_RIGHT;
break;
case Mic:
cmd_left = cmd_right = MIXER_MIC_LEVEL;
break;
case Speaker:
cmd_left = cmd_right = MIXER_PC_LEVEL;
shift = 6;
max_level = 0x03;
break;
case Treble:
cmd_left = MIXER_TREBLE_LEFT;
cmd_right = MIXER_TREBLE_RIGHT;
shift = 4;
max_level = 0x0F;
break;
case Bass:
cmd_left = MIXER_BASS_LEFT;
cmd_right = MIXER_BASS_RIGHT;
shift = 4;
max_level = 0x0F;
break;
default:
return EINVAL;
}
if(flag) { /* Set volume level */
if(level->right < 0) level->right = 0;
else if(level->right > max_level) level->right = max_level;
if(level->left < 0) level->left = 0;
else if(level->left > max_level) level->left = max_level;
mixer_set(cmd_right, (level->right << shift));
mixer_set(cmd_left, (level->left << shift));
} else { /* Get volume level */
level->left = mixer_get(cmd_left);
level->right = mixer_get(cmd_right);
level->left >>= shift;
level->right >>= shift;
}
return OK;
}
/*=========================================================================*
* get_set_input *
*=========================================================================*/
PRIVATE int get_set_input(struct inout_ctrl *input, int flag, int channel) {
int input_cmd, input_mask, mask, del_mask, shift;
input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
mask = mixer_get(input_cmd);
switch (input->device) {
case Fm:
shift = 5;
del_mask = 0x1F;
break;
case Cd:
shift = 1;
del_mask = 0x79;
break;
case Line:
shift = 3;
del_mask = 0x67;
break;
case Mic:
shift = 0;
del_mask = 0x7E;
break;
default:
return EINVAL;
}
if (flag) { /* Set input */
input_mask = ((input->left == ON ? 1 : 0) << 1) | (input->right == ON ? 1 : 0);
if (shift > 0) input_mask <<= shift;
else input_mask >>= 1;
mask &= del_mask;
mask |= input_mask;
mixer_set(input_cmd, mask);
} else { /* Get input */
if (shift > 0) {
input->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
input->right = ((mask >> shift) & 1 == 1 ? ON : OFF);
} else {
input->left = ((mask & 1) == 1 ? ON : OFF);
}
}
return OK;
}
/*=========================================================================*
* get_set_output *
*=========================================================================*/
PRIVATE int get_set_output(struct inout_ctrl *output, int flag) {
int output_mask, mask, del_mask, shift;
mask = mixer_get(MIXER_OUTPUT_CTRL);
switch (output->device) {
case Cd:
shift = 1;
del_mask = 0x79;
break;
case Line:
shift = 3;
del_mask = 0x67;
break;
case Mic:
shift = 0;
del_mask = 0x7E;
break;
default:
return EINVAL;
}
if (flag) { /* Set input */
output_mask = ((output->left == ON ? 1 : 0) << 1) | (output->right == ON ? 1 : 0);
if (shift > 0) output_mask <<= shift;
else output_mask >>= 1;
mask &= del_mask;
mask |= output_mask;
mixer_set(MIXER_OUTPUT_CTRL, mask);
} else { /* Get input */
if (shift > 0) {
output->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
output->right = ((mask >> shift) & 1 == 1 ? ON : OFF);
} else {
output->left = ((mask & 1) == 1 ? ON : OFF);
}
}
return OK;
}
PUBLIC int mixer_set(int reg, int data) {
int i;
sb16_outb(MIXER_REG, reg);
for(i = 0; i < 100; i++);
sb16_outb(MIXER_DATA, data);
return OK;
}
PUBLIC int mixer_get(int reg) {
int i;
sb16_outb(MIXER_REG, reg);
for(i = 0; i < 100; i++);
return sb16_inb(MIXER_DATA) & 0xff;
}