]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/pc98/pc98/pcaudio.c
unfinished sblive driver, playback/mixer only for now - not enabled in
[FreeBSD/FreeBSD.git] / sys / pc98 / pc98 / pcaudio.c
1 /*-
2  * Copyright (c) 1994-1998 S\ fen Schmidt
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer,
10  *    without modification, immediately at the beginning of the file.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/conf.h>
34 #include <sys/proc.h>
35 #include <sys/kernel.h>
36 #include <sys/bus.h>
37 #include <sys/filio.h>
38 #include <sys/poll.h>
39 #include <sys/vnode.h>
40
41 #include <machine/clock.h>
42 #include <machine/pcaudioio.h>
43
44 #include <isa/isareg.h>
45 #include <isa/isavar.h>
46 #include <i386/isa/timerreg.h>
47
48 #define BUF_SIZE        8192
49 #define SAMPLE_RATE     8000
50 #define INTERRUPT_RATE  16000
51
52 static struct pca_status {
53         char            open;           /* device open */
54         char            queries;        /* did others try opening */
55         unsigned char   *buf[3];        /* triple buffering */
56         unsigned char   *buffer;        /* current buffer ptr */
57         unsigned        in_use[3];      /* buffers fill */
58         unsigned        index;          /* index in current buffer */
59         unsigned        counter;        /* sample counter */
60         unsigned        scale;          /* sample counter scale */
61         unsigned        sample_rate;    /* sample rate */
62         unsigned        processed;      /* samples processed */
63         unsigned        volume;         /* volume for pc-speaker */
64         char            encoding;       /* Ulaw, Alaw or linear */
65         u_char          current;        /* current buffer */
66         unsigned char   oldval;         /* old timer port value */
67         char            timer_on;       /* is playback running */
68         struct selinfo  wsel;           /* select/poll status */
69 } pca_status;
70
71 static char buffer1[BUF_SIZE];
72 static char buffer2[BUF_SIZE];
73 static char buffer3[BUF_SIZE];
74 static char volume_table[256];
75
76 static unsigned char ulaw_dsp[] = {
77      3,    7,   11,   15,   19,   23,   27,   31,
78     35,   39,   43,   47,   51,   55,   59,   63,
79     66,   68,   70,   72,   74,   76,   78,   80,
80     82,   84,   86,   88,   90,   92,   94,   96,
81     98,   99,  100,  101,  102,  103,  104,  105,
82    106,  107,  108,  109,  110,  111,  112,  113,
83    113,  114,  114,  115,  115,  116,  116,  117,
84    117,  118,  118,  119,  119,  120,  120,  121,
85    121,  121,  122,  122,  122,  122,  123,  123,
86    123,  123,  124,  124,  124,  124,  125,  125,
87    125,  125,  125,  125,  126,  126,  126,  126,
88    126,  126,  126,  126,  127,  127,  127,  127,
89    127,  127,  127,  127,  127,  127,  127,  127,
90    128,  128,  128,  128,  128,  128,  128,  128,
91    128,  128,  128,  128,  128,  128,  128,  128,
92    128,  128,  128,  128,  128,  128,  128,  128,
93    253,  249,  245,  241,  237,  233,  229,  225,
94    221,  217,  213,  209,  205,  201,  197,  193,
95    190,  188,  186,  184,  182,  180,  178,  176,
96    174,  172,  170,  168,  166,  164,  162,  160,
97    158,  157,  156,  155,  154,  153,  152,  151,
98    150,  149,  148,  147,  146,  145,  144,  143,
99    143,  142,  142,  141,  141,  140,  140,  139,
100    139,  138,  138,  137,  137,  136,  136,  135,
101    135,  135,  134,  134,  134,  134,  133,  133,
102    133,  133,  132,  132,  132,  132,  131,  131,
103    131,  131,  131,  131,  130,  130,  130,  130,
104    130,  130,  130,  130,  129,  129,  129,  129,
105    129,  129,  129,  129,  129,  129,  129,  129,
106    128,  128,  128,  128,  128,  128,  128,  128,
107    128,  128,  128,  128,  128,  128,  128,  128,
108    128,  128,  128,  128,  128,  128,  128,  128,
109 };
110
111 static unsigned char alaw_linear[] = {
112         45,     214,    122,    133,    0,              255,    107,    149, 
113         86,     171,    126,    129,    0,              255,    117,    138, 
114         13,     246,    120,    135,    0,              255,    99,     157, 
115         70,     187,    124,    131,    0,              255,    113,    142, 
116         61,     198,    123,    132,    0,              255,    111,    145, 
117         94,     163,    127,    128,    0,              255,    119,    136, 
118         29,     230,    121,    134,    0,              255,    103,    153, 
119         78,     179,    125,    130,    0,              255,    115,    140, 
120         37,     222,    122,    133,    0,              255,    105,    151, 
121         82,     175,    126,    129,    0,              255,    116,    139, 
122         5,      254,    120,    135,    0,              255,    97,     159, 
123         66,     191,    124,    131,    0,              255,    112,    143, 
124         53,     206,    123,    132,    0,              255,    109,    147, 
125         90,     167,    127,    128,    0,              255,    118,    137, 
126         21,     238,    121,    134,    0,              255,    101,    155, 
127         74,     183,    125,    130,    0,              255,    114,    141, 
128         49,     210,    123,    133,    0,              255,    108,    148, 
129         88,     169,    127,    129,    0,              255,    118,    138, 
130         17,     242,    121,    135,    0,              255,    100,    156, 
131         72,     185,    125,    131,    0,              255,    114,    142, 
132         64,     194,    124,    132,    0,              255,    112,    144, 
133         96,     161,    128,    128,    1,              255,    120,    136, 
134         33,     226,    122,    134,    0,              255,    104,    152, 
135         80,     177,    126,    130,    0,              255,    116,    140, 
136         41,     218,    122,    133,    0,              255,    106,    150, 
137         84,     173,    126,    129,    0,              255,    117,    139, 
138         9,      250,    120,    135,    0,              255,    98,     158, 
139         68,     189,    124,    131,    0,              255,    113,    143, 
140         57,     202,    123,    132,    0,              255,    110,    146, 
141         92,     165,    127,    128,    0,              255,    119,    137, 
142         25,     234,    121,    134,    0,              255,    102,    154, 
143         76,     181,    125,    130,    0,              255,    115,    141, 
144 };
145
146 static int pca_sleep = 0;
147 static int pca_initialized = 0;
148
149 static void pcaintr(struct clockframe *frame);
150
151 static  d_open_t        pcaopen;
152 static  d_close_t       pcaclose;
153 static  d_write_t       pcawrite;
154 static  d_ioctl_t       pcaioctl;
155 static  d_poll_t        pcapoll;
156
157 #define CDEV_MAJOR 24
158 static struct cdevsw pca_cdevsw = {
159         /* open */      pcaopen,
160         /* close */     pcaclose,
161         /* read */      noread,
162         /* write */     pcawrite,
163         /* ioctl */     pcaioctl,
164         /* poll */      pcapoll,
165         /* mmap */      nommap,
166         /* strategy */  nostrategy,
167         /* name */      "pca",
168         /* maj */       CDEV_MAJOR,
169         /* dump */      nodump,
170         /* psize */     nopsize,
171         /* flags */     0,
172         /* bmaj */      -1
173 };
174
175 static void pca_continue __P((void));
176 static void pca_init __P((void));
177 static void pca_pause __P((void));
178
179 static void
180 conv(const unsigned char *table, unsigned char *buff, unsigned n)
181 {
182         unsigned i;
183
184         for (i = 0; i < n; i++)
185                 buff[i] = table[buff[i]];
186 }
187
188
189 static void
190 pca_volume(int volume)
191 {
192         int i, j;
193
194         for (i=0; i<256; i++) {
195                 j = ((i-128)*volume)/25;
196 /* XXX
197                 j = ((i-128)*volume)/100;
198 */
199                 if (j<-128)
200                         j = -128;
201                 if (j>127)
202                         j = 127;
203                 volume_table[i] = (((255-(j + 128))/4)+1);
204         }
205 }
206
207
208 static void
209 pca_init(void)
210 {
211         pca_status.open = 0;
212         pca_status.queries = 0;
213         pca_status.timer_on = 0;
214         pca_status.buf[0] = (unsigned char *)&buffer1[0];
215         pca_status.buf[1] = (unsigned char *)&buffer2[0];
216         pca_status.buf[2] = (unsigned char *)&buffer3[0];
217         pca_status.buffer = pca_status.buf[0];
218         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
219         pca_status.current = 0;
220         pca_status.sample_rate = SAMPLE_RATE;
221         pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE;
222         pca_status.encoding = AUDIO_ENCODING_ULAW;
223         pca_status.volume = 100;
224
225         pca_volume(pca_status.volume);
226 }
227
228
229 static int
230 pca_start(void)
231 {
232         int x = splhigh();
233         int rv = 0;
234
235         /* use the first buffer */
236         pca_status.current  = 0;
237         pca_status.index = 0;
238         pca_status.counter = 0;
239         pca_status.buffer  = pca_status.buf[pca_status.current];
240 #ifdef PC98
241         pca_status.oldval = inb(IO_PPI) & ~0x08;
242 #else
243         pca_status.oldval = inb(IO_PPI) | 0x03;
244 #endif
245         /* acquire the timers */
246 #ifdef PC98
247         if (acquire_timer1(TIMER_LSB|TIMER_ONESHOT))
248 #else
249         if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT))
250 #endif
251                 rv = -1;
252         else if (acquire_timer0(INTERRUPT_RATE, pcaintr)) {
253 #ifdef PC98
254                 release_timer1();
255 #else
256                 release_timer2();
257 #endif
258                 rv =  -1;
259         } else
260                 pca_status.timer_on = 1;
261
262         splx(x);
263         return rv;
264 }
265
266
267 static void
268 pca_stop(void)
269 {
270         int x = splhigh();
271
272         /* release the timers */
273         release_timer0();
274 #ifdef PC98
275         release_timer1();
276 #else
277         release_timer2();
278 #endif
279         /* reset the buffer */
280         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
281         pca_status.index = 0;
282         pca_status.counter = 0;
283         pca_status.current = 0;
284         pca_status.buffer = pca_status.buf[pca_status.current];
285         pca_status.timer_on = 0;
286         splx(x);
287 }
288
289
290 static void
291 pca_pause(void)
292 {
293         int x = splhigh();
294
295         release_timer0();
296 #ifdef PC98
297         release_timer1();
298 #else
299         release_timer2();
300 #endif
301         pca_status.timer_on = 0;
302         splx(x);
303 }
304
305
306 static void
307 pca_continue(void)
308 {
309         int x = splhigh();
310
311 #ifdef PC98
312         pca_status.oldval = inb(IO_PPI) & ~0x08;
313 #else
314         pca_status.oldval = inb(IO_PPI) | 0x03;
315 #endif
316         acquire_timer2(TIMER_LSB|TIMER_ONESHOT);
317         acquire_timer0(INTERRUPT_RATE, pcaintr);
318         pca_status.timer_on = 1;
319         splx(x);
320 }
321
322
323 static int
324 pca_wait(void)
325 {
326         int error, x;
327
328         if (!pca_status.timer_on)
329                 return 0;
330
331         while (pca_status.in_use[0] || pca_status.in_use[1] ||
332             pca_status.in_use[2]) {
333                 x = spltty();
334                 pca_sleep = 1;
335                 error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_drain", 0);
336                 pca_sleep = 0;
337                 splx(x);
338                 if (error != 0 && error != ERESTART) {
339                         pca_stop();
340                         return error;
341                 }
342         }
343         return 0;
344 }
345
346
347 static struct isa_pnp_id pca_ids[] = {
348         {0x0008d041, "AT-style speaker sound"}, /* PNP0800 */
349         {0}
350 };
351
352 static int
353 pcaprobe(device_t dev)
354 {
355         int error;
356
357         /* Check isapnp ids */
358         error = ISA_PNP_PROBE(device_get_parent(dev), dev, pca_ids);
359         if (error == ENXIO)
360                 return ENXIO;
361         return 0;
362 }
363
364
365 static int
366 pcaattach(device_t dev)
367 {
368         pca_init();
369         make_dev(&pca_cdevsw, 0, 0, 0, 0600, "pcaudio");
370         make_dev(&pca_cdevsw, 128, 0, 0, 0600, "pcaudioctl");
371         return 0;
372 }
373
374 static device_method_t pca_methods[] = {
375         DEVMETHOD(device_probe,         pcaprobe),
376         DEVMETHOD(device_attach,        pcaattach),
377         { 0, 0 }
378 };
379
380 static driver_t pca_driver = {
381         "pca",
382         pca_methods,
383         1
384 };
385
386 static devclass_t pca_devclass;
387
388 DRIVER_MODULE(pca, isa, pca_driver, pca_devclass, 0, 0);
389
390
391 static int
392 pcaopen(dev_t dev, int flags, int fmt, struct proc *p)
393 {
394         /* audioctl device can always be opened */
395         if (minor(dev) == 128)
396                 return 0;
397         if (minor(dev) > 0)
398                 return ENXIO;
399
400         if (!pca_initialized) {
401                 pca_init();
402                 pca_initialized = 1;
403         }
404
405         /* audio device can only be open by one process */
406         if (pca_status.open) {
407                 pca_status.queries = 1;
408                 return EBUSY;
409         }
410         pca_status.buffer = pca_status.buf[0];
411         pca_status.in_use[0] = pca_status.in_use[1] = pca_status.in_use[2] = 0;
412         pca_status.timer_on = 0;
413         pca_status.open = 1;
414         pca_status.processed = 0;
415         return 0;
416 }
417
418
419 static int
420 pcaclose(dev_t dev, int flags, int fmt, struct proc *p)
421 {
422         /* audioctl device can always be closed */
423         if (minor(dev) == 128)
424                 return 0;
425         if (minor(dev) > 0)
426                 return ENXIO;
427         /* audio device close drains all output and restores timers */
428         pca_wait();
429         pca_stop();
430         pca_status.open = 0;
431         return 0;
432 }
433
434
435 static int
436 pcawrite(dev_t dev, struct uio *uio, int flag)
437 {
438         int count, error, which, x;
439
440         /* only audio device can be written */
441         if (minor(dev) > 0)
442                 return ENXIO;
443
444         while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) {
445                 if (pca_status.in_use[0] && pca_status.in_use[1] &&
446                     pca_status.in_use[2]) {
447                         if (flag & IO_NDELAY)
448                                 return EWOULDBLOCK;
449                         x = spltty();
450                         pca_sleep = 1;
451                         error = tsleep(&pca_sleep, PZERO|PCATCH, "pca_wait", 0);
452                         pca_sleep = 0;
453                         splx(x);
454                         if (error != 0 && error != ERESTART) {
455                                 pca_stop();
456                                 return error;
457                         }
458                 }
459                 if (!pca_status.in_use[0])
460                         which = 0;
461                 else if (!pca_status.in_use[1])
462                         which = 1;
463                 else
464                         which = 2;
465                 if (count && !pca_status.in_use[which]) {
466                         uiomove(pca_status.buf[which], count, uio);
467                         pca_status.processed += count;
468                         switch (pca_status.encoding) {
469                         case AUDIO_ENCODING_ULAW:
470                                 conv(ulaw_dsp, pca_status.buf[which], count);
471                                 break;
472
473                         case AUDIO_ENCODING_ALAW:
474                                 conv(alaw_linear, pca_status.buf[which], count);
475                                 break;
476
477                         case AUDIO_ENCODING_RAW:
478                                 break;
479                         }
480                         pca_status.in_use[which] = count;
481                         if (!pca_status.timer_on)
482                                 if (pca_start())
483                                         return EBUSY;
484                 }
485         }
486         return 0;
487 }
488
489
490 static int
491 pcaioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
492 {
493         audio_info_t *auptr;
494
495         switch(cmd) {
496
497         case AUDIO_GETINFO:
498                 auptr = (audio_info_t *)data;
499                 auptr->play.sample_rate = pca_status.sample_rate;
500                 auptr->play.channels = 1;
501                 auptr->play.precision = 8;
502                 auptr->play.encoding = pca_status.encoding;
503
504                 auptr->play.gain = pca_status.volume;
505                 auptr->play.port = 0;
506
507                 auptr->play.samples = pca_status.processed;
508                 auptr->play.eof = 0;
509                 auptr->play.pause = !pca_status.timer_on;
510                 auptr->play.error = 0;
511                 auptr->play.waiting = pca_status.queries;
512
513                 auptr->play.open = pca_status.open;
514                 auptr->play.active = pca_status.timer_on;
515                 return 0;
516
517         case AUDIO_SETINFO:
518                 auptr = (audio_info_t *)data;
519                 if (auptr->play.sample_rate != (unsigned int)~0) {
520                         pca_status.sample_rate = auptr->play.sample_rate;
521                         pca_status.scale =
522                                 (pca_status.sample_rate << 8) / INTERRUPT_RATE;
523                 }
524                 if (auptr->play.encoding != (unsigned int)~0) {
525                         pca_status.encoding = auptr->play.encoding;
526                 }
527                 if (auptr->play.gain != (unsigned int)~0) {
528                         pca_status.volume = auptr->play.gain;
529                         pca_volume(pca_status.volume);
530                 }
531                 if (auptr->play.pause != (unsigned char)~0) {
532                         if (auptr->play.pause)
533                                 pca_pause();
534                         else
535                                 pca_continue();
536                 }
537
538                 return 0;
539
540         case AUDIO_DRAIN:
541         case AUDIO_COMPAT_DRAIN:
542                 return pca_wait();
543
544         case AUDIO_FLUSH:
545         case AUDIO_COMPAT_FLUSH:
546                 pca_stop();
547                 return 0;
548         case FIONBIO:
549                 return 0;
550         }
551         return ENXIO;
552 }
553
554
555 static void
556 pcaintr(struct clockframe *frame)
557 {
558         if (pca_status.index < pca_status.in_use[pca_status.current]) {
559                 disable_intr();
560 #ifdef PC98
561                 __asm__("outb %0,$0x35\n"
562                         "andb $0x08,%0\n"
563                         "outb %0,$0x35"
564 #else
565                 __asm__("outb %0,$0x61\n"
566                         "andb $0xFE,%0\n"
567                         "outb %0,$0x61"
568 #endif
569                         : : "a" ((char)pca_status.oldval) );
570                 __asm__("xlatb\n"
571 #ifdef PC98
572                         "outb %0,$0x3fdb"
573 #else
574                         "outb %0,$0x42"
575 #endif
576                         : : "a" ((char)pca_status.buffer[pca_status.index]),
577                             "b" (volume_table) );
578                 enable_intr();
579                 pca_status.counter += pca_status.scale;
580                 pca_status.index = (pca_status.counter >> 8);
581         }
582         if (pca_status.index >= pca_status.in_use[pca_status.current]) {
583                 pca_status.index = pca_status.counter = 0;
584                 pca_status.in_use[pca_status.current] = 0;
585                 pca_status.current++;
586                 if (pca_status.current > 2)
587                         pca_status.current = 0;
588                 pca_status.buffer = pca_status.buf[pca_status.current];
589                 if (pca_sleep)
590                         wakeup(&pca_sleep);
591                 if (pca_status.wsel.si_pid) {
592                         selwakeup((struct selinfo *)&pca_status.wsel.si_pid);
593                         pca_status.wsel.si_pid = 0;
594                         pca_status.wsel.si_flags = 0;
595                 }
596         }
597 }
598
599
600 static int
601 pcapoll(dev_t dev, int events, struct proc *p)
602 {
603         int s;
604         struct proc *p1;
605         int revents = 0;
606
607         s = spltty();
608
609         if (events & (POLLOUT | POLLWRNORM)) {
610                 if (!pca_status.in_use[0] || !pca_status.in_use[1] ||
611                     !pca_status.in_use[2])
612                         revents |= events & (POLLOUT | POLLWRNORM);
613                 else {
614                         if (pca_status.wsel.si_pid &&
615                             (p1=pfind(pca_status.wsel.si_pid))
616                             && p1->p_wchan == (caddr_t)&selwait)
617                                 pca_status.wsel.si_flags = SI_COLL;
618                         else
619                                 pca_status.wsel.si_pid = p->p_pid;
620                 }
621         }
622         splx(s);
623         return (revents);
624 }