blob: 7d0cb9db428046f0b27a10375b9ad65b36efca10 [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;
133 if (emu->card_capabilities->ca0108_chip) {
134 reg=0x3c; /* PTR20, reg 0x3c */
135 } else {
136 return 1; /* For other cards types the SPI register is currently unknown. */
137 }
138 if (data > 0xffff) return 1; /* Only 16bit values allowed */
139
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);
146 result = 1;
147 /* Wait for status bit to return to 0 */
148 for (n=0;n<100;n++) {
149 udelay(10);
150 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
151 if (!(tmp & 0x10000)) {
152 result=0;
153 break;
154 }
155 }
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 */
159 return 0;
160}
161
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100162void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163{
164 unsigned long flags;
165 unsigned int enable;
166
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);
171}
172
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100173void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174{
175 unsigned long flags;
176 unsigned int enable;
177
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);
182}
183
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100184void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185{
186 unsigned long flags;
187 unsigned int val;
188
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);
195 } else {
196 outl(CLIEL << 16, emu->port + PTR);
197 val = inl(emu->port + DATA);
198 val |= 1 << voicenum;
199 }
200 outl(val, emu->port + DATA);
201 spin_unlock_irqrestore(&emu->emu_lock, flags);
202}
203
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100204void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205{
206 unsigned long flags;
207 unsigned int val;
208
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));
215 } else {
216 outl(CLIEL << 16, emu->port + PTR);
217 val = inl(emu->port + DATA);
218 val &= ~(1 << voicenum);
219 }
220 outl(val, emu->port + DATA);
221 spin_unlock_irqrestore(&emu->emu_lock, flags);
222}
223
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100224void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225{
226 unsigned long flags;
227
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);
233 } else {
234 outl(CLIPL << 16, emu->port + PTR);
235 voicenum = 1 << voicenum;
236 }
237 outl(voicenum, emu->port + DATA);
238 spin_unlock_irqrestore(&emu->emu_lock, flags);
239}
240
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100241void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242{
243 unsigned long flags;
244 unsigned int val;
245
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);
252 } else {
253 outl(HLIEL << 16, emu->port + PTR);
254 val = inl(emu->port + DATA);
255 val |= 1 << voicenum;
256 }
257 outl(val, emu->port + DATA);
258 spin_unlock_irqrestore(&emu->emu_lock, flags);
259}
260
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100261void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262{
263 unsigned long flags;
264 unsigned int val;
265
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));
272 } else {
273 outl(HLIEL << 16, emu->port + PTR);
274 val = inl(emu->port + DATA);
275 val &= ~(1 << voicenum);
276 }
277 outl(val, emu->port + DATA);
278 spin_unlock_irqrestore(&emu->emu_lock, flags);
279}
280
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100281void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282{
283 unsigned long flags;
284
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);
290 } else {
291 outl(HLIPL << 16, emu->port + PTR);
292 voicenum = 1 << voicenum;
293 }
294 outl(voicenum, emu->port + DATA);
295 spin_unlock_irqrestore(&emu->emu_lock, flags);
296}
297
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100298void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299{
300 unsigned long flags;
301 unsigned int sol;
302
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);
309 } else {
310 outl(SOLEL << 16, emu->port + PTR);
311 sol = inl(emu->port + DATA);
312 sol |= 1 << voicenum;
313 }
314 outl(sol, emu->port + DATA);
315 spin_unlock_irqrestore(&emu->emu_lock, flags);
316}
317
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100318void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
320 unsigned long flags;
321 unsigned int sol;
322
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));
329 } else {
330 outl(SOLEL << 16, emu->port + PTR);
331 sol = inl(emu->port + DATA);
332 sol &= ~(1 << voicenum);
333 }
334 outl(sol, emu->port + DATA);
335 spin_unlock_irqrestore(&emu->emu_lock, flags);
336}
337
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100338void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339{
340 volatile unsigned count;
341 unsigned int newtime = 0, curtime;
342
343 curtime = inl(emu->port + WC) >> 6;
344 while (wait-- > 0) {
345 count = 0;
346 while (count++ < 16384) {
347 newtime = inl(emu->port + WC) >> 6;
348 if (newtime != curtime)
349 break;
350 }
351 if (count >= 16384)
352 break;
353 curtime = newtime;
354 }
355}
356
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100357unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100359 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 unsigned long flags;
361 unsigned short val;
362
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);
367 return val;
368}
369
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100370void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100372 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 unsigned long flags;
374
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);
379}
380
381/*
382 * convert rate to pitch
383 */
384
385unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
386{
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
404 };
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
422 };
423 int i;
424
425 if (rate == 0)
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)]);
434 }
435 rate <<= 1;
436 }
437
438 return 0; /* Should never reach this point */
439}
440