Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[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 #include <linux/delay.h>
33
34 unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
35 {
36         unsigned long flags;
37         unsigned int regptr, val;
38         unsigned int mask;
39
40         mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
41         regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
42
43         if (reg & 0xff000000) {
44                 unsigned char size, offset;
45                 
46                 size = (reg >> 24) & 0x3f;
47                 offset = (reg >> 16) & 0x1f;
48                 mask = ((1 << size) - 1) << offset;
49                 
50                 spin_lock_irqsave(&emu->emu_lock, flags);
51                 outl(regptr, emu->port + PTR);
52                 val = inl(emu->port + DATA);
53                 spin_unlock_irqrestore(&emu->emu_lock, flags);
54                 
55                 return (val & mask) >> offset;
56         } else {
57                 spin_lock_irqsave(&emu->emu_lock, flags);
58                 outl(regptr, emu->port + PTR);
59                 val = inl(emu->port + DATA);
60                 spin_unlock_irqrestore(&emu->emu_lock, flags);
61                 return val;
62         }
63 }
64
65 EXPORT_SYMBOL(snd_emu10k1_ptr_read);
66
67 void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
68 {
69         unsigned int regptr;
70         unsigned long flags;
71         unsigned int mask;
72
73         mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
74         regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
75
76         if (reg & 0xff000000) {
77                 unsigned char size, offset;
78
79                 size = (reg >> 24) & 0x3f;
80                 offset = (reg >> 16) & 0x1f;
81                 mask = ((1 << size) - 1) << offset;
82                 data = (data << offset) & mask;
83
84                 spin_lock_irqsave(&emu->emu_lock, flags);
85                 outl(regptr, emu->port + PTR);
86                 data |= inl(emu->port + DATA) & ~mask;
87                 outl(data, emu->port + DATA);
88                 spin_unlock_irqrestore(&emu->emu_lock, flags);          
89         } else {
90                 spin_lock_irqsave(&emu->emu_lock, flags);
91                 outl(regptr, emu->port + PTR);
92                 outl(data, emu->port + DATA);
93                 spin_unlock_irqrestore(&emu->emu_lock, flags);
94         }
95 }
96
97 EXPORT_SYMBOL(snd_emu10k1_ptr_write);
98
99 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu, 
100                                           unsigned int reg, 
101                                           unsigned int chn)
102 {
103         unsigned long flags;
104         unsigned int regptr, val;
105   
106         regptr = (reg << 16) | chn;
107
108         spin_lock_irqsave(&emu->emu_lock, flags);
109         outl(regptr, emu->port + 0x20 + PTR);
110         val = inl(emu->port + 0x20 + DATA);
111         spin_unlock_irqrestore(&emu->emu_lock, flags);
112         return val;
113 }
114
115 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu, 
116                                    unsigned int reg, 
117                                    unsigned int chn, 
118                                    unsigned int data)
119 {
120         unsigned int regptr;
121         unsigned long flags;
122
123         regptr = (reg << 16) | chn;
124
125         spin_lock_irqsave(&emu->emu_lock, flags);
126         outl(regptr, emu->port + 0x20 + PTR);
127         outl(data, emu->port + 0x20 + DATA);
128         spin_unlock_irqrestore(&emu->emu_lock, flags);
129 }
130
131 int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
132                                    unsigned int data)
133 {
134         unsigned int reset, set;
135         unsigned int reg, tmp;
136         int n, result;
137         if (emu->card_capabilities->ca0108_chip)
138                 reg = 0x3c; /* PTR20, reg 0x3c */
139         else {
140                 /* For other chip types the SPI register
141                  * is currently unknown. */
142                 return 1;
143         }
144         if (data > 0xffff) /* Only 16bit values allowed */
145                 return 1;
146
147         tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
148         reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
149         set = reset | 0x10000; /* Set xxx1xxxx */
150         snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
151         tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
152         snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
153         result = 1;
154         /* Wait for status bit to return to 0 */
155         for (n = 0; n < 100; n++) {
156                 udelay(10);
157                 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
158                 if (!(tmp & 0x10000)) {
159                         result = 0;
160                         break;
161                 }
162         }
163         if (result) /* Timed out */
164                 return 1;
165         snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
166         tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
167         return 0;
168 }
169
170 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
171 {
172         unsigned long flags;
173         unsigned int enable;
174
175         spin_lock_irqsave(&emu->emu_lock, flags);
176         enable = inl(emu->port + INTE) | intrenb;
177         outl(enable, emu->port + INTE);
178         spin_unlock_irqrestore(&emu->emu_lock, flags);
179 }
180
181 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
182 {
183         unsigned long flags;
184         unsigned int enable;
185
186         spin_lock_irqsave(&emu->emu_lock, flags);
187         enable = inl(emu->port + INTE) & ~intrenb;
188         outl(enable, emu->port + INTE);
189         spin_unlock_irqrestore(&emu->emu_lock, flags);
190 }
191
192 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
193 {
194         unsigned long flags;
195         unsigned int val;
196
197         spin_lock_irqsave(&emu->emu_lock, flags);
198         /* voice interrupt */
199         if (voicenum >= 32) {
200                 outl(CLIEH << 16, emu->port + PTR);
201                 val = inl(emu->port + DATA);
202                 val |= 1 << (voicenum - 32);
203         } else {
204                 outl(CLIEL << 16, emu->port + PTR);
205                 val = inl(emu->port + DATA);
206                 val |= 1 << voicenum;
207         }
208         outl(val, emu->port + DATA);
209         spin_unlock_irqrestore(&emu->emu_lock, flags);
210 }
211
212 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
213 {
214         unsigned long flags;
215         unsigned int val;
216
217         spin_lock_irqsave(&emu->emu_lock, flags);
218         /* voice interrupt */
219         if (voicenum >= 32) {
220                 outl(CLIEH << 16, emu->port + PTR);
221                 val = inl(emu->port + DATA);
222                 val &= ~(1 << (voicenum - 32));
223         } else {
224                 outl(CLIEL << 16, emu->port + PTR);
225                 val = inl(emu->port + DATA);
226                 val &= ~(1 << voicenum);
227         }
228         outl(val, emu->port + DATA);
229         spin_unlock_irqrestore(&emu->emu_lock, flags);
230 }
231
232 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
233 {
234         unsigned long flags;
235
236         spin_lock_irqsave(&emu->emu_lock, flags);
237         /* voice interrupt */
238         if (voicenum >= 32) {
239                 outl(CLIPH << 16, emu->port + PTR);
240                 voicenum = 1 << (voicenum - 32);
241         } else {
242                 outl(CLIPL << 16, emu->port + PTR);
243                 voicenum = 1 << voicenum;
244         }
245         outl(voicenum, emu->port + DATA);
246         spin_unlock_irqrestore(&emu->emu_lock, flags);
247 }
248
249 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
250 {
251         unsigned long flags;
252         unsigned int val;
253
254         spin_lock_irqsave(&emu->emu_lock, flags);
255         /* voice interrupt */
256         if (voicenum >= 32) {
257                 outl(HLIEH << 16, emu->port + PTR);
258                 val = inl(emu->port + DATA);
259                 val |= 1 << (voicenum - 32);
260         } else {
261                 outl(HLIEL << 16, emu->port + PTR);
262                 val = inl(emu->port + DATA);
263                 val |= 1 << voicenum;
264         }
265         outl(val, emu->port + DATA);
266         spin_unlock_irqrestore(&emu->emu_lock, flags);
267 }
268
269 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
270 {
271         unsigned long flags;
272         unsigned int val;
273
274         spin_lock_irqsave(&emu->emu_lock, flags);
275         /* voice interrupt */
276         if (voicenum >= 32) {
277                 outl(HLIEH << 16, emu->port + PTR);
278                 val = inl(emu->port + DATA);
279                 val &= ~(1 << (voicenum - 32));
280         } else {
281                 outl(HLIEL << 16, emu->port + PTR);
282                 val = inl(emu->port + DATA);
283                 val &= ~(1 << voicenum);
284         }
285         outl(val, emu->port + DATA);
286         spin_unlock_irqrestore(&emu->emu_lock, flags);
287 }
288
289 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
290 {
291         unsigned long flags;
292
293         spin_lock_irqsave(&emu->emu_lock, flags);
294         /* voice interrupt */
295         if (voicenum >= 32) {
296                 outl(HLIPH << 16, emu->port + PTR);
297                 voicenum = 1 << (voicenum - 32);
298         } else {
299                 outl(HLIPL << 16, emu->port + PTR);
300                 voicenum = 1 << voicenum;
301         }
302         outl(voicenum, emu->port + DATA);
303         spin_unlock_irqrestore(&emu->emu_lock, flags);
304 }
305
306 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
307 {
308         unsigned long flags;
309         unsigned int sol;
310
311         spin_lock_irqsave(&emu->emu_lock, flags);
312         /* voice interrupt */
313         if (voicenum >= 32) {
314                 outl(SOLEH << 16, emu->port + PTR);
315                 sol = inl(emu->port + DATA);
316                 sol |= 1 << (voicenum - 32);
317         } else {
318                 outl(SOLEL << 16, emu->port + PTR);
319                 sol = inl(emu->port + DATA);
320                 sol |= 1 << voicenum;
321         }
322         outl(sol, emu->port + DATA);
323         spin_unlock_irqrestore(&emu->emu_lock, flags);
324 }
325
326 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
327 {
328         unsigned long flags;
329         unsigned int sol;
330
331         spin_lock_irqsave(&emu->emu_lock, flags);
332         /* voice interrupt */
333         if (voicenum >= 32) {
334                 outl(SOLEH << 16, emu->port + PTR);
335                 sol = inl(emu->port + DATA);
336                 sol &= ~(1 << (voicenum - 32));
337         } else {
338                 outl(SOLEL << 16, emu->port + PTR);
339                 sol = inl(emu->port + DATA);
340                 sol &= ~(1 << voicenum);
341         }
342         outl(sol, emu->port + DATA);
343         spin_unlock_irqrestore(&emu->emu_lock, flags);
344 }
345
346 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
347 {
348         volatile unsigned count;
349         unsigned int newtime = 0, curtime;
350
351         curtime = inl(emu->port + WC) >> 6;
352         while (wait-- > 0) {
353                 count = 0;
354                 while (count++ < 16384) {
355                         newtime = inl(emu->port + WC) >> 6;
356                         if (newtime != curtime)
357                                 break;
358                 }
359                 if (count >= 16384)
360                         break;
361                 curtime = newtime;
362         }
363 }
364
365 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
366 {
367         struct snd_emu10k1 *emu = ac97->private_data;
368         unsigned long flags;
369         unsigned short val;
370
371         spin_lock_irqsave(&emu->emu_lock, flags);
372         outb(reg, emu->port + AC97ADDRESS);
373         val = inw(emu->port + AC97DATA);
374         spin_unlock_irqrestore(&emu->emu_lock, flags);
375         return val;
376 }
377
378 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
379 {
380         struct snd_emu10k1 *emu = ac97->private_data;
381         unsigned long flags;
382
383         spin_lock_irqsave(&emu->emu_lock, flags);
384         outb(reg, emu->port + AC97ADDRESS);
385         outw(data, emu->port + AC97DATA);
386         spin_unlock_irqrestore(&emu->emu_lock, flags);
387 }
388
389 /*
390  *  convert rate to pitch
391  */
392
393 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
394 {
395         static u32 logMagTable[128] = {
396                 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
397                 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
398                 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
399                 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
400                 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
401                 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
402                 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
403                 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
404                 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
405                 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
406                 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
407                 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
408                 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
409                 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
410                 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
411                 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
412         };
413         static char logSlopeTable[128] = {
414                 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
415                 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
416                 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
417                 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
418                 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
419                 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
420                 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
421                 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
422                 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
423                 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
424                 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
425                 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
426                 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
427                 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
428                 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
429                 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
430         };
431         int i;
432
433         if (rate == 0)
434                 return 0;       /* Bail out if no leading "1" */
435         rate *= 11185;          /* Scale 48000 to 0x20002380 */
436         for (i = 31; i > 0; i--) {
437                 if (rate & 0x80000000) {        /* Detect leading "1" */
438                         return (((unsigned int) (i - 15) << 20) +
439                                logMagTable[0x7f & (rate >> 24)] +
440                                         (0x7f & (rate >> 17)) *
441                                         logSlopeTable[0x7f & (rate >> 24)]);
442                 }
443                 rate <<= 1;
444         }
445
446         return 0;               /* Should never reach this point */
447 }
448