xref: /linux/sound/pci/emu10k1/io.c (revision de2fe5e07d58424bc286fff3fd3c1b0bf933cd58)
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 void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
66 {
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 
95 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
96 					  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 
111 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
112 				   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 
127 int 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 		/* For other chip types the SPI register
137 		 * is currently unknown. */
138 		return 1;
139 	}
140 	if (data > 0xffff) /* Only 16bit values allowed */
141 		return 1;
142 
143 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
144 	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
145 	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 */
151 	for (n = 0; n < 100; n++) {
152 		udelay(10);
153 		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
154 		if (!(tmp & 0x10000)) {
155 			result = 0;
156 			break;
157 		}
158 	}
159 	if (result) /* Timed out */
160 		return 1;
161 	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 
166 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
167 {
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 
177 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
178 {
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 
188 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
189 {
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 
208 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
209 {
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 
228 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
229 {
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 
245 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
246 {
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 
265 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
266 {
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 
285 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
286 {
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 
302 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
303 {
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 
322 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
323 {
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 
342 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
343 {
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 
361 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
362 {
363 	struct snd_emu10k1 *emu = ac97->private_data;
364 	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 
374 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
375 {
376 	struct snd_emu10k1 *emu = ac97->private_data;
377 	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 
389 unsigned 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 
445