254 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			6.0 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;
 | 
						|
}  
 |