xref: /openbmc/linux/sound/pci/emu10k1/io.c (revision a09d2831)
1 /*
2  *  Copyright (c) by Jaroslav Kysela <perex@perex.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 <linux/time.h>
29 #include <sound/core.h>
30 #include <sound/emu10k1.h>
31 #include <linux/delay.h>
32 #include "p17v.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 	if (!emu) {
74 		snd_printk(KERN_ERR "ptr_write: emu is null!\n");
75 		dump_stack();
76 		return;
77 	}
78 	mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
79 	regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
80 
81 	if (reg & 0xff000000) {
82 		unsigned char size, offset;
83 
84 		size = (reg >> 24) & 0x3f;
85 		offset = (reg >> 16) & 0x1f;
86 		mask = ((1 << size) - 1) << offset;
87 		data = (data << offset) & mask;
88 
89 		spin_lock_irqsave(&emu->emu_lock, flags);
90 		outl(regptr, emu->port + PTR);
91 		data |= inl(emu->port + DATA) & ~mask;
92 		outl(data, emu->port + DATA);
93 		spin_unlock_irqrestore(&emu->emu_lock, flags);
94 	} else {
95 		spin_lock_irqsave(&emu->emu_lock, flags);
96 		outl(regptr, emu->port + PTR);
97 		outl(data, emu->port + DATA);
98 		spin_unlock_irqrestore(&emu->emu_lock, flags);
99 	}
100 }
101 
102 EXPORT_SYMBOL(snd_emu10k1_ptr_write);
103 
104 unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
105 					  unsigned int reg,
106 					  unsigned int chn)
107 {
108 	unsigned long flags;
109 	unsigned int regptr, val;
110 
111 	regptr = (reg << 16) | chn;
112 
113 	spin_lock_irqsave(&emu->emu_lock, flags);
114 	outl(regptr, emu->port + 0x20 + PTR);
115 	val = inl(emu->port + 0x20 + DATA);
116 	spin_unlock_irqrestore(&emu->emu_lock, flags);
117 	return val;
118 }
119 
120 void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
121 				   unsigned int reg,
122 				   unsigned int chn,
123 				   unsigned int data)
124 {
125 	unsigned int regptr;
126 	unsigned long flags;
127 
128 	regptr = (reg << 16) | chn;
129 
130 	spin_lock_irqsave(&emu->emu_lock, flags);
131 	outl(regptr, emu->port + 0x20 + PTR);
132 	outl(data, emu->port + 0x20 + DATA);
133 	spin_unlock_irqrestore(&emu->emu_lock, flags);
134 }
135 
136 int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
137 				   unsigned int data)
138 {
139 	unsigned int reset, set;
140 	unsigned int reg, tmp;
141 	int n, result;
142 	int err = 0;
143 
144 	/* This function is not re-entrant, so protect against it. */
145 	spin_lock(&emu->spi_lock);
146 	if (emu->card_capabilities->ca0108_chip)
147 		reg = 0x3c; /* PTR20, reg 0x3c */
148 	else {
149 		/* For other chip types the SPI register
150 		 * is currently unknown. */
151 		err = 1;
152 		goto spi_write_exit;
153 	}
154 	if (data > 0xffff) {
155 		/* Only 16bit values allowed */
156 		err = 1;
157 		goto spi_write_exit;
158 	}
159 
160 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
161 	reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
162 	set = reset | 0x10000; /* Set xxx1xxxx */
163 	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
164 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
165 	snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
166 	result = 1;
167 	/* Wait for status bit to return to 0 */
168 	for (n = 0; n < 100; n++) {
169 		udelay(10);
170 		tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
171 		if (!(tmp & 0x10000)) {
172 			result = 0;
173 			break;
174 		}
175 	}
176 	if (result) {
177 		/* Timed out */
178 		err = 1;
179 		goto spi_write_exit;
180 	}
181 	snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
182 	tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
183 	err = 0;
184 spi_write_exit:
185 	spin_unlock(&emu->spi_lock);
186 	return err;
187 }
188 
189 /* The ADC does not support i2c read, so only write is implemented */
190 int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
191 				u32 reg,
192 				u32 value)
193 {
194 	u32 tmp;
195 	int timeout = 0;
196 	int status;
197 	int retry;
198 	int err = 0;
199 
200 	if ((reg > 0x7f) || (value > 0x1ff)) {
201 		snd_printk(KERN_ERR "i2c_write: invalid values.\n");
202 		return -EINVAL;
203 	}
204 
205 	/* This function is not re-entrant, so protect against it. */
206 	spin_lock(&emu->i2c_lock);
207 
208 	tmp = reg << 25 | value << 16;
209 
210 	/* This controls the I2C connected to the WM8775 ADC Codec */
211 	snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
212 	tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
213 
214 	for (retry = 0; retry < 10; retry++) {
215 		/* Send the data to i2c */
216 		tmp = 0;
217 		tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
218 		snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
219 
220 		/* Wait till the transaction ends */
221 		while (1) {
222 			mdelay(1);
223 			status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
224 			timeout++;
225 			if ((status & I2C_A_ADC_START) == 0)
226 				break;
227 
228 			if (timeout > 1000) {
229                 		snd_printk(KERN_WARNING
230 					   "emu10k1:I2C:timeout status=0x%x\n",
231 					   status);
232 				break;
233 			}
234 		}
235 		//Read back and see if the transaction is successful
236 		if ((status & I2C_A_ADC_ABORT) == 0)
237 			break;
238 	}
239 
240 	if (retry == 10) {
241 		snd_printk(KERN_ERR "Writing to ADC failed!\n");
242 		snd_printk(KERN_ERR "status=0x%x, reg=%d, value=%d\n",
243 			status, reg, value);
244 		/* dump_stack(); */
245 		err = -EINVAL;
246 	}
247 
248 	spin_unlock(&emu->i2c_lock);
249 	return err;
250 }
251 
252 int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, u32 reg, u32 value)
253 {
254 	unsigned long flags;
255 
256 	if (reg > 0x3f)
257 		return 1;
258 	reg += 0x40; /* 0x40 upwards are registers. */
259 	if (value > 0x3f) /* 0 to 0x3f are values */
260 		return 1;
261 	spin_lock_irqsave(&emu->emu_lock, flags);
262 	outl(reg, emu->port + A_IOCFG);
263 	udelay(10);
264 	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
265 	udelay(10);
266 	outl(value, emu->port + A_IOCFG);
267 	udelay(10);
268 	outl(value | 0x80 , emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
269 	spin_unlock_irqrestore(&emu->emu_lock, flags);
270 
271 	return 0;
272 }
273 
274 int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, u32 reg, u32 *value)
275 {
276 	unsigned long flags;
277 	if (reg > 0x3f)
278 		return 1;
279 	reg += 0x40; /* 0x40 upwards are registers. */
280 	spin_lock_irqsave(&emu->emu_lock, flags);
281 	outl(reg, emu->port + A_IOCFG);
282 	udelay(10);
283 	outl(reg | 0x80, emu->port + A_IOCFG);  /* High bit clocks the value into the fpga. */
284 	udelay(10);
285 	*value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
286 	spin_unlock_irqrestore(&emu->emu_lock, flags);
287 
288 	return 0;
289 }
290 
291 /* Each Destination has one and only one Source,
292  * but one Source can feed any number of Destinations simultaneously.
293  */
294 int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, u32 dst, u32 src)
295 {
296 	snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
297 	snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
298 	snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
299 	snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
300 
301 	return 0;
302 }
303 
304 void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
305 {
306 	unsigned long flags;
307 	unsigned int enable;
308 
309 	spin_lock_irqsave(&emu->emu_lock, flags);
310 	enable = inl(emu->port + INTE) | intrenb;
311 	outl(enable, emu->port + INTE);
312 	spin_unlock_irqrestore(&emu->emu_lock, flags);
313 }
314 
315 void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
316 {
317 	unsigned long flags;
318 	unsigned int enable;
319 
320 	spin_lock_irqsave(&emu->emu_lock, flags);
321 	enable = inl(emu->port + INTE) & ~intrenb;
322 	outl(enable, emu->port + INTE);
323 	spin_unlock_irqrestore(&emu->emu_lock, flags);
324 }
325 
326 void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
327 {
328 	unsigned long flags;
329 	unsigned int val;
330 
331 	spin_lock_irqsave(&emu->emu_lock, flags);
332 	/* voice interrupt */
333 	if (voicenum >= 32) {
334 		outl(CLIEH << 16, emu->port + PTR);
335 		val = inl(emu->port + DATA);
336 		val |= 1 << (voicenum - 32);
337 	} else {
338 		outl(CLIEL << 16, emu->port + PTR);
339 		val = inl(emu->port + DATA);
340 		val |= 1 << voicenum;
341 	}
342 	outl(val, emu->port + DATA);
343 	spin_unlock_irqrestore(&emu->emu_lock, flags);
344 }
345 
346 void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
347 {
348 	unsigned long flags;
349 	unsigned int val;
350 
351 	spin_lock_irqsave(&emu->emu_lock, flags);
352 	/* voice interrupt */
353 	if (voicenum >= 32) {
354 		outl(CLIEH << 16, emu->port + PTR);
355 		val = inl(emu->port + DATA);
356 		val &= ~(1 << (voicenum - 32));
357 	} else {
358 		outl(CLIEL << 16, emu->port + PTR);
359 		val = inl(emu->port + DATA);
360 		val &= ~(1 << voicenum);
361 	}
362 	outl(val, emu->port + DATA);
363 	spin_unlock_irqrestore(&emu->emu_lock, flags);
364 }
365 
366 void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
367 {
368 	unsigned long flags;
369 
370 	spin_lock_irqsave(&emu->emu_lock, flags);
371 	/* voice interrupt */
372 	if (voicenum >= 32) {
373 		outl(CLIPH << 16, emu->port + PTR);
374 		voicenum = 1 << (voicenum - 32);
375 	} else {
376 		outl(CLIPL << 16, emu->port + PTR);
377 		voicenum = 1 << voicenum;
378 	}
379 	outl(voicenum, emu->port + DATA);
380 	spin_unlock_irqrestore(&emu->emu_lock, flags);
381 }
382 
383 void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
384 {
385 	unsigned long flags;
386 	unsigned int val;
387 
388 	spin_lock_irqsave(&emu->emu_lock, flags);
389 	/* voice interrupt */
390 	if (voicenum >= 32) {
391 		outl(HLIEH << 16, emu->port + PTR);
392 		val = inl(emu->port + DATA);
393 		val |= 1 << (voicenum - 32);
394 	} else {
395 		outl(HLIEL << 16, emu->port + PTR);
396 		val = inl(emu->port + DATA);
397 		val |= 1 << voicenum;
398 	}
399 	outl(val, emu->port + DATA);
400 	spin_unlock_irqrestore(&emu->emu_lock, flags);
401 }
402 
403 void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
404 {
405 	unsigned long flags;
406 	unsigned int val;
407 
408 	spin_lock_irqsave(&emu->emu_lock, flags);
409 	/* voice interrupt */
410 	if (voicenum >= 32) {
411 		outl(HLIEH << 16, emu->port + PTR);
412 		val = inl(emu->port + DATA);
413 		val &= ~(1 << (voicenum - 32));
414 	} else {
415 		outl(HLIEL << 16, emu->port + PTR);
416 		val = inl(emu->port + DATA);
417 		val &= ~(1 << voicenum);
418 	}
419 	outl(val, emu->port + DATA);
420 	spin_unlock_irqrestore(&emu->emu_lock, flags);
421 }
422 
423 void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
424 {
425 	unsigned long flags;
426 
427 	spin_lock_irqsave(&emu->emu_lock, flags);
428 	/* voice interrupt */
429 	if (voicenum >= 32) {
430 		outl(HLIPH << 16, emu->port + PTR);
431 		voicenum = 1 << (voicenum - 32);
432 	} else {
433 		outl(HLIPL << 16, emu->port + PTR);
434 		voicenum = 1 << voicenum;
435 	}
436 	outl(voicenum, emu->port + DATA);
437 	spin_unlock_irqrestore(&emu->emu_lock, flags);
438 }
439 
440 void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
441 {
442 	unsigned long flags;
443 	unsigned int sol;
444 
445 	spin_lock_irqsave(&emu->emu_lock, flags);
446 	/* voice interrupt */
447 	if (voicenum >= 32) {
448 		outl(SOLEH << 16, emu->port + PTR);
449 		sol = inl(emu->port + DATA);
450 		sol |= 1 << (voicenum - 32);
451 	} else {
452 		outl(SOLEL << 16, emu->port + PTR);
453 		sol = inl(emu->port + DATA);
454 		sol |= 1 << voicenum;
455 	}
456 	outl(sol, emu->port + DATA);
457 	spin_unlock_irqrestore(&emu->emu_lock, flags);
458 }
459 
460 void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
461 {
462 	unsigned long flags;
463 	unsigned int sol;
464 
465 	spin_lock_irqsave(&emu->emu_lock, flags);
466 	/* voice interrupt */
467 	if (voicenum >= 32) {
468 		outl(SOLEH << 16, emu->port + PTR);
469 		sol = inl(emu->port + DATA);
470 		sol &= ~(1 << (voicenum - 32));
471 	} else {
472 		outl(SOLEL << 16, emu->port + PTR);
473 		sol = inl(emu->port + DATA);
474 		sol &= ~(1 << voicenum);
475 	}
476 	outl(sol, emu->port + DATA);
477 	spin_unlock_irqrestore(&emu->emu_lock, flags);
478 }
479 
480 void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
481 {
482 	volatile unsigned count;
483 	unsigned int newtime = 0, curtime;
484 
485 	curtime = inl(emu->port + WC) >> 6;
486 	while (wait-- > 0) {
487 		count = 0;
488 		while (count++ < 16384) {
489 			newtime = inl(emu->port + WC) >> 6;
490 			if (newtime != curtime)
491 				break;
492 		}
493 		if (count > 16384)
494 			break;
495 		curtime = newtime;
496 	}
497 }
498 
499 unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
500 {
501 	struct snd_emu10k1 *emu = ac97->private_data;
502 	unsigned long flags;
503 	unsigned short val;
504 
505 	spin_lock_irqsave(&emu->emu_lock, flags);
506 	outb(reg, emu->port + AC97ADDRESS);
507 	val = inw(emu->port + AC97DATA);
508 	spin_unlock_irqrestore(&emu->emu_lock, flags);
509 	return val;
510 }
511 
512 void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
513 {
514 	struct snd_emu10k1 *emu = ac97->private_data;
515 	unsigned long flags;
516 
517 	spin_lock_irqsave(&emu->emu_lock, flags);
518 	outb(reg, emu->port + AC97ADDRESS);
519 	outw(data, emu->port + AC97DATA);
520 	spin_unlock_irqrestore(&emu->emu_lock, flags);
521 }
522 
523 /*
524  *  convert rate to pitch
525  */
526 
527 unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
528 {
529 	static u32 logMagTable[128] = {
530 		0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
531 		0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
532 		0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
533 		0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
534 		0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
535 		0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
536 		0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
537 		0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
538 		0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
539 		0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
540 		0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
541 		0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
542 		0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
543 		0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
544 		0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
545 		0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
546 	};
547 	static char logSlopeTable[128] = {
548 		0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
549 		0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
550 		0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
551 		0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
552 		0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
553 		0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
554 		0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
555 		0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
556 		0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
557 		0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
558 		0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
559 		0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
560 		0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
561 		0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
562 		0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
563 		0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
564 	};
565 	int i;
566 
567 	if (rate == 0)
568 		return 0;	/* Bail out if no leading "1" */
569 	rate *= 11185;		/* Scale 48000 to 0x20002380 */
570 	for (i = 31; i > 0; i--) {
571 		if (rate & 0x80000000) {	/* Detect leading "1" */
572 			return (((unsigned int) (i - 15) << 20) +
573 			       logMagTable[0x7f & (rate >> 24)] +
574 					(0x7f & (rate >> 17)) *
575 					logSlopeTable[0x7f & (rate >> 24)]);
576 		}
577 		rate <<= 1;
578 	}
579 
580 	return 0;		/* Should never reach this point */
581 }
582 
583