blob: 08a0cb545822fb0d4357c1b3886c855e194fad31 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
James Courtier-Dutton27fe8642005-12-21 15:06:08 +010032#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Takashi Iwaieb4698f2005-11-17 14:50:13 +010034unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
Linus Torvalds1da177e2005-04-16 15:20:36 -070035{
36 unsigned long flags;
37 unsigned int regptr, val;
38 unsigned int mask;
39
40 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
41 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
42
43 if (reg & 0xff000000) {
44 unsigned char size, offset;
45
46 size = (reg >> 24) & 0x3f;
47 offset = (reg >> 16) & 0x1f;
48 mask = ((1 << size) - 1) << offset;
49
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);
54
55 return (val & mask) >> offset;
56 } else {
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);
61 return val;
62 }
63}
64
Takashi Iwaieb4698f2005-11-17 14:50:13 +010065void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066{
67 unsigned int regptr;
68 unsigned long flags;
69 unsigned int mask;
70
71 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
72 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
73
74 if (reg & 0xff000000) {
75 unsigned char size, offset;
76
77 size = (reg >> 24) & 0x3f;
78 offset = (reg >> 16) & 0x1f;
79 mask = ((1 << size) - 1) << offset;
80 data = (data << offset) & mask;
81
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);
87 } else {
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);
92 }
93}
94
Takashi Iwaieb4698f2005-11-17 14:50:13 +010095unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 unsigned int reg,
97 unsigned int chn)
98{
99 unsigned long flags;
100 unsigned int regptr, val;
101
102 regptr = (reg << 16) | chn;
103
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);
108 return val;
109}
110
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100111void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112 unsigned int reg,
113 unsigned int chn,
114 unsigned int data)
115{
116 unsigned int regptr;
117 unsigned long flags;
118
119 regptr = (reg << 16) | chn;
120
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);
125}
126
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100127int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
128 unsigned int data)
129{
130 unsigned int reset, set;
131 unsigned int reg, tmp;
132 int n, result;
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100133 if (emu->card_capabilities->ca0108_chip)
134 reg = 0x3c; /* PTR20, reg 0x3c */
135 else {
136 /* For other chip types the SPI register
137 * is currently unknown. */
138 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100139 }
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100140 if (data > 0xffff) /* Only 16bit values allowed */
141 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100142
143 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100144 reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100145 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);
149 result = 1;
150 /* Wait for status bit to return to 0 */
151 for (n=0;n<100;n++) {
152 udelay(10);
153 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
154 if (!(tmp & 0x10000)) {
155 result=0;
156 break;
157 }
158 }
159 if (result) return 1; /* Timed out */
160 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
161 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
162 return 0;
163}
164
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100165void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166{
167 unsigned long flags;
168 unsigned int enable;
169
170 spin_lock_irqsave(&emu->emu_lock, flags);
171 enable = inl(emu->port + INTE) | intrenb;
172 outl(enable, emu->port + INTE);
173 spin_unlock_irqrestore(&emu->emu_lock, flags);
174}
175
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100176void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177{
178 unsigned long flags;
179 unsigned int enable;
180
181 spin_lock_irqsave(&emu->emu_lock, flags);
182 enable = inl(emu->port + INTE) & ~intrenb;
183 outl(enable, emu->port + INTE);
184 spin_unlock_irqrestore(&emu->emu_lock, flags);
185}
186
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100187void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188{
189 unsigned long flags;
190 unsigned int val;
191
192 spin_lock_irqsave(&emu->emu_lock, flags);
193 /* voice interrupt */
194 if (voicenum >= 32) {
195 outl(CLIEH << 16, emu->port + PTR);
196 val = inl(emu->port + DATA);
197 val |= 1 << (voicenum - 32);
198 } else {
199 outl(CLIEL << 16, emu->port + PTR);
200 val = inl(emu->port + DATA);
201 val |= 1 << voicenum;
202 }
203 outl(val, emu->port + DATA);
204 spin_unlock_irqrestore(&emu->emu_lock, flags);
205}
206
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100207void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208{
209 unsigned long flags;
210 unsigned int val;
211
212 spin_lock_irqsave(&emu->emu_lock, flags);
213 /* voice interrupt */
214 if (voicenum >= 32) {
215 outl(CLIEH << 16, emu->port + PTR);
216 val = inl(emu->port + DATA);
217 val &= ~(1 << (voicenum - 32));
218 } else {
219 outl(CLIEL << 16, emu->port + PTR);
220 val = inl(emu->port + DATA);
221 val &= ~(1 << voicenum);
222 }
223 outl(val, emu->port + DATA);
224 spin_unlock_irqrestore(&emu->emu_lock, flags);
225}
226
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100227void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228{
229 unsigned long flags;
230
231 spin_lock_irqsave(&emu->emu_lock, flags);
232 /* voice interrupt */
233 if (voicenum >= 32) {
234 outl(CLIPH << 16, emu->port + PTR);
235 voicenum = 1 << (voicenum - 32);
236 } else {
237 outl(CLIPL << 16, emu->port + PTR);
238 voicenum = 1 << voicenum;
239 }
240 outl(voicenum, emu->port + DATA);
241 spin_unlock_irqrestore(&emu->emu_lock, flags);
242}
243
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100244void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
246 unsigned long flags;
247 unsigned int val;
248
249 spin_lock_irqsave(&emu->emu_lock, flags);
250 /* voice interrupt */
251 if (voicenum >= 32) {
252 outl(HLIEH << 16, emu->port + PTR);
253 val = inl(emu->port + DATA);
254 val |= 1 << (voicenum - 32);
255 } else {
256 outl(HLIEL << 16, emu->port + PTR);
257 val = inl(emu->port + DATA);
258 val |= 1 << voicenum;
259 }
260 outl(val, emu->port + DATA);
261 spin_unlock_irqrestore(&emu->emu_lock, flags);
262}
263
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100264void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265{
266 unsigned long flags;
267 unsigned int val;
268
269 spin_lock_irqsave(&emu->emu_lock, flags);
270 /* voice interrupt */
271 if (voicenum >= 32) {
272 outl(HLIEH << 16, emu->port + PTR);
273 val = inl(emu->port + DATA);
274 val &= ~(1 << (voicenum - 32));
275 } else {
276 outl(HLIEL << 16, emu->port + PTR);
277 val = inl(emu->port + DATA);
278 val &= ~(1 << voicenum);
279 }
280 outl(val, emu->port + DATA);
281 spin_unlock_irqrestore(&emu->emu_lock, flags);
282}
283
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100284void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285{
286 unsigned long flags;
287
288 spin_lock_irqsave(&emu->emu_lock, flags);
289 /* voice interrupt */
290 if (voicenum >= 32) {
291 outl(HLIPH << 16, emu->port + PTR);
292 voicenum = 1 << (voicenum - 32);
293 } else {
294 outl(HLIPL << 16, emu->port + PTR);
295 voicenum = 1 << voicenum;
296 }
297 outl(voicenum, emu->port + DATA);
298 spin_unlock_irqrestore(&emu->emu_lock, flags);
299}
300
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100301void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302{
303 unsigned long flags;
304 unsigned int sol;
305
306 spin_lock_irqsave(&emu->emu_lock, flags);
307 /* voice interrupt */
308 if (voicenum >= 32) {
309 outl(SOLEH << 16, emu->port + PTR);
310 sol = inl(emu->port + DATA);
311 sol |= 1 << (voicenum - 32);
312 } else {
313 outl(SOLEL << 16, emu->port + PTR);
314 sol = inl(emu->port + DATA);
315 sol |= 1 << voicenum;
316 }
317 outl(sol, emu->port + DATA);
318 spin_unlock_irqrestore(&emu->emu_lock, flags);
319}
320
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100321void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322{
323 unsigned long flags;
324 unsigned int sol;
325
326 spin_lock_irqsave(&emu->emu_lock, flags);
327 /* voice interrupt */
328 if (voicenum >= 32) {
329 outl(SOLEH << 16, emu->port + PTR);
330 sol = inl(emu->port + DATA);
331 sol &= ~(1 << (voicenum - 32));
332 } else {
333 outl(SOLEL << 16, emu->port + PTR);
334 sol = inl(emu->port + DATA);
335 sol &= ~(1 << voicenum);
336 }
337 outl(sol, emu->port + DATA);
338 spin_unlock_irqrestore(&emu->emu_lock, flags);
339}
340
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100341void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342{
343 volatile unsigned count;
344 unsigned int newtime = 0, curtime;
345
346 curtime = inl(emu->port + WC) >> 6;
347 while (wait-- > 0) {
348 count = 0;
349 while (count++ < 16384) {
350 newtime = inl(emu->port + WC) >> 6;
351 if (newtime != curtime)
352 break;
353 }
354 if (count >= 16384)
355 break;
356 curtime = newtime;
357 }
358}
359
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100360unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100362 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 unsigned long flags;
364 unsigned short val;
365
366 spin_lock_irqsave(&emu->emu_lock, flags);
367 outb(reg, emu->port + AC97ADDRESS);
368 val = inw(emu->port + AC97DATA);
369 spin_unlock_irqrestore(&emu->emu_lock, flags);
370 return val;
371}
372
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100373void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100375 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 unsigned long flags;
377
378 spin_lock_irqsave(&emu->emu_lock, flags);
379 outb(reg, emu->port + AC97ADDRESS);
380 outw(data, emu->port + AC97DATA);
381 spin_unlock_irqrestore(&emu->emu_lock, flags);
382}
383
384/*
385 * convert rate to pitch
386 */
387
388unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
389{
390 static u32 logMagTable[128] = {
391 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
392 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
393 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
394 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
395 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
396 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
397 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
398 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
399 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
400 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
401 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
402 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
403 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
404 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
405 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
406 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
407 };
408 static char logSlopeTable[128] = {
409 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
410 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
411 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
412 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
413 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
414 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
415 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
416 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
417 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
418 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
419 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
420 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
421 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
422 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
423 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
424 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
425 };
426 int i;
427
428 if (rate == 0)
429 return 0; /* Bail out if no leading "1" */
430 rate *= 11185; /* Scale 48000 to 0x20002380 */
431 for (i = 31; i > 0; i--) {
432 if (rate & 0x80000000) { /* Detect leading "1" */
433 return (((unsigned int) (i - 15) << 20) +
434 logMagTable[0x7f & (rate >> 24)] +
435 (0x7f & (rate >> 17)) *
436 logSlopeTable[0x7f & (rate >> 24)]);
437 }
438 rate <<= 1;
439 }
440
441 return 0; /* Should never reach this point */
442}
443