xref: /linux/sound/pci/emu10k1/io.c (revision f3d9478b2ce468c3115b02ecae7e975990697f15)
1 /*
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 #include <linux/delay.h>
33 
34 unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
35 {
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 
65 EXPORT_SYMBOL(snd_emu10k1_ptr_read);
66 
67 void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
68 {
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 
97 EXPORT_SYMBOL(snd_emu10k1_ptr_write);
98 
99 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
100 					  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 
115 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
116 				   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 
131 int 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;
137 	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;
143 	}
144 	if (data > 0xffff) /* Only 16bit values allowed */
145 		return 1;
146 
147 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
148 	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
149 	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 */
155 	for (n = 0; n < 100; n++) {
156 		udelay(10);
157 		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
158 		if (!(tmp & 0x10000)) {
159 			result = 0;
160 			break;
161 		}
162 	}
163 	if (result) /* Timed out */
164 		return 1;
165 	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 
170 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
171 {
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 
181 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
182 {
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 
192 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
193 {
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 
212 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
213 {
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 
232 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
233 {
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 
249 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
250 {
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 
269 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
270 {
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 
289 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
290 {
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 
306 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
307 {
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 
326 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
327 {
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 
346 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
347 {
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 
365 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
366 {
367 	struct snd_emu10k1 *emu = ac97->private_data;
368 	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 
378 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
379 {
380 	struct snd_emu10k1 *emu = ac97->private_data;
381 	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 
393 unsigned 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 
449