blob: 116e1c8d93616bd9df9c6b19452a9e4781f59400 [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>
James Courtier-Dutton27fe8642005-12-21 15:06:08 +010032#include <linux/delay.h>
James Courtier-Dutton184c1e22006-12-06 15:58:02 +000033#include "p17v.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Takashi Iwaieb4698f2005-11-17 14:50:13 +010035unsigned int snd_emu10k1_ptr_read(struct snd_emu10k1 * emu, unsigned int reg, unsigned int chn)
Linus Torvalds1da177e2005-04-16 15:20:36 -070036{
37 unsigned long flags;
38 unsigned int regptr, val;
39 unsigned int mask;
40
41 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
42 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
43
44 if (reg & 0xff000000) {
45 unsigned char size, offset;
46
47 size = (reg >> 24) & 0x3f;
48 offset = (reg >> 16) & 0x1f;
49 mask = ((1 << size) - 1) << offset;
50
51 spin_lock_irqsave(&emu->emu_lock, flags);
52 outl(regptr, emu->port + PTR);
53 val = inl(emu->port + DATA);
54 spin_unlock_irqrestore(&emu->emu_lock, flags);
55
56 return (val & mask) >> offset;
57 } else {
58 spin_lock_irqsave(&emu->emu_lock, flags);
59 outl(regptr, emu->port + PTR);
60 val = inl(emu->port + DATA);
61 spin_unlock_irqrestore(&emu->emu_lock, flags);
62 return val;
63 }
64}
65
Takashi Iwai2dd31de2006-04-28 15:13:39 +020066EXPORT_SYMBOL(snd_emu10k1_ptr_read);
67
Takashi Iwaieb4698f2005-11-17 14:50:13 +010068void snd_emu10k1_ptr_write(struct snd_emu10k1 *emu, unsigned int reg, unsigned int chn, unsigned int data)
Linus Torvalds1da177e2005-04-16 15:20:36 -070069{
70 unsigned int regptr;
71 unsigned long flags;
72 unsigned int mask;
73
74 mask = emu->audigy ? A_PTR_ADDRESS_MASK : PTR_ADDRESS_MASK;
75 regptr = ((reg << 16) & mask) | (chn & PTR_CHANNELNUM_MASK);
76
77 if (reg & 0xff000000) {
78 unsigned char size, offset;
79
80 size = (reg >> 24) & 0x3f;
81 offset = (reg >> 16) & 0x1f;
82 mask = ((1 << size) - 1) << offset;
83 data = (data << offset) & mask;
84
85 spin_lock_irqsave(&emu->emu_lock, flags);
86 outl(regptr, emu->port + PTR);
87 data |= inl(emu->port + DATA) & ~mask;
88 outl(data, emu->port + DATA);
89 spin_unlock_irqrestore(&emu->emu_lock, flags);
90 } else {
91 spin_lock_irqsave(&emu->emu_lock, flags);
92 outl(regptr, emu->port + PTR);
93 outl(data, emu->port + DATA);
94 spin_unlock_irqrestore(&emu->emu_lock, flags);
95 }
96}
97
Takashi Iwai2dd31de2006-04-28 15:13:39 +020098EXPORT_SYMBOL(snd_emu10k1_ptr_write);
99
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100100unsigned int snd_emu10k1_ptr20_read(struct snd_emu10k1 * emu,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101 unsigned int reg,
102 unsigned int chn)
103{
104 unsigned long flags;
105 unsigned int regptr, val;
106
107 regptr = (reg << 16) | chn;
108
109 spin_lock_irqsave(&emu->emu_lock, flags);
110 outl(regptr, emu->port + 0x20 + PTR);
111 val = inl(emu->port + 0x20 + DATA);
112 spin_unlock_irqrestore(&emu->emu_lock, flags);
113 return val;
114}
115
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100116void snd_emu10k1_ptr20_write(struct snd_emu10k1 *emu,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 unsigned int reg,
118 unsigned int chn,
119 unsigned int data)
120{
121 unsigned int regptr;
122 unsigned long flags;
123
124 regptr = (reg << 16) | chn;
125
126 spin_lock_irqsave(&emu->emu_lock, flags);
127 outl(regptr, emu->port + 0x20 + PTR);
128 outl(data, emu->port + 0x20 + DATA);
129 spin_unlock_irqrestore(&emu->emu_lock, flags);
130}
131
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100132int snd_emu10k1_spi_write(struct snd_emu10k1 * emu,
133 unsigned int data)
134{
135 unsigned int reset, set;
136 unsigned int reg, tmp;
137 int n, result;
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100138 if (emu->card_capabilities->ca0108_chip)
139 reg = 0x3c; /* PTR20, reg 0x3c */
140 else {
141 /* For other chip types the SPI register
142 * is currently unknown. */
143 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100144 }
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100145 if (data > 0xffff) /* Only 16bit values allowed */
146 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100147
148 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
James Courtier-Dutton28bcbdd2005-12-21 15:41:50 +0100149 reset = (tmp & ~0x3ffff) | 0x20000; /* Set xxx20000 */
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100150 set = reset | 0x10000; /* Set xxx1xxxx */
151 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
152 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* write post */
153 snd_emu10k1_ptr20_write(emu, reg, 0, set | data);
154 result = 1;
155 /* Wait for status bit to return to 0 */
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100156 for (n = 0; n < 100; n++) {
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100157 udelay(10);
158 tmp = snd_emu10k1_ptr20_read(emu, reg, 0);
159 if (!(tmp & 0x10000)) {
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100160 result = 0;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100161 break;
162 }
163 }
James Courtier-Duttonc6a02ca2005-12-21 15:56:01 +0100164 if (result) /* Timed out */
165 return 1;
James Courtier-Dutton27fe8642005-12-21 15:06:08 +0100166 snd_emu10k1_ptr20_write(emu, reg, 0, reset | data);
167 tmp = snd_emu10k1_ptr20_read(emu, reg, 0); /* Write post */
168 return 0;
169}
170
James Courtier-Dutton184c1e22006-12-06 15:58:02 +0000171/* The ADC does not support i2c read, so only write is implemented */
172int snd_emu10k1_i2c_write(struct snd_emu10k1 *emu,
173 u32 reg,
174 u32 value)
175{
176 u32 tmp;
177 int timeout = 0;
178 int status;
179 int retry;
180 if ((reg > 0x7f) || (value > 0x1ff)) {
181 snd_printk(KERN_ERR "i2c_write: invalid values.\n");
182 return -EINVAL;
183 }
184
185 tmp = reg << 25 | value << 16;
186 // snd_printk("I2C-write:reg=0x%x, value=0x%x\n", reg, value);
187 /* Not sure what this I2C channel controls. */
188 /* snd_emu10k1_ptr_write(emu, P17V_I2C_0, 0, tmp); */
189
190 /* This controls the I2C connected to the WM8775 ADC Codec */
191 snd_emu10k1_ptr20_write(emu, P17V_I2C_1, 0, tmp);
192 tmp = snd_emu10k1_ptr20_read(emu, P17V_I2C_1, 0); /* write post */
193
194 for (retry = 0; retry < 10; retry++) {
195 /* Send the data to i2c */
196 //tmp = snd_emu10k1_ptr_read(emu, P17V_I2C_ADDR, 0);
197 //tmp = tmp & ~(I2C_A_ADC_READ|I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD_MASK);
198 tmp = 0;
199 tmp = tmp | (I2C_A_ADC_LAST|I2C_A_ADC_START|I2C_A_ADC_ADD);
200 snd_emu10k1_ptr20_write(emu, P17V_I2C_ADDR, 0, tmp);
201
202 /* Wait till the transaction ends */
203 while (1) {
204 udelay(10);
205 status = snd_emu10k1_ptr20_read(emu, P17V_I2C_ADDR, 0);
206 // snd_printk("I2C:status=0x%x\n", status);
207 timeout++;
208 if ((status & I2C_A_ADC_START) == 0)
209 break;
210
211 if (timeout > 1000) {
212 snd_printk("emu10k1:I2C:timeout status=0x%x\n", status);
213 break;
214 }
215 }
216 //Read back and see if the transaction is successful
217 if ((status & I2C_A_ADC_ABORT) == 0)
218 break;
219 }
220
221 if (retry == 10) {
222 snd_printk(KERN_ERR "Writing to ADC failed!\n");
223 return -EINVAL;
224 }
225
226 return 0;
227}
228
James Courtier-Dutton9f4bd5d2006-10-01 10:48:04 +0100229int snd_emu1010_fpga_write(struct snd_emu10k1 * emu, int reg, int value)
230{
231 if (reg < 0 || reg > 0x3f)
232 return 1;
233 reg += 0x40; /* 0x40 upwards are registers. */
234 if (value < 0 || value > 0x3f) /* 0 to 0x3f are values */
235 return 1;
236 outl(reg, emu->port + A_IOCFG);
237 udelay(10);
238 outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
239 udelay(10);
240 outl(value, emu->port + A_IOCFG);
241 udelay(10);
242 outl(value | 0x80 , emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
243
244 return 0;
245}
246
247int snd_emu1010_fpga_read(struct snd_emu10k1 * emu, int reg, int *value)
248{
249 if (reg < 0 || reg > 0x3f)
250 return 1;
251 reg += 0x40; /* 0x40 upwards are registers. */
252 outl(reg, emu->port + A_IOCFG);
253 udelay(10);
254 outl(reg | 0x80, emu->port + A_IOCFG); /* High bit clocks the value into the fpga. */
255 udelay(10);
256 *value = ((inl(emu->port + A_IOCFG) >> 8) & 0x7f);
257
258 return 0;
259}
260
261/* Each Destination has one and only one Source,
262 * but one Source can feed any number of Destinations simultaneously.
263 */
264int snd_emu1010_fpga_link_dst_src_write(struct snd_emu10k1 * emu, int dst, int src)
265{
266 snd_emu1010_fpga_write(emu, 0x00, ((dst >> 8) & 0x3f) );
267 snd_emu1010_fpga_write(emu, 0x01, (dst & 0x3f) );
268 snd_emu1010_fpga_write(emu, 0x02, ((src >> 8) & 0x3f) );
269 snd_emu1010_fpga_write(emu, 0x03, (src & 0x3f) );
270
271 return 0;
272}
273
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100274void snd_emu10k1_intr_enable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
276 unsigned long flags;
277 unsigned int enable;
278
279 spin_lock_irqsave(&emu->emu_lock, flags);
280 enable = inl(emu->port + INTE) | intrenb;
281 outl(enable, emu->port + INTE);
282 spin_unlock_irqrestore(&emu->emu_lock, flags);
283}
284
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100285void snd_emu10k1_intr_disable(struct snd_emu10k1 *emu, unsigned int intrenb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286{
287 unsigned long flags;
288 unsigned int enable;
289
290 spin_lock_irqsave(&emu->emu_lock, flags);
291 enable = inl(emu->port + INTE) & ~intrenb;
292 outl(enable, emu->port + INTE);
293 spin_unlock_irqrestore(&emu->emu_lock, flags);
294}
295
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100296void snd_emu10k1_voice_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700297{
298 unsigned long flags;
299 unsigned int val;
300
301 spin_lock_irqsave(&emu->emu_lock, flags);
302 /* voice interrupt */
303 if (voicenum >= 32) {
304 outl(CLIEH << 16, emu->port + PTR);
305 val = inl(emu->port + DATA);
306 val |= 1 << (voicenum - 32);
307 } else {
308 outl(CLIEL << 16, emu->port + PTR);
309 val = inl(emu->port + DATA);
310 val |= 1 << voicenum;
311 }
312 outl(val, emu->port + DATA);
313 spin_unlock_irqrestore(&emu->emu_lock, flags);
314}
315
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100316void snd_emu10k1_voice_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317{
318 unsigned long flags;
319 unsigned int val;
320
321 spin_lock_irqsave(&emu->emu_lock, flags);
322 /* voice interrupt */
323 if (voicenum >= 32) {
324 outl(CLIEH << 16, emu->port + PTR);
325 val = inl(emu->port + DATA);
326 val &= ~(1 << (voicenum - 32));
327 } else {
328 outl(CLIEL << 16, emu->port + PTR);
329 val = inl(emu->port + DATA);
330 val &= ~(1 << voicenum);
331 }
332 outl(val, emu->port + DATA);
333 spin_unlock_irqrestore(&emu->emu_lock, flags);
334}
335
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100336void snd_emu10k1_voice_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337{
338 unsigned long flags;
339
340 spin_lock_irqsave(&emu->emu_lock, flags);
341 /* voice interrupt */
342 if (voicenum >= 32) {
343 outl(CLIPH << 16, emu->port + PTR);
344 voicenum = 1 << (voicenum - 32);
345 } else {
346 outl(CLIPL << 16, emu->port + PTR);
347 voicenum = 1 << voicenum;
348 }
349 outl(voicenum, emu->port + DATA);
350 spin_unlock_irqrestore(&emu->emu_lock, flags);
351}
352
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100353void snd_emu10k1_voice_half_loop_intr_enable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354{
355 unsigned long flags;
356 unsigned int val;
357
358 spin_lock_irqsave(&emu->emu_lock, flags);
359 /* voice interrupt */
360 if (voicenum >= 32) {
361 outl(HLIEH << 16, emu->port + PTR);
362 val = inl(emu->port + DATA);
363 val |= 1 << (voicenum - 32);
364 } else {
365 outl(HLIEL << 16, emu->port + PTR);
366 val = inl(emu->port + DATA);
367 val |= 1 << voicenum;
368 }
369 outl(val, emu->port + DATA);
370 spin_unlock_irqrestore(&emu->emu_lock, flags);
371}
372
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100373void snd_emu10k1_voice_half_loop_intr_disable(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374{
375 unsigned long flags;
376 unsigned int val;
377
378 spin_lock_irqsave(&emu->emu_lock, flags);
379 /* voice interrupt */
380 if (voicenum >= 32) {
381 outl(HLIEH << 16, emu->port + PTR);
382 val = inl(emu->port + DATA);
383 val &= ~(1 << (voicenum - 32));
384 } else {
385 outl(HLIEL << 16, emu->port + PTR);
386 val = inl(emu->port + DATA);
387 val &= ~(1 << voicenum);
388 }
389 outl(val, emu->port + DATA);
390 spin_unlock_irqrestore(&emu->emu_lock, flags);
391}
392
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100393void snd_emu10k1_voice_half_loop_intr_ack(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394{
395 unsigned long flags;
396
397 spin_lock_irqsave(&emu->emu_lock, flags);
398 /* voice interrupt */
399 if (voicenum >= 32) {
400 outl(HLIPH << 16, emu->port + PTR);
401 voicenum = 1 << (voicenum - 32);
402 } else {
403 outl(HLIPL << 16, emu->port + PTR);
404 voicenum = 1 << voicenum;
405 }
406 outl(voicenum, emu->port + DATA);
407 spin_unlock_irqrestore(&emu->emu_lock, flags);
408}
409
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100410void snd_emu10k1_voice_set_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 unsigned long flags;
413 unsigned int sol;
414
415 spin_lock_irqsave(&emu->emu_lock, flags);
416 /* voice interrupt */
417 if (voicenum >= 32) {
418 outl(SOLEH << 16, emu->port + PTR);
419 sol = inl(emu->port + DATA);
420 sol |= 1 << (voicenum - 32);
421 } else {
422 outl(SOLEL << 16, emu->port + PTR);
423 sol = inl(emu->port + DATA);
424 sol |= 1 << voicenum;
425 }
426 outl(sol, emu->port + DATA);
427 spin_unlock_irqrestore(&emu->emu_lock, flags);
428}
429
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100430void snd_emu10k1_voice_clear_loop_stop(struct snd_emu10k1 *emu, unsigned int voicenum)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
432 unsigned long flags;
433 unsigned int sol;
434
435 spin_lock_irqsave(&emu->emu_lock, flags);
436 /* voice interrupt */
437 if (voicenum >= 32) {
438 outl(SOLEH << 16, emu->port + PTR);
439 sol = inl(emu->port + DATA);
440 sol &= ~(1 << (voicenum - 32));
441 } else {
442 outl(SOLEL << 16, emu->port + PTR);
443 sol = inl(emu->port + DATA);
444 sol &= ~(1 << voicenum);
445 }
446 outl(sol, emu->port + DATA);
447 spin_unlock_irqrestore(&emu->emu_lock, flags);
448}
449
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100450void snd_emu10k1_wait(struct snd_emu10k1 *emu, unsigned int wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700451{
452 volatile unsigned count;
453 unsigned int newtime = 0, curtime;
454
455 curtime = inl(emu->port + WC) >> 6;
456 while (wait-- > 0) {
457 count = 0;
458 while (count++ < 16384) {
459 newtime = inl(emu->port + WC) >> 6;
460 if (newtime != curtime)
461 break;
462 }
463 if (count >= 16384)
464 break;
465 curtime = newtime;
466 }
467}
468
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100469unsigned short snd_emu10k1_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100471 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 unsigned long flags;
473 unsigned short val;
474
475 spin_lock_irqsave(&emu->emu_lock, flags);
476 outb(reg, emu->port + AC97ADDRESS);
477 val = inw(emu->port + AC97DATA);
478 spin_unlock_irqrestore(&emu->emu_lock, flags);
479 return val;
480}
481
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100482void snd_emu10k1_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483{
Takashi Iwaieb4698f2005-11-17 14:50:13 +0100484 struct snd_emu10k1 *emu = ac97->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 unsigned long flags;
486
487 spin_lock_irqsave(&emu->emu_lock, flags);
488 outb(reg, emu->port + AC97ADDRESS);
489 outw(data, emu->port + AC97DATA);
490 spin_unlock_irqrestore(&emu->emu_lock, flags);
491}
492
493/*
494 * convert rate to pitch
495 */
496
497unsigned int snd_emu10k1_rate_to_pitch(unsigned int rate)
498{
499 static u32 logMagTable[128] = {
500 0x00000, 0x02dfc, 0x05b9e, 0x088e6, 0x0b5d6, 0x0e26f, 0x10eb3, 0x13aa2,
501 0x1663f, 0x1918a, 0x1bc84, 0x1e72e, 0x2118b, 0x23b9a, 0x2655d, 0x28ed5,
502 0x2b803, 0x2e0e8, 0x30985, 0x331db, 0x359eb, 0x381b6, 0x3a93d, 0x3d081,
503 0x3f782, 0x41e42, 0x444c1, 0x46b01, 0x49101, 0x4b6c4, 0x4dc49, 0x50191,
504 0x5269e, 0x54b6f, 0x57006, 0x59463, 0x5b888, 0x5dc74, 0x60029, 0x623a7,
505 0x646ee, 0x66a00, 0x68cdd, 0x6af86, 0x6d1fa, 0x6f43c, 0x7164b, 0x73829,
506 0x759d4, 0x77b4f, 0x79c9a, 0x7bdb5, 0x7dea1, 0x7ff5e, 0x81fed, 0x8404e,
507 0x86082, 0x88089, 0x8a064, 0x8c014, 0x8df98, 0x8fef1, 0x91e20, 0x93d26,
508 0x95c01, 0x97ab4, 0x9993e, 0x9b79f, 0x9d5d9, 0x9f3ec, 0xa11d8, 0xa2f9d,
509 0xa4d3c, 0xa6ab5, 0xa8808, 0xaa537, 0xac241, 0xadf26, 0xafbe7, 0xb1885,
510 0xb3500, 0xb5157, 0xb6d8c, 0xb899f, 0xba58f, 0xbc15e, 0xbdd0c, 0xbf899,
511 0xc1404, 0xc2f50, 0xc4a7b, 0xc6587, 0xc8073, 0xc9b3f, 0xcb5ed, 0xcd07c,
512 0xceaec, 0xd053f, 0xd1f73, 0xd398a, 0xd5384, 0xd6d60, 0xd8720, 0xda0c3,
513 0xdba4a, 0xdd3b4, 0xded03, 0xe0636, 0xe1f4e, 0xe384a, 0xe512c, 0xe69f3,
514 0xe829f, 0xe9b31, 0xeb3a9, 0xecc08, 0xee44c, 0xefc78, 0xf148a, 0xf2c83,
515 0xf4463, 0xf5c2a, 0xf73da, 0xf8b71, 0xfa2f0, 0xfba57, 0xfd1a7, 0xfe8df
516 };
517 static char logSlopeTable[128] = {
518 0x5c, 0x5c, 0x5b, 0x5a, 0x5a, 0x59, 0x58, 0x58,
519 0x57, 0x56, 0x56, 0x55, 0x55, 0x54, 0x53, 0x53,
520 0x52, 0x52, 0x51, 0x51, 0x50, 0x50, 0x4f, 0x4f,
521 0x4e, 0x4d, 0x4d, 0x4d, 0x4c, 0x4c, 0x4b, 0x4b,
522 0x4a, 0x4a, 0x49, 0x49, 0x48, 0x48, 0x47, 0x47,
523 0x47, 0x46, 0x46, 0x45, 0x45, 0x45, 0x44, 0x44,
524 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x41, 0x41,
525 0x41, 0x40, 0x40, 0x40, 0x3f, 0x3f, 0x3f, 0x3e,
526 0x3e, 0x3e, 0x3d, 0x3d, 0x3d, 0x3c, 0x3c, 0x3c,
527 0x3b, 0x3b, 0x3b, 0x3b, 0x3a, 0x3a, 0x3a, 0x39,
528 0x39, 0x39, 0x39, 0x38, 0x38, 0x38, 0x38, 0x37,
529 0x37, 0x37, 0x37, 0x36, 0x36, 0x36, 0x36, 0x35,
530 0x35, 0x35, 0x35, 0x34, 0x34, 0x34, 0x34, 0x34,
531 0x33, 0x33, 0x33, 0x33, 0x32, 0x32, 0x32, 0x32,
532 0x32, 0x31, 0x31, 0x31, 0x31, 0x31, 0x30, 0x30,
533 0x30, 0x30, 0x30, 0x2f, 0x2f, 0x2f, 0x2f, 0x2f
534 };
535 int i;
536
537 if (rate == 0)
538 return 0; /* Bail out if no leading "1" */
539 rate *= 11185; /* Scale 48000 to 0x20002380 */
540 for (i = 31; i > 0; i--) {
541 if (rate & 0x80000000) { /* Detect leading "1" */
542 return (((unsigned int) (i - 15) << 20) +
543 logMagTable[0x7f & (rate >> 24)] +
544 (0x7f & (rate >> 17)) *
545 logSlopeTable[0x7f & (rate >> 24)]);
546 }
547 rate <<= 1;
548 }
549
550 return 0; /* Should never reach this point */
551}
552