1/* 2 * pas2_pcm.c Audio routines for PAS16 3 * 4 * 5 * Copyright (C) by Hannu Savolainen 1993-1997 6 * 7 * OSS/Free for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) 8 * Version 2 (June 1991). See the "COPYING" file distributed with this software 9 * for more info. 10 * 11 * 12 * Thomas Sailer : ioctl code reworked (vmalloc/vfree removed) 13 * Alan Cox : Swatted a double allocation of device bug. Made a few 14 * more things module options. 15 * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init() 16 */ 17 18#include <linux/init.h> 19#include <linux/spinlock.h> 20#include <linux/timex.h> 21#include "sound_config.h" 22 23#include "pas2.h" 24 25#define PAS_PCM_INTRBITS (0x08) 26/* 27 * Sample buffer timer interrupt enable 28 */ 29 30#define PCM_NON 0 31#define PCM_DAC 1 32#define PCM_ADC 2 33 34static unsigned long pcm_speed; /* sampling rate */ 35static unsigned char pcm_channels = 1; /* channels (1 or 2) */ 36static unsigned char pcm_bits = 8; /* bits/sample (8 or 16) */ 37static unsigned char pcm_filter; /* filter FLAG */ 38static unsigned char pcm_mode = PCM_NON; 39static unsigned long pcm_count; 40static unsigned short pcm_bitsok = 8; /* mask of OK bits */ 41static int pcm_busy; 42int pas_audiodev = -1; 43static int open_mode; 44 45extern spinlock_t pas_lock; 46 47static int pcm_set_speed(int arg) 48{ 49 int foo, tmp; 50 unsigned long flags; 51 52 if (arg == 0) 53 return pcm_speed; 54 55 if (arg > 44100) 56 arg = 44100; 57 if (arg < 5000) 58 arg = 5000; 59 60 if (pcm_channels & 2) 61 { 62 foo = ((PIT_TICK_RATE / 2) + (arg / 2)) / arg; 63 arg = ((PIT_TICK_RATE / 2) + (foo / 2)) / foo; 64 } 65 else 66 { 67 foo = (PIT_TICK_RATE + (arg / 2)) / arg; 68 arg = (PIT_TICK_RATE + (foo / 2)) / foo; 69 } 70 71 pcm_speed = arg; 72 73 tmp = pas_read(0x0B8A); 74 75 /* 76 * Set anti-aliasing filters according to sample rate. You really *NEED* 77 * to enable this feature for all normal recording unless you want to 78 * experiment with aliasing effects. 79 * These filters apply to the selected "recording" source. 80 * I (pfw) don't know the encoding of these 5 bits. The values shown 81 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/. 82 * 83 * I cleared bit 5 of these values, since that bit controls the master 84 * mute flag. (Olav Wölfelschneider) 85 * 86 */ 87#if !defined NO_AUTO_FILTER_SET 88 tmp &= 0xe0; 89 if (pcm_speed >= 2 * 17897) 90 tmp |= 0x01; 91 else if (pcm_speed >= 2 * 15909) 92 tmp |= 0x02; 93 else if (pcm_speed >= 2 * 11931) 94 tmp |= 0x09; 95 else if (pcm_speed >= 2 * 8948) 96 tmp |= 0x11; 97 else if (pcm_speed >= 2 * 5965) 98 tmp |= 0x19; 99 else if (pcm_speed >= 2 * 2982) 100 tmp |= 0x04; 101 pcm_filter = tmp; 102#endif 103 104 spin_lock_irqsave(&pas_lock, flags); 105 106 pas_write(tmp & ~(0x40 | 0x80), 0x0B8A); 107 pas_write(0x00 | 0x30 | 0x04, 0x138B); 108 pas_write(foo & 0xff, 0x1388); 109 pas_write((foo >> 8) & 0xff, 0x1388); 110 pas_write(tmp, 0x0B8A); 111 112 spin_unlock_irqrestore(&pas_lock, flags); 113 114 return pcm_speed; 115} 116 117static int pcm_set_channels(int arg) 118{ 119 120 if ((arg != 1) && (arg != 2)) 121 return pcm_channels; 122 123 if (arg != pcm_channels) 124 { 125 pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A); 126 127 pcm_channels = arg; 128 pcm_set_speed(pcm_speed); /* The speed must be reinitialized */ 129 } 130 return pcm_channels; 131} 132 133static int pcm_set_bits(int arg) 134{ 135 if (arg == 0) 136 return pcm_bits; 137 138 if ((arg & pcm_bitsok) != arg) 139 return pcm_bits; 140 141 if (arg != pcm_bits) 142 { 143 pas_write(pas_read(0x8389) ^ 0x04, 0x8389); 144 145 pcm_bits = arg; 146 } 147 return pcm_bits; 148} 149 150static int pas_audio_ioctl(int dev, unsigned int cmd, void __user *arg) 151{ 152 int val, ret; 153 int __user *p = arg; 154 155 switch (cmd) 156 { 157 case SOUND_PCM_WRITE_RATE: 158 if (get_user(val, p)) 159 return -EFAULT; 160 ret = pcm_set_speed(val); 161 break; 162 163 case SOUND_PCM_READ_RATE: 164 ret = pcm_speed; 165 break; 166 167 case SNDCTL_DSP_STEREO: 168 if (get_user(val, p)) 169 return -EFAULT; 170 ret = pcm_set_channels(val + 1) - 1; 171 break; 172 173 case SOUND_PCM_WRITE_CHANNELS: 174 if (get_user(val, p)) 175 return -EFAULT; 176 ret = pcm_set_channels(val); 177 break; 178 179 case SOUND_PCM_READ_CHANNELS: 180 ret = pcm_channels; 181 break; 182 183 case SNDCTL_DSP_SETFMT: 184 if (get_user(val, p)) 185 return -EFAULT; 186 ret = pcm_set_bits(val); 187 break; 188 189 case SOUND_PCM_READ_BITS: 190 ret = pcm_bits; 191 break; 192 193 default: 194 return -EINVAL; 195 } 196 return put_user(ret, p); 197} 198 199static void pas_audio_reset(int dev) 200{ 201 pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); /* Disable PCM */ 202} 203 204static int pas_audio_open(int dev, int mode) 205{ 206 int err; 207 unsigned long flags; 208 209 spin_lock_irqsave(&pas_lock, flags); 210 if (pcm_busy) 211 { 212 spin_unlock_irqrestore(&pas_lock, flags); 213 return -EBUSY; 214 } 215 pcm_busy = 1; 216 spin_unlock_irqrestore(&pas_lock, flags); 217 218 if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0) 219 return err; 220 221 222 pcm_count = 0; 223 open_mode = mode; 224 225 return 0; 226} 227 228static void pas_audio_close(int dev) 229{ 230 unsigned long flags; 231 232 spin_lock_irqsave(&pas_lock, flags); 233 234 pas_audio_reset(dev); 235 pas_remove_intr(PAS_PCM_INTRBITS); 236 pcm_mode = PCM_NON; 237 238 pcm_busy = 0; 239 spin_unlock_irqrestore(&pas_lock, flags); 240} 241 242static void pas_audio_output_block(int dev, unsigned long buf, int count, 243 int intrflag) 244{ 245 unsigned long flags, cnt; 246 247 cnt = count; 248 if (audio_devs[dev]->dmap_out->dma > 3) 249 cnt >>= 1; 250 251 if (audio_devs[dev]->flags & DMA_AUTOMODE && 252 intrflag && 253 cnt == pcm_count) 254 return; 255 256 spin_lock_irqsave(&pas_lock, flags); 257 258 pas_write(pas_read(0xF8A) & ~0x40, 259 0xF8A); 260 261 /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */ 262 263 if (audio_devs[dev]->dmap_out->dma > 3) 264 count >>= 1; 265 266 if (count != pcm_count) 267 { 268 pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); 269 pas_write(0x40 | 0x30 | 0x04, 0x138B); 270 pas_write(count & 0xff, 0x1389); 271 pas_write((count >> 8) & 0xff, 0x1389); 272 pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); 273 274 pcm_count = count; 275 } 276 pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); 277#ifdef NO_TRIGGER 278 pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); 279#endif 280 281 pcm_mode = PCM_DAC; 282 283 spin_unlock_irqrestore(&pas_lock, flags); 284} 285 286static void pas_audio_start_input(int dev, unsigned long buf, int count, 287 int intrflag) 288{ 289 unsigned long flags; 290 int cnt; 291 292 cnt = count; 293 if (audio_devs[dev]->dmap_out->dma > 3) 294 cnt >>= 1; 295 296 if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE && 297 intrflag && 298 cnt == pcm_count) 299 return; 300 301 spin_lock_irqsave(&pas_lock, flags); 302 303 /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */ 304 305 if (audio_devs[dev]->dmap_out->dma > 3) 306 count >>= 1; 307 308 if (count != pcm_count) 309 { 310 pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A); 311 pas_write(0x40 | 0x30 | 0x04, 0x138B); 312 pas_write(count & 0xff, 0x1389); 313 pas_write((count >> 8) & 0xff, 0x1389); 314 pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A); 315 316 pcm_count = count; 317 } 318 pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A); 319#ifdef NO_TRIGGER 320 pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); 321#endif 322 323 pcm_mode = PCM_ADC; 324 325 spin_unlock_irqrestore(&pas_lock, flags); 326} 327 328#ifndef NO_TRIGGER 329static void pas_audio_trigger(int dev, int state) 330{ 331 unsigned long flags; 332 333 spin_lock_irqsave(&pas_lock, flags); 334 state &= open_mode; 335 336 if (state & PCM_ENABLE_OUTPUT) 337 pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A); 338 else if (state & PCM_ENABLE_INPUT) 339 pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A); 340 else 341 pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); 342 343 spin_unlock_irqrestore(&pas_lock, flags); 344} 345#endif 346 347static int pas_audio_prepare_for_input(int dev, int bsize, int bcount) 348{ 349 pas_audio_reset(dev); 350 return 0; 351} 352 353static int pas_audio_prepare_for_output(int dev, int bsize, int bcount) 354{ 355 pas_audio_reset(dev); 356 return 0; 357} 358 359static struct audio_driver pas_audio_driver = 360{ 361 .owner = THIS_MODULE, 362 .open = pas_audio_open, 363 .close = pas_audio_close, 364 .output_block = pas_audio_output_block, 365 .start_input = pas_audio_start_input, 366 .ioctl = pas_audio_ioctl, 367 .prepare_for_input = pas_audio_prepare_for_input, 368 .prepare_for_output = pas_audio_prepare_for_output, 369 .halt_io = pas_audio_reset, 370 .trigger = pas_audio_trigger 371}; 372 373void __init pas_pcm_init(struct address_info *hw_config) 374{ 375 pcm_bitsok = 8; 376 if (pas_read(0xEF8B) & 0x08) 377 pcm_bitsok |= 16; 378 379 pcm_set_speed(DSP_DEFAULT_SPEED); 380 381 if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, 382 "Pro Audio Spectrum", 383 &pas_audio_driver, 384 sizeof(struct audio_driver), 385 DMA_AUTOMODE, 386 AFMT_U8 | AFMT_S16_LE, 387 NULL, 388 hw_config->dma, 389 hw_config->dma)) < 0) 390 printk(KERN_WARNING "PAS16: Too many PCM devices available\n"); 391} 392 393void pas_pcm_interrupt(unsigned char status, int cause) 394{ 395 if (cause == 1) 396 { 397 /* 398 * Halt the PCM first. Otherwise we don't have time to start a new 399 * block before the PCM chip proceeds to the next sample 400 */ 401 402 if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE)) 403 pas_write(pas_read(0xF8A) & ~0x40, 0xF8A); 404 405 switch (pcm_mode) 406 { 407 case PCM_DAC: 408 DMAbuf_outputintr(pas_audiodev, 1); 409 break; 410 411 case PCM_ADC: 412 DMAbuf_inputintr(pas_audiodev); 413 break; 414 415 default: 416 printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n"); 417 } 418 } 419} 420