]> err.no Git - linux-2.6/blob - sound/pci/emu10k1/io.c
[ALSA] snd-emu10k1: Added support for emu1010, including E-Mu 1212m and E-Mu 1820m
[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 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 int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
171 {
172         if (reg < 0 || reg > 0x3f)
173                 return 1;
174         reg += 0x40; /* 0x40 upwards are registers. */
175         if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
176                 return 1;
177         outl(reg, emu->port + A_IOCFG);
178         udelay(10);
179         outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
180         udelay(10);
181         outl(value, emu->port + A_IOCFG);
182         udelay(10);
183         outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
184
185         return 0;
186 }
187
188 int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value)
189 {
190         if (reg < 0 || reg > 0x3f)
191                 return 1;
192         reg += 0x40; /* 0x40 upwards are registers. */
193         outl(reg, emu->port + A_IOCFG);
194         udelay(10);
195         outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
196         udelay(10);
197         *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
198
199         return 0;
200 }
201
202 /* Each Destination has one and only one Source,
203  * but one Source can feed any number of Destinations simultaneously.
204  */
205 int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src)
206 {
207         snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
208         snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
209         snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
210         snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
211
212         return 0;
213 }
214
215 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
216 {
217         unsigned long flags;
218         unsigned int enable;
219
220         spin_lock_irqsave(&emu->emu_lock, flags);
221         enable = inl(emu->port + INTE) | intrenb;
222         outl(enable, emu->port + INTE);
223         spin_unlock_irqrestore(&emu->emu_lock, flags);
224 }
225
226 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
227 {
228         unsigned long flags;
229         unsigned int enable;
230
231         spin_lock_irqsave(&emu->emu_lock, flags);
232         enable = inl(emu->port + INTE) & ~intrenb;
233         outl(enable, emu->port + INTE);
234         spin_unlock_irqrestore(&emu->emu_lock, flags);
235 }
236
237 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
238 {
239         unsigned long flags;
240         unsigned int val;
241
242         spin_lock_irqsave(&emu->emu_lock, flags);
243         /* voice interrupt */
244         if (voicenum >= 32) {
245                 outl(CLIEH << 16, emu->port + PTR);
246                 val = inl(emu->port + DATA);
247                 val |= 1 << (voicenum - 32);
248         } else {
249                 outl(CLIEL << 16, emu->port + PTR);
250                 val = inl(emu->port + DATA);
251                 val |= 1 << voicenum;
252         }
253         outl(val, emu->port + DATA);
254         spin_unlock_irqrestore(&emu->emu_lock, flags);
255 }
256
257 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
258 {
259         unsigned long flags;
260         unsigned int val;
261
262         spin_lock_irqsave(&emu->emu_lock, flags);
263         /* voice interrupt */
264         if (voicenum >= 32) {
265                 outl(CLIEH << 16, emu->port + PTR);
266                 val = inl(emu->port + DATA);
267                 val &= ~(1 << (voicenum - 32));
268         } else {
269                 outl(CLIEL << 16, emu->port + PTR);
270                 val = inl(emu->port + DATA);
271                 val &= ~(1 << voicenum);
272         }
273         outl(val, emu->port + DATA);
274         spin_unlock_irqrestore(&emu->emu_lock, flags);
275 }
276
277 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
278 {
279         unsigned long flags;
280
281         spin_lock_irqsave(&emu->emu_lock, flags);
282         /* voice interrupt */
283         if (voicenum >= 32) {
284                 outl(CLIPH << 16, emu->port + PTR);
285                 voicenum = 1 << (voicenum - 32);
286         } else {
287                 outl(CLIPL << 16, emu->port + PTR);
288                 voicenum = 1 << voicenum;
289         }
290         outl(voicenum, emu->port + DATA);
291         spin_unlock_irqrestore(&emu->emu_lock, flags);
292 }
293
294 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
295 {
296         unsigned long flags;
297         unsigned int val;
298
299         spin_lock_irqsave(&emu->emu_lock, flags);
300         /* voice interrupt */
301         if (voicenum >= 32) {
302                 outl(HLIEH << 16, emu->port + PTR);
303                 val = inl(emu->port + DATA);
304                 val |= 1 << (voicenum - 32);
305         } else {
306                 outl(HLIEL << 16, emu->port + PTR);
307                 val = inl(emu->port + DATA);
308                 val |= 1 << voicenum;
309         }
310         outl(val, emu->port + DATA);
311         spin_unlock_irqrestore(&emu->emu_lock, flags);
312 }
313
314 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
315 {
316         unsigned long flags;
317         unsigned int val;
318
319         spin_lock_irqsave(&emu->emu_lock, flags);
320         /* voice interrupt */
321         if (voicenum >= 32) {
322                 outl(HLIEH << 16, emu->port + PTR);
323                 val = inl(emu->port + DATA);
324                 val &= ~(1 << (voicenum - 32));
325         } else {
326                 outl(HLIEL << 16, emu->port + PTR);
327                 val = inl(emu->port + DATA);
328                 val &= ~(1 << voicenum);
329         }
330         outl(val, emu->port + DATA);
331         spin_unlock_irqrestore(&emu->emu_lock, flags);
332 }
333
334 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
335 {
336         unsigned long flags;
337
338         spin_lock_irqsave(&emu->emu_lock, flags);
339         /* voice interrupt */
340         if (voicenum >= 32) {
341                 outl(HLIPH << 16, emu->port + PTR);
342                 voicenum = 1 << (voicenum - 32);
343         } else {
344                 outl(HLIPL << 16, emu->port + PTR);
345                 voicenum = 1 << voicenum;
346         }
347         outl(voicenum, emu->port + DATA);
348         spin_unlock_irqrestore(&emu->emu_lock, flags);
349 }
350
351 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
352 {
353         unsigned long flags;
354         unsigned int sol;
355
356         spin_lock_irqsave(&emu->emu_lock, flags);
357         /* voice interrupt */
358         if (voicenum >= 32) {
359                 outl(SOLEH << 16, emu->port + PTR);
360                 sol = inl(emu->port + DATA);
361                 sol |= 1 << (voicenum - 32);
362         } else {
363                 outl(SOLEL << 16, emu->port + PTR);
364                 sol = inl(emu->port + DATA);
365                 sol |= 1 << voicenum;
366         }
367         outl(sol, emu->port + DATA);
368         spin_unlock_irqrestore(&emu->emu_lock, flags);
369 }
370
371 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
372 {
373         unsigned long flags;
374         unsigned int sol;
375
376         spin_lock_irqsave(&emu->emu_lock, flags);
377         /* voice interrupt */
378         if (voicenum >= 32) {
379                 outl(SOLEH << 16, emu->port + PTR);
380                 sol = inl(emu->port + DATA);
381                 sol &= ~(1 << (voicenum - 32));
382         } else {
383                 outl(SOLEL << 16, emu->port + PTR);
384                 sol = inl(emu->port + DATA);
385                 sol &= ~(1 << voicenum);
386         }
387         outl(sol, emu->port + DATA);
388         spin_unlock_irqrestore(&emu->emu_lock, flags);
389 }
390
391 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
392 {
393         volatile unsigned count;
394         unsigned int newtime = 0, curtime;
395
396         curtime = inl(emu->port + WC) >> 6;
397         while (wait-- > 0) {
398                 count = 0;
399                 while (count++ < 16384) {
400                         newtime = inl(emu->port + WC) >> 6;
401                         if (newtime != curtime)
402                                 break;
403                 }
404                 if (count >= 16384)
405                         break;
406                 curtime = newtime;
407         }
408 }
409
410 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
411 {
412         struct snd_emu10k1 *emu = ac97->private_data;
413         unsigned long flags;
414         unsigned short val;
415
416         spin_lock_irqsave(&emu->emu_lock, flags);
417         outb(reg, emu->port + AC97ADDRESS);
418         val = inw(emu->port + AC97DATA);
419         spin_unlock_irqrestore(&emu->emu_lock, flags);
420         return val;
421 }
422
423 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
424 {
425         struct snd_emu10k1 *emu = ac97->private_data;
426         unsigned long flags;
427
428         spin_lock_irqsave(&emu->emu_lock, flags);
429         outb(reg, emu->port + AC97ADDRESS);
430         outw(data, emu->port + AC97DATA);
431         spin_unlock_irqrestore(&emu->emu_lock, flags);
432 }
433
434 /*
435  *  convert rate to pitch
436  */
437
438 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
439 {
440         static u32 logMagTable[128] = {
441                 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
442                 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
443                 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
444                 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
445                 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
446                 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
447                 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
448                 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
449                 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
450                 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
451                 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
452                 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
453                 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
454                 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
455                 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
456                 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
457         };
458         static char logSlopeTable[128] = {
459                 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
460                 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
461                 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
462                 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
463                 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
464                 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
465                 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
466                 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
467                 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
468                 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
469                 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
470                 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
471                 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
472                 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
473                 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
474                 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
475         };
476         int i;
477
478         if (rate == 0)
479                 return 0;       /* Bail out if no leading "1" */
480         rate *= 11185;          /* Scale 48000 to 0x20002380 */
481         for (i = 31; i > 0; i--) {
482                 if (rate & 0x80000000) {        /* Detect leading "1" */
483                         return (((unsigned int) (i - 15) << 20) +
484                                logMagTable[0x7f & (rate >> 24)] +
485                                         (0x7f & (rate >> 17)) *
486                                         logSlopeTable[0x7f & (rate >> 24)]);
487                 }
488                 rate <<= 1;
489         }
490
491         return 0;               /* Should never reach this point */
492 }
493