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