Root/
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 = ((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 | } |
438 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9