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 void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
71 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
72 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
74 if (reg & 0xff000000) {
75 unsigned char size, offset;
77 size = (reg >> 24) & 0x3f;
78 offset = (reg >> 16) & 0x1f;
79 mask = ((1 << size) - 1) << offset;
80 data = (data << offset) & mask;
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);
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);
95 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
100 unsigned int regptr, val;
102 regptr = (reg << 16) | chn;
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);
111 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
119 regptr = (reg << 16) | chn;
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);
127 int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
130 unsigned int reset, set;
131 unsigned int reg, tmp;
133 if (emu->card_capabilities->ca0108_chip)
134 reg = 0x3c; /* PTR20, reg 0x3c */
136 /* For other chip types the SPI register
137 * is currently unknown. */
140 if (data > 0xffff) /* Only 16bit values allowed */
143 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
144 reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
145 set = reset | 0x10000; /* Set xxx1xxxx */
146 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
147 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
148 snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
150 /* Wait for status bit to return to 0 */
151 for (n = 0; n < 100; n++) {
153 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
154 if (!(tmp & 0x10000)) {
159 if (result) /* Timed out */
161 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
162 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
166 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
171 spin_lock_irqsave(&emu->emu_lock, flags);
172 enable = inl(emu->port + INTE) | intrenb;
173 outl(enable, emu->port + INTE);
174 spin_unlock_irqrestore(&emu->emu_lock, flags);
177 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
182 spin_lock_irqsave(&emu->emu_lock, flags);
183 enable = inl(emu->port + INTE) & ~intrenb;
184 outl(enable, emu->port + INTE);
185 spin_unlock_irqrestore(&emu->emu_lock, flags);
188 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
193 spin_lock_irqsave(&emu->emu_lock, flags);
194 /* voice interrupt */
195 if (voicenum >= 32) {
196 outl(CLIEH << 16, emu->port + PTR);
197 val = inl(emu->port + DATA);
198 val |= 1 << (voicenum - 32);
200 outl(CLIEL << 16, emu->port + PTR);
201 val = inl(emu->port + DATA);
202 val |= 1 << voicenum;
204 outl(val, emu->port + DATA);
205 spin_unlock_irqrestore(&emu->emu_lock, flags);
208 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
213 spin_lock_irqsave(&emu->emu_lock, flags);
214 /* voice interrupt */
215 if (voicenum >= 32) {
216 outl(CLIEH << 16, emu->port + PTR);
217 val = inl(emu->port + DATA);
218 val &= ~(1 << (voicenum - 32));
220 outl(CLIEL << 16, emu->port + PTR);
221 val = inl(emu->port + DATA);
222 val &= ~(1 << voicenum);
224 outl(val, emu->port + DATA);
225 spin_unlock_irqrestore(&emu->emu_lock, flags);
228 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
232 spin_lock_irqsave(&emu->emu_lock, flags);
233 /* voice interrupt */
234 if (voicenum >= 32) {
235 outl(CLIPH << 16, emu->port + PTR);
236 voicenum = 1 << (voicenum - 32);
238 outl(CLIPL << 16, emu->port + PTR);
239 voicenum = 1 << voicenum;
241 outl(voicenum, emu->port + DATA);
242 spin_unlock_irqrestore(&emu->emu_lock, flags);
245 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
250 spin_lock_irqsave(&emu->emu_lock, flags);
251 /* voice interrupt */
252 if (voicenum >= 32) {
253 outl(HLIEH << 16, emu->port + PTR);
254 val = inl(emu->port + DATA);
255 val |= 1 << (voicenum - 32);
257 outl(HLIEL << 16, emu->port + PTR);
258 val = inl(emu->port + DATA);
259 val |= 1 << voicenum;
261 outl(val, emu->port + DATA);
262 spin_unlock_irqrestore(&emu->emu_lock, flags);
265 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
270 spin_lock_irqsave(&emu->emu_lock, flags);
271 /* voice interrupt */
272 if (voicenum >= 32) {
273 outl(HLIEH << 16, emu->port + PTR);
274 val = inl(emu->port + DATA);
275 val &= ~(1 << (voicenum - 32));
277 outl(HLIEL << 16, emu->port + PTR);
278 val = inl(emu->port + DATA);
279 val &= ~(1 << voicenum);
281 outl(val, emu->port + DATA);
282 spin_unlock_irqrestore(&emu->emu_lock, flags);
285 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
289 spin_lock_irqsave(&emu->emu_lock, flags);
290 /* voice interrupt */
291 if (voicenum >= 32) {
292 outl(HLIPH << 16, emu->port + PTR);
293 voicenum = 1 << (voicenum - 32);
295 outl(HLIPL << 16, emu->port + PTR);
296 voicenum = 1 << voicenum;
298 outl(voicenum, emu->port + DATA);
299 spin_unlock_irqrestore(&emu->emu_lock, flags);
302 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
307 spin_lock_irqsave(&emu->emu_lock, flags);
308 /* voice interrupt */
309 if (voicenum >= 32) {
310 outl(SOLEH << 16, emu->port + PTR);
311 sol = inl(emu->port + DATA);
312 sol |= 1 << (voicenum - 32);
314 outl(SOLEL << 16, emu->port + PTR);
315 sol = inl(emu->port + DATA);
316 sol |= 1 << voicenum;
318 outl(sol, emu->port + DATA);
319 spin_unlock_irqrestore(&emu->emu_lock, flags);
322 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
327 spin_lock_irqsave(&emu->emu_lock, flags);
328 /* voice interrupt */
329 if (voicenum >= 32) {
330 outl(SOLEH << 16, emu->port + PTR);
331 sol = inl(emu->port + DATA);
332 sol &= ~(1 << (voicenum - 32));
334 outl(SOLEL << 16, emu->port + PTR);
335 sol = inl(emu->port + DATA);
336 sol &= ~(1 << voicenum);
338 outl(sol, emu->port + DATA);
339 spin_unlock_irqrestore(&emu->emu_lock, flags);
342 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
344 volatile unsigned count;
345 unsigned int newtime = 0, curtime;
347 curtime = inl(emu->port + WC) >> 6;
350 while (count++ < 16384) {
351 newtime = inl(emu->port + WC) >> 6;
352 if (newtime != curtime)
361 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
363 struct snd_emu10k1 *emu = ac97->private_data;
367 spin_lock_irqsave(&emu->emu_lock, flags);
368 outb(reg, emu->port + AC97ADDRESS);
369 val = inw(emu->port + AC97DATA);
370 spin_unlock_irqrestore(&emu->emu_lock, flags);
374 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
376 struct snd_emu10k1 *emu = ac97->private_data;
379 spin_lock_irqsave(&emu->emu_lock, flags);
380 outb(reg, emu->port + AC97ADDRESS);
381 outw(data, emu->port + AC97DATA);
382 spin_unlock_irqrestore(&emu->emu_lock, flags);
386 * convert rate to pitch
389 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
391 static u32 logMagTable[128] = {
392 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
393 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
394 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
395 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
396 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
397 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
398 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
399 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
400 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
401 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
402 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
403 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
404 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
405 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
406 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
407 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
409 static char logSlopeTable[128] = {
410 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
411 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
412 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
413 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
414 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
415 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
416 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
417 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
418 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
419 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
420 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
421 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
422 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
423 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
424 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
425 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
430 return 0; /* Bail out if no leading "1" */
431 rate *= 11185; /* Scale 48000 to 0x20002380 */
432 for (i = 31; i > 0; i--) {
433 if (rate & 0x80000000) { /* Detect leading "1" */
434 return (((unsigned int) (i - 15) << 20) +
435 logMagTable[0x7f & (rate >> 24)] +
436 (0x7f & (rate >> 17)) *
437 logSlopeTable[0x7f & (rate >> 24)]);
442 return 0; /* Should never reach this point */