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