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