255 lines
5.9 KiB
C
255 lines
5.9 KiB
C
|
#include "sb16.h"
|
||
|
#include "mixer.h"
|
||
|
|
||
|
|
||
|
|
||
|
static int get_set_volume(struct volume_level *level, int flag);
|
||
|
static int get_set_input(struct inout_ctrl *input, int flag, int
|
||
|
channel);
|
||
|
static int get_set_output(struct inout_ctrl *output, int flag);
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/*=========================================================================*
|
||
|
* mixer_ioctl
|
||
|
*=========================================================================*/
|
||
|
int mixer_ioctl(unsigned long request, void *val, int *UNUSED(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
|
||
|
*=========================================================================*/
|
||
|
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 *
|
||
|
*=========================================================================*/
|
||
|
static 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 *
|
||
|
*=========================================================================*/
|
||
|
static 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 *
|
||
|
*=========================================================================*/
|
||
|
static 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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
int mixer_get(int reg) {
|
||
|
int i;
|
||
|
|
||
|
sb16_outb(MIXER_REG, reg);
|
||
|
for(i = 0; i < 100; i++);
|
||
|
return sb16_inb(MIXER_DATA) & 0xff;
|
||
|
}
|