~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/drivers/sound/pas2_pcm.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 "sound_config.h"
 20 
 21 #include "pas2.h"
 22 
 23 #ifndef DEB
 24 #define DEB(WHAT)
 25 #endif
 26 
 27 #define PAS_PCM_INTRBITS (0x08)
 28 /*
 29  * Sample buffer timer interrupt enable
 30  */
 31 
 32 #define PCM_NON 0
 33 #define PCM_DAC 1
 34 #define PCM_ADC 2
 35 
 36 static unsigned long pcm_speed = 0;     /* sampling rate */
 37 static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
 38 static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
 39 static unsigned char pcm_filter = 0;    /* filter FLAG */
 40 static unsigned char pcm_mode = PCM_NON;
 41 static unsigned long pcm_count = 0;
 42 static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
 43 static int      pcm_busy = 0;
 44 int             pas_audiodev = -1;
 45 static int      open_mode = 0;
 46 
 47 static 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 = (596590 + (arg / 2)) / arg;
 63                 arg = (596590 + (foo / 2)) / foo;
 64         }
 65         else
 66         {
 67                 foo = (1193180 + (arg / 2)) / arg;
 68                 arg = (1193180 + (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         save_flags(flags);
105         cli();
106 
107         pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);
108         pas_write(0x00 | 0x30 | 0x04, 0x138B);
109         pas_write(foo & 0xff, 0x1388);
110         pas_write((foo >> 8) & 0xff, 0x1388);
111         pas_write(tmp, 0x0B8A);
112 
113         restore_flags(flags);
114 
115         return pcm_speed;
116 }
117 
118 static int pcm_set_channels(int arg)
119 {
120 
121         if ((arg != 1) && (arg != 2))
122                 return pcm_channels;
123 
124         if (arg != pcm_channels)
125         {
126                 pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);
127 
128                 pcm_channels = arg;
129                 pcm_set_speed(pcm_speed);       /* The speed must be reinitialized */
130         }
131         return pcm_channels;
132 }
133 
134 static int pcm_set_bits(int arg)
135 {
136         if (arg == 0)
137                 return pcm_bits;
138 
139         if ((arg & pcm_bitsok) != arg)
140                 return pcm_bits;
141 
142         if (arg != pcm_bits)
143         {
144                 pas_write(pas_read(0x8389) ^ 0x04, 0x8389);
145 
146                 pcm_bits = arg;
147         }
148         return pcm_bits;
149 }
150 
151 static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg)
152 {
153         int val, ret;
154 
155         DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));
156 
157         switch (cmd) 
158         {
159         case SOUND_PCM_WRITE_RATE:
160                 if (get_user(val, (int *)arg)) 
161                         return -EFAULT;
162                 ret = pcm_set_speed(val);
163                 break;
164 
165         case SOUND_PCM_READ_RATE:
166                 ret = pcm_speed;
167                 break;
168                 
169         case SNDCTL_DSP_STEREO:
170                 if (get_user(val, (int *)arg)) 
171                         return -EFAULT;
172                 ret = pcm_set_channels(val + 1) - 1;
173                 break;
174 
175         case SOUND_PCM_WRITE_CHANNELS:
176                 if (get_user(val, (int *)arg)) 
177                         return -EFAULT;
178                 ret = pcm_set_channels(val);
179                 break;
180 
181         case SOUND_PCM_READ_CHANNELS:
182                 ret = pcm_channels;
183                 break;
184 
185         case SNDCTL_DSP_SETFMT:
186                 if (get_user(val, (int *)arg))
187                         return -EFAULT;
188                 ret = pcm_set_bits(val);
189                 break;
190                 
191         case SOUND_PCM_READ_BITS:
192                 ret = pcm_bits;
193                 break;
194   
195         default:
196                 return -EINVAL;
197         }
198         return put_user(ret, (int *)arg);
199 }
200 
201 static void pas_audio_reset(int dev)
202 {
203         DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));
204 
205         pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);      /* Disable PCM */
206 }
207 
208 static int pas_audio_open(int dev, int mode)
209 {
210         int             err;
211         unsigned long   flags;
212 
213         DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));
214 
215         save_flags(flags);
216         cli();
217         if (pcm_busy)
218         {
219                 restore_flags(flags);
220                 return -EBUSY;
221         }
222         pcm_busy = 1;
223         restore_flags(flags);
224 
225         if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
226                 return err;
227 
228 
229         pcm_count = 0;
230         open_mode = mode;
231 
232         return 0;
233 }
234 
235 static void pas_audio_close(int dev)
236 {
237         unsigned long   flags;
238 
239         DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));
240 
241         save_flags(flags);
242         cli();
243 
244         pas_audio_reset(dev);
245         pas_remove_intr(PAS_PCM_INTRBITS);
246         pcm_mode = PCM_NON;
247 
248         pcm_busy = 0;
249         restore_flags(flags);
250 }
251 
252 static void pas_audio_output_block(int dev, unsigned long buf, int count,
253                        int intrflag)
254 {
255         unsigned long   flags, cnt;
256 
257         DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));
258 
259         cnt = count;
260         if (audio_devs[dev]->dmap_out->dma > 3)
261                 cnt >>= 1;
262 
263         if (audio_devs[dev]->flags & DMA_AUTOMODE &&
264             intrflag &&
265             cnt == pcm_count)
266                 return;
267 
268         save_flags(flags);
269         cli();
270 
271         pas_write(pas_read(0xF8A) & ~0x40,
272                   0xF8A);
273 
274         /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */
275 
276         if (audio_devs[dev]->dmap_out->dma > 3)
277                 count >>= 1;
278 
279         if (count != pcm_count)
280         {
281                 pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
282                 pas_write(0x40 | 0x30 | 0x04, 0x138B);
283                 pas_write(count & 0xff, 0x1389);
284                 pas_write((count >> 8) & 0xff, 0x1389);
285                 pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
286 
287                 pcm_count = count;
288         }
289         pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
290 #ifdef NO_TRIGGER
291         pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
292 #endif
293 
294         pcm_mode = PCM_DAC;
295 
296         restore_flags(flags);
297 }
298 
299 static void pas_audio_start_input(int dev, unsigned long buf, int count,
300                       int intrflag)
301 {
302         unsigned long   flags;
303         int             cnt;
304 
305         DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));
306 
307         cnt = count;
308         if (audio_devs[dev]->dmap_out->dma > 3)
309                 cnt >>= 1;
310 
311         if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&
312             intrflag &&
313             cnt == pcm_count)
314                 return;
315 
316         save_flags(flags);
317         cli();
318 
319         /* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */
320 
321         if (audio_devs[dev]->dmap_out->dma > 3)
322                 count >>= 1;
323 
324         if (count != pcm_count)
325         {
326                 pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);
327                 pas_write(0x40 | 0x30 | 0x04, 0x138B);
328                 pas_write(count & 0xff, 0x1389);
329                 pas_write((count >> 8) & 0xff, 0x1389);
330                 pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);
331 
332                 pcm_count = count;
333         }
334         pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);
335 #ifdef NO_TRIGGER
336         pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
337 #endif
338 
339         pcm_mode = PCM_ADC;
340 
341         restore_flags(flags);
342 }
343 
344 #ifndef NO_TRIGGER
345 static void pas_audio_trigger(int dev, int state)
346 {
347         unsigned long   flags;
348 
349         save_flags(flags);
350         cli();
351         state &= open_mode;
352 
353         if (state & PCM_ENABLE_OUTPUT)
354                 pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);
355         else if (state & PCM_ENABLE_INPUT)
356                 pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);
357         else
358                 pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
359 
360         restore_flags(flags);
361 }
362 #endif
363 
364 static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
365 {
366         pas_audio_reset(dev);
367         return 0;
368 }
369 
370 static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
371 {
372         pas_audio_reset(dev);
373         return 0;
374 }
375 
376 static struct audio_driver pas_audio_driver =
377 {
378         owner:          THIS_MODULE,
379         open:           pas_audio_open,
380         close:          pas_audio_close,
381         output_block:   pas_audio_output_block,
382         start_input:    pas_audio_start_input,
383         ioctl:          pas_audio_ioctl,
384         prepare_for_input:      pas_audio_prepare_for_input,
385         prepare_for_output:     pas_audio_prepare_for_output,
386         halt_io:                pas_audio_reset,
387         trigger:        pas_audio_trigger
388 };
389 
390 void __init pas_pcm_init(struct address_info *hw_config)
391 {
392         DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));
393 
394         pcm_bitsok = 8;
395         if (pas_read(0xEF8B) & 0x08)
396                 pcm_bitsok |= 16;
397 
398         pcm_set_speed(DSP_DEFAULT_SPEED);
399 
400         if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,
401                                         "Pro Audio Spectrum",
402                                         &pas_audio_driver,
403                                         sizeof(struct audio_driver),
404                                         DMA_AUTOMODE,
405                                         AFMT_U8 | AFMT_S16_LE,
406                                         NULL,
407                                         hw_config->dma,
408                                         hw_config->dma)) < 0)
409                 printk(KERN_WARNING "PAS16: Too many PCM devices available\n");
410 }
411 
412 void pas_pcm_interrupt(unsigned char status, int cause)
413 {
414         if (cause == 1)
415         {
416                 /*
417                  * Halt the PCM first. Otherwise we don't have time to start a new
418                  * block before the PCM chip proceeds to the next sample
419                  */
420 
421                 if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))
422                         pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);
423 
424                 switch (pcm_mode)
425                 {
426                         case PCM_DAC:
427                                 DMAbuf_outputintr(pas_audiodev, 1);
428                                 break;
429 
430                         case PCM_ADC:
431                                 DMAbuf_inputintr(pas_audiodev);
432                                 break;
433 
434                         default:
435                                 printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
436                 }
437         }
438 }
439 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.