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 return 1; /* For other cards types the SPI register is currently unknown. */
138 if (data > 0xffff) return 1; /* Only 16bit values allowed */
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);
147 /* Wait for status bit to return to 0 */
148 for (n=0;n<100;n++) {
150 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
151 if (!(tmp & 0x10000)) {
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 */
162 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
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);
173 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
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);
184 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
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);
196 outl(CLIEL << 16, emu->port + PTR);
197 val = inl(emu->port + DATA);
198 val |= 1 << voicenum;
200 outl(val, emu->port + DATA);
201 spin_unlock_irqrestore(&emu->emu_lock, flags);
204 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
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));
216 outl(CLIEL << 16, emu->port + PTR);
217 val = inl(emu->port + DATA);
218 val &= ~(1 << voicenum);
220 outl(val, emu->port + DATA);
221 spin_unlock_irqrestore(&emu->emu_lock, flags);
224 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
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);
234 outl(CLIPL << 16, emu->port + PTR);
235 voicenum = 1 << voicenum;
237 outl(voicenum, emu->port + DATA);
238 spin_unlock_irqrestore(&emu->emu_lock, flags);
241 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
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);
253 outl(HLIEL << 16, emu->port + PTR);
254 val = inl(emu->port + DATA);
255 val |= 1 << voicenum;
257 outl(val, emu->port + DATA);
258 spin_unlock_irqrestore(&emu->emu_lock, flags);
261 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
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));
273 outl(HLIEL << 16, emu->port + PTR);
274 val = inl(emu->port + DATA);
275 val &= ~(1 << voicenum);
277 outl(val, emu->port + DATA);
278 spin_unlock_irqrestore(&emu->emu_lock, flags);
281 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
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);
291 outl(HLIPL << 16, emu->port + PTR);
292 voicenum = 1 << voicenum;
294 outl(voicenum, emu->port + DATA);
295 spin_unlock_irqrestore(&emu->emu_lock, flags);
298 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
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);
310 outl(SOLEL << 16, emu->port + PTR);
311 sol = inl(emu->port + DATA);
312 sol |= 1 << voicenum;
314 outl(sol, emu->port + DATA);
315 spin_unlock_irqrestore(&emu->emu_lock, flags);
318 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
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));
330 outl(SOLEL << 16, emu->port + PTR);
331 sol = inl(emu->port + DATA);
332 sol &= ~(1 << voicenum);
334 outl(sol, emu->port + DATA);
335 spin_unlock_irqrestore(&emu->emu_lock, flags);
338 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
340 volatile unsigned count;
341 unsigned int newtime = 0, curtime;
343 curtime = inl(emu->port + WC) >> 6;
346 while (count++ < 16384) {
347 newtime = inl(emu->port + WC) >> 6;
348 if (newtime != curtime)
357 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
359 struct snd_emu10k1 *emu = ac97->private_data;
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);
370 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
372 struct snd_emu10k1 *emu = ac97->private_data;
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);
382 * convert rate to pitch
385 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
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
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
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)]);
438 return 0; /* Should never reach this point */