ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / sound / pci / emu10k1 / io.c
1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@suse.cz>
3  *                   Creative Labs, Inc.
4  *  Routines for control of EMU10K1 chips
5  *
6  *  BUGS:
7  *    --
8  *
9  *  TODO:
10  *    --
11  *
12  *   This program is free software; you can redistribute it and/or modify
13  *   it under the terms of the GNU General Public License as published by
14  *   the Free Software Foundation; either version 2 of the License, or
15  *   (at your option) any later version.
16  *
17  *   This program is distributed in the hope that it will be useful,
18  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  *   GNU General Public License for more details.
21  *
22  *   You should have received a copy of the GNU General Public License
23  *   along with this program; if not, write to the Free Software
24  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
25  *
26  */
27
28 #include <sound/driver.h>
29 #include <linux/time.h>
30 #include <sound/core.h>
31 #include <sound/emu10k1.h>
32
33 unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn)
34 {
35         unsigned long flags;
36         unsigned int regptr, val;
37         unsigned int mask;
38
39         mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
40         regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
41
42         if (reg & 0xff000000) {
43                 unsigned char size, offset;
44                 
45                 size = (reg >> 24) & 0x3f;
46                 offset = (reg >> 16) & 0x1f;
47                 mask = ((1 << size) - 1) << offset;
48                 
49                 spin_lock_irqsave(&emu->emu_lock, flags);
50                 outl(regptr, emu->port + PTR);
51                 val = inl(emu->port + DATA);
52                 spin_unlock_irqrestore(&emu->emu_lock, flags);
53                 
54                 return (val & mask) >> offset;
55         } else {
56                 spin_lock_irqsave(&emu->emu_lock, flags);
57                 outl(regptr, emu->port + PTR);
58                 val = inl(emu->port + DATA);
59                 spin_unlock_irqrestore(&emu->emu_lock, flags);
60                 return val;
61         }
62 }
63
64 void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data)
65 {
66         unsigned int regptr;
67         unsigned long flags;
68         unsigned int mask;
69
70         mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
71         regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
72
73         if (reg & 0xff000000) {
74                 unsigned char size, offset;
75
76                 size = (reg >> 24) & 0x3f;
77                 offset = (reg >> 16) & 0x1f;
78                 mask = ((1 << size) - 1) << offset;
79                 data = (data << offset) & mask;
80
81                 spin_lock_irqsave(&emu->emu_lock, flags);
82                 outl(regptr, emu->port + PTR);
83                 data |= inl(emu->port + DATA) & ~mask;
84                 outl(data, emu->port + DATA);
85                 spin_unlock_irqrestore(&emu->emu_lock, flags);          
86         } else {
87                 spin_lock_irqsave(&emu->emu_lock, flags);
88                 outl(regptr, emu->port + PTR);
89                 outl(data, emu->port + DATA);
90                 spin_unlock_irqrestore(&emu->emu_lock, flags);
91         }
92 }
93
94 void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb)
95 {
96         unsigned long flags;
97         unsigned int enable;
98
99         spin_lock_irqsave(&emu->emu_lock, flags);
100         enable = inl(emu->port + INTE) | intrenb;
101         outl(enable, emu->port + INTE);
102         spin_unlock_irqrestore(&emu->emu_lock, flags);
103 }
104
105 void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb)
106 {
107         unsigned long flags;
108         unsigned int enable;
109
110         spin_lock_irqsave(&emu->emu_lock, flags);
111         enable = inl(emu->port + INTE) & ~intrenb;
112         outl(enable, emu->port + INTE);
113         spin_unlock_irqrestore(&emu->emu_lock, flags);
114 }
115
116 void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum)
117 {
118         unsigned long flags;
119         unsigned int val;
120
121         spin_lock_irqsave(&emu->emu_lock, flags);
122         /* voice interrupt */
123         if (voicenum >= 32) {
124                 outl(CLIEH << 16, emu->port + PTR);
125                 val = inl(emu->port + DATA);
126                 val |= 1 << (voicenum - 32);
127         } else {
128                 outl(CLIEL << 16, emu->port + PTR);
129                 val = inl(emu->port + DATA);
130                 val |= 1 << voicenum;
131         }
132         outl(val, emu->port + DATA);
133         spin_unlock_irqrestore(&emu->emu_lock, flags);
134 }
135
136 void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum)
137 {
138         unsigned long flags;
139         unsigned int val;
140
141         spin_lock_irqsave(&emu->emu_lock, flags);
142         /* voice interrupt */
143         if (voicenum >= 32) {
144                 outl(CLIEH << 16, emu->port + PTR);
145                 val = inl(emu->port + DATA);
146                 val &= ~(1 << (voicenum - 32));
147         } else {
148                 outl(CLIEL << 16, emu->port + PTR);
149                 val = inl(emu->port + DATA);
150                 val &= ~(1 << voicenum);
151         }
152         outl(val, emu->port + DATA);
153         spin_unlock_irqrestore(&emu->emu_lock, flags);
154 }
155
156 void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum)
157 {
158         unsigned long flags;
159
160         spin_lock_irqsave(&emu->emu_lock, flags);
161         /* voice interrupt */
162         if (voicenum >= 32) {
163                 outl(CLIPH << 16, emu->port + PTR);
164                 voicenum = 1 << (voicenum - 32);
165         } else {
166                 outl(CLIPL << 16, emu->port + PTR);
167                 voicenum = 1 << voicenum;
168         }
169         outl(voicenum, emu->port + DATA);
170         spin_unlock_irqrestore(&emu->emu_lock, flags);
171 }
172
173 void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum)
174 {
175         unsigned long flags;
176         unsigned int sol;
177
178         spin_lock_irqsave(&emu->emu_lock, flags);
179         /* voice interrupt */
180         if (voicenum >= 32) {
181                 outl(SOLEH << 16, emu->port + PTR);
182                 sol = inl(emu->port + DATA);
183                 sol |= 1 << (voicenum - 32);
184         } else {
185                 outl(SOLEL << 16, emu->port + PTR);
186                 sol = inl(emu->port + DATA);
187                 sol |= 1 << voicenum;
188         }
189         outl(sol, emu->port + DATA);
190         spin_unlock_irqrestore(&emu->emu_lock, flags);
191 }
192
193 void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum)
194 {
195         unsigned long flags;
196         unsigned int sol;
197
198         spin_lock_irqsave(&emu->emu_lock, flags);
199         /* voice interrupt */
200         if (voicenum >= 32) {
201                 outl(SOLEH << 16, emu->port + PTR);
202                 sol = inl(emu->port + DATA);
203                 sol &= ~(1 << (voicenum - 32));
204         } else {
205                 outl(SOLEL << 16, emu->port + PTR);
206                 sol = inl(emu->port + DATA);
207                 sol &= ~(1 << voicenum);
208         }
209         outl(sol, emu->port + DATA);
210         spin_unlock_irqrestore(&emu->emu_lock, flags);
211 }
212
213 void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait)
214 {
215         volatile unsigned count;
216         unsigned int newtime = 0, curtime;
217
218         curtime = inl(emu->port + WC) >> 6;
219         while (wait-- > 0) {
220                 count = 0;
221                 while (count++ < 16384) {
222                         newtime = inl(emu->port + WC) >> 6;
223                         if (newtime != curtime)
224                                 break;
225                 }
226                 if (count >= 16384)
227                         break;
228                 curtime = newtime;
229         }
230 }
231
232 unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg)
233 {
234         emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return -ENXIO);
235         unsigned long flags;
236         unsigned short val;
237
238         spin_lock_irqsave(&emu->emu_lock, flags);
239         outb(reg, emu->port + AC97ADDRESS);
240         val = inw(emu->port + AC97DATA);
241         spin_unlock_irqrestore(&emu->emu_lock, flags);
242         return val;
243 }
244
245 void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data)
246 {
247         emu10k1_t *emu = snd_magic_cast(emu10k1_t, ac97->private_data, return);
248         unsigned long flags;
249
250         spin_lock_irqsave(&emu->emu_lock, flags);
251         outb(reg, emu->port + AC97ADDRESS);
252         outw(data, emu->port + AC97DATA);
253         spin_unlock_irqrestore(&emu->emu_lock, flags);
254 }
255
256 /*
257  *  convert rate to pitch
258  */
259
260 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
261 {
262         static u32 logMagTable[128] = {
263                 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
264                 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
265                 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
266                 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
267                 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
268                 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
269                 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
270                 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
271                 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
272                 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
273                 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
274                 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
275                 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
276                 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
277                 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
278                 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
279         };
280         static char logSlopeTable[128] = {
281                 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
282                 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
283                 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
284                 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
285                 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
286                 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
287                 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
288                 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
289                 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
290                 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
291                 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
292                 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
293                 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
294                 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
295                 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
296                 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
297         };
298         int i;
299
300         if (rate == 0)
301                 return 0;       /* Bail out if no leading "1" */
302         rate *= 11185;          /* Scale 48000 to 0x20002380 */
303         for (i = 31; i > 0; i--) {
304                 if (rate & 0x80000000) {        /* Detect leading "1" */
305                         return (((unsigned int) (i - 15) << 20) +
306                                logMagTable[0x7f & (rate >> 24)] +
307                                         (0x7f & (rate >> 17)) *
308                                         logSlopeTable[0x7f & (rate >> 24)]);
309                 }
310                 rate <<= 1;
311         }
312
313         return 0;               /* Should never reach this point */
314 }
315
316 /*
317  *  Returns an attenuation based upon a cumulative volume value
318  *  Algorithm calculates 0x200 - 0x10 log2 (input)
319  */
320  
321 unsigned char snd_emu10k1_sum_vol_attn(unsigned int value)
322 {
323         unsigned short count = 16, ans;
324
325         if (value == 0)
326                 return 0xFF;
327
328         /* Find first SET bit. This is the integer part of the value */
329         while ((value & 0x10000) == 0) {
330                 value <<= 1;
331                 count--;
332         }
333
334         /* The REST of the data is the fractional part. */
335         ans = (unsigned short) (0x110 - ((count << 4) + ((value & 0x0FFFFL) >> 12)));
336         if (ans > 0xFF)
337                 ans = 0xFF;
338
339         return (unsigned char) ans;
340 }