regmap: Clean up hwspinlock on regmap exit
[sfrench/cifs-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 <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
34 static unsigned long pcm_speed;         /* sampling rate */
35 static unsigned char pcm_channels = 1;  /* channels (1 or 2) */
36 static unsigned char pcm_bits = 8;      /* bits/sample (8 or 16) */
37 static unsigned char pcm_filter;        /* filter FLAG */
38 static unsigned char pcm_mode = PCM_NON;
39 static unsigned long pcm_count;
40 static unsigned short pcm_bitsok = 8;   /* mask of OK bits */
41 static int      pcm_busy;
42 int             pas_audiodev = -1;
43 static int      open_mode;
44
45 extern spinlock_t pas_lock;
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 = ((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
117 static 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
133 static 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
150 static 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
199 static void pas_audio_reset(int dev)
200 {
201         pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);      /* Disable PCM */
202 }
203
204 static 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
228 static 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
242 static 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
286 static 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
329 static 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
347 static int pas_audio_prepare_for_input(int dev, int bsize, int bcount)
348 {
349         pas_audio_reset(dev);
350         return 0;
351 }
352
353 static int pas_audio_prepare_for_output(int dev, int bsize, int bcount)
354 {
355         pas_audio_reset(dev);
356         return 0;
357 }
358
359 static 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
373 void __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
393 void 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 }