Merge tag 'r8169-20060920-00' of git://electric-eye.fr.zoreil.com/home/romieu/linux...
[linux-2.6] / sound / oss / ics2101.c
1 /*
2  * sound/ics2101.c
3  *
4  * Driver for the ICS2101 mixer of GUS v3.7.
5  *
6  *
7  * Copyright (C) by Hannu Savolainen 1993-1997
8  *
9  * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL)
10  * Version 2 (June 1991). See the "COPYING" file distributed with this software
11  * for more info.
12  *
13  *
14  * Thomas Sailer   : ioctl code reworked (vmalloc/vfree removed)
15  * Bartlomiej Zolnierkiewicz : added __init to ics2101_mixer_init()
16  */
17 #include <linux/init.h>
18 #include <linux/spinlock.h>
19 #include "sound_config.h"
20
21 #include <linux/ultrasound.h>
22
23 #include "gus.h"
24 #include "gus_hw.h"
25
26 #define MIX_DEVS        (SOUND_MASK_MIC|SOUND_MASK_LINE| \
27                          SOUND_MASK_SYNTH| \
28                          SOUND_MASK_CD | SOUND_MASK_VOLUME)
29
30 extern int     *gus_osp;
31 extern int      gus_base;
32 extern spinlock_t gus_lock;
33 static int      volumes[ICS_MIXDEVS];
34 static int      left_fix[ICS_MIXDEVS] =
35 {1, 1, 1, 2, 1, 2};
36 static int      right_fix[ICS_MIXDEVS] =
37 {2, 2, 2, 1, 2, 1};
38
39 static int scale_vol(int vol)
40 {
41         /*
42          *  Experimental volume scaling by Risto Kankkunen.
43          *  This should give smoother volume response than just
44          *  a plain multiplication.
45          */
46          
47         int e;
48
49         if (vol < 0)
50                 vol = 0;
51         if (vol > 100)
52                 vol = 100;
53         vol = (31 * vol + 50) / 100;
54         e = 0;
55         if (vol)
56         {
57                 while (vol < 16)
58                 {
59                         vol <<= 1;
60                         e--;
61                 }
62                 vol -= 16;
63                 e += 7;
64         }
65         return ((e << 4) + vol);
66 }
67
68 static void write_mix(int dev, int chn, int vol)
69 {
70         int *selector;
71         unsigned long flags;
72         int ctrl_addr = dev << 3;
73         int attn_addr = dev << 3;
74
75         vol = scale_vol(vol);
76
77         if (chn == CHN_LEFT)
78         {
79                 selector = left_fix;
80                 ctrl_addr |= 0x00;
81                 attn_addr |= 0x02;
82         }
83         else
84         {
85                 selector = right_fix;
86                 ctrl_addr |= 0x01;
87                 attn_addr |= 0x03;
88         }
89
90         spin_lock_irqsave(&gus_lock, flags);
91         outb((ctrl_addr), u_MixSelect);
92         outb((selector[dev]), u_MixData);
93         outb((attn_addr), u_MixSelect);
94         outb(((unsigned char) vol), u_MixData);
95         spin_unlock_irqrestore(&gus_lock,flags);
96 }
97
98 static int set_volumes(int dev, int vol)
99 {
100         int left = vol & 0x00ff;
101         int right = (vol >> 8) & 0x00ff;
102
103         if (left < 0)
104                 left = 0;
105         if (left > 100)
106                 left = 100;
107         if (right < 0)
108                 right = 0;
109         if (right > 100)
110                 right = 100;
111
112         write_mix(dev, CHN_LEFT, left);
113         write_mix(dev, CHN_RIGHT, right);
114
115         vol = left + (right << 8);
116         volumes[dev] = vol;
117         return vol;
118 }
119
120 static int ics2101_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
121 {
122         int val;
123         
124         if (((cmd >> 8) & 0xff) == 'M') {
125                 if (_SIOC_DIR(cmd) & _SIOC_WRITE) {
126                         
127                         if (get_user(val, (int __user *)arg))
128                                 return -EFAULT;
129                         switch (cmd & 0xff) {
130                         case SOUND_MIXER_RECSRC:
131                                 return gus_default_mixer_ioctl(dev, cmd, arg);
132
133                         case SOUND_MIXER_MIC:
134                                 val = set_volumes(DEV_MIC, val);
135                                 break;
136                                 
137                         case SOUND_MIXER_CD:
138                                 val = set_volumes(DEV_CD, val);
139                                 break;
140
141                         case SOUND_MIXER_LINE:
142                                 val = set_volumes(DEV_LINE, val);
143                                 break;
144
145                         case SOUND_MIXER_SYNTH:
146                                 val = set_volumes(DEV_GF1, val);
147                                 break;
148
149                         case SOUND_MIXER_VOLUME:
150                                 val = set_volumes(DEV_VOL, val);
151                                 break;
152
153                         default:
154                                 return -EINVAL;
155                         }
156                         return put_user(val, (int __user *)arg);
157                 } else {
158                         switch (cmd & 0xff) {
159                                 /*
160                                  * Return parameters
161                                  */
162                         case SOUND_MIXER_RECSRC:
163                                 return gus_default_mixer_ioctl(dev, cmd, arg);
164
165                         case SOUND_MIXER_DEVMASK:
166                                 val = MIX_DEVS; 
167                                 break;
168
169                         case SOUND_MIXER_STEREODEVS:
170                                 val = SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_SYNTH | SOUND_MASK_VOLUME | SOUND_MASK_MIC; 
171                                 break;
172
173                         case SOUND_MIXER_RECMASK:
174                                 val = SOUND_MASK_MIC | SOUND_MASK_LINE; 
175                                 break;
176                                 
177                         case SOUND_MIXER_CAPS:
178                                 val = 0; 
179                                 break;
180
181                         case SOUND_MIXER_MIC:
182                                 val = volumes[DEV_MIC];
183                                 break;
184                                 
185                         case SOUND_MIXER_LINE:
186                                 val = volumes[DEV_LINE];
187                                 break;
188
189                         case SOUND_MIXER_CD:
190                                 val = volumes[DEV_CD];
191                                 break;
192
193                         case SOUND_MIXER_VOLUME:
194                                 val = volumes[DEV_VOL];
195                                 break;
196
197                         case SOUND_MIXER_SYNTH:
198                                 val = volumes[DEV_GF1]; 
199                                 break;
200
201                         default:
202                                 return -EINVAL;
203                         }
204                         return put_user(val, (int __user *)arg);
205                 }
206         }
207         return -EINVAL;
208 }
209
210 static struct mixer_operations ics2101_mixer_operations =
211 {
212         .owner  = THIS_MODULE,
213         .id     = "ICS2101",
214         .name   = "ICS2101 Multimedia Mixer",
215         .ioctl  = ics2101_mixer_ioctl
216 };
217
218 int __init ics2101_mixer_init(void)
219 {
220         int i;
221         int n;
222
223         if ((n = sound_alloc_mixerdev()) != -1)
224         {
225                 mixer_devs[n] = &ics2101_mixer_operations;
226
227                 /*
228                  * Some GUS v3.7 cards had some channels flipped. Disable
229                  * the flipping feature if the model id is other than 5.
230                  */
231
232                 if (inb(u_MixSelect) != 5)
233                 {
234                         for (i = 0; i < ICS_MIXDEVS; i++)
235                                 left_fix[i] = 1;
236                         for (i = 0; i < ICS_MIXDEVS; i++)
237                                 right_fix[i] = 2;
238                 }
239                 set_volumes(DEV_GF1, 0x5a5a);
240                 set_volumes(DEV_CD, 0x5a5a);
241                 set_volumes(DEV_MIC, 0x0000);
242                 set_volumes(DEV_LINE, 0x5a5a);
243                 set_volumes(DEV_VOL, 0x5a5a);
244                 set_volumes(DEV_UNUSED, 0x0000);
245         }
246         return n;
247 }