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