Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/oss/pas2_pcm.c
10814 views
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
439