diff options
author | asami <asami@FreeBSD.org> | 1996-11-02 10:41:28 +0000 |
---|---|---|
committer | asami <asami@FreeBSD.org> | 1996-11-02 10:41:28 +0000 |
commit | 223875ec7d51de4b686f9069a84494af29734cf6 (patch) | |
tree | 9675654af532c3965b39dbb0dd6f132eab3714d7 /sys/i386 | |
parent | 2ccb731676c10d2eff50861371a9cb4cdb505e81 (diff) | |
download | FreeBSD-src-223875ec7d51de4b686f9069a84494af29734cf6.zip FreeBSD-src-223875ec7d51de4b686f9069a84494af29734cf6.tar.gz |
The last update/merge of PC98 stuff before 2.2. The whole
pc98/pc98/sound directory has vanished now!
Submitted by: FreeBSD(98) Development Team
Diffstat (limited to 'sys/i386')
-rw-r--r-- | sys/i386/isa/lptreg.h | 26 | ||||
-rw-r--r-- | sys/i386/isa/sound/ad1848.c | 56 | ||||
-rw-r--r-- | sys/i386/isa/sound/opl3.c | 8 | ||||
-rw-r--r-- | sys/i386/isa/sound/pas2_pcm.c | 4 | ||||
-rw-r--r-- | sys/i386/isa/sound/pcm86.c | 2189 | ||||
-rw-r--r-- | sys/i386/isa/sound/sb16_dsp.c | 24 | ||||
-rw-r--r-- | sys/i386/isa/sound/sb16_midi.c | 6 | ||||
-rw-r--r-- | sys/i386/isa/sound/sb_dsp.c | 29 | ||||
-rw-r--r-- | sys/i386/isa/sound/sound_switch.c | 6 | ||||
-rw-r--r-- | sys/i386/isa/sound/soundcard.c | 26 | ||||
-rw-r--r-- | sys/i386/isa/wdreg.h | 33 |
11 files changed, 2404 insertions, 3 deletions
diff --git a/sys/i386/isa/lptreg.h b/sys/i386/isa/lptreg.h index 9e10ba9..583e96f 100644 --- a/sys/i386/isa/lptreg.h +++ b/sys/i386/isa/lptreg.h @@ -6,7 +6,7 @@ * William Jolitz. * * form: @(#)lptreg.h 1.1 (Berkeley) 12/19/90 - * $Id$ + * $Id: lptreg.h,v 1.2 1993/10/16 13:46:12 rgrimes Exp $ */ /* @@ -16,6 +16,29 @@ * Copyright (C) William Jolitz 1990 */ +/* + * modified for PC9801 by A.Kojima + * Kyoto University Microcomputer Club (KMC) + */ + +#ifdef PC98 +#define lpt_pstb_ctrl (-9) /* PSTB enable control */ +#define LPC_EN_PSTB 0xc /* PSTB enable */ +#define LPC_DIS_PSTB 0xd /* PSTB disable */ + +#define lpt_data 0 /* Data to/from printer (R/W) */ + +#define lpt_status 2 /* Status of printer (R) */ +#define LPS_NBSY 0x4 /* printer no ack of data */ + +#define lpt_control 6 /* Control printer (W) */ +#define LPC_MODE8255 0x82 /* 8255 mode */ +#define LPC_IRQ8 0x6 /* IRQ8 active */ +#define LPC_NIRQ8 0x7 /* IRQ8 inactive */ +#define LPC_PSTB 0xe /* PSTB active */ +#define LPC_NPSTB 0xf /* PSTB inactive */ + +#else /* IBM-PC */ #define lpt_data 0 /* Data to/from printer (R/W) */ #define lpt_status 1 /* Status of printer (R) */ @@ -30,4 +53,5 @@ #define LPC_AUTOL 0x02 /* automatic linefeed */ #define LPC_NINIT 0x04 /* initialize printer */ #define LPC_SEL 0x08 /* printer selected */ +#endif #define LPC_ENA 0x10 /* printer out of paper */ diff --git a/sys/i386/isa/sound/ad1848.c b/sys/i386/isa/sound/ad1848.c index 8c385bd..0dc5464 100644 --- a/sys/i386/isa/sound/ad1848.c +++ b/sys/i386/isa/sound/ad1848.c @@ -157,9 +157,15 @@ wait_for_calibration (ad1848_info * devc) */ timeout = 100000; +#ifdef PC98 + while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) + timeout--; + if ((INB (devc->base) & 0x80) == 0x80) +#else while (timeout > 0 && INB (devc->base) & 0x80) timeout--; if (INB (devc->base) & 0x80) +#endif printk ("ad1848: Auto calibration timed out(1).\n"); timeout = 100; @@ -474,7 +480,11 @@ static struct audio_operations ad1848_pcm_operations[MAX_AUDIO_DEV] = { { "Generic AD1848 codec", +#ifdef PC98 + NEEDS_RESTART, +#else DMA_AUTOMODE, +#endif AFMT_U8, /* Will be set later */ NULL, ad1848_open, @@ -910,8 +920,22 @@ ad1848_prepare_for_IO (int dev, int bsize, int bcount) * Write to I8 starts resyncronization. Wait until it completes. */ timeout = 10000; +#ifdef PC98 + while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) +#else while (timeout > 0 && INB (devc->base) == 0x80) +#endif + timeout--; + +#ifdef PC98 + ad_write (devc, 8, fs); + /* + * Write to I8 starts resyncronization. Wait until it completes. + */ + timeout = 10000; + while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) timeout--; +#endif /* * If mode == 2 (CS4231), set I28 also. It's the capture format register. @@ -924,11 +948,26 @@ ad1848_prepare_for_IO (int dev, int bsize, int bcount) * Write to I28 starts resyncronization. Wait until it completes. */ timeout = 10000; +#ifdef PC98 + while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) +#else while (timeout > 0 && INB (devc->base) == 0x80) +#endif timeout--; } +#ifdef PC98 + ad_write (devc, 28, fs); + + /* + * Write to I28 starts resyncronization. Wait until it completes. + */ + timeout = 10000; + while (timeout > 0 && (INB (devc->base) & 0x80) == 0x80) + timeout--; +#endif + ad_leave_MCE (devc); /* * Starts the calibration process and * enters playback mode after it. @@ -950,7 +989,13 @@ ad1848_halt (int dev) ad1848_info *devc = (ad1848_info *) audio_devs[dev]->devc; ad_mute (devc); +#ifdef PC98 + ad_enter_MCE (devc); +#endif ad_write (devc, 9, ad_read (devc, 9) & ~0x03); /* Stop DMA */ +#ifdef PC98 + ad_leave_MCE (devc); +#endif OUTB (0, io_Status (devc)); /* Clear interrupt status */ ad_enter_MCE (devc); @@ -1399,7 +1444,11 @@ probe_ms_sound (struct address_info *hw_config) return 0; } +#ifdef PC98 + if (hw_config->irq > 12) +#else if (hw_config->irq > 11) +#endif { printk ("MSS: Bad IRQ %d\n", hw_config->irq); return 0; @@ -1433,10 +1482,17 @@ probe_ms_sound (struct address_info *hw_config) long attach_ms_sound (long mem_start, struct address_info *hw_config) { +#ifdef PC98 + static char interrupt_bits[13] = + { + -1, -1, -1, 0x08, -1, 0x10, -1, -1, -1, -1, 0x18, -1, 0x20 + }; +#else static char interrupt_bits[12] = { -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20 }; +#endif char bits; static char dma_bits[4] = diff --git a/sys/i386/isa/sound/opl3.c b/sys/i386/isa/sound/opl3.c index b3660b7..42379cd 100644 --- a/sys/i386/isa/sound/opl3.c +++ b/sys/i386/isa/sound/opl3.c @@ -47,7 +47,11 @@ static int opl3_enabled = 0; static int opl4_enabled = 0; +#ifdef PC98 +static int left_address = 0x28d2, right_address = 0x28d2, both_address = 0; +#else static int left_address = 0x388, right_address = 0x388, both_address = 0; +#endif static int nr_voices = 9; static int logical_voices[MAX_VOICE] = @@ -812,10 +816,14 @@ opl3_command (int io_addr, unsigned int addr, unsigned int val) for (i = 0; i < 2; i++) INB (io_addr); +#ifdef PC98 + OUTB ((unsigned char) (val & 0xff), io_addr + 0x100); +#else OUTB ((unsigned char) (val & 0xff), io_addr + 1); /* * Write to register * */ +#endif if (!opl3_enabled) { diff --git a/sys/i386/isa/sound/pas2_pcm.c b/sys/i386/isa/sound/pas2_pcm.c index 97ae76c..db91165 100644 --- a/sys/i386/isa/sound/pas2_pcm.c +++ b/sys/i386/isa/sound/pas2_pcm.c @@ -377,7 +377,11 @@ pas_pcm_prepare_for_output (int dev, int bsize, int bcount) static struct audio_operations pas_pcm_operations = { "Pro Audio Spectrum", +#ifdef PC98 + NEEDS_RESTART, +#else DMA_AUTOMODE, +#endif AFMT_U8 | AFMT_S16_LE, NULL, pas_pcm_open, diff --git a/sys/i386/isa/sound/pcm86.c b/sys/i386/isa/sound/pcm86.c new file mode 100644 index 0000000..aa7e3d7 --- /dev/null +++ b/sys/i386/isa/sound/pcm86.c @@ -0,0 +1,2189 @@ +/* + * PC-9801-86 PCM driver for FreeBSD(98). + * + * Copyright (c) 1995 NAGAO Tadaaki (ABTK) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: pcm86.c,v 2.4 1996/01/24 19:53:34 abtk Exp $ + */ + +/* + * !! NOTE !! : + * This file DOES NOT belong to the VoxWare distribution though it works + * as part of the VoxWare drivers. It is FreeBSD(98) original. + * -- Nagao (nagao@cs.titech.ac.jp) + */ + + +#include <i386/isa/sound/sound_config.h> + +#ifdef CONFIGURE_SOUNDCARD + +#if !defined(EXCLUDE_PCM86) && !defined(EXCLUDE_AUDIO) + + +/* + * Constants + */ + +#define YES 1 +#define NO 0 + +#define IMODE_NONE 0 +#define IMODE_INPUT 1 +#define IMODE_OUTPUT 2 + +/* PC-9801-86 specific constants */ +#define PCM86_IOBASE 0xa460 /* PCM I/O ports */ +#define PCM86_FIFOSIZE 32768 /* There is a 32kB FIFO buffer on 86-board */ + +/* XXX -- These values should be chosen appropriately. */ +#define PCM86_INTRSIZE_OUT 1024 +#define PCM86_INTRSIZE_IN (PCM86_FIFOSIZE / 2 - 128) +#define DEFAULT_VOLUME 15 /* 0(min) - 15(max) */ + + +/* + * Switches for debugging and experiments + */ + +/* #define PCM86_DEBUG */ + +#ifdef PCM86_DEBUG +# ifdef DEB +# undef DEB +# endif +# define DEB(x) x +#endif + + +/* + * Private variables and types + */ + +typedef unsigned char pcm_data; + +enum board_type { + NO_SUPPORTED_BOARD = 0, + PC980186_FAMILY = 1, + PC980173_FAMILY = 2 +}; + +static char *board_name[] = { + /* Each must be of the length less than 32 bytes. */ + "No supported board", + "PC-9801-86 soundboard", + "PC-9801-73 soundboard" +}; + +/* Current status of the driver */ +static struct { + int iobase; + int irq; + enum board_type board_type; + int opened; + int format; + int bytes; + int chipspeedno; + int chipspeed; + int speed; + int stereo; + int volume; + int intr_busy; + int intr_size; + int intr_mode; + int intr_last; + int intr_trailer; + pcm_data * pdma_buf; + int pdma_count; + int pdma_chunkcount; + int acc; + int last_l; + int last_r; +} pcm_s; + +static struct { + pcm_data buff[4]; + int size; +} tmpbuf; + +static int my_dev = 0; +static char pcm_initialized = NO; + +/* 86-board supports only the following rates. */ +static int rates_tbl[8] = { +#ifndef WAVEMASTER_FREQ + 44100, 33075, 22050, 16538, 11025, 8269, 5513, 4134 +#else + /* + * It is said that Q-Vision's WaveMaster of some earlier lot(s?) has + * sampling rates incompatible with PC-9801-86. + * But I'm not sure whether the following rates are correct, especially + * 4000Hz. + */ + 44100, 33075, 22050, 16000, 11025, 8000, 5510, 4000 +#endif +}; + +/* u-law to linear translation table */ +static pcm_data ulaw2linear[256] = { + 130, 134, 138, 142, 146, 150, 154, 158, + 162, 166, 170, 174, 178, 182, 186, 190, + 193, 195, 197, 199, 201, 203, 205, 207, + 209, 211, 213, 215, 217, 219, 221, 223, + 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, + 240, 241, 241, 242, 242, 243, 243, 244, + 244, 245, 245, 246, 246, 247, 247, 248, + 248, 248, 249, 249, 249, 249, 250, 250, + 250, 250, 251, 251, 251, 251, 252, 252, + 252, 252, 252, 252, 253, 253, 253, 253, + 253, 253, 253, 253, 254, 254, 254, 254, + 254, 254, 254, 254, 254, 254, 254, 254, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, + 125, 121, 117, 113, 109, 105, 101, 97, + 93, 89, 85, 81, 77, 73, 69, 65, + 62, 60, 58, 56, 54, 52, 50, 48, + 46, 44, 42, 40, 38, 36, 34, 32, + 30, 29, 28, 27, 26, 25, 24, 23, + 22, 21, 20, 19, 18, 17, 16, 15, + 15, 14, 14, 13, 13, 12, 12, 11, + 11, 10, 10, 9, 9, 8, 8, 7, + 7, 7, 6, 6, 6, 6, 5, 5, + 5, 5, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 2, 2, 2, 2, + 2, 2, 2, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* linear to u-law translation table */ +static pcm_data linear2ulaw[256] = { + 255, 231, 219, 211, 205, 201, 197, 193, + 190, 188, 186, 184, 182, 180, 178, 176, + 175, 174, 173, 172, 171, 170, 169, 168, + 167, 166, 165, 164, 163, 162, 161, 160, + 159, 159, 158, 158, 157, 157, 156, 156, + 155, 155, 154, 154, 153, 153, 152, 152, + 151, 151, 150, 150, 149, 149, 148, 148, + 147, 147, 146, 146, 145, 145, 144, 144, + 143, 143, 143, 143, 142, 142, 142, 142, + 141, 141, 141, 141, 140, 140, 140, 140, + 139, 139, 139, 139, 138, 138, 138, 138, + 137, 137, 137, 137, 136, 136, 136, 136, + 135, 135, 135, 135, 134, 134, 134, 134, + 133, 133, 133, 133, 132, 132, 132, 132, + 131, 131, 131, 131, 130, 130, 130, 130, + 129, 129, 129, 129, 128, 128, 128, 128, + 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, + 3, 4, 4, 4, 4, 5, 5, 5, + 5, 6, 6, 6, 6, 7, 7, 7, + 7, 8, 8, 8, 8, 9, 9, 9, + 9, 10, 10, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 13, 13, 13, + 13, 14, 14, 14, 14, 15, 15, 15, + 15, 16, 16, 17, 17, 18, 18, 19, + 19, 20, 20, 21, 21, 22, 22, 23, + 23, 24, 24, 25, 25, 26, 26, 27, + 27, 28, 28, 29, 29, 30, 30, 31, + 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, + 47, 48, 50, 52, 54, 56, 58, 60, + 62, 65, 69, 73, 77, 83, 91, 103 +}; + + +/* + * Prototypes + */ + +static int pcm86_detect(struct address_info *); + +static int pcm86_open(int, int); +static void pcm86_close(int); +static void pcm86_output_block(int, unsigned long, int, int, int); +static void pcm86_start_input(int, unsigned long, int, int, int); +static int pcm86_ioctl(int, unsigned int, unsigned int, int); +static int pcm86_prepare_for_input(int, int, int); +static int pcm86_prepare_for_output(int, int, int); +static void pcm86_reset(int); +static void pcm86_halt_xfer(int); + +static void dsp73_send_command(unsigned char); +static void dsp73_send_data(unsigned char); +static void dsp73_init(void); +static int set_format(int); +static int set_speed(int); +static int set_stereo(int); +static void set_volume(int); +static void fifo_start(int); +static void fifo_stop(void); +static void fifo_reset(void); +static void fifo_output_block(void); +static int fifo_send(pcm_data *, int); +static void fifo_sendtrailer(int); +static void fifo_send_stereo(pcm_data *, int); +static void fifo_send_monoral(pcm_data *, int); +static void fifo_send_stereo_ulaw(pcm_data *, int); +static void fifo_send_stereo_8(pcm_data *, int, int); +static void fifo_send_stereo_16le(pcm_data *, int, int); +static void fifo_send_stereo_16be(pcm_data *, int, int); +static void fifo_send_mono_ulaw(pcm_data *, int); +static void fifo_send_mono_8(pcm_data *, int, int); +static void fifo_send_mono_16le(pcm_data *, int, int); +static void fifo_send_mono_16be(pcm_data *, int, int); +static void fifo_input_block(void); +static void fifo_recv(pcm_data *, int); +static void fifo_recv_stereo(pcm_data *, int); +static void fifo_recv_monoral(pcm_data *, int); +static void fifo_recv_stereo_ulaw(pcm_data *, int); +static void fifo_recv_stereo_8(pcm_data *, int, int); +static void fifo_recv_stereo_16le(pcm_data *, int, int); +static void fifo_recv_stereo_16be(pcm_data *, int, int); +static void fifo_recv_mono_ulaw(pcm_data *, int); +static void fifo_recv_mono_8(pcm_data *, int, int); +static void fifo_recv_mono_16le(pcm_data *, int, int); +static void fifo_recv_mono_16be(pcm_data *, int, int); +static void pcm_stop(void); +static void pcm_init(void); + + +/* + * Identity + */ + +static struct audio_operations pcm86_operations = +{ + "PC-9801-86 SoundBoard", /* filled in properly by auto configuration */ + NOTHING_SPECIAL, + ( AFMT_MU_LAW | + AFMT_U8 | AFMT_S16_LE | AFMT_S16_BE | + AFMT_S8 | AFMT_U16_LE | AFMT_U16_BE ), + NULL, + pcm86_open, + pcm86_close, + pcm86_output_block, + pcm86_start_input, + pcm86_ioctl, + pcm86_prepare_for_input, + pcm86_prepare_for_output, + pcm86_reset, + pcm86_halt_xfer, + NULL, + NULL +}; + + +/* + * Codes for internal use + */ + +static void +dsp73_send_command(unsigned char command) +{ + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* command mode */ + outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 3); + + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* send command */ + outb(pcm_s.iobase + 4, command); +} + + +static void +dsp73_send_data(unsigned char data) +{ + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* data mode */ + outb(pcm_s.iobase + 2, (inb(pcm_s.iobase + 2) & 0x20) | 0x83); + + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + /* send command */ + outb(pcm_s.iobase + 4, data); +} + + +static void +dsp73_init(void) +{ + const unsigned char dspinst[15] = { + 0x00, 0x00, 0x27, + 0x3f, 0xe0, 0x01, + 0x00, 0x00, 0x27, + 0x36, 0x5a, 0x0d, + 0x3e, 0x60, 0x04 + }; + unsigned char t; + int i; + + /* reset DSP */ + t = inb(pcm_s.iobase + 2); + outb(pcm_s.iobase + 2, (t & 0x80) | 0x23); + + /* mute on */ + dsp73_send_command(0x04); + dsp73_send_data(0x6f); + dsp73_send_data(0x3c); + + /* write DSP instructions */ + dsp73_send_command(0x01); + dsp73_send_data(0x00); + for (i = 0; i < 16; i++) + dsp73_send_data(dspinst[i]); + + /* mute off */ + dsp73_send_command(0x04); + dsp73_send_data(0x6f); + dsp73_send_data(0x30); + + /* wait for RDY */ + while ((inb(pcm_s.iobase + 2) & 0x48) != 8); + + outb(pcm_s.iobase + 2, 3); +} + + +static int +set_format(int format) +{ + switch (format) { + case AFMT_MU_LAW: + case AFMT_S8: + case AFMT_U8: + pcm_s.format = format; + pcm_s.bytes = 1; /* 8bit */ + break; + case AFMT_S16_LE: + case AFMT_U16_LE: + case AFMT_S16_BE: + case AFMT_U16_BE: + pcm_s.format = format; + pcm_s.bytes = 2; /* 16bit */ + break; + case AFMT_QUERY: + break; + default: + return -1; + } + + return pcm_s.format; +} + + +static int +set_speed(int speed) +{ + int i; + + if (speed < 4000) /* Minimum 4000Hz */ + speed = 4000; + if (speed > 44100) /* Maximum 44100Hz */ + speed = 44100; + for (i = 7; i >= 0; i--) { + if (speed <= rates_tbl[i]) { + pcm_s.chipspeedno = i; + pcm_s.chipspeed = rates_tbl[i]; + break; + } + } + pcm_s.speed = speed; + + return speed; +} + + +static int +set_stereo(int stereo) +{ + pcm_s.stereo = stereo ? YES : NO; + + return pcm_s.stereo; +} + + +static void +set_volume(int volume) +{ + if (volume < 0) + volume = 0; + if (volume > 15) + volume = 15; + pcm_s.volume = volume; + + outb(pcm_s.iobase + 6, 0xaf - volume); /* D/A -> LINE OUT */ + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(pcm_s.iobase + 6, 0x20); /* FM -> A/D */ + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(pcm_s.iobase + 6, 0x60); /* LINE IN -> A/D */ + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); + outb(0x5f,0); +} + + +static void +fifo_start(int mode) +{ + unsigned char tmp; + + /* Set frame length & panpot(LR). */ + tmp = inb(pcm_s.iobase + 10) & 0x88; + outb(pcm_s.iobase + 10, tmp | ((pcm_s.bytes == 1) ? 0x72 : 0x32)); + + tmp = pcm_s.chipspeedno; + if (mode == IMODE_INPUT) + tmp |= 0x40; + + /* Reset intr. flag. */ + outb(pcm_s.iobase + 8, tmp); + outb(pcm_s.iobase + 8, tmp | 0x10); + + /* Enable FIFO intr. */ + outb(pcm_s.iobase + 8, tmp | 0x30); + + /* Set intr. interval. */ + outb(pcm_s.iobase + 10, pcm_s.intr_size / 128 - 1); + + /* Start intr. */ + outb(pcm_s.iobase + 8, tmp | 0xb0); +} + + +static void +fifo_stop(void) +{ + unsigned char tmp; + + /* Reset intr. flag, and disable FIFO intr. */ + tmp = inb(pcm_s.iobase + 8) & 0x0f; + outb(pcm_s.iobase + 8, tmp); +} + + +static void +fifo_reset(void) +{ + unsigned char tmp; + + /* Reset FIFO. */ + tmp = inb(pcm_s.iobase + 8) & 0x77; + outb(pcm_s.iobase + 8, tmp | 0x8); + outb(pcm_s.iobase + 8, tmp); +} + + +static void +fifo_output_block(void) +{ + int chunksize, count; + + if (pcm_s.pdma_chunkcount) { + /* Update chunksize and then send the next chunk to FIFO. */ + chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--; + count = fifo_send(pcm_s.pdma_buf, chunksize); + } else { + /* ??? something wrong... */ + printk("pcm0: chunkcount overrun\n"); + chunksize = count = 0; + } + + if (((audio_devs[my_dev]->dmap->qlen < 2) && (pcm_s.pdma_chunkcount == 0)) + || (count < pcm_s.intr_size)) { + /* The sent chunk seems to be the last one. */ + fifo_sendtrailer(pcm_s.intr_size); + pcm_s.intr_last = YES; + } + + pcm_s.pdma_buf += chunksize; + pcm_s.pdma_count -= chunksize; +} + + +static int +fifo_send(pcm_data *buf, int count) +{ + int i, length, r, cnt, rslt; + pcm_data *p; + + /* Calculate the length of PCM frames. */ + cnt = count + tmpbuf.size; + length = pcm_s.bytes << pcm_s.stereo; + r = cnt % length; + cnt -= r; + + if (cnt > 0) { + if (pcm_s.stereo) + fifo_send_stereo(buf, cnt); + else + fifo_send_monoral(buf, cnt); + /* Carry over extra data which doesn't seem to be a full PCM frame. */ + p = (pcm_data *)buf + count - r; + for (i = 0; i < r; i++) + tmpbuf.buff[i] = *p++; + } else { + /* Carry over extra data which doesn't seem to be a full PCM frame. */ + p = (pcm_data *)buf; + for (i = tmpbuf.size; i < r; i++) + tmpbuf.buff[i] = *p++; + } + tmpbuf.size = r; + + rslt = ((cnt / length) * pcm_s.chipspeed / pcm_s.speed) * pcm_s.bytes * 2; +#ifdef PCM86_DEBUG + printk("fifo_send(): %d bytes sent\n", rslt); +#endif + return rslt; +} + + +static void +fifo_sendtrailer(int count) +{ + /* Send trailing zeros to the FIFO buffer. */ + int i; + + for (i = 0; i < count; i++) + outb(pcm_s.iobase + 12, 0); + pcm_s.intr_trailer = YES; + +#ifdef PCM86_DEBUG + printk("fifo_sendtrailer(): %d bytes sent\n", count); +#endif +} + + +static void +fifo_send_stereo(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_send_stereo_ulaw(buf, count); + break; + case AFMT_S8: + fifo_send_stereo_8(buf, count, NO); + break; + case AFMT_U8: + fifo_send_stereo_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_send_stereo_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_send_stereo_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_send_stereo_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_send_stereo_16be(buf, count, YES); + break; + } +} + + +static void +fifo_send_monoral(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_send_mono_ulaw(buf, count); + break; + case AFMT_S8: + fifo_send_mono_8(buf, count, NO); + break; + case AFMT_U8: + fifo_send_mono_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_send_mono_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_send_mono_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_send_mono_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_send_mono_16be(buf, count, YES); + break; + } +} + + +static void +fifo_send_stereo_ulaw(pcm_data *buf, int count) +{ + int i; + signed char dl, dl0, dl1, dr, dr0, dr1; + pcm_data t[2]; + + if (tmpbuf.size > 0) + t[0] = ulaw2linear[tmpbuf.buff[0]]; + else + t[0] = ulaw2linear[*buf++]; + t[1] = ulaw2linear[*buf++]; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[1]); + count -= 2; + for (i = 0; i < count; i++) + outb(pcm_s.iobase + 12, ulaw2linear[*buf++]); + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = t[0]; + dr1 = t[1]; + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = ulaw2linear[*buf++]; + dr1 = ulaw2linear[*buf++]; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, dl); + outb(pcm_s.iobase + 12, dr); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_stereo_8(pcm_data *buf, int count, int uflag) +{ + int i; + signed char dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[2]; + + zlev = uflag ? -128 : 0; + + if (tmpbuf.size > 0) + t[0] = tmpbuf.buff[0] + zlev; + else + t[0] = *buf++ + zlev; + t[1] = *buf++ + zlev; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[1]); + count -= 2; + for (i = 0; i < count; i++) + outb(pcm_s.iobase + 12, *buf++ + zlev); + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = t[0]; + dr1 = t[1]; + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = *buf++ + zlev; + dr1 = *buf++ + zlev; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, dl); + outb(pcm_s.iobase + 12, dr); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_stereo_16le(pcm_data *buf, int count, int uflag) +{ + int i; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 4; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[1] + zlev); + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[3] + zlev); + outb(pcm_s.iobase + 12, t[2]); + count = count / 2 - 2; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *(buf + 1) + zlev); + outb(pcm_s.iobase + 12, *buf); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = t[0] + ((t[1] + zlev) << 8); + dr1 = t[2] + ((t[3] + zlev) << 8); + i = 0; + count /= 4; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = *buf + ((*(buf + 1) + zlev) << 8); + buf += 2; + dr1 = *buf + ((*(buf + 1) + zlev) << 8); + buf += 2; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, (dl >> 8) & 0xff); + outb(pcm_s.iobase + 12, dl & 0xff); + outb(pcm_s.iobase + 12, (dr >> 8) & 0xff); + outb(pcm_s.iobase + 12, dr & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_stereo_16be(pcm_data *buf, int count, int uflag) +{ + int i; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 4; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0] + zlev); + outb(pcm_s.iobase + 12, t[1]); + outb(pcm_s.iobase + 12, t[2] + zlev); + outb(pcm_s.iobase + 12, t[3]); + count = count / 2 - 2; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *buf + zlev); + outb(pcm_s.iobase + 12, *(buf + 1)); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = ((t[0] + zlev) << 8) + t[1]; + dr1 = ((t[2] + zlev) << 8) + t[3]; + i = 0; + count /= 4; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + dl0 = dl1; + dr0 = dr1; + if (i < count) { + dl1 = ((*buf + zlev) << 8) + *(buf + 1); + buf += 2; + dr1 = ((*buf + zlev) << 8) + *(buf + 1); + buf += 2; + } else + dl1 = dr1 = 0; + } + dl = ((dl0 * (pcm_s.chipspeed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.chipspeed; + dr = ((dr0 * (pcm_s.chipspeed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, (dl >> 8) & 0xff); + outb(pcm_s.iobase + 12, dl & 0xff); + outb(pcm_s.iobase + 12, (dr >> 8) & 0xff); + outb(pcm_s.iobase + 12, dr & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_send_mono_ulaw(pcm_data *buf, int count) +{ + int i; + signed char d, d0, d1; + + if (pcm_s.speed == pcm_s.chipspeed) + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = ulaw2linear[*buf++]; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + } + else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ulaw2linear[*buf++]; + i = 0; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + d1 = (i < count) ? ulaw2linear[*buf++] : 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_send_mono_8(pcm_data *buf, int count, int uflag) +{ + int i; + signed char d, d0, d1, zlev; + + zlev = uflag ? -128 : 0; + + if (pcm_s.speed == pcm_s.chipspeed) + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = *buf++ + zlev; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + } + else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = *buf++ + zlev; + i = 0; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + d1 = (i < count) ? *buf++ + zlev : 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, d); + outb(pcm_s.iobase + 12, d); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_send_mono_16le(pcm_data *buf, int count, int uflag) +{ + int i; + short d, d0, d1, zlev; + pcm_data t[2]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 2; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[1] + zlev); + outb(pcm_s.iobase + 12, t[0]); + outb(pcm_s.iobase + 12, t[1] + zlev); + outb(pcm_s.iobase + 12, t[0]); + count = count / 2 - 1; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *(buf + 1) + zlev); + outb(pcm_s.iobase + 12, *buf); + outb(pcm_s.iobase + 12, *(buf + 1) + zlev); + outb(pcm_s.iobase + 12, *buf); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = t[0] + ((t[1] + zlev) << 8); + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + if (i < count) { + d1 = *buf + ((*(buf + 1) + zlev) << 8); + buf += 2; + } else + d1 = 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_send_mono_16be(pcm_data *buf, int count, int uflag) +{ + int i; + short d, d0, d1, zlev; + pcm_data t[2]; + + zlev = uflag ? -128 : 0; + + for (i = 0; i < 2; i++) + t[i] = (tmpbuf.size > i) ? tmpbuf.buff[i] : *buf++; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + outb(pcm_s.iobase + 12, t[0] + zlev); + outb(pcm_s.iobase + 12, t[1]); + outb(pcm_s.iobase + 12, t[0] + zlev); + outb(pcm_s.iobase + 12, t[1]); + count = count / 2 - 1; + for (i = 0; i < count; i++) { + outb(pcm_s.iobase + 12, *buf + zlev); + outb(pcm_s.iobase + 12, *(buf + 1)); + outb(pcm_s.iobase + 12, *buf + zlev); + outb(pcm_s.iobase + 12, *(buf + 1)); + buf += 2; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ((t[0] + zlev) << 8) + t[1]; + i = 0; + count /= 2; + while (i < count) { + while (pcm_s.acc >= pcm_s.chipspeed) { + pcm_s.acc -= pcm_s.chipspeed; + i++; + d0 = d1; + if (i < count) { + d1 = ((*buf + zlev) << 8) + *(buf + 1); + buf += 2; + } else + d1 = 0; + } + d = ((d0 * (pcm_s.chipspeed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.chipspeed; + outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + outb(pcm_s.iobase + 12, d & 0xff); + outb(pcm_s.iobase + 12, (d >> 8) & 0xff); + pcm_s.acc += pcm_s.speed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_input_block(void) +{ + int chunksize; + + if (pcm_s.pdma_chunkcount) { + /* Update chunksize and then receive the next chunk from FIFO. */ + chunksize = pcm_s.pdma_count / pcm_s.pdma_chunkcount--; + fifo_recv(pcm_s.pdma_buf, chunksize); + pcm_s.pdma_buf += chunksize; + pcm_s.pdma_count -= chunksize; + } else + /* ??? something wrong... */ + printk("pcm0: chunkcount overrun\n"); +} + + +static void +fifo_recv(pcm_data *buf, int count) +{ + int i; + + if (count > tmpbuf.size) { + for (i = 0; i < tmpbuf.size; i++) + *buf++ = tmpbuf.buff[i]; + count -= tmpbuf.size; + tmpbuf.size = 0; + if (pcm_s.stereo) + fifo_recv_stereo(buf, count); + else + fifo_recv_monoral(buf, count); + } else { + for (i = 0; i < count; i++) + *buf++ = tmpbuf.buff[i]; + for (i = 0; i < tmpbuf.size - count; i++) + tmpbuf.buff[i] = tmpbuf.buff[i + count]; + tmpbuf.size -= count; + } + +#ifdef PCM86_DEBUG + printk("fifo_recv(): %d bytes received\n", + ((count / (pcm_s.bytes << pcm_s.stereo)) * pcm_s.chipspeed + / pcm_s.speed) * pcm_s.bytes * 2); +#endif +} + + +static void +fifo_recv_stereo(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_recv_stereo_ulaw(buf, count); + break; + case AFMT_S8: + fifo_recv_stereo_8(buf, count, NO); + break; + case AFMT_U8: + fifo_recv_stereo_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_recv_stereo_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_recv_stereo_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_recv_stereo_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_recv_stereo_16be(buf, count, YES); + break; + } +} + + +static void +fifo_recv_monoral(pcm_data *buf, int count) +{ + /* Convert format and sampling speed. */ + switch (pcm_s.format) { + case AFMT_MU_LAW: + fifo_recv_mono_ulaw(buf, count); + break; + case AFMT_S8: + fifo_recv_mono_8(buf, count, NO); + break; + case AFMT_U8: + fifo_recv_mono_8(buf, count, YES); + break; + case AFMT_S16_LE: + fifo_recv_mono_16le(buf, count, NO); + break; + case AFMT_U16_LE: + fifo_recv_mono_16le(buf, count, YES); + break; + case AFMT_S16_BE: + fifo_recv_mono_16be(buf, count, NO); + break; + case AFMT_U16_BE: + fifo_recv_mono_16be(buf, count, YES); + break; + } +} + + +static void +fifo_recv_stereo_ulaw(pcm_data *buf, int count) +{ + int i, cnt; + signed char dl, dl0, dl1, dr, dr0, dr1; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)]; + *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)]; + } + if (count % 2) { + *buf++ = linear2ulaw[inb(pcm_s.iobase + 12)]; + tmpbuf.buff[0] = linear2ulaw[inb(pcm_s.iobase + 12)]; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = linear2ulaw[dl & 0xff]; + *buf++ = linear2ulaw[dr & 0xff]; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = linear2ulaw[dl & 0xff]; + tmpbuf.buff[0] = linear2ulaw[dr & 0xff]; + tmpbuf.size = 1; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_stereo_8(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + signed char dl, dl0, dl1, dr, dr0, dr1, zlev; + + zlev = uflag ? -128 : 0; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *buf++ = inb(pcm_s.iobase + 12) + zlev; + *buf++ = inb(pcm_s.iobase + 12) + zlev; + } + if (count % 2) { + *buf++ = inb(pcm_s.iobase + 12) + zlev; + tmpbuf.buff[0] = inb(pcm_s.iobase + 12) + zlev; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = dl + zlev; + *buf++ = dr + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = dl + zlev; + tmpbuf.buff[0] = dr + zlev; + tmpbuf.size = 1; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_stereo_16le(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + cnt = count / 4; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *(buf + 1) = inb(pcm_s.iobase + 12) + zlev; + *buf = inb(pcm_s.iobase + 12); + *(buf + 3) = inb(pcm_s.iobase + 12) + zlev; + *(buf + 2) = inb(pcm_s.iobase + 12); + buf += 4; + } + if (count % 4) { + t[1] = inb(pcm_s.iobase + 12) + zlev; + t[0] = inb(pcm_s.iobase + 12); + t[3] = inb(pcm_s.iobase + 12) + zlev; + t[2] = inb(pcm_s.iobase + 12); + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = dl & 0xff; + *buf++ = ((dl >> 8) & 0xff) + zlev; + *buf++ = dr & 0xff; + *buf++ = ((dr >> 8) & 0xff) + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 4) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + t[0] = dl & 0xff; + t[1] = ((dl >> 8) & 0xff) + zlev; + t[2] = dr & 0xff; + t[3] = ((dr >> 8) & 0xff) + zlev; + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_stereo_16be(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short dl, dl0, dl1, dr, dr0, dr1, zlev; + pcm_data t[4]; + + zlev = uflag ? -128 : 0; + + cnt = count / 4; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + *buf++ = inb(pcm_s.iobase + 12) + zlev; + *buf++ = inb(pcm_s.iobase + 12); + *buf++ = inb(pcm_s.iobase + 12) + zlev; + *buf++ = inb(pcm_s.iobase + 12); + } + if (count % 4) { + t[0] = inb(pcm_s.iobase + 12) + zlev; + t[1] = inb(pcm_s.iobase + 12); + t[2] = inb(pcm_s.iobase + 12) + zlev; + t[3] = inb(pcm_s.iobase + 12); + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + } else { + /* Speed conversion with linear interpolation method. */ + dl0 = pcm_s.last_l; + dr0 = pcm_s.last_r; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = ((dl >> 8) & 0xff) + zlev; + *buf++ = dl & 0xff; + *buf++ = ((dr >> 8) & 0xff) + zlev; + *buf++ = dr & 0xff; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 4) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + dl0 = dl1; + dr0 = dr1; + dl1 = inb(pcm_s.iobase + 12) << 8; + dl1 |= inb(pcm_s.iobase + 12); + dr1 = inb(pcm_s.iobase + 12) << 8; + dr1 |= inb(pcm_s.iobase + 12); + } + dl = ((dl0 * (pcm_s.speed - pcm_s.acc)) + (dl1 * pcm_s.acc)) + / pcm_s.speed; + dr = ((dr0 * (pcm_s.speed - pcm_s.acc)) + (dr1 * pcm_s.acc)) + / pcm_s.speed; + t[0] = ((dl >> 8) & 0xff) + zlev; + t[1] = dl & 0xff; + t[2] = ((dr >> 8) & 0xff) + zlev; + t[3] = dr & 0xff; + tmpbuf.size = 0; + for (i = 0; i < count % 4; i++) + *buf++ = t[i]; + for (i = count % 4; i < 4; i++) + tmpbuf.buff[tmpbuf.size++] = t[i]; + } + + pcm_s.last_l = dl0; + pcm_s.last_r = dr0; + } +} + + +static void +fifo_recv_mono_ulaw(pcm_data *buf, int count) +{ + int i; + signed char d, d0, d1; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + *buf++ = linear2ulaw[d & 0xff]; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + for (i = 0; i < count; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = linear2ulaw[d & 0xff]; + pcm_s.acc += pcm_s.chipspeed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_recv_mono_8(pcm_data *buf, int count, int uflag) +{ + int i; + signed char d, d0, d1, zlev; + + zlev = uflag ? -128 : 0; + + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < count; i++) { + d = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + *buf++ = d + zlev; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + for (i = 0; i < count; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + d1 = ((signed char)inb(pcm_s.iobase + 12) + + (signed char)inb(pcm_s.iobase + 12)) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = d + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_recv_mono_16le(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short d, d0, d1, el, er, zlev; + + zlev = uflag ? -128 : 0; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = d & 0xff; + *buf++ = ((d >> 8) & 0xff) + zlev; + } + if (count % 2) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = d & 0xff; + tmpbuf.buff[0] = ((d >> 8) & 0xff) + zlev; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = d & 0xff; + *buf++ = ((d >> 8) & 0xff) + zlev; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = d & 0xff; + tmpbuf.buff[0] = ((d >> 8) & 0xff) + zlev; + tmpbuf.size = 1; + } + + pcm_s.last_l = d0; + } +} + + +static void +fifo_recv_mono_16be(pcm_data *buf, int count, int uflag) +{ + int i, cnt; + short d, d0, d1, el, er, zlev; + + zlev = uflag ? -128 : 0; + + cnt = count / 2; + if (pcm_s.speed == pcm_s.chipspeed) { + /* No reason to convert the pcm speed. */ + for (i = 0; i < cnt; i++) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = ((d >> 8) & 0xff) + zlev; + *buf++ = d & 0xff; + } + if (count % 2) { + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d = (el + er) >> 1; + *buf++ = ((d >> 8) & 0xff) + zlev; + tmpbuf.buff[0] = d & 0xff; + tmpbuf.size = 1; + } + } else { + /* Speed conversion with linear interpolation method. */ + d0 = pcm_s.last_l; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + for (i = 0; i < cnt; i++) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = ((d >> 8) & 0xff) + zlev; + *buf++ = d & 0xff; + pcm_s.acc += pcm_s.chipspeed; + } + if (count % 2) { + while (pcm_s.acc >= pcm_s.speed) { + pcm_s.acc -= pcm_s.speed; + d0 = d1; + el = inb(pcm_s.iobase + 12) << 8; + el |= inb(pcm_s.iobase + 12); + er = inb(pcm_s.iobase + 12) << 8; + er |= inb(pcm_s.iobase + 12); + d1 = (el + er) >> 1; + } + d = ((d0 * (pcm_s.speed - pcm_s.acc)) + (d1 * pcm_s.acc)) + / pcm_s.speed; + *buf++ = ((d >> 8) & 0xff) + zlev; + tmpbuf.buff[0] = d & 0xff; + tmpbuf.size = 1; + } + + pcm_s.last_l = d0; + } +} + + +static void +pcm_stop(void) +{ + fifo_stop(); /* stop FIFO */ + fifo_reset(); /* reset FIFO buffer */ + + /* Reset driver's status. */ + pcm_s.intr_busy = NO; + pcm_s.intr_last = NO; + pcm_s.intr_trailer = NO; + pcm_s.acc = 0; + pcm_s.last_l = 0; + pcm_s.last_r = 0; + + DEB(printk("pcm_stop\n")); +} + + +static void +pcm_init(void) +{ + /* Initialize registers on the board. */ + pcm_stop(); + if (pcm_s.board_type == PC980173_FAMILY) + dsp73_init(); + + /* Set default volume. */ + set_volume(DEFAULT_VOLUME); + + /* Initialize driver's status. */ + pcm_s.opened = NO; + pcm_initialized = YES; +} + + +/* + * Codes for global use + */ + +int +probe_pcm86(struct address_info *hw_config) +{ + return pcm86_detect(hw_config); +} + + +long +attach_pcm86(long mem_start, struct address_info *hw_config) +{ + if (pcm_s.board_type == NO_SUPPORTED_BOARD) + return mem_start; + + /* Initialize the board. */ + pcm_init(); + + printk("pcm0: <%s>", pcm86_operations.name); + + if (num_audiodevs < MAX_AUDIO_DEV) { + my_dev = num_audiodevs++; + audio_devs[my_dev] = &pcm86_operations; + audio_devs[my_dev]->buffcount = DSP_BUFFCOUNT; + audio_devs[my_dev]->buffsize = DSP_BUFFSIZE; +#ifdef PCM86_DEBUG + printk("\nbuffsize = %d", DSP_BUFFSIZE); +#endif + } else + printk("pcm0: Too many PCM devices available"); + + return mem_start; +} + + +static int +pcm86_detect(struct address_info *hw_config) +{ + int opna_iobase = 0x188, irq = 12, i; + unsigned char tmp; + + if (hw_config->io_base == -1) { + printf("pcm0: iobase not specified. Assume default port(0x%x)\n", + PCM86_IOBASE); + hw_config->io_base = PCM86_IOBASE; + } + pcm_s.iobase = hw_config->io_base; + + /* auto configuration */ + tmp = inb(pcm_s.iobase) & 0xfc; + switch ((tmp & 0xf0) >> 4) { + case 2: + opna_iobase = 0x188; + pcm_s.board_type = PC980173_FAMILY; + break; + case 3: + opna_iobase = 0x288; + pcm_s.board_type = PC980173_FAMILY; + break; + case 4: + opna_iobase = 0x188; + pcm_s.board_type = PC980186_FAMILY; + break; + case 5: + opna_iobase = 0x288; + pcm_s.board_type = PC980186_FAMILY; + break; + default: + pcm_s.board_type = NO_SUPPORTED_BOARD; + return NO; + } + + /* Enable OPNA(YM2608) facilities. */ + outb(pcm_s.iobase, tmp | 0x01); + + /* Wait for OPNA to be ready. */ + i = 100000; /* Some large value */ + while((inb(opna_iobase) & 0x80) && (i-- > 0)); + + /* Make IOA/IOB port ready (IOA:input, IOB:output) */ + outb(opna_iobase, 0x07); + outb(0x5f, 0); /* Because OPNA ports are comparatively slow(?), */ + outb(0x5f, 0); /* we'd better wait a moment. */ + outb(0x5f, 0); + outb(0x5f, 0); + tmp = inb(opna_iobase + 2) & 0x3f; + outb(opna_iobase + 2, tmp | 0x80); + + /* Wait for OPNA to be ready. */ + i = 100000; /* Some large value */ + while((inb(opna_iobase) & 0x80) && (i-- > 0)); + + /* Get irq number from IOA port. */ + outb(opna_iobase, 0x0e); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + tmp = inb(opna_iobase + 2) & 0xc0; + switch (tmp >> 6) { + case 0: /* INT0 (IRQ3)*/ + irq = 3; + break; + case 1: /* INT6 (IRQ13)*/ + irq = 13; + break; + case 2: /* INT4 (IRQ10)*/ + irq = 10; + break; + case 3: /* INT5 (IRQ12)*/ + irq = 12; + break; + default: /* error */ + return NO; + } + + /* Wait for OPNA to be ready. */ + i = 100000; /* Some large value */ + while((inb(opna_iobase) & 0x80) && (i-- > 0)); + + /* Reset OPNA timer register. */ + outb(opna_iobase, 0x27); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + outb(0x5f, 0); + outb(opna_iobase + 2, 0x30); + + /* Ok. Detection finished. */ + sprintf(pcm86_operations.name, board_name[pcm_s.board_type]); + pcm_initialized = NO; + pcm_s.irq = irq; + + if ((hw_config->irq > 0) && (hw_config->irq != irq)) + printf("pcm0: change irq %d -> %d\n", hw_config->irq, irq); + hw_config->irq = irq; + + return YES; +} + + +static int +pcm86_open(int dev, int mode) +{ + int err; + + if (!pcm_initialized) + return RET_ERROR(ENXIO); + + if (pcm_s.intr_busy || pcm_s.opened) + return RET_ERROR(EBUSY); + + if ((err = snd_set_irq_handler(pcm_s.irq, pcmintr, "PC-9801-73/86")) < 0) + return err; + + pcm_stop(); + + tmpbuf.size = 0; + pcm_s.intr_mode = IMODE_NONE; + pcm_s.opened = YES; + + return 0; +} + + +static void +pcm86_close(int dev) +{ + snd_release_irq(pcm_s.irq); + + pcm_s.opened = NO; +} + + +static void +pcm86_output_block(int dev, unsigned long buf, int count, int intrflag, + int dma_restart) +{ + unsigned long flags, cnt; + int maxchunksize; + +#ifdef PCM86_DEBUG + printk("pcm86_output_block():"); + if (audio_devs[dev]->dmap->flags & DMA_BUSY) + printk(" DMA_BUSY"); + if (audio_devs[dev]->dmap->flags & DMA_RESTART) + printk(" DMA_RESTART"); + if (audio_devs[dev]->dmap->flags & DMA_ACTIVE) + printk(" DMA_ACTIVE"); + if (audio_devs[dev]->dmap->flags & DMA_STARTED) + printk(" DMA_STARTED"); + if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE) + printk(" DMA_ALLOC_DONE"); + printk("\n"); +#endif + +#if 0 + DISABLE_INTR(flags); +#endif + +#ifdef PCM86_DEBUG + printk("pcm86_output_block(): count = %d, intrsize= %d\n", + count, pcm_s.intr_size); +#endif + + pcm_s.pdma_buf = (pcm_data *)buf; + pcm_s.pdma_count = count; + pcm_s.pdma_chunkcount = 1; + maxchunksize = (((PCM86_FIFOSIZE - pcm_s.intr_size * 2) + / (pcm_s.bytes * 2)) * pcm_s.speed + / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo); + if (count > maxchunksize) + pcm_s.pdma_chunkcount = 2 * count / maxchunksize; + /* + * Let chunksize = (float)count / (float)pcm_s.pdma_chunkcount. + * Data of size chunksize is sent to the FIFO buffer on the 86-board + * on every occuring of interrupt. + * By assuming that pcm_s.intr_size < PCM86_FIFOSIZE / 2, we can conclude + * that the FIFO buffer never overflows from the following lemma. + * + * Lemma: + * maxchunksize / 2 <= chunksize <= maxchunksize. + * (Though pcm_s.pdma_chunkcount is obtained through the flooring + * function, this inequality holds.) + * Proof) Omitted. + */ + + fifo_output_block(); + + pcm_s.intr_last = NO; + pcm_s.intr_mode = IMODE_OUTPUT; + if (!pcm_s.intr_busy) + fifo_start(IMODE_OUTPUT); + pcm_s.intr_busy = YES; + +#if 0 + RESTORE_INTR(flags); +#endif +} + + +static void +pcm86_start_input(int dev, unsigned long buf, int count, int intrflag, + int dma_restart) +{ + unsigned long flags, cnt; + int maxchunksize; + +#ifdef PCM86_DEBUG + printk("pcm86_start_input():"); + if (audio_devs[dev]->dmap->flags & DMA_BUSY) + printk(" DMA_BUSY"); + if (audio_devs[dev]->dmap->flags & DMA_RESTART) + printk(" DMA_RESTART"); + if (audio_devs[dev]->dmap->flags & DMA_ACTIVE) + printk(" DMA_ACTIVE"); + if (audio_devs[dev]->dmap->flags & DMA_STARTED) + printk(" DMA_STARTED"); + if (audio_devs[dev]->dmap->flags & DMA_ALLOC_DONE) + printk(" DMA_ALLOC_DONE"); + printk("\n"); +#endif + +#if 0 + DISABLE_INTR(flags); +#endif + + pcm_s.intr_size = PCM86_INTRSIZE_IN; + +#ifdef PCM86_DEBUG + printk("pcm86_start_input(): count = %d, intrsize= %d\n", + count, pcm_s.intr_size); +#endif + + pcm_s.pdma_buf = (pcm_data *)buf; + pcm_s.pdma_count = count; + pcm_s.pdma_chunkcount = 1; + maxchunksize = ((pcm_s.intr_size / (pcm_s.bytes * 2)) * pcm_s.speed + / pcm_s.chipspeed) * (pcm_s.bytes << pcm_s.stereo); + if (count > maxchunksize) + pcm_s.pdma_chunkcount = 2 * count / maxchunksize; + + pcm_s.intr_mode = IMODE_INPUT; + if (!pcm_s.intr_busy) + fifo_start(IMODE_INPUT); + pcm_s.intr_busy = YES; + +#if 0 + RESTORE_INTR(flags); +#endif +} + + +static int +pcm86_ioctl(int dev, unsigned int cmd, unsigned int arg, int local) +{ + switch (cmd) { + case SOUND_PCM_WRITE_RATE: + if (local) + return set_speed(arg); + return IOCTL_OUT(arg, set_speed(IOCTL_IN(arg))); + + case SOUND_PCM_READ_RATE: + if (local) + return pcm_s.speed; + return IOCTL_OUT(arg, pcm_s.speed); + + case SNDCTL_DSP_STEREO: + if (local) + return set_stereo(arg); + return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg))); + + case SOUND_PCM_WRITE_CHANNELS: + if (local) + return set_stereo(arg - 1) + 1; + return IOCTL_OUT(arg, set_stereo(IOCTL_IN(arg) - 1) + 1); + + case SOUND_PCM_READ_CHANNELS: + if (local) + return pcm_s.stereo + 1; + return IOCTL_OUT(arg, pcm_s.stereo + 1); + + case SNDCTL_DSP_SETFMT: + if (local) + return set_format(arg); + return IOCTL_OUT(arg, set_format(IOCTL_IN(arg))); + + case SOUND_PCM_READ_BITS: + if (local) + return pcm_s.bytes * 8; + return IOCTL_OUT(arg, pcm_s.bytes * 8); + } + + /* Invalid ioctl request */ + return RET_ERROR(EINVAL); +} + + +static int +pcm86_prepare_for_input(int dev, int bufsize, int nbufs) +{ + pcm_s.intr_size = PCM86_INTRSIZE_IN; + pcm_s.intr_mode = IMODE_NONE; + pcm_s.acc = 0; + pcm_s.last_l = 0; + pcm_s.last_r = 0; + + DEB(printk("pcm86_prepare_for_input\n")); + + return 0; +} + + +static int +pcm86_prepare_for_output(int dev, int bufsize, int nbufs) +{ + pcm_s.intr_size = PCM86_INTRSIZE_OUT; + pcm_s.intr_mode = IMODE_NONE; + pcm_s.acc = 0; + pcm_s.last_l = 0; + pcm_s.last_r = 0; + + DEB(printk("pcm86_prepare_for_output\n")); + + return 0; +} + + +static void +pcm86_reset(int dev) +{ + pcm_stop(); +} + + +static void +pcm86_halt_xfer(int dev) +{ + pcm_stop(); + + DEB(printk("pcm86_halt_xfer\n")); +} + + +void +pcmintr(int unit) +{ + unsigned char tmp; + + if ((inb(pcm_s.iobase + 8) & 0x10) == 0) + return; /* not FIFO intr. */ + + switch(pcm_s.intr_mode) { + case IMODE_OUTPUT: + if (pcm_s.intr_trailer) { + DEB(printk("pcmintr(): fifo_reset\n")); + fifo_reset(); + pcm_s.intr_trailer = NO; + pcm_s.intr_busy = NO; + } + if (pcm_s.pdma_count > 0) + fifo_output_block(); + else + DMAbuf_outputintr(my_dev, 1); + /* Reset intr. flag. */ + tmp = inb(pcm_s.iobase + 8); + outb(pcm_s.iobase + 8, tmp & ~0x10); + outb(pcm_s.iobase + 8, tmp | 0x10); + break; + + case IMODE_INPUT: + fifo_input_block(); + if (pcm_s.pdma_count == 0) + DMAbuf_inputintr(my_dev); + /* Reset intr. flag. */ + tmp = inb(pcm_s.iobase + 8); + outb(pcm_s.iobase + 8, tmp & ~0x10); + outb(pcm_s.iobase + 8, tmp | 0x10); + break; + + default: + pcm_stop(); + printk("pcm0: unexpected interrupt\n"); + } +} + + +#endif /* EXCLUDE_PCM86, EXCLUDE_AUDIO */ + +#endif /* CONFIGURE_SOUNDCARD */ diff --git a/sys/i386/isa/sound/sb16_dsp.c b/sys/i386/isa/sound/sb16_dsp.c index d5af2c1..5e84e67 100644 --- a/sys/i386/isa/sound/sb16_dsp.c +++ b/sys/i386/isa/sound/sb16_dsp.c @@ -84,7 +84,11 @@ static void dsp_cleanup (void); static struct audio_operations sb16_dsp_operations = { "SoundBlaster 16", +#ifdef PC98 + NEEDS_RESTART, +#else DMA_AUTOMODE, +#endif AFMT_U8 | AFMT_S16_LE, NULL, sb16_dsp_open, @@ -447,6 +451,17 @@ set_irq_hw (int level) switch (level) { +#ifdef PC98 + case 5: + ival = 8; + break; + case 3: + ival = 1; + break; + case 10: + ival = 2; + break; +#else case 5: ival = 2; break; @@ -459,6 +474,7 @@ set_irq_hw (int level) case 10: ival = 8; break; +#endif default: printk ("SB16_IRQ_LEVEL %d does not exist\n", level); return; @@ -517,6 +533,9 @@ sb16_dsp_detect (struct address_info *hw_config) if (sbc_major < 4) /* Set by the plain SB driver */ return 0; /* Not a SB16 */ +#ifdef PC98 + hw_config->dma = sb_config->dma; +#else if (hw_config->dma < 4) if (hw_config->dma != sb_config->dma) { @@ -524,11 +543,16 @@ sb16_dsp_detect (struct address_info *hw_config) sb_config->dma, hw_config->dma); return 0; } +#endif dma16 = hw_config->dma; dma8 = sb_config->dma; set_irq_hw (sb_config->irq); +#ifdef PC98 + sb_setmixer (DMA_NR, hw_config->dma == 0 ? 1 : 2); +#else sb_setmixer (DMA_NR, (1 << hw_config->dma) | (1 << sb_config->dma)); +#endif DEB (printk ("SoundBlaster 16: IRQ %d DMA %d OK\n", sb_config->irq, hw_config->dma)); diff --git a/sys/i386/isa/sound/sb16_midi.c b/sys/i386/isa/sound/sb16_midi.c index 4e45e2e..7dae750 100644 --- a/sys/i386/isa/sound/sb16_midi.c +++ b/sys/i386/isa/sound/sb16_midi.c @@ -35,9 +35,15 @@ #include <i386/isa/sound/sb.h> +#ifdef PC98 +#define DATAPORT (sb16midi_base) +#define COMDPORT (sb16midi_base+0x100) +#define STATPORT (sb16midi_base+0x100) +#else #define DATAPORT (sb16midi_base) #define COMDPORT (sb16midi_base+1) #define STATPORT (sb16midi_base+1) +#endif #define sb16midi_status() INB(STATPORT) #define input_avail() (!(sb16midi_status()&INPUT_AVAIL)) diff --git a/sys/i386/isa/sound/sb_dsp.c b/sys/i386/isa/sound/sb_dsp.c index 560fc79..82a2178 100644 --- a/sys/i386/isa/sound/sb_dsp.c +++ b/sys/i386/isa/sound/sb_dsp.c @@ -1067,6 +1067,30 @@ sb_dsp_detect (struct address_info *hw_config) return 0; #endif +#ifdef PC98 + switch (sbc_irq) + { + case 3: + sb_setmixer (IRQ_NR, 1); + break; + case 5: + sb_setmixer (IRQ_NR, 8); + break; + case 10: + sb_setmixer (IRQ_NR, 2); + break; + } + switch (hw_config->dma) + { + case 0: + sb_setmixer (DMA_NR, 1); + break; + case 3: + sb_setmixer (DMA_NR, 2); + break; + } +#endif + return 1; /* * Detected */ @@ -1137,8 +1161,13 @@ sb_dsp_init (long mem_start, struct address_info *hw_config) #ifndef EXCLUDE_YM3812 +#ifdef PC98 + if (sbc_major > 3 || + (sbc_major == 3 && INB (0x28d2) == 0x00)) +#else if (sbc_major > 3 || (sbc_major == 3 && INB (0x388) == 0x00)) /* Should be 0x06 if not OPL-3 */ +#endif enable_opl3_mode (OPL3_LEFT, OPL3_RIGHT, OPL3_BOTH); #endif diff --git a/sys/i386/isa/sound/sound_switch.c b/sys/i386/isa/sound/sound_switch.c index 21bcd69..83994e0 100644 --- a/sys/i386/isa/sound/sound_switch.c +++ b/sys/i386/isa/sound/sound_switch.c @@ -171,10 +171,16 @@ init_status (void) return; if (!put_status_int (snd_installed_cards[i].config.irq, 10)) return; +#ifdef PC98 + if (snd_installed_cards[i].config.dma >= 0) { +#endif if (!put_status (" drq ")) return; if (!put_status_int (snd_installed_cards[i].config.dma, 10)) return; +#ifdef PC98 + } +#endif if (!snd_installed_cards[i].enabled) if (!put_status (")")) diff --git a/sys/i386/isa/sound/soundcard.c b/sys/i386/isa/sound/soundcard.c index c7af8ab..3a06b6c 100644 --- a/sys/i386/isa/sound/soundcard.c +++ b/sys/i386/isa/sound/soundcard.c @@ -26,7 +26,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: soundcard.c,v 1.42 1996/03/28 14:31:13 scrappy Exp $ + * $Id: soundcard.c,v 1.43 1996/09/10 08:26:06 bde Exp $ */ #include <i386/isa/sound/sound_config.h> @@ -91,6 +91,9 @@ struct isa_driver gusxvidriver = {sndprobe, sndattach, "gusxvi"}; struct isa_driver gusmaxdriver = {sndprobe, sndattach, "gusmax"}; struct isa_driver uartdriver = {sndprobe, sndattach, "uart"}; struct isa_driver mssdriver = {sndprobe, sndattach, "mss"}; +#ifdef PC98 +struct isa_driver pcmdriver = {sndprobe, sndattach, "pcm"}; +#endif static unsigned short ipri_to_irq (unsigned short ipri); @@ -274,6 +277,10 @@ driver_to_voxunit(struct isa_driver *driver) return(SNDCARD_GUS16); else if(driver == &mssdriver) return(SNDCARD_MSS); +#ifdef PC98 + else if(driver == &pcmdriver) + return(SNDCARD_PCM86); +#endif else return(0); } @@ -291,6 +298,19 @@ sndprobe (struct isa_device *dev) hw_config.dma_read = dev->id_flags; /* misuse the flags field for read dma*/ if(unit) +#ifdef PC98 + if(unit == SNDCARD_PCM86) { + int result; + + result = sndtable_probe (unit, &hw_config); + dev->id_iobase = hw_config.io_base; + dev->id_irq = (1 << hw_config.irq); + dev->id_drq = hw_config.dma; + + return result; + } + else +#endif return sndtable_probe (unit, &hw_config); else return 0; @@ -468,7 +488,11 @@ sound_mem_init (void) for (dev = 0; dev < num_audiodevs; dev++) /* Enumerate devices */ if (!(dsp_init_mask & (1 << dev))) /* Not already done */ +#ifdef PC98 + if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan >= 0) +#else if (audio_devs[dev]->buffcount > 0 && audio_devs[dev]->dmachan > 0) +#endif { dsp_init_mask |= (1 << dev); dmap = audio_devs[dev]->dmap; diff --git a/sys/i386/isa/wdreg.h b/sys/i386/isa/wdreg.h index 20d37da..2a0a32f 100644 --- a/sys/i386/isa/wdreg.h +++ b/sys/i386/isa/wdreg.h @@ -34,12 +34,42 @@ * SUCH DAMAGE. * * from: @(#)wdreg.h 7.1 (Berkeley) 5/9/91 - * $Id: wdreg.h,v 1.11 1996/01/28 22:16:20 wollman Exp $ + * $Id: wdreg.h,v 1.12 1996/06/08 10:03:38 bde Exp $ + */ + +/* + * modified for PC9801 by F.Ukai + * Kyoto University Microcomputer Club (KMC) */ /* * Disk Controller register definitions. */ +#ifdef PC98 +#define wd_data 0x0 /* data register (R/W - 16 bits) */ +#define wd_error 0x2 /* error register (R) */ +#define wd_precomp wd_error /* write precompensation (W) */ +#define wd_features wd_error /* features register (W) */ +#define wd_seccnt 0x4 /* sector count (R/W) */ +#define wd_sector 0x6 /* first sector number (R/W) */ +#define wd_cyl_lo 0x8 /* cylinder address, low byte (R/W) */ +#define wd_cyl_hi 0xa /* cylinder address, high byte (R/W)*/ +#define wd_sdh 0xc /* sector size/drive/head (R/W)*/ +#define wd_command 0xe /* command register (W) */ +#define wd_status wd_command /* immediate status (R) */ + +#define wd_altsts_nec 0x10c /*alternate fixed disk status(via 1015) (R)*/ +#define wd_ctlr_nec 0x10c /*fixed disk controller control(via 1015) (W)*/ +#define wd_altsts_epson 0x3 /*alternate fixed disk status(via 1015) (R)*/ +#define wd_ctlr_epson 0x3 /*fixed disk controller control(via 1015) (W)*/ +#define wd_altsts wd_alsts_nec +#define wd_ctlr wd_ctlr_nec + +#define WDCTL_4BIT 0x8 /* use four head bits (wd1003) */ +#define WDCTL_RST 0x4 /* reset the controller */ +#define WDCTL_IDS 0x2 /* disable controller interrupts */ +#define wd_digin 0x10e /* disk controller input(via 1015) (R)*/ +#else /* IBM-PC */ #define wd_data 0x0 /* data register (R/W - 16 bits) */ #define wd_error 0x1 /* error register (R) */ #define wd_precomp wd_error /* write precompensation (W) */ @@ -58,6 +88,7 @@ #define WDCTL_RST 0x4 /* reset the controller */ #define WDCTL_IDS 0x2 /* disable controller interrupts */ #define wd_digin 0x207 /* disk controller input(via 1015) (R)*/ +#endif /* PC98 */ /* * Status Bits. |