]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - sound/oss/pas2_pcm.c
Use rtc_lock to protect RTC operations
[linux-2.6.git] / sound / oss / pas2_pcm.c
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 <asm/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 = ((CLOCK_TICK_RATE / 2) + (arg / 2)) / arg;
67                 arg = ((CLOCK_TICK_RATE / 2) + (foo / 2)) / foo;
68         }
69         else
70         {
71                 foo = (CLOCK_TICK_RATE + (arg / 2)) / arg;
72                 arg = (CLOCK_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 }