xref: /openbmc/linux/sound/pci/emu10k1/io.c (revision 1da177e4)
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 
33 unsigned 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 
64 void 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 
94 unsigned 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 
110 void 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 
126 void 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 
137 void 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 
148 void 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 
168 void 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 
188 void 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 
205 void 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 
225 void 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 
245 void 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 
262 void 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 
282 void 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 
302 void 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 
321 unsigned 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 
334 void 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 
349 unsigned 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 
405