xref: /minix3/minix/drivers/audio/sb16/mixer.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1*433d6423SLionel Sambuc #include "sb16.h"
2*433d6423SLionel Sambuc #include "mixer.h"
3*433d6423SLionel Sambuc 
4*433d6423SLionel Sambuc 
5*433d6423SLionel Sambuc 
6*433d6423SLionel Sambuc static int get_set_volume(struct volume_level *level, int flag);
7*433d6423SLionel Sambuc static int get_set_input(struct inout_ctrl *input, int flag, int
8*433d6423SLionel Sambuc 	channel);
9*433d6423SLionel Sambuc static int get_set_output(struct inout_ctrl *output, int flag);
10*433d6423SLionel Sambuc 
11*433d6423SLionel Sambuc 
12*433d6423SLionel Sambuc 
13*433d6423SLionel Sambuc 
14*433d6423SLionel Sambuc /*=========================================================================*
15*433d6423SLionel Sambuc  *				mixer_ioctl
16*433d6423SLionel Sambuc  *=========================================================================*/
mixer_ioctl(unsigned long request,void * val,int * UNUSED (len))17*433d6423SLionel Sambuc int mixer_ioctl(unsigned long request, void *val, int *UNUSED(len)) {
18*433d6423SLionel Sambuc 	int status;
19*433d6423SLionel Sambuc 
20*433d6423SLionel Sambuc 	switch(request) {
21*433d6423SLionel Sambuc 		case MIXIOGETVOLUME:      status = get_set_volume(val, 0); break;
22*433d6423SLionel Sambuc 		case MIXIOSETVOLUME:      status = get_set_volume(val, 1); break;
23*433d6423SLionel Sambuc 		case MIXIOGETINPUTLEFT:   status = get_set_input(val, 0, 0); break;
24*433d6423SLionel Sambuc 		case MIXIOGETINPUTRIGHT:  status = get_set_input(val, 0, 1); break;
25*433d6423SLionel Sambuc 		case MIXIOGETOUTPUT:      status = get_set_output(val, 0); break;
26*433d6423SLionel Sambuc 		case MIXIOSETINPUTLEFT:   status = get_set_input(val, 1, 0); break;
27*433d6423SLionel Sambuc 		case MIXIOSETINPUTRIGHT:  status = get_set_input(val, 1, 1); break;
28*433d6423SLionel Sambuc 		case MIXIOSETOUTPUT:      status = get_set_output(val, 1); break;
29*433d6423SLionel Sambuc 		default:                  status = ENOTTY;
30*433d6423SLionel Sambuc 	}
31*433d6423SLionel Sambuc 
32*433d6423SLionel Sambuc 	return status;
33*433d6423SLionel Sambuc }
34*433d6423SLionel Sambuc 
35*433d6423SLionel Sambuc 
36*433d6423SLionel Sambuc /*=========================================================================*
37*433d6423SLionel Sambuc  *				mixer_init
38*433d6423SLionel Sambuc  *=========================================================================*/
mixer_init()39*433d6423SLionel Sambuc int mixer_init() {
40*433d6423SLionel Sambuc 	/* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
41*433d6423SLionel Sambuc 	* value written can be read back the mixer is there
42*433d6423SLionel Sambuc 	*/
43*433d6423SLionel Sambuc 
44*433d6423SLionel Sambuc 	mixer_set(MIXER_DAC_LEVEL, 0x10);       /* write something to it */
45*433d6423SLionel Sambuc 	if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
46*433d6423SLionel Sambuc 		Dprint(("sb16: Mixer not detected\n"));
47*433d6423SLionel Sambuc 		return EIO;
48*433d6423SLionel Sambuc 	}
49*433d6423SLionel Sambuc 
50*433d6423SLionel Sambuc 	/* Enable Automatic Gain Control */
51*433d6423SLionel Sambuc 	mixer_set(MIXER_AGC, 0x01);
52*433d6423SLionel Sambuc 
53*433d6423SLionel Sambuc 	Dprint(("Mixer detected\n"));
54*433d6423SLionel Sambuc 
55*433d6423SLionel Sambuc 	return OK;
56*433d6423SLionel Sambuc }
57*433d6423SLionel Sambuc 
58*433d6423SLionel Sambuc 
59*433d6423SLionel Sambuc 
60*433d6423SLionel Sambuc /*=========================================================================*
61*433d6423SLionel Sambuc  *				get_set_volume				   *
62*433d6423SLionel Sambuc  *=========================================================================*/
get_set_volume(struct volume_level * level,int flag)63*433d6423SLionel Sambuc static int get_set_volume(struct volume_level *level, int flag) {
64*433d6423SLionel Sambuc 	int cmd_left, cmd_right, shift, max_level;
65*433d6423SLionel Sambuc 
66*433d6423SLionel Sambuc 	shift = 3;
67*433d6423SLionel Sambuc 	max_level = 0x1F;
68*433d6423SLionel Sambuc 	switch(level->device) {
69*433d6423SLionel Sambuc 		case Master:
70*433d6423SLionel Sambuc 			cmd_left = MIXER_MASTER_LEFT;
71*433d6423SLionel Sambuc 			cmd_right = MIXER_MASTER_RIGHT;
72*433d6423SLionel Sambuc 			break;
73*433d6423SLionel Sambuc 		case Dac:
74*433d6423SLionel Sambuc 			cmd_left = MIXER_DAC_LEFT;
75*433d6423SLionel Sambuc 			cmd_right = MIXER_DAC_RIGHT;
76*433d6423SLionel Sambuc 			break;
77*433d6423SLionel Sambuc 		case Fm:
78*433d6423SLionel Sambuc 			cmd_left = MIXER_FM_LEFT;
79*433d6423SLionel Sambuc 			cmd_right = MIXER_FM_RIGHT;
80*433d6423SLionel Sambuc 			break;
81*433d6423SLionel Sambuc 		case Cd:
82*433d6423SLionel Sambuc 			cmd_left = MIXER_CD_LEFT;
83*433d6423SLionel Sambuc 			cmd_right = MIXER_CD_RIGHT;
84*433d6423SLionel Sambuc 			break;
85*433d6423SLionel Sambuc 		case Line:
86*433d6423SLionel Sambuc 			cmd_left = MIXER_LINE_LEFT;
87*433d6423SLionel Sambuc 			cmd_right = MIXER_LINE_RIGHT;
88*433d6423SLionel Sambuc 			break;
89*433d6423SLionel Sambuc 		case Mic:
90*433d6423SLionel Sambuc 			cmd_left = cmd_right = MIXER_MIC_LEVEL;
91*433d6423SLionel Sambuc 			break;
92*433d6423SLionel Sambuc 		case Speaker:
93*433d6423SLionel Sambuc 			cmd_left = cmd_right = MIXER_PC_LEVEL;
94*433d6423SLionel Sambuc 			shift = 6;
95*433d6423SLionel Sambuc 			max_level = 0x03;
96*433d6423SLionel Sambuc 			break;
97*433d6423SLionel Sambuc 		case Treble:
98*433d6423SLionel Sambuc 			cmd_left = MIXER_TREBLE_LEFT;
99*433d6423SLionel Sambuc 			cmd_right = MIXER_TREBLE_RIGHT;
100*433d6423SLionel Sambuc 			shift = 4;
101*433d6423SLionel Sambuc 			max_level = 0x0F;
102*433d6423SLionel Sambuc 			break;
103*433d6423SLionel Sambuc 		case Bass:
104*433d6423SLionel Sambuc 			cmd_left = MIXER_BASS_LEFT;
105*433d6423SLionel Sambuc 			cmd_right = MIXER_BASS_RIGHT;
106*433d6423SLionel Sambuc 			shift = 4;
107*433d6423SLionel Sambuc 			max_level = 0x0F;
108*433d6423SLionel Sambuc 			break;
109*433d6423SLionel Sambuc 		default:
110*433d6423SLionel Sambuc 			return EINVAL;
111*433d6423SLionel Sambuc 	}
112*433d6423SLionel Sambuc 
113*433d6423SLionel Sambuc 	if(flag) { /* Set volume level */
114*433d6423SLionel Sambuc 		if(level->right < 0) level->right = 0;
115*433d6423SLionel Sambuc 		else if(level->right > max_level) level->right = max_level;
116*433d6423SLionel Sambuc 		if(level->left < 0) level->left = 0;
117*433d6423SLionel Sambuc 		else if(level->left > max_level) level->left = max_level;
118*433d6423SLionel Sambuc 
119*433d6423SLionel Sambuc 		mixer_set(cmd_right, (level->right << shift));
120*433d6423SLionel Sambuc 		mixer_set(cmd_left, (level->left << shift));
121*433d6423SLionel Sambuc 	} else { /* Get volume level */
122*433d6423SLionel Sambuc 		level->left = mixer_get(cmd_left);
123*433d6423SLionel Sambuc 		level->right = mixer_get(cmd_right);
124*433d6423SLionel Sambuc 
125*433d6423SLionel Sambuc 		level->left >>= shift;
126*433d6423SLionel Sambuc 		level->right >>= shift;
127*433d6423SLionel Sambuc 	}
128*433d6423SLionel Sambuc 
129*433d6423SLionel Sambuc 	return OK;
130*433d6423SLionel Sambuc }
131*433d6423SLionel Sambuc 
132*433d6423SLionel Sambuc 
133*433d6423SLionel Sambuc /*=========================================================================*
134*433d6423SLionel Sambuc  *				get_set_input				   *
135*433d6423SLionel Sambuc  *=========================================================================*/
get_set_input(struct inout_ctrl * input,int flag,int channel)136*433d6423SLionel Sambuc static int get_set_input(struct inout_ctrl *input, int flag, int channel) {
137*433d6423SLionel Sambuc 	int input_cmd, input_mask, mask, del_mask, shift;
138*433d6423SLionel Sambuc 
139*433d6423SLionel Sambuc 	input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
140*433d6423SLionel Sambuc 
141*433d6423SLionel Sambuc 	mask = mixer_get(input_cmd);
142*433d6423SLionel Sambuc 
143*433d6423SLionel Sambuc 	switch (input->device) {
144*433d6423SLionel Sambuc 		case Fm:
145*433d6423SLionel Sambuc 			shift = 5;
146*433d6423SLionel Sambuc 			del_mask = 0x1F;
147*433d6423SLionel Sambuc 			break;
148*433d6423SLionel Sambuc 		case Cd:
149*433d6423SLionel Sambuc 			shift = 1;
150*433d6423SLionel Sambuc 			del_mask = 0x79;
151*433d6423SLionel Sambuc 			break;
152*433d6423SLionel Sambuc 		case Line:
153*433d6423SLionel Sambuc 			shift = 3;
154*433d6423SLionel Sambuc 			del_mask = 0x67;
155*433d6423SLionel Sambuc 			break;
156*433d6423SLionel Sambuc 		case Mic:
157*433d6423SLionel Sambuc 			shift = 0;
158*433d6423SLionel Sambuc 			del_mask = 0x7E;
159*433d6423SLionel Sambuc 			break;
160*433d6423SLionel Sambuc 		default:
161*433d6423SLionel Sambuc 			return EINVAL;
162*433d6423SLionel Sambuc 	}
163*433d6423SLionel Sambuc 
164*433d6423SLionel Sambuc 	if (flag) {  /* Set input */
165*433d6423SLionel Sambuc 		input_mask = ((input->left == ON ? 1 : 0) << 1) | (input->right == ON ? 1 : 0);
166*433d6423SLionel Sambuc 
167*433d6423SLionel Sambuc 		if (shift > 0) input_mask <<= shift;
168*433d6423SLionel Sambuc 		else input_mask >>= 1;
169*433d6423SLionel Sambuc 
170*433d6423SLionel Sambuc 		mask &= del_mask;
171*433d6423SLionel Sambuc 		mask |= input_mask;
172*433d6423SLionel Sambuc 
173*433d6423SLionel Sambuc 		mixer_set(input_cmd, mask);
174*433d6423SLionel Sambuc 	} else {	/* Get input */
175*433d6423SLionel Sambuc 		if (shift > 0) {
176*433d6423SLionel Sambuc 			input->left = (((mask >> (shift+1)) & 1) == 1 ? ON : OFF);
177*433d6423SLionel Sambuc 			input->right = (((mask >> shift) & 1) == 1 ? ON : OFF);
178*433d6423SLionel Sambuc 		} else {
179*433d6423SLionel Sambuc 			input->left = ((mask & 1) == 1 ? ON : OFF);
180*433d6423SLionel Sambuc 		}
181*433d6423SLionel Sambuc 	}
182*433d6423SLionel Sambuc 
183*433d6423SLionel Sambuc 	return OK;
184*433d6423SLionel Sambuc }
185*433d6423SLionel Sambuc 
186*433d6423SLionel Sambuc 
187*433d6423SLionel Sambuc /*=========================================================================*
188*433d6423SLionel Sambuc  *				get_set_output				   *
189*433d6423SLionel Sambuc  *=========================================================================*/
get_set_output(struct inout_ctrl * output,int flag)190*433d6423SLionel Sambuc static int get_set_output(struct inout_ctrl *output, int flag) {
191*433d6423SLionel Sambuc 	int output_mask, mask, del_mask, shift;
192*433d6423SLionel Sambuc 
193*433d6423SLionel Sambuc 	mask = mixer_get(MIXER_OUTPUT_CTRL);
194*433d6423SLionel Sambuc 
195*433d6423SLionel Sambuc 	switch (output->device) {
196*433d6423SLionel Sambuc 		case Cd:
197*433d6423SLionel Sambuc 			shift = 1;
198*433d6423SLionel Sambuc 			del_mask = 0x79;
199*433d6423SLionel Sambuc 			break;
200*433d6423SLionel Sambuc 		case Line:
201*433d6423SLionel Sambuc 			shift = 3;
202*433d6423SLionel Sambuc 			del_mask = 0x67;
203*433d6423SLionel Sambuc 			break;
204*433d6423SLionel Sambuc 		case Mic:
205*433d6423SLionel Sambuc 			shift = 0;
206*433d6423SLionel Sambuc 			del_mask = 0x7E;
207*433d6423SLionel Sambuc 			break;
208*433d6423SLionel Sambuc 		default:
209*433d6423SLionel Sambuc 			return EINVAL;
210*433d6423SLionel Sambuc 	}
211*433d6423SLionel Sambuc 
212*433d6423SLionel Sambuc 	if (flag) {  /* Set input */
213*433d6423SLionel Sambuc 		output_mask = ((output->left == ON ? 1 : 0) << 1) | (output->right == ON ? 1 : 0);
214*433d6423SLionel Sambuc 
215*433d6423SLionel Sambuc 		if (shift > 0) output_mask <<= shift;
216*433d6423SLionel Sambuc 		else output_mask >>= 1;
217*433d6423SLionel Sambuc 
218*433d6423SLionel Sambuc 		mask &= del_mask;
219*433d6423SLionel Sambuc 		mask |= output_mask;
220*433d6423SLionel Sambuc 
221*433d6423SLionel Sambuc 		mixer_set(MIXER_OUTPUT_CTRL, mask);
222*433d6423SLionel Sambuc 	} else {    /* Get input */
223*433d6423SLionel Sambuc 		if (shift > 0) {
224*433d6423SLionel Sambuc 			output->left = (((mask >> (shift+1)) & 1) == 1 ? ON : OFF);
225*433d6423SLionel Sambuc 			output->right = (((mask >> shift) & 1) == 1 ? ON : OFF);
226*433d6423SLionel Sambuc 		} else {
227*433d6423SLionel Sambuc 			output->left = ((mask & 1) == 1 ? ON : OFF);
228*433d6423SLionel Sambuc 		}
229*433d6423SLionel Sambuc 	}
230*433d6423SLionel Sambuc 
231*433d6423SLionel Sambuc 	return OK;
232*433d6423SLionel Sambuc }
233*433d6423SLionel Sambuc 
234*433d6423SLionel Sambuc 
235*433d6423SLionel Sambuc 
mixer_set(int reg,int data)236*433d6423SLionel Sambuc int mixer_set(int reg, int data) {
237*433d6423SLionel Sambuc 	int i;
238*433d6423SLionel Sambuc 
239*433d6423SLionel Sambuc 	sb16_outb(MIXER_REG, reg);
240*433d6423SLionel Sambuc 	for(i = 0; i < 100; i++);
241*433d6423SLionel Sambuc 	sb16_outb(MIXER_DATA, data);
242*433d6423SLionel Sambuc 
243*433d6423SLionel Sambuc 	return OK;
244*433d6423SLionel Sambuc }
245*433d6423SLionel Sambuc 
246*433d6423SLionel Sambuc 
247*433d6423SLionel Sambuc 
mixer_get(int reg)248*433d6423SLionel Sambuc int mixer_get(int reg) {
249*433d6423SLionel Sambuc 	int i;
250*433d6423SLionel Sambuc 
251*433d6423SLionel Sambuc 	sb16_outb(MIXER_REG, reg);
252*433d6423SLionel Sambuc 	for(i = 0; i < 100; i++);
253*433d6423SLionel Sambuc 	return sb16_inb(MIXER_DATA) & 0xff;
254*433d6423SLionel Sambuc }
255