OpenCores
URL https://opencores.org/ocsvn/or1k_soc_on_altera_embedded_dev_kit/or1k_soc_on_altera_embedded_dev_kit/trunk

Subversion Repositories or1k_soc_on_altera_embedded_dev_kit

[/] [or1k_soc_on_altera_embedded_dev_kit/] [trunk/] [linux-2.6/] [linux-2.6.24/] [sound/] [oss/] [pas2_pcm.c] - Blame information for rev 3

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 3 xianfeng
/*
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
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.