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>
33 unsigned int snd_emu10k1_ptr_read(emu10k1_t
* emu
, unsigned int reg
, unsigned int chn
)
36 unsigned int regptr
, val
;
39 mask
= emu
->audigy
? A_PTR_ADDRESS_MASK
: PTR_ADDRESS_MASK
;
40 regptr
= ((reg
<< 16) & mask
) | (chn
& PTR_CHANNELNUM_MASK
);
42 if (reg
& 0xff000000) {
43 unsigned char size
, offset
;
45 size
= (reg
>> 24) & 0x3f;
46 offset
= (reg
>> 16) & 0x1f;
47 mask
= ((1 << size
) - 1) << offset
;
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
);
54 return (val
& mask
) >> offset
;
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
);
64 void snd_emu10k1_ptr_write(emu10k1_t
*emu
, unsigned int reg
, unsigned int chn
, unsigned int data
)
70 mask
= emu
->audigy
? A_PTR_ADDRESS_MASK
: PTR_ADDRESS_MASK
;
71 regptr
= ((reg
<< 16) & mask
) | (chn
& PTR_CHANNELNUM_MASK
);
73 if (reg
& 0xff000000) {
74 unsigned char size
, offset
;
76 size
= (reg
>> 24) & 0x3f;
77 offset
= (reg
>> 16) & 0x1f;
78 mask
= ((1 << size
) - 1) << offset
;
79 data
= (data
<< offset
) & mask
;
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
);
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
);
94 unsigned int snd_emu10k1_ptr20_read(emu10k1_t
* emu
,
99 unsigned int regptr
, val
;
101 regptr
= (reg
<< 16) | chn
;
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
);
110 void snd_emu10k1_ptr20_write(emu10k1_t
*emu
,
118 regptr
= (reg
<< 16) | chn
;
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
);
126 void snd_emu10k1_intr_enable(emu10k1_t
*emu
, unsigned int intrenb
)
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
);
137 void snd_emu10k1_intr_disable(emu10k1_t
*emu
, unsigned int intrenb
)
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
);
148 void snd_emu10k1_voice_intr_enable(emu10k1_t
*emu
, unsigned int voicenum
)
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);
160 outl(CLIEL
<< 16, emu
->port
+ PTR
);
161 val
= inl(emu
->port
+ DATA
);
162 val
|= 1 << voicenum
;
164 outl(val
, emu
->port
+ DATA
);
165 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
168 void snd_emu10k1_voice_intr_disable(emu10k1_t
*emu
, unsigned int voicenum
)
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));
180 outl(CLIEL
<< 16, emu
->port
+ PTR
);
181 val
= inl(emu
->port
+ DATA
);
182 val
&= ~(1 << voicenum
);
184 outl(val
, emu
->port
+ DATA
);
185 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
188 void snd_emu10k1_voice_intr_ack(emu10k1_t
*emu
, unsigned int voicenum
)
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);
198 outl(CLIPL
<< 16, emu
->port
+ PTR
);
199 voicenum
= 1 << voicenum
;
201 outl(voicenum
, emu
->port
+ DATA
);
202 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
205 void snd_emu10k1_voice_half_loop_intr_enable(emu10k1_t
*emu
, unsigned int voicenum
)
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);
217 outl(HLIEL
<< 16, emu
->port
+ PTR
);
218 val
= inl(emu
->port
+ DATA
);
219 val
|= 1 << voicenum
;
221 outl(val
, emu
->port
+ DATA
);
222 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
225 void snd_emu10k1_voice_half_loop_intr_disable(emu10k1_t
*emu
, unsigned int voicenum
)
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));
237 outl(HLIEL
<< 16, emu
->port
+ PTR
);
238 val
= inl(emu
->port
+ DATA
);
239 val
&= ~(1 << voicenum
);
241 outl(val
, emu
->port
+ DATA
);
242 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
245 void snd_emu10k1_voice_half_loop_intr_ack(emu10k1_t
*emu
, unsigned int voicenum
)
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);
255 outl(HLIPL
<< 16, emu
->port
+ PTR
);
256 voicenum
= 1 << voicenum
;
258 outl(voicenum
, emu
->port
+ DATA
);
259 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
262 void snd_emu10k1_voice_set_loop_stop(emu10k1_t
*emu
, unsigned int voicenum
)
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);
274 outl(SOLEL
<< 16, emu
->port
+ PTR
);
275 sol
= inl(emu
->port
+ DATA
);
276 sol
|= 1 << voicenum
;
278 outl(sol
, emu
->port
+ DATA
);
279 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
282 void snd_emu10k1_voice_clear_loop_stop(emu10k1_t
*emu
, unsigned int voicenum
)
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));
294 outl(SOLEL
<< 16, emu
->port
+ PTR
);
295 sol
= inl(emu
->port
+ DATA
);
296 sol
&= ~(1 << voicenum
);
298 outl(sol
, emu
->port
+ DATA
);
299 spin_unlock_irqrestore(&emu
->emu_lock
, flags
);
302 void snd_emu10k1_wait(emu10k1_t
*emu
, unsigned int wait
)
304 volatile unsigned count
;
305 unsigned int newtime
= 0, curtime
;
307 curtime
= inl(emu
->port
+ WC
) >> 6;
310 while (count
++ < 16384) {
311 newtime
= inl(emu
->port
+ WC
) >> 6;
312 if (newtime
!= curtime
)
321 unsigned short snd_emu10k1_ac97_read(ac97_t
*ac97
, unsigned short reg
)
323 emu10k1_t
*emu
= ac97
->private_data
;
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
);
334 void snd_emu10k1_ac97_write(ac97_t
*ac97
, unsigned short reg
, unsigned short data
)
336 emu10k1_t
*emu
= ac97
->private_data
;
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
);
346 * convert rate to pitch
349 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate
)
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
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
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)]);
402 return 0; /* Should never reach this point */