blob: b9d3ae0dcab7aaf297eb76a8470b3a042911c177 [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>
32
33unsigned int snd_emu10k1_ptr_read(emu10k1_t * emu, unsigned int reg, unsigned int chn)
34{
35 unsigned long flags;
36 unsigned int regptr, val;
37 unsigned int mask;
38
39 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
40 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
41
42 if (reg & 0xff000000) {
43 unsigned char size, offset;
44
45 size = (reg >> 24) & 0x3f;
46 offset = (reg >> 16) & 0x1f;
47 mask = ((1 << size) - 1) << offset;
48
49 spin_lock_irqsave(&emu->emu_lock, flags);
50 outl(regptr, emu->port + PTR);
51 val = inl(emu->port + DATA);
52 spin_unlock_irqrestore(&emu->emu_lock, flags);
53
54 return (val & mask) >> offset;
55 } else {
56 spin_lock_irqsave(&emu->emu_lock, flags);
57 outl(regptr, emu->port + PTR);
58 val = inl(emu->port + DATA);
59 spin_unlock_irqrestore(&emu->emu_lock, flags);
60 return val;
61 }
62}
63
64void snd_emu10k1_ptr_write(emu10k1_t *emu, unsigned int reg, unsigned int chn, unsigned int data)
65{
66 unsigned int regptr;
67 unsigned long flags;
68 unsigned int mask;
69
70 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
71 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
72
73 if (reg & 0xff000000) {
74 unsigned char size, offset;
75
76 size = (reg >> 24) & 0x3f;
77 offset = (reg >> 16) & 0x1f;
78 mask = ((1 << size) - 1) << offset;
79 data = (data << offset) & mask;
80
81 spin_lock_irqsave(&emu->emu_lock, flags);
82 outl(regptr, emu->port + PTR);
83 data |= inl(emu->port + DATA) & ~mask;
84 outl(data, emu->port + DATA);
85 spin_unlock_irqrestore(&emu->emu_lock, flags);
86 } else {
87 spin_lock_irqsave(&emu->emu_lock, flags);
88 outl(regptr, emu->port + PTR);
89 outl(data, emu->port + DATA);
90 spin_unlock_irqrestore(&emu->emu_lock, flags);
91 }
92}
93
94unsigned int snd_emu10k1_ptr20_read(emu10k1_t * emu,
95 unsigned int reg,
96 unsigned int chn)
97{
98 unsigned long flags;
99 unsigned int regptr, val;
100
101 regptr = (reg << 16) | chn;
102
103 spin_lock_irqsave(&emu->emu_lock, flags);
104 outl(regptr, emu->port + 0x20 + PTR);
105 val = inl(emu->port + 0x20 + DATA);
106 spin_unlock_irqrestore(&emu->emu_lock, flags);
107 return val;
108}
109
110void snd_emu10k1_ptr20_write(emu10k1_t *emu,
111 unsigned int reg,
112 unsigned int chn,
113 unsigned int data)
114{
115 unsigned int regptr;
116 unsigned long flags;
117
118 regptr = (reg << 16) | chn;
119
120 spin_lock_irqsave(&emu->emu_lock, flags);
121 outl(regptr, emu->port + 0x20 + PTR);
122 outl(data, emu->port + 0x20 + DATA);
123 spin_unlock_irqrestore(&emu->emu_lock, flags);
124}
125
126void snd_emu10k1_intr_enable(emu10k1_t *emu, unsigned int intrenb)
127{
128 unsigned long flags;
129 unsigned int enable;
130
131 spin_lock_irqsave(&emu->emu_lock, flags);
132 enable = inl(emu->port + INTE) | intrenb;
133 outl(enable, emu->port + INTE);
134 spin_unlock_irqrestore(&emu->emu_lock, flags);
135}
136
137void snd_emu10k1_intr_disable(emu10k1_t *emu, unsigned int intrenb)
138{
139 unsigned long flags;
140 unsigned int enable;
141
142 spin_lock_irqsave(&emu->emu_lock, flags);
143 enable = inl(emu->port + INTE) & ~intrenb;
144 outl(enable, emu->port + INTE);
145 spin_unlock_irqrestore(&emu->emu_lock, flags);
146}
147
148void snd_emu10k1_voice_intr_enable(emu10k1_t *emu, unsigned int voicenum)
149{
150 unsigned long flags;
151 unsigned int val;
152
153 spin_lock_irqsave(&emu->emu_lock, flags);
154 /* voice interrupt */
155 if (voicenum >= 32) {
156 outl(CLIEH << 16, emu->port + PTR);
157 val = inl(emu->port + DATA);
158 val |= 1 << (voicenum - 32);
159 } else {
160 outl(CLIEL << 16, emu->port + PTR);
161 val = inl(emu->port + DATA);
162 val |= 1 << voicenum;
163 }
164 outl(val, emu->port + DATA);
165 spin_unlock_irqrestore(&emu->emu_lock, flags);
166}
167
168void snd_emu10k1_voice_intr_disable(emu10k1_t *emu, unsigned int voicenum)
169{
170 unsigned long flags;
171 unsigned int val;
172
173 spin_lock_irqsave(&emu->emu_lock, flags);
174 /* voice interrupt */
175 if (voicenum >= 32) {
176 outl(CLIEH << 16, emu->port + PTR);
177 val = inl(emu->port + DATA);
178 val &= ~(1 << (voicenum - 32));
179 } else {
180 outl(CLIEL << 16, emu->port + PTR);
181 val = inl(emu->port + DATA);
182 val &= ~(1 << voicenum);
183 }
184 outl(val, emu->port + DATA);
185 spin_unlock_irqrestore(&emu->emu_lock, flags);
186}
187
188void snd_emu10k1_voice_intr_ack(emu10k1_t *emu, unsigned int voicenum)
189{
190 unsigned long flags;
191
192 spin_lock_irqsave(&emu->emu_lock, flags);
193 /* voice interrupt */
194 if (voicenum >= 32) {
195 outl(CLIPH << 16, emu->port + PTR);
196 voicenum = 1 << (voicenum - 32);
197 } else {
198 outl(CLIPL << 16, emu->port + PTR);
199 voicenum = 1 << voicenum;
200 }
201 outl(voicenum, emu->port + DATA);
202 spin_unlock_irqrestore(&emu->emu_lock, flags);
203}
204
205void snd_emu10k1_voice_half_loop_intr_enable(emu10k1_t *emu, unsigned int voicenum)
206{
207 unsigned long flags;
208 unsigned int val;
209
210 spin_lock_irqsave(&emu->emu_lock, flags);
211 /* voice interrupt */
212 if (voicenum >= 32) {
213 outl(HLIEH << 16, emu->port + PTR);
214 val = inl(emu->port + DATA);
215 val |= 1 << (voicenum - 32);
216 } else {
217 outl(HLIEL << 16, emu->port + PTR);
218 val = inl(emu->port + DATA);
219 val |= 1 << voicenum;
220 }
221 outl(val, emu->port + DATA);
222 spin_unlock_irqrestore(&emu->emu_lock, flags);
223}
224
225void snd_emu10k1_voice_half_loop_intr_disable(emu10k1_t *emu, unsigned int voicenum)
226{
227 unsigned long flags;
228 unsigned int val;
229
230 spin_lock_irqsave(&emu->emu_lock, flags);
231 /* voice interrupt */
232 if (voicenum >= 32) {
233 outl(HLIEH << 16, emu->port + PTR);
234 val = inl(emu->port + DATA);
235 val &= ~(1 << (voicenum - 32));
236 } else {
237 outl(HLIEL << 16, emu->port + PTR);
238 val = inl(emu->port + DATA);
239 val &= ~(1 << voicenum);
240 }
241 outl(val, emu->port + DATA);
242 spin_unlock_irqrestore(&emu->emu_lock, flags);
243}
244
245void snd_emu10k1_voice_half_loop_intr_ack(emu10k1_t *emu, unsigned int voicenum)
246{
247 unsigned long flags;
248
249 spin_lock_irqsave(&emu->emu_lock, flags);
250 /* voice interrupt */
251 if (voicenum >= 32) {
252 outl(HLIPH << 16, emu->port + PTR);
253 voicenum = 1 << (voicenum - 32);
254 } else {
255 outl(HLIPL << 16, emu->port + PTR);
256 voicenum = 1 << voicenum;
257 }
258 outl(voicenum, emu->port + DATA);
259 spin_unlock_irqrestore(&emu->emu_lock, flags);
260}
261
262void snd_emu10k1_voice_set_loop_stop(emu10k1_t *emu, unsigned int voicenum)
263{
264 unsigned long flags;
265 unsigned int sol;
266
267 spin_lock_irqsave(&emu->emu_lock, flags);
268 /* voice interrupt */
269 if (voicenum >= 32) {
270 outl(SOLEH << 16, emu->port + PTR);
271 sol = inl(emu->port + DATA);
272 sol |= 1 << (voicenum - 32);
273 } else {
274 outl(SOLEL << 16, emu->port + PTR);
275 sol = inl(emu->port + DATA);
276 sol |= 1 << voicenum;
277 }
278 outl(sol, emu->port + DATA);
279 spin_unlock_irqrestore(&emu->emu_lock, flags);
280}
281
282void snd_emu10k1_voice_clear_loop_stop(emu10k1_t *emu, unsigned int voicenum)
283{
284 unsigned long flags;
285 unsigned int sol;
286
287 spin_lock_irqsave(&emu->emu_lock, flags);
288 /* voice interrupt */
289 if (voicenum >= 32) {
290 outl(SOLEH << 16, emu->port + PTR);
291 sol = inl(emu->port + DATA);
292 sol &= ~(1 << (voicenum - 32));
293 } else {
294 outl(SOLEL << 16, emu->port + PTR);
295 sol = inl(emu->port + DATA);
296 sol &= ~(1 << voicenum);
297 }
298 outl(sol, emu->port + DATA);
299 spin_unlock_irqrestore(&emu->emu_lock, flags);
300}
301
302void snd_emu10k1_wait(emu10k1_t *emu, unsigned int wait)
303{
304 volatile unsigned count;
305 unsigned int newtime = 0, curtime;
306
307 curtime = inl(emu->port + WC) >> 6;
308 while (wait-- > 0) {
309 count = 0;
310 while (count++ < 16384) {
311 newtime = inl(emu->port + WC) >> 6;
312 if (newtime != curtime)
313 break;
314 }
315 if (count >= 16384)
316 break;
317 curtime = newtime;
318 }
319}
320
321unsigned short snd_emu10k1_ac97_read(ac97_t *ac97, unsigned short reg)
322{
323 emu10k1_t *emu = ac97->private_data;
324 unsigned long flags;
325 unsigned short val;
326
327 spin_lock_irqsave(&emu->emu_lock, flags);
328 outb(reg, emu->port + AC97ADDRESS);
329 val = inw(emu->port + AC97DATA);
330 spin_unlock_irqrestore(&emu->emu_lock, flags);
331 return val;
332}
333
334void snd_emu10k1_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short data)
335{
336 emu10k1_t *emu = ac97->private_data;
337 unsigned long flags;
338
339 spin_lock_irqsave(&emu->emu_lock, flags);
340 outb(reg, emu->port + AC97ADDRESS);
341 outw(data, emu->port + AC97DATA);
342 spin_unlock_irqrestore(&emu->emu_lock, flags);
343}
344
345/*
346 * convert rate to pitch
347 */
348
349unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
350{
351 static u32 logMagTable[128] = {
352 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
353 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
354 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
355 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
356 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
357 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
358 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
359 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
360 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
361 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
362 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
363 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
364 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
365 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
366 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
367 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
368 };
369 static char logSlopeTable[128] = {
370 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
371 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
372 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
373 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
374 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
375 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
376 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
377 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
378 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
379 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
380 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
381 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
382 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
383 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
384 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
385 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
386 };
387 int i;
388
389 if (rate == 0)
390 return 0; /* Bail out if no leading "1" */
391 rate *= 11185; /* Scale 48000 to 0x20002380 */
392 for (i = 31; i > 0; i--) {
393 if (rate & 0x80000000) { /* Detect leading "1" */
394 return (((unsigned int) (i - 15) << 20) +
395 logMagTable[0x7f & (rate >> 24)] +
396 (0x7f & (rate >> 17)) *
397 logSlopeTable[0x7f & (rate >> 24)]);
398 }
399 rate <<= 1;
400 }
401
402 return 0; /* Should never reach this point */
403}
404