Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / sound / isa / i386 / pas2 / pas2_pcm.c
1 #define _PAS2_PCM_C_
2 /*
3  * sound/pas2_pcm.c
4  * 
5  * The low level driver for the Pro Audio Spectrum ADC/DAC.
6  * 
7  * Copyright by Hannu Savolainen 1993
8  * 
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions are
11  * met: 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer. 2.
13  * Redistributions in binary form must reproduce the above copyright notice,
14  * this list of conditions and the following disclaimer in the documentation
15  * and/or other materials provided with the distribution.
16  * 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
21  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  * 
29  */
30
31 #include <i386/isa/sound/sound_config.h>
32
33 #if defined(CONFIG_PAS) && defined(CONFIG_AUDIO)
34 #include <i386/isa/sound/pas_hw.h>
35
36
37 #define TRACE(WHAT)             /* * * (WHAT)   */
38
39 #define PAS_PCM_INTRBITS (0x08)
40 /*
41  * Sample buffer timer interrupt enable
42  */
43
44 #define PCM_NON 0
45 #define PCM_DAC 1
46 #define PCM_ADC 2
47
48 static u_long pcm_speed = 0;    /* sampling rate */
49 static u_char pcm_channels = 1; /* channels (1 or 2) */
50 static u_char pcm_bits = 8;     /* bits/sample (8 or 16) */
51 static u_char pcm_filter = 0;   /* filter FLAG */
52 static u_char pcm_mode = PCM_NON;
53 static u_long pcm_count = 0;
54 static u_short pcm_bitsok = 8;  /* mask of OK bits */
55 static int      my_devnum = 0;
56 static int      open_mode = 0;
57
58 static int
59 pcm_set_speed(int arg)
60 {
61         int             foo, tmp;
62         u_long   flags;
63
64         if (arg > 44100)
65                 arg = 44100;
66         if (arg < 5000)
67                 arg = 5000;
68
69         foo = (1193180 + (arg / 2)) / arg;
70         arg = 1193180 / foo;
71
72         if (pcm_channels & 2)
73                 foo = foo >> 1;
74
75         pcm_speed = arg;
76
77         tmp = pas_read(FILTER_FREQUENCY);
78
79         /*
80          * Set anti-aliasing filters according to sample rate. You reall
81          * *NEED* to enable this feature for all normal recording unless you
82          * want to experiment with aliasing effects. These filters apply to
83          * the selected "recording" source. I (pfw) don't know the encoding
84          * of these 5 bits. The values shown come from the SDK found on
85          * ftp.uwp.edu:/pub/msdos/proaudio/.
86          */
87 #if !defined NO_AUTO_FILTER_SET
88         tmp &= 0xe0;
89         if (pcm_speed >= 2 * 17897)
90                 tmp |= 0x21;
91         else if (pcm_speed >= 2 * 15909)
92                 tmp |= 0x22;
93         else if (pcm_speed >= 2 * 11931)
94                 tmp |= 0x29;
95         else if (pcm_speed >= 2 * 8948)
96                 tmp |= 0x31;
97         else if (pcm_speed >= 2 * 5965)
98                 tmp |= 0x39;
99         else if (pcm_speed >= 2 * 2982)
100                 tmp |= 0x24;
101         pcm_filter = tmp;
102 #endif
103
104         flags = splhigh();
105
106         pas_write(tmp & ~(F_F_PCM_RATE_COUNTER | F_F_PCM_BUFFER_COUNTER), FILTER_FREQUENCY);
107         pas_write(S_C_C_SAMPLE_RATE | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
108         pas_write(foo & 0xff, SAMPLE_RATE_TIMER);
109         pas_write((foo >> 8) & 0xff, SAMPLE_RATE_TIMER);
110         pas_write(tmp, FILTER_FREQUENCY);
111
112         splx(flags);
113
114         return pcm_speed;
115 }
116
117 static int
118 pcm_set_channels(int arg)
119 {
120
121         if ((arg != 1) && (arg != 2))
122                 return pcm_channels;
123
124         if (arg != pcm_channels) {
125                 pas_write(pas_read(PCM_CONTROL) ^ P_C_PCM_MONO, PCM_CONTROL);
126
127                 pcm_channels = arg;
128                 pcm_set_speed(pcm_speed);       /* The speed must be
129                                                  * reinitialized */
130         }
131         return pcm_channels;
132 }
133
134 static int
135 pcm_set_bits(int arg)
136 {
137         if ((arg & pcm_bitsok) != arg)
138                 return pcm_bits;
139
140         if (arg != pcm_bits) {
141                 pas_write(pas_read(SYSTEM_CONFIGURATION_2) ^ S_C_2_PCM_16_BIT, SYSTEM_CONFIGURATION_2);
142
143                 pcm_bits = arg;
144         }
145         return pcm_bits;
146 }
147
148 static int
149 pas_pcm_ioctl(int dev, u_int cmd, ioctl_arg arg, int local)
150 {
151         TRACE(printf("pas2_pcm.c: static int pas_pcm_ioctl(u_int cmd = %X, u_int arg = %X)\n", cmd, arg));
152
153         switch (cmd) {
154         case SOUND_PCM_WRITE_RATE:
155                 if (local)
156                         return pcm_set_speed((int) arg);
157                 return *(int *) arg = pcm_set_speed((*(int *) arg));
158                 break;
159
160         case SOUND_PCM_READ_RATE:
161                 if (local)
162                         return pcm_speed;
163                 return *(int *) arg = pcm_speed;
164                 break;
165
166         case SNDCTL_DSP_STEREO:
167                 if (local)
168                         return pcm_set_channels((int) arg + 1) - 1;
169                 return *(int *) arg = pcm_set_channels((*(int *) arg) + 1) - 1;
170                 break;
171
172         case SOUND_PCM_WRITE_CHANNELS:
173                 if (local)
174                         return pcm_set_channels((int) arg);
175                 return *(int *) arg = pcm_set_channels((*(int *) arg));
176                 break;
177
178         case SOUND_PCM_READ_CHANNELS:
179                 if (local)
180                         return pcm_channels;
181                 return *(int *) arg = pcm_channels;
182                 break;
183
184         case SNDCTL_DSP_SETFMT:
185                 if (local)
186                         return pcm_set_bits((int) arg);
187                 return *(int *) arg = pcm_set_bits((*(int *) arg));
188                 break;
189
190         case SOUND_PCM_READ_BITS:
191                 if (local)
192                         return pcm_bits;
193                 return *(int *) arg = pcm_bits;
194
195         case SOUND_PCM_WRITE_FILTER:    /* NOT YET IMPLEMENTED */
196                 if ((*(int *) arg) > 1)
197                         return -(EINVAL);
198                 pcm_filter = (*(int *) arg);
199                 break;
200
201         case SOUND_PCM_READ_FILTER:
202                 return *(int *) arg = pcm_filter;
203                 break;
204
205         default:
206                 return -(EINVAL);
207         }
208
209         return -(EINVAL);
210 }
211
212 static void
213 pas_pcm_reset(int dev)
214 {
215         TRACE(printf("pas2_pcm.c: static void pas_pcm_reset(void)\n"));
216
217         pas_write(pas_read(PCM_CONTROL) & ~P_C_PCM_ENABLE, PCM_CONTROL);
218 }
219
220 static int
221 pas_pcm_open(int dev, int mode)
222 {
223         int             err;
224
225         TRACE(printf("pas2_pcm.c: static int pas_pcm_open(int mode = %X)\n", mode));
226
227         if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)
228                 return err;
229
230
231         pcm_count = 0;
232         open_mode = mode;
233
234         return 0;
235 }
236
237 static void
238 pas_pcm_close(int dev)
239 {
240         u_long   flags;
241
242         TRACE(printf("pas2_pcm.c: static void pas_pcm_close(void)\n"));
243
244         flags = splhigh();
245
246         pas_pcm_reset(dev);
247         pas_remove_intr(PAS_PCM_INTRBITS);
248         pcm_mode = PCM_NON;
249
250         open_mode = 0;
251
252         splx(flags);
253 }
254
255 static void
256 pas_pcm_output_block(int dev, u_long buf, int count,
257                      int intrflag, int restart_dma)
258 {
259         u_long   flags, cnt;
260
261         TRACE(printf("pas2_pcm.c: static void pas_pcm_output_block(char *buf = %P, int count = %X)\n", buf, count));
262
263         cnt = count;
264         if (audio_devs[dev]->dmachan1 > 3)
265                 cnt >>= 1;
266
267         if (audio_devs[dev]->flags & DMA_AUTOMODE &&
268             intrflag &&
269             cnt == pcm_count)
270                 return;         /* Auto mode on. No need to react */
271
272         flags = splhigh();
273
274         pas_write(pas_read(PCM_CONTROL) & ~P_C_PCM_ENABLE,
275                   PCM_CONTROL);
276
277         if (restart_dma)
278                 DMAbuf_start_dma(dev, buf, count, 1);
279
280         if (audio_devs[dev]->dmachan1 > 3)
281                 count >>= 1;
282
283         if (count != pcm_count) {
284                 pas_write(pas_read(FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
285                 pas_write(S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
286                 pas_write(count & 0xff, SAMPLE_BUFFER_COUNTER);
287                 pas_write((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
288                 pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
289
290                 pcm_count = count;
291         }
292         pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
293 #ifdef NO_TRIGGER
294         pas_write(pas_read(PCM_CONTROL) | P_C_PCM_ENABLE | P_C_PCM_DAC_MODE, PCM_CONTROL);
295 #endif
296
297         pcm_mode = PCM_DAC;
298
299         splx(flags);
300 }
301
302 static void
303 pas_pcm_start_input(int dev, u_long buf, int count,
304                     int intrflag, int restart_dma)
305 {
306         u_long   flags;
307         int             cnt;
308
309         TRACE(printf("pas2_pcm.c: static void pas_pcm_start_input(char *buf = %P, int count = %X)\n", buf, count));
310
311         cnt = count;
312         if (audio_devs[dev]->dmachan1 > 3)
313                 cnt >>= 1;
314
315         if (audio_devs[my_devnum]->flags & DMA_AUTOMODE &&
316             intrflag &&
317             cnt == pcm_count)
318                 return;         /* Auto mode on. No need to react */
319
320         flags = splhigh();
321
322         if (restart_dma)
323                 DMAbuf_start_dma(dev, buf, count, 0);
324
325         if (audio_devs[dev]->dmachan1 > 3)
326                 count >>= 1;
327
328         if (count != pcm_count) {
329                 pas_write(pas_read(FILTER_FREQUENCY) & ~F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
330                 pas_write(S_C_C_SAMPLE_BUFFER | S_C_C_LSB_THEN_MSB | S_C_C_SQUARE_WAVE, SAMPLE_COUNTER_CONTROL);
331                 pas_write(count & 0xff, SAMPLE_BUFFER_COUNTER);
332                 pas_write((count >> 8) & 0xff, SAMPLE_BUFFER_COUNTER);
333                 pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER, FILTER_FREQUENCY);
334
335                 pcm_count = count;
336         }
337         pas_write(pas_read(FILTER_FREQUENCY) | F_F_PCM_BUFFER_COUNTER | F_F_PCM_RATE_COUNTER, FILTER_FREQUENCY);
338 #ifdef NO_TRIGGER
339         pas_write((pas_read(PCM_CONTROL) | P_C_PCM_ENABLE) & ~P_C_PCM_DAC_MODE, PCM_CONTROL);
340 #endif
341
342         pcm_mode = PCM_ADC;
343
344         splx(flags);
345 }
346 #ifndef NO_TRIGGER
347 static void
348 pas_audio_trigger (int dev, int state)
349 {
350   unsigned long   flags;
351
352   flags = splhigh();
353
354   state &= open_mode;
355
356   if (state & PCM_ENABLE_OUTPUT)
357     pas_write (pas_read (0xF8A) | 0x40 | 0x10, 0xF8A);
358   else if (state & PCM_ENABLE_INPUT)
359     pas_write ((pas_read (0xF8A) | 0x40) & ~0x10, 0xF8A);
360   else
361     pas_write (pas_read (0xF8A) & ~0x40, 0xF8A);
362
363   splx(flags);
364 }
365 #endif
366
367 static int
368 pas_pcm_prepare_for_input(int dev, int bsize, int bcount)
369 {
370         return 0;
371 }
372 static int
373 pas_pcm_prepare_for_output(int dev, int bsize, int bcount)
374 {
375         return 0;
376 }
377
378 static struct audio_operations pas_pcm_operations =
379 {
380         "Pro Audio Spectrum",
381         DMA_AUTOMODE,
382         AFMT_U8 | AFMT_S16_LE,
383         NULL,
384         pas_pcm_open,
385         pas_pcm_close,
386         pas_pcm_output_block,
387         pas_pcm_start_input,
388         pas_pcm_ioctl,
389         pas_pcm_prepare_for_input,
390         pas_pcm_prepare_for_output,
391         pas_pcm_reset,
392         pas_pcm_reset,
393         NULL,
394         NULL,
395         NULL,
396         NULL,
397         pas_audio_trigger
398 };
399
400 void
401 pas_pcm_init(struct address_info * hw_config)
402 {
403         pcm_bitsok = 8;
404         if (pas_read(OPERATION_MODE_1) & O_M_1_PCM_TYPE)
405                 pcm_bitsok |= 16;
406
407         pcm_set_speed(DSP_DEFAULT_SPEED);
408
409         if (num_audiodevs < MAX_AUDIO_DEV) {
410                 audio_devs[my_devnum = num_audiodevs++] = &pas_pcm_operations;
411                 audio_devs[my_devnum]->dmachan1 = hw_config->dma;
412                 audio_devs[my_devnum]->buffsize = DSP_BUFFSIZE;
413         } else
414                 printf("PAS2: Too many PCM devices available\n");
415
416         return;
417 }
418
419 void
420 pas_pcm_interrupt(u_char status, int cause)
421 {
422         if (cause == 1) {       /* PCM buffer done */
423                 /*
424                  * Halt the PCM first. Otherwise we don't have time to start
425                  * a new block before the PCM chip proceeds to the next
426                  * sample
427                  */
428
429                 if (!(audio_devs[my_devnum]->flags & DMA_AUTOMODE)) {
430                         pas_write(pas_read(PCM_CONTROL) & ~P_C_PCM_ENABLE,
431                                   PCM_CONTROL);
432                 }
433                 switch (pcm_mode) {
434
435                 case PCM_DAC:
436                         DMAbuf_outputintr(my_devnum, 1);
437                         break;
438
439                 case PCM_ADC:
440                         DMAbuf_inputintr(my_devnum);
441                         break;
442
443                 default:
444                         printf("PAS: Unexpected PCM interrupt\n");
445                 }
446         }
447 }
448
449 #endif