2127f26023
for possible buffer overflow problems. Replaced most sprintf()'s with snprintf(); for others cases, added terminating NUL bytes where appropriate, replaced constants like "16" with sizeof(), etc. These changes include several bug fixes, but most changes are for maintainability's sake. Any instance where it wasn't "immediately obvious" that a buffer overflow could not occur was made safer. Reviewed by: Bruce Evans <bde@zeta.org.au> Reviewed by: Matthew Dillon <dillon@apollo.backplane.com> Reviewed by: Mike Spengler <mks@networkcs.com>
2195 lines
52 KiB
C
2195 lines
52 KiB
C
/*
|
|
* 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 1.4 1997/06/03 10:25:41 kato 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); */
|
|
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_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. */
|
|
snprintf(pcm86_operations.name, sizeof(pcm86_operations.name),
|
|
"%s", 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 */
|