blob: ef5304df8c1184a25c5955f94d6c9132e9ddea51 [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 */
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100151 for (n = 0; n < 100; n++) {
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100152 udelay(10);
153 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
154 if (!(tmp & 0x10000)) {
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100155 result = 0;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100156 break;
157 }
158 }
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100159 if (result) /* Timed out */
160 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100161 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
162 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
163 return 0;
164}
165
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100166void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167{
168 unsigned long flags;
169 unsigned int enable;
170
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);
175}
176
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100177void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178{
179 unsigned long flags;
180 unsigned int enable;
181
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);
186}
187
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100188void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189{
190 unsigned long flags;
191 unsigned int val;
192
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);
199 } else {
200 outl(CLIEL << 16, emu->port + PTR);
201 val = inl(emu->port + DATA);
202 val |= 1 << voicenum;
203 }
204 outl(val, emu->port + DATA);
205 spin_unlock_irqrestore(&emu->emu_lock, flags);
206}
207
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100208void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209{
210 unsigned long flags;
211 unsigned int val;
212
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));
219 } else {
220 outl(CLIEL << 16, emu->port + PTR);
221 val = inl(emu->port + DATA);
222 val &= ~(1 << voicenum);
223 }
224 outl(val, emu->port + DATA);
225 spin_unlock_irqrestore(&emu->emu_lock, flags);
226}
227
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100228void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229{
230 unsigned long flags;
231
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);
237 } else {
238 outl(CLIPL << 16, emu->port + PTR);
239 voicenum = 1 << voicenum;
240 }
241 outl(voicenum, emu->port + DATA);
242 spin_unlock_irqrestore(&emu->emu_lock, flags);
243}
244
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100245void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246{
247 unsigned long flags;
248 unsigned int val;
249
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);
256 } else {
257 outl(HLIEL << 16, emu->port + PTR);
258 val = inl(emu->port + DATA);
259 val |= 1 << voicenum;
260 }
261 outl(val, emu->port + DATA);
262 spin_unlock_irqrestore(&emu->emu_lock, flags);
263}
264
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100265void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266{
267 unsigned long flags;
268 unsigned int val;
269
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));
276 } else {
277 outl(HLIEL << 16, emu->port + PTR);
278 val = inl(emu->port + DATA);
279 val &= ~(1 << voicenum);
280 }
281 outl(val, emu->port + DATA);
282 spin_unlock_irqrestore(&emu->emu_lock, flags);
283}
284
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100285void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
287 unsigned long flags;
288
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);
294 } else {
295 outl(HLIPL << 16, emu->port + PTR);
296 voicenum = 1 << voicenum;
297 }
298 outl(voicenum, emu->port + DATA);
299 spin_unlock_irqrestore(&emu->emu_lock, flags);
300}
301
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100302void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
304 unsigned long flags;
305 unsigned int sol;
306
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);
313 } else {
314 outl(SOLEL << 16, emu->port + PTR);
315 sol = inl(emu->port + DATA);
316 sol |= 1 << voicenum;
317 }
318 outl(sol, emu->port + DATA);
319 spin_unlock_irqrestore(&emu->emu_lock, flags);
320}
321
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100322void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323{
324 unsigned long flags;
325 unsigned int sol;
326
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));
333 } else {
334 outl(SOLEL << 16, emu->port + PTR);
335 sol = inl(emu->port + DATA);
336 sol &= ~(1 << voicenum);
337 }
338 outl(sol, emu->port + DATA);
339 spin_unlock_irqrestore(&emu->emu_lock, flags);
340}
341
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100342void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343{
344 volatile unsigned count;
345 unsigned int newtime = 0, curtime;
346
347 curtime = inl(emu->port + WC) >> 6;
348 while (wait-- > 0) {
349 count = 0;
350 while (count++ < 16384) {
351 newtime = inl(emu->port + WC) >> 6;
352 if (newtime != curtime)
353 break;
354 }
355 if (count >= 16384)
356 break;
357 curtime = newtime;
358 }
359}
360
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100361unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100363 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 unsigned long flags;
365 unsigned short val;
366
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);
371 return val;
372}
373
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100374void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100376 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 unsigned long flags;
378
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);
383}
384
385/*
386 * convert rate to pitch
387 */
388
389unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
390{
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
408 };
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
426 };
427 int i;
428
429 if (rate == 0)
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)]);
438 }
439 rate <<= 1;
440 }
441
442 return 0; /* Should never reach this point */
443}
444