dec21140A ethernet driver for virtualpc, contributed by nicolas tittley.
[minix.git] / drivers / audio / sb16 / mixer.c
blob770af03ea90f94829c2006b63229e04017248871
1 #include "sb16.h"
2 #include "mixer.h"
6 FORWARD _PROTOTYPE( int get_set_volume, (struct volume_level *level, int flag));
7 FORWARD _PROTOTYPE( int get_set_input, (struct inout_ctrl *input, int flag, int channel));
8 FORWARD _PROTOTYPE( int get_set_output, (struct inout_ctrl *output, int flag));
13 /*=========================================================================*
14 * mixer_ioctl
15 *=========================================================================*/
16 PUBLIC int mixer_ioctl(int request, void *val, int *len) {
17 int status;
19 switch(request) {
20 case MIXIOGETVOLUME: status = get_set_volume(val, 0); break;
21 case MIXIOSETVOLUME: status = get_set_volume(val, 1); break;
22 case MIXIOGETINPUTLEFT: status = get_set_input(val, 0, 0); break;
23 case MIXIOGETINPUTRIGHT: status = get_set_input(val, 0, 1); break;
24 case MIXIOGETOUTPUT: status = get_set_output(val, 0); break;
25 case MIXIOSETINPUTLEFT: status = get_set_input(val, 1, 0); break;
26 case MIXIOSETINPUTRIGHT: status = get_set_input(val, 1, 1); break;
27 case MIXIOSETOUTPUT: status = get_set_output(val, 1); break;
28 default: status = ENOTTY;
31 return status;
35 /*=========================================================================*
36 * mixer_init
37 *=========================================================================*/
38 PUBLIC int mixer_init() {
39 /* Try to detect the mixer by writing to MIXER_DAC_LEVEL if the
40 * value written can be read back the mixer is there
43 mixer_set(MIXER_DAC_LEVEL, 0x10); /* write something to it */
44 if(mixer_get(MIXER_DAC_LEVEL) != 0x10) {
45 dprint("sb16: Mixer not detected\n");
46 return EIO;
49 /* Enable Automatic Gain Control */
50 mixer_set(MIXER_AGC, 0x01);
52 dprint("Mixer detected\n");
54 return OK;
59 /*=========================================================================*
60 * get_set_volume *
61 *=========================================================================*/
62 PRIVATE int get_set_volume(struct volume_level *level, int flag) {
63 int cmd_left, cmd_right, shift, max_level;
65 shift = 3;
66 max_level = 0x1F;
67 switch(level->device) {
68 case Master:
69 cmd_left = MIXER_MASTER_LEFT;
70 cmd_right = MIXER_MASTER_RIGHT;
71 break;
72 case Dac:
73 cmd_left = MIXER_DAC_LEFT;
74 cmd_right = MIXER_DAC_RIGHT;
75 break;
76 case Fm:
77 cmd_left = MIXER_FM_LEFT;
78 cmd_right = MIXER_FM_RIGHT;
79 break;
80 case Cd:
81 cmd_left = MIXER_CD_LEFT;
82 cmd_right = MIXER_CD_RIGHT;
83 break;
84 case Line:
85 cmd_left = MIXER_LINE_LEFT;
86 cmd_right = MIXER_LINE_RIGHT;
87 break;
88 case Mic:
89 cmd_left = cmd_right = MIXER_MIC_LEVEL;
90 break;
91 case Speaker:
92 cmd_left = cmd_right = MIXER_PC_LEVEL;
93 shift = 6;
94 max_level = 0x03;
95 break;
96 case Treble:
97 cmd_left = MIXER_TREBLE_LEFT;
98 cmd_right = MIXER_TREBLE_RIGHT;
99 shift = 4;
100 max_level = 0x0F;
101 break;
102 case Bass:
103 cmd_left = MIXER_BASS_LEFT;
104 cmd_right = MIXER_BASS_RIGHT;
105 shift = 4;
106 max_level = 0x0F;
107 break;
108 default:
109 return EINVAL;
112 if(flag) { /* Set volume level */
113 if(level->right < 0) level->right = 0;
114 else if(level->right > max_level) level->right = max_level;
115 if(level->left < 0) level->left = 0;
116 else if(level->left > max_level) level->left = max_level;
118 mixer_set(cmd_right, (level->right << shift));
119 mixer_set(cmd_left, (level->left << shift));
120 } else { /* Get volume level */
121 level->left = mixer_get(cmd_left);
122 level->right = mixer_get(cmd_right);
124 level->left >>= shift;
125 level->right >>= shift;
128 return OK;
132 /*=========================================================================*
133 * get_set_input *
134 *=========================================================================*/
135 PRIVATE int get_set_input(struct inout_ctrl *input, int flag, int channel) {
136 int input_cmd, input_mask, mask, del_mask, shift;
138 input_cmd = (channel == 0 ? MIXER_IN_LEFT : MIXER_IN_RIGHT);
140 mask = mixer_get(input_cmd);
142 switch (input->device) {
143 case Fm:
144 shift = 5;
145 del_mask = 0x1F;
146 break;
147 case Cd:
148 shift = 1;
149 del_mask = 0x79;
150 break;
151 case Line:
152 shift = 3;
153 del_mask = 0x67;
154 break;
155 case Mic:
156 shift = 0;
157 del_mask = 0x7E;
158 break;
159 default:
160 return EINVAL;
163 if (flag) { /* Set input */
164 input_mask = ((input->left == ON ? 1 : 0) << 1) | (input->right == ON ? 1 : 0);
166 if (shift > 0) input_mask <<= shift;
167 else input_mask >>= 1;
169 mask &= del_mask;
170 mask |= input_mask;
172 mixer_set(input_cmd, mask);
173 } else { /* Get input */
174 if (shift > 0) {
175 input->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
176 input->right = ((mask >> shift) & 1 == 1 ? ON : OFF);
177 } else {
178 input->left = ((mask & 1) == 1 ? ON : OFF);
182 return OK;
186 /*=========================================================================*
187 * get_set_output *
188 *=========================================================================*/
189 PRIVATE int get_set_output(struct inout_ctrl *output, int flag) {
190 int output_mask, mask, del_mask, shift;
192 mask = mixer_get(MIXER_OUTPUT_CTRL);
194 switch (output->device) {
195 case Cd:
196 shift = 1;
197 del_mask = 0x79;
198 break;
199 case Line:
200 shift = 3;
201 del_mask = 0x67;
202 break;
203 case Mic:
204 shift = 0;
205 del_mask = 0x7E;
206 break;
207 default:
208 return EINVAL;
211 if (flag) { /* Set input */
212 output_mask = ((output->left == ON ? 1 : 0) << 1) | (output->right == ON ? 1 : 0);
214 if (shift > 0) output_mask <<= shift;
215 else output_mask >>= 1;
217 mask &= del_mask;
218 mask |= output_mask;
220 mixer_set(MIXER_OUTPUT_CTRL, mask);
221 } else { /* Get input */
222 if (shift > 0) {
223 output->left = ((mask >> (shift+1)) & 1 == 1 ? ON : OFF);
224 output->right = ((mask >> shift) & 1 == 1 ? ON : OFF);
225 } else {
226 output->left = ((mask & 1) == 1 ? ON : OFF);
230 return OK;
235 PUBLIC int mixer_set(int reg, int data) {
236 int i;
238 sb16_outb(MIXER_REG, reg);
239 for(i = 0; i < 100; i++);
240 sb16_outb(MIXER_DATA, data);
242 return OK;
247 PUBLIC int mixer_get(int reg) {
248 int i;
250 sb16_outb(MIXER_REG, reg);
251 for(i = 0; i < 100; i++);
252 return sb16_inb(MIXER_DATA) & 0xff;