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