b7b905e770
Reviewed by: NAGAO Tadaaki <nagao@cs.titech.ac.jp> Submitted by: Takuya SHIOZAKI <tshiozak@makino.ise.chuo-u.ac.jp>
2194 lines
52 KiB
C
2194 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.3 1997/02/22 09:38:14 peter 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. */
|
|
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 */
|