blob: 029e7856c43beaa7d171a22696d95410119b7b43 [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 Iwai2dd31de2006-04-28 15:13:39 +020065EXPORT_SYMBOL(snd_emu10k1_ptr_read);
66
Takashi Iwaieb4698f2005-11-17 14:50:13 +010067void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070068{
69 unsigned int regptr;
70 unsigned long flags;
71 unsigned int mask;
72
73 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
74 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
75
76 if (reg & 0xff000000) {
77 unsigned char size, offset;
78
79 size = (reg >> 24) & 0x3f;
80 offset = (reg >> 16) & 0x1f;
81 mask = ((1 << size) - 1) << offset;
82 data = (data << offset) & mask;
83
84 spin_lock_irqsave(&emu->emu_lock, flags);
85 outl(regptr, emu->port + PTR);
86 data |= inl(emu->port + DATA) & ~mask;
87 outl(data, emu->port + DATA);
88 spin_unlock_irqrestore(&emu->emu_lock, flags);
89 } else {
90 spin_lock_irqsave(&emu->emu_lock, flags);
91 outl(regptr, emu->port + PTR);
92 outl(data, emu->port + DATA);
93 spin_unlock_irqrestore(&emu->emu_lock, flags);
94 }
95}
96
Takashi Iwai2dd31de2006-04-28 15:13:39 +020097EXPORT_SYMBOL(snd_emu10k1_ptr_write);
98
Takashi Iwaieb4698f2005-11-17 14:50:13 +010099unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100 unsigned int reg,
101 unsigned int chn)
102{
103 unsigned long flags;
104 unsigned int regptr, val;
105
106 regptr = (reg << 16) | chn;
107
108 spin_lock_irqsave(&emu->emu_lock, flags);
109 outl(regptr, emu->port + 0x20 + PTR);
110 val = inl(emu->port + 0x20 + DATA);
111 spin_unlock_irqrestore(&emu->emu_lock, flags);
112 return val;
113}
114
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100115void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 unsigned int reg,
117 unsigned int chn,
118 unsigned int data)
119{
120 unsigned int regptr;
121 unsigned long flags;
122
123 regptr = (reg << 16) | chn;
124
125 spin_lock_irqsave(&emu->emu_lock, flags);
126 outl(regptr, emu->port + 0x20 + PTR);
127 outl(data, emu->port + 0x20 + DATA);
128 spin_unlock_irqrestore(&emu->emu_lock, flags);
129}
130
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100131int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
132 unsigned int data)
133{
134 unsigned int reset, set;
135 unsigned int reg, tmp;
136 int n, result;
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100137 if (emu->card_capabilities->ca0108_chip)
138 reg = 0x3c; /* PTR20, reg 0x3c */
139 else {
140 /* For other chip types the SPI register
141 * is currently unknown. */
142 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100143 }
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100144 if (data > 0xffff) /* Only 16bit values allowed */
145 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100146
147 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100148 reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100149 set = reset | 0x10000; /* Set xxx1xxxx */
150 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
151 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
152 snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
153 result = 1;
154 /* Wait for status bit to return to 0 */
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100155 for (n = 0; n < 100; n++) {
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100156 udelay(10);
157 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
158 if (!(tmp & 0x10000)) {
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100159 result = 0;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100160 break;
161 }
162 }
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100163 if (result) /* Timed out */
164 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100165 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
166 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
167 return 0;
168}
169
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100170void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171{
172 unsigned long flags;
173 unsigned int enable;
174
175 spin_lock_irqsave(&emu->emu_lock, flags);
176 enable = inl(emu->port + INTE) | intrenb;
177 outl(enable, emu->port + INTE);
178 spin_unlock_irqrestore(&emu->emu_lock, flags);
179}
180
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100181void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182{
183 unsigned long flags;
184 unsigned int enable;
185
186 spin_lock_irqsave(&emu->emu_lock, flags);
187 enable = inl(emu->port + INTE) & ~intrenb;
188 outl(enable, emu->port + INTE);
189 spin_unlock_irqrestore(&emu->emu_lock, flags);
190}
191
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100192void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193{
194 unsigned long flags;
195 unsigned int val;
196
197 spin_lock_irqsave(&emu->emu_lock, flags);
198 /* voice interrupt */
199 if (voicenum >= 32) {
200 outl(CLIEH << 16, emu->port + PTR);
201 val = inl(emu->port + DATA);
202 val |= 1 << (voicenum - 32);
203 } else {
204 outl(CLIEL << 16, emu->port + PTR);
205 val = inl(emu->port + DATA);
206 val |= 1 << voicenum;
207 }
208 outl(val, emu->port + DATA);
209 spin_unlock_irqrestore(&emu->emu_lock, flags);
210}
211
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100212void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
214 unsigned long flags;
215 unsigned int val;
216
217 spin_lock_irqsave(&emu->emu_lock, flags);
218 /* voice interrupt */
219 if (voicenum >= 32) {
220 outl(CLIEH << 16, emu->port + PTR);
221 val = inl(emu->port + DATA);
222 val &= ~(1 << (voicenum - 32));
223 } else {
224 outl(CLIEL << 16, emu->port + PTR);
225 val = inl(emu->port + DATA);
226 val &= ~(1 << voicenum);
227 }
228 outl(val, emu->port + DATA);
229 spin_unlock_irqrestore(&emu->emu_lock, flags);
230}
231
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100232void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233{
234 unsigned long flags;
235
236 spin_lock_irqsave(&emu->emu_lock, flags);
237 /* voice interrupt */
238 if (voicenum >= 32) {
239 outl(CLIPH << 16, emu->port + PTR);
240 voicenum = 1 << (voicenum - 32);
241 } else {
242 outl(CLIPL << 16, emu->port + PTR);
243 voicenum = 1 << voicenum;
244 }
245 outl(voicenum, emu->port + DATA);
246 spin_unlock_irqrestore(&emu->emu_lock, flags);
247}
248
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100249void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
251 unsigned long flags;
252 unsigned int val;
253
254 spin_lock_irqsave(&emu->emu_lock, flags);
255 /* voice interrupt */
256 if (voicenum >= 32) {
257 outl(HLIEH << 16, emu->port + PTR);
258 val = inl(emu->port + DATA);
259 val |= 1 << (voicenum - 32);
260 } else {
261 outl(HLIEL << 16, emu->port + PTR);
262 val = inl(emu->port + DATA);
263 val |= 1 << voicenum;
264 }
265 outl(val, emu->port + DATA);
266 spin_unlock_irqrestore(&emu->emu_lock, flags);
267}
268
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100269void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270{
271 unsigned long flags;
272 unsigned int val;
273
274 spin_lock_irqsave(&emu->emu_lock, flags);
275 /* voice interrupt */
276 if (voicenum >= 32) {
277 outl(HLIEH << 16, emu->port + PTR);
278 val = inl(emu->port + DATA);
279 val &= ~(1 << (voicenum - 32));
280 } else {
281 outl(HLIEL << 16, emu->port + PTR);
282 val = inl(emu->port + DATA);
283 val &= ~(1 << voicenum);
284 }
285 outl(val, emu->port + DATA);
286 spin_unlock_irqrestore(&emu->emu_lock, flags);
287}
288
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100289void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290{
291 unsigned long flags;
292
293 spin_lock_irqsave(&emu->emu_lock, flags);
294 /* voice interrupt */
295 if (voicenum >= 32) {
296 outl(HLIPH << 16, emu->port + PTR);
297 voicenum = 1 << (voicenum - 32);
298 } else {
299 outl(HLIPL << 16, emu->port + PTR);
300 voicenum = 1 << voicenum;
301 }
302 outl(voicenum, emu->port + DATA);
303 spin_unlock_irqrestore(&emu->emu_lock, flags);
304}
305
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100306void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307{
308 unsigned long flags;
309 unsigned int sol;
310
311 spin_lock_irqsave(&emu->emu_lock, flags);
312 /* voice interrupt */
313 if (voicenum >= 32) {
314 outl(SOLEH << 16, emu->port + PTR);
315 sol = inl(emu->port + DATA);
316 sol |= 1 << (voicenum - 32);
317 } else {
318 outl(SOLEL << 16, emu->port + PTR);
319 sol = inl(emu->port + DATA);
320 sol |= 1 << voicenum;
321 }
322 outl(sol, emu->port + DATA);
323 spin_unlock_irqrestore(&emu->emu_lock, flags);
324}
325
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100326void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327{
328 unsigned long flags;
329 unsigned int sol;
330
331 spin_lock_irqsave(&emu->emu_lock, flags);
332 /* voice interrupt */
333 if (voicenum >= 32) {
334 outl(SOLEH << 16, emu->port + PTR);
335 sol = inl(emu->port + DATA);
336 sol &= ~(1 << (voicenum - 32));
337 } else {
338 outl(SOLEL << 16, emu->port + PTR);
339 sol = inl(emu->port + DATA);
340 sol &= ~(1 << voicenum);
341 }
342 outl(sol, emu->port + DATA);
343 spin_unlock_irqrestore(&emu->emu_lock, flags);
344}
345
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100346void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347{
348 volatile unsigned count;
349 unsigned int newtime = 0, curtime;
350
351 curtime = inl(emu->port + WC) >> 6;
352 while (wait-- > 0) {
353 count = 0;
354 while (count++ < 16384) {
355 newtime = inl(emu->port + WC) >> 6;
356 if (newtime != curtime)
357 break;
358 }
359 if (count >= 16384)
360 break;
361 curtime = newtime;
362 }
363}
364
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100365unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100367 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 unsigned long flags;
369 unsigned short val;
370
371 spin_lock_irqsave(&emu->emu_lock, flags);
372 outb(reg, emu->port + AC97ADDRESS);
373 val = inw(emu->port + AC97DATA);
374 spin_unlock_irqrestore(&emu->emu_lock, flags);
375 return val;
376}
377
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100378void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100380 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 unsigned long flags;
382
383 spin_lock_irqsave(&emu->emu_lock, flags);
384 outb(reg, emu->port + AC97ADDRESS);
385 outw(data, emu->port + AC97DATA);
386 spin_unlock_irqrestore(&emu->emu_lock, flags);
387}
388
389/*
390 * convert rate to pitch
391 */
392
393unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
394{
395 static u32 logMagTable[128] = {
396 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
397 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
398 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
399 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
400 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
401 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
402 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
403 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
404 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
405 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
406 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
407 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
408 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
409 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
410 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
411 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
412 };
413 static char logSlopeTable[128] = {
414 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
415 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
416 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
417 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
418 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
419 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
420 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
421 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
422 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
423 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
424 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
425 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
426 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
427 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
428 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
429 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
430 };
431 int i;
432
433 if (rate == 0)
434 return 0; /* Bail out if no leading "1" */
435 rate *= 11185; /* Scale 48000 to 0x20002380 */
436 for (i = 31; i > 0; i--) {
437 if (rate & 0x80000000) { /* Detect leading "1" */
438 return (((unsigned int) (i - 15) << 20) +
439 logMagTable[0x7f & (rate >> 24)] +
440 (0x7f & (rate >> 17)) *
441 logSlopeTable[0x7f & (rate >> 24)]);
442 }
443 rate <<= 1;
444 }
445
446 return 0; /* Should never reach this point */
447}
448