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

TOMOYO Linux Cross Reference
Linux/sound/oss/pas2_pcm.c

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

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

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp