Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
pas2_pcm.c
Go to the documentation of this file.
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  {
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 
180  if (get_user(val, p))
181  return -EFAULT;
182  ret = pcm_set_channels(val);
183  break;
184 
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);
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 
400  "Pro Audio Spectrum",
401  &pas_audio_driver,
402  sizeof(struct audio_driver),
403  DMA_AUTOMODE,
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:
427  break;
428 
429  case PCM_ADC:
431  break;
432 
433  default:
434  printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");
435  }
436  }
437 }