2 * Copyright (c) by Jaroslav Kysela <perex@suse.cz>
4 * Routines for control of EMU10K1 chips
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.
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.
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
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>
34 unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1
* emu
, unsigned int reg
, unsigned int chn
)
37 unsigned int regptr
, val
;
40 mask
= emu
->audigy
? A_PTR_ADDRESS_MASK
: PTR_ADDRESS_MASK
;
41 regptr
= ((reg
<< 16) & mask
) | (chn
& PTR_CHANNELNUM_MASK
);
43 if (reg
& 0xff000000) {
44 unsigned char size
, offset
;
46 size
= (reg
>> 24) & 0x3f;
47 offset
= (reg
>> 16) & 0x1f;
48 mask
= ((1 << size
) - 1) << offset
;
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
);
55 return (val
& mask
) >> offset
;
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
);
65 EXPORT_SYMBOL(snd_emu10k1_ptr_read
);
67 void snd_emu10k1_ptr_write(struct snd_emu10k1
*emu
, unsigned int reg
, unsigned int chn
, unsigned int data
)
73 mask
= emu
->audigy
? A_PTR_ADDRESS_MASK
: PTR_ADDRESS_MASK
;
74 regptr
= ((reg
<< 16) & mask
) | (chn
& PTR_CHANNELNUM_MASK
);
76 if (reg
& 0xff000000) {
77 unsigned char size
, offset
;
79 size
= (reg
>> 24) & 0x3f;
80 offset
= (reg
>> 16) & 0x1f;
81 mask
= ((1 << size
) - 1) << offset
;
82 data
= (data
<< offset
) & mask
;
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
);
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
);
97 EXPORT_SYMBOL(snd_emu10k1_ptr_write
);
99 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1
* emu
,
104 unsigned int regptr
, val
;
106 regptr
= (reg
<< 16) | chn
;
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
);
115 void snd_emu10k1_ptr20_write(struct snd_emu10k1
*emu
,
123 regptr
= (reg
<< 16) | chn
;
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
);
131 int snd_emu10k1_spi_write(struct snd_emu10k1
* emu
,
134 unsigned int reset
, set
;
135 unsigned int reg
, tmp
;
137 if (emu
->card_capabilities
->ca0108_chip
)
138 reg
= 0x3c; /* PTR20, reg 0x3c */
140 /* For other chip types the SPI register
141 * is currently unknown. */
144 if (data
> 0xffff) /* Only 16bit values allowed */
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
);
154 /* Wait for status bit to return to 0 */
155 for (n
= 0; n
< 100; n
++) {
157 tmp
= snd_emu10k1_ptr20_read(emu
, reg
, 0);
158 if (!(tmp
& 0x10000)) {
163 if (result
) /* Timed out */
165 snd_emu10k1_ptr20_write(emu
, reg
, 0, reset
| data
);
166 tmp
= snd_emu10k1_ptr20_read(emu
, reg
, 0); /* Write post */
170 void snd_emu10k1_intr_enable(struct snd_emu10k1
*emu
, unsigned int intrenb
)
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
);
181 void snd_emu10k1_intr_disable(struct snd_emu10k1
*emu
, unsigned int intrenb
)
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
);
192 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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);
204 outl(CLIEL
<< 16, emu
->port
+ PTR
);
205 val
= inl(emu
->port
+ DATA
);
206 val
|= 1 << voicenum
;
208 outl(val
, emu
->port
+ DATA
);
209 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
212 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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));
224 outl(CLIEL
<< 16, emu
->port
+ PTR
);
225 val
= inl(emu
->port
+ DATA
);
226 val
&= ~(1 << voicenum
);
228 outl(val
, emu
->port
+ DATA
);
229 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
232 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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);
242 outl(CLIPL
<< 16, emu
->port
+ PTR
);
243 voicenum
= 1 << voicenum
;
245 outl(voicenum
, emu
->port
+ DATA
);
246 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
249 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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);
261 outl(HLIEL
<< 16, emu
->port
+ PTR
);
262 val
= inl(emu
->port
+ DATA
);
263 val
|= 1 << voicenum
;
265 outl(val
, emu
->port
+ DATA
);
266 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
269 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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));
281 outl(HLIEL
<< 16, emu
->port
+ PTR
);
282 val
= inl(emu
->port
+ DATA
);
283 val
&= ~(1 << voicenum
);
285 outl(val
, emu
->port
+ DATA
);
286 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
289 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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);
299 outl(HLIPL
<< 16, emu
->port
+ PTR
);
300 voicenum
= 1 << voicenum
;
302 outl(voicenum
, emu
->port
+ DATA
);
303 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
306 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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);
318 outl(SOLEL
<< 16, emu
->port
+ PTR
);
319 sol
= inl(emu
->port
+ DATA
);
320 sol
|= 1 << voicenum
;
322 outl(sol
, emu
->port
+ DATA
);
323 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
326 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1
*emu
, unsigned int voicenum
)
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));
338 outl(SOLEL
<< 16, emu
->port
+ PTR
);
339 sol
= inl(emu
->port
+ DATA
);
340 sol
&= ~(1 << voicenum
);
342 outl(sol
, emu
->port
+ DATA
);
343 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
346 void snd_emu10k1_wait(struct snd_emu10k1
*emu
, unsigned int wait
)
348 volatile unsigned count
;
349 unsigned int newtime
= 0, curtime
;
351 curtime
= inl(emu
->port
+ WC
) >> 6;
354 while (count
++ < 16384) {
355 newtime
= inl(emu
->port
+ WC
) >> 6;
356 if (newtime
!= curtime
)
365 unsigned short snd_emu10k1_ac97_read(struct snd_ac97
*ac97
, unsigned short reg
)
367 struct snd_emu10k1
*emu
= ac97
->private_data
;
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
);
378 void snd_emu10k1_ac97_write(struct snd_ac97
*ac97
, unsigned short reg
, unsigned short data
)
380 struct snd_emu10k1
*emu
= ac97
->private_data
;
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
);
390 * convert rate to pitch
393 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate
)
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
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
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)]);
446 return 0; /* Should never reach this point */