#include <sys/param.h>
#include <sys/systm.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/syslog.h>
#include <sys/device.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <machine/bus.h>
#include <machine/cpu.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#if 0
#include <dev/ic/ym2203reg.h>
#endif
#include <luna88k/cbus/nec86reg.h>
#include <luna88k/cbus/nec86hwvar.h>
#include <luna88k/cbus/nec86var.h>
#ifdef AUDIO_DEBUG
extern void Dprintf(const char *, ...);
#define DPRINTF(x) if (nec86hwdebug) printf x
#define DPRINTF2(l, x) if (nec86hwdebug >= l) printf x
int nec86hwdebug = 3;
#else
#define DPRINTF(x)
#define DPRINTF2(l, x)
#endif
#ifndef VOLUME_DELAY
#define VOLUME_DELAY 10
#endif
static int nec86hw_rate_table[NEC86HW_NRATE_TYPE][NEC86_NRATE] = {
{ 44100, 33075, 22050, 16538, 11025, 8269, 5513, 4134 },
{ 44100, 33075, 22050, 16000, 11025, 8000, 5513, 4000 },
};
static struct audio_params nec86hw_audio_default =
{44100, AUDIO_ENCODING_SLINEAR_LE, 16, 2, 1, 2};
int nec86hw_set_output_block(struct nec86hw_softc *, int);
int nec86hw_set_input_block(struct nec86hw_softc *, int);
static struct nec86hw_functable_entry nec86hw_functable[] = {
{ 8, 1,
nec86fifo_output_mono_8_direct, nec86fifo_input_mono_8_direct },
{ 16, 1,
nec86fifo_output_mono_16_direct, nec86fifo_input_mono_16_direct },
{ 8, 2,
nec86fifo_output_stereo_8_direct, nec86fifo_input_stereo_8_direct },
{ 16, 2,
nec86fifo_output_stereo_16_direct, nec86fifo_input_stereo_16_direct },
};
#define NFUNCTABLEENTRY (sizeof(nec86hw_functable) / sizeof(nec86hw_functable[0]))
void
nec86hw_attach(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
sc->func_fifo_output = nec86fifo_output_mono_8_direct;
sc->func_fifo_input = nec86fifo_input_mono_8_direct;
(void) nec86hw_set_params(sc, AUMODE_RECORD, 0,
&nec86hw_audio_default, &nec86hw_audio_default);
(void) nec86hw_set_params(sc, AUMODE_PLAY, 0,
&nec86hw_audio_default, &nec86hw_audio_default);
(void) nec86hw_set_in_port(sc, NEC86HW_INPUT_MIXER);
(void) nec86hw_set_out_port(sc, NEC86HW_OUTPUT_MIXER);
nec86hw_set_volume(sc, NEC86_VOLUME_PORT_OPNAD, NEC86_MAXVOL);
nec86hw_set_volume(sc, NEC86_VOLUME_PORT_OPNAI, NEC86_MAXVOL);
nec86hw_set_volume(sc, NEC86_VOLUME_PORT_LINED, NEC86_MAXVOL);
nec86hw_set_volume(sc, NEC86_VOLUME_PORT_LINEI, NEC86_MAXVOL);
nec86hw_set_volume(sc, NEC86_VOLUME_PORT_PCMD, NEC86_MAXVOL);
nec86hw_speaker_ctl(sc, SPKR_ON);
data = bus_space_read_1(iot, ioh, NEC86_CTRL);
data &= NEC86_CTRL_MASK_PAN | NEC86_CTRL_MASK_PORT;
data |= NEC86_CTRL_PAN_L | NEC86_CTRL_PAN_R;
data |= NEC86_CTRL_PORT_STD;
bus_space_write_1(iot, ioh, NEC86_CTRL, data);
}
int
nec86hw_open(void *arg, int flags)
{
struct nec86hw_softc *sc = arg;
DPRINTF(("nec86hw_open: sc=%p\n", sc));
if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
return ENXIO;
if (sc->sc_open != 0 || nec86hw_reset(sc) != 0)
return ENXIO;
nec86hw_speaker_ctl(sc, (flags & FWRITE) ? SPKR_ON : SPKR_OFF);
sc->sc_open = 1;
sc->sc_intr = NULL;
sc->sc_arg = NULL;
sc->conv_acc = 0;
sc->conv_last0 = 0;
sc->conv_last0_l = 0;
sc->conv_last0_r = 0;
sc->conv_last1 = 0;
sc->conv_last1_l = 0;
sc->conv_last1_r = 0;
DPRINTF(("nec86hw_open: opened\n"));
return 0;
}
void
nec86hw_close(void *addr)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_close: sc=%p\n", sc));
sc->sc_open = 0;
sc->sc_intr = NULL;
(void) nec86hw_reset(sc);
DPRINTF(("nec86hw_close: closed\n"));
}
int
nec86hw_set_params(void *addr, int mode, int usemode, struct audio_params *p,
struct audio_params *r)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
int rate_type = NEC86HW_RATE_TYPE(sc->sc_cfgflags);
if ((p->channels != 1) && (p->channels != 2))
return EINVAL;
if (p->precision == 8)
p->encoding = AUDIO_ENCODING_ULINEAR_LE;
else {
p->precision = 16;
p->encoding = AUDIO_ENCODING_SLINEAR_LE;
}
sc->channels = p->channels;
sc->precision = p->precision;
sc->encoding = p->encoding;
sc->hw_orate_bits = nec86hw_rate_bits(sc, p->sample_rate);
sc->sc_orate = p->sample_rate = sc->hw_orate =
nec86hw_rate_table[rate_type][sc->hw_orate_bits];
sc->hw_irate_bits = nec86hw_rate_bits(sc, r->sample_rate);
sc->sc_irate = r->sample_rate = sc->hw_irate =
nec86hw_rate_table[rate_type][sc->hw_irate_bits];
return 0;
}
int
nec86hw_round_blocksize(void *addr, int blk)
{
u_int base = NEC86_INTRBLK_UNIT;
if (blk < NEC86_INTRBLK_UNIT * 2)
return NEC86_INTRBLK_UNIT * 2;
for ( ; base <= blk; base *= 2)
;
return base / 2;
}
int
nec86hw_set_out_port(void *addr, int port)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_set_out_port:\n"));
if (port != NEC86HW_OUTPUT_MIXER)
return EINVAL;
sc->out_port = port;
return 0;
}
int
nec86hw_get_out_port(void *addr)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_get_out_port:\n"));
return sc->out_port;
}
int
nec86hw_set_in_port(void *addr, int port)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_set_in_port:\n"));
if (port != NEC86HW_INPUT_MIXER)
return EINVAL;
sc->in_port = port;
return 0;
}
int
nec86hw_get_in_port(void *addr)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_get_in_port:\n"));
return sc->in_port;
}
int
nec86hw_commit_settings(void *addr)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
int i;
for (i = 0; i < NFUNCTABLEENTRY; i++) {
if ((nec86hw_functable[i].precision == sc->precision)
&& (nec86hw_functable[i].channels == sc->channels))
break;
}
if (i >= NFUNCTABLEENTRY) {
return EINVAL;
}
sc->func_fifo_output =
nec86hw_functable[i].func_fifo_output_direct;
sc->func_fifo_input =
nec86hw_functable[i].func_fifo_input_direct;
return 0;
}
int
nec86hw_mixer_set_port(void *addr, mixer_ctrl_t *cp)
{
DPRINTF(("nec86hw_mixer_set_port:\n"));
return ENXIO;
}
int
nec86hw_mixer_get_port(void *addr, mixer_ctrl_t *cp)
{
DPRINTF(("nec86hw_mixer_get_port:\n"));
return ENXIO;
}
int
nec86hw_mixer_query_devinfo(void *addr, mixer_devinfo_t *dip)
{
DPRINTF(("nec86hw_mixer_query_devinfo:\n"));
return ENXIO;
}
int
nec86hw_set_output_block(struct nec86hw_softc *sc, int cc)
{
int bpf, hw_blocksize, watermark;
bpf = (sc->channels * sc->precision) / NBBY;
sc->pdma_count = cc / bpf;
hw_blocksize = sc->pdma_count * (sc->precision / NBBY * 2);
sc->pdma_nchunk =
((hw_blocksize * (WATERMARK_MAX_RATIO + WATERMARK_RATIO_OUT))
+ (NEC86_BUFFSIZE * WATERMARK_MAX_RATIO - 1))
/ (NEC86_BUFFSIZE * WATERMARK_MAX_RATIO);
watermark =
(hw_blocksize * WATERMARK_RATIO_OUT)
/ (sc->pdma_nchunk * WATERMARK_MAX_RATIO);
sc->pdma_watermark = nec86hw_round_watermark(watermark);
if (hw_blocksize + sc->pdma_watermark > NEC86_BUFFSIZE) {
sc->pdma_nchunk =
(hw_blocksize + (NEC86_BUFFSIZE - sc->pdma_watermark - 1))
/ (NEC86_BUFFSIZE - sc->pdma_watermark);
}
DPRINTF2(2, ("nec86hw_pdma_output: cc=%d count=%d hw_blocksize=%d "
"watermark=%d nchunk=%d ptr=%p\n",
cc, sc->pdma_count, hw_blocksize, sc->pdma_watermark,
sc->pdma_nchunk, sc->pdma_ptr));
return 0;
}
int
nec86hw_set_input_block(struct nec86hw_softc *sc, int cc)
{
int bpf, hw_blocksize, watermark, maxwatermark;
bpf = (sc->channels * sc->precision) / NBBY;
sc->pdma_count = cc / bpf;
watermark =
(NEC86_BUFFSIZE * WATERMARK_RATIO_IN) / WATERMARK_MAX_RATIO;
maxwatermark = nec86hw_round_watermark(watermark);
hw_blocksize = sc->pdma_count * (sc->precision / NBBY * 2);
sc->pdma_nchunk = (hw_blocksize + (maxwatermark - 1)) / maxwatermark;
watermark = (hw_blocksize / sc->pdma_nchunk) + (NEC86_INTRBLK_UNIT - 1);
sc->pdma_watermark = nec86hw_round_watermark(watermark);
DPRINTF2(2, ("nec86hw_pdma_input: cc=%d count=%d hw_blocksize=%d "
"watermark=%d nchunk=%d ptr=%p\n",
cc, sc->pdma_count, hw_blocksize, sc->pdma_watermark,
sc->pdma_nchunk, sc->pdma_ptr));
return 0;
}
int
nec86hw_pdma_init_output(void *addr, void *buf, int cc)
{
struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
nec86hw_halt_pdma(addr);
nec86hw_reset_fifo(sc);
nec86hw_set_mode_playing(sc);
nec86hw_set_rate_real(sc, sc->hw_orate_bits);
nec86hw_set_precision_real(sc, sc->precision);
nec86hw_set_output_block(sc, cc);
nec86hw_reset_fifo(sc);
nec86hw_enable_fifointr(sc);
nec86hw_set_watermark(sc, sc->pdma_watermark);
nec86hw_disable_fifointr(sc);
return 0;
}
int
nec86hw_pdma_init_input(void *addr, void *buf, int cc)
{
struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
nec86hw_halt_pdma(addr);
nec86hw_reset_fifo(sc);
nec86hw_set_mode_recording(sc);
nec86hw_set_rate_real(sc, sc->hw_irate_bits);
nec86hw_set_precision_real(sc, sc->precision);
nec86hw_set_input_block(sc, cc);
nec86hw_reset_fifo(sc);
nec86hw_enable_fifointr(sc);
nec86hw_set_watermark(sc, sc->pdma_watermark);
nec86hw_disable_fifointr(sc);
return 0;
}
int
nec86hw_pdma_output(void *addr, void *p, int cc, void (*intr)(void *),
void *arg)
{
struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
int bpf;
bpf = (sc->channels * sc->precision) / NBBY;
if ((cc % bpf) != 0) {
DPRINTF(("nec86hw_pdma_output: odd bytes\n"));
return EIO;
}
sc->sc_intr = intr;
sc->sc_arg = arg;
sc->pdma_ptr = (u_char *) p;
sc->pdma_padded = 0;
sc->pdma_mode = PDMA_MODE_OUTPUT;
if (sc->pdma_count != cc / bpf)
nec86hw_set_output_block(sc, cc);
if (!sc->intr_busy) {
nec86hw_set_precision_real(sc, sc->precision);
nec86hw_set_rate_real(sc, sc->hw_orate_bits);
nec86hw_set_mode_playing(sc);
nec86hw_reset_fifo(sc);
}
nec86hw_disable_fifointr(sc);
nec86hw_output_chunk(sc);
nec86hw_enable_fifointr(sc);
if (!sc->intr_busy) {
nec86hw_start_fifo(sc);
sc->intr_busy = 1;
}
nec86hw_set_watermark(sc, sc->pdma_watermark);
return 0;
}
int
nec86hw_pdma_input(void *addr, void *p, int cc, void (*intr)(void *),
void *arg)
{
struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
int bpf;
bpf = (sc->channels * sc->precision) / NBBY;
if ((cc % bpf) != 0) {
DPRINTF(("nec86hw_pdma_input: odd bytes\n"));
return EIO;
}
sc->sc_intr = intr;
sc->sc_arg = arg;
sc->pdma_ptr = (u_int8_t *) p;
sc->pdma_padded = 0;
sc->pdma_mode = PDMA_MODE_INPUT;
if (sc->pdma_count != cc / bpf)
nec86hw_set_input_block(sc, cc);
if (!sc->intr_busy) {
nec86hw_set_precision_real(sc, sc->precision);
nec86hw_set_rate_real(sc, sc->hw_irate_bits);
nec86hw_set_mode_recording(sc);
nec86hw_reset_fifo(sc);
nec86hw_enable_fifointr(sc);
nec86hw_start_fifo(sc);
sc->intr_busy = 1;
}
nec86hw_set_watermark(sc, sc->pdma_watermark);
return 0;
}
int
nec86hw_halt_pdma(void *addr)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_halt_pdma: sc=%p\n", sc));
nec86hw_stop_fifo(sc);
nec86hw_disable_fifointr(sc);
sc->intr_busy = 0;
return 0;
}
int
nec86hw_cont_pdma(void *addr)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
DPRINTF(("nec86hw_cont_pdma: sc=%p\n", sc));
nec86hw_enable_fifointr(sc);
nec86hw_start_fifo(sc);
sc->intr_busy = 1;
return 0;
}
int
nec86hw_speaker_ctl(void *addr, int onoff)
{
register struct nec86hw_softc *sc = (struct nec86hw_softc *) addr;
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
DPRINTF(("nec86hw_speaker_ctl:\n"));
switch (onoff) {
case SPKR_ON:
bus_space_write_1(iot, ioh, NEC86_VOLUME, 0x0d1);
delay(VOLUME_DELAY);
break;
case SPKR_OFF:
bus_space_write_1(iot, ioh, NEC86_VOLUME, 0x0d0);
delay(VOLUME_DELAY);
break;
default:
return EINVAL;
}
return 0;
}
u_int8_t
nec86hw_rate_bits(struct nec86hw_softc *sc, u_long sr)
{
int i;
u_long rval, hr, min;
int rate_type = NEC86HW_RATE_TYPE(sc->sc_cfgflags);
min = 0;
rval = 0;
for (i = 0; i < NEC86_NRATE; i++) {
hr = nec86hw_rate_table[rate_type][i];
if ((hr >= sr) && ((min == 0) || (min > hr))) {
min = hr;
rval = (u_int8_t) i;
}
}
return rval;
}
int
nec86hw_round_watermark(int wm)
{
wm = (wm / NEC86_INTRBLK_UNIT) * NEC86_INTRBLK_UNIT;
if (wm < NEC86_INTRBLK_UNIT)
wm = NEC86_INTRBLK_UNIT;
return wm;
}
int
nec86hw_reset(struct nec86hw_softc *sc)
{
nec86hw_stop_fifo(sc);
nec86hw_disable_fifointr(sc);
nec86hw_clear_intrflg(sc);
nec86hw_reset_fifo(sc);
sc->intr_busy = 0;
sc->pdma_mode = PDMA_MODE_NONE;
if (nec86hw_seeif_intrflg(sc))
return -1;
return 0;
}
void
nec86hw_set_mode_playing(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data &= ~NEC86_FIFOCTL_RECMODE;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_set_mode_recording(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data |= NEC86_FIFOCTL_RECMODE;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_set_volume(struct nec86hw_softc *sc, int port, u_int8_t vol)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
bus_space_write_1(iot, ioh, NEC86_VOLUME,
NEC86_VOL_TO_BITS(port, vol));
delay(VOLUME_DELAY);
}
void
nec86hw_start_fifo(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data |= NEC86_FIFOCTL_RUN;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_stop_fifo(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data &= ~NEC86_FIFOCTL_RUN;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_enable_fifointr(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data |= NEC86_FIFOCTL_ENBLINTR;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_disable_fifointr(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data &= ~NEC86_FIFOCTL_ENBLINTR;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
int
nec86hw_seeif_intrflg(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
return (data & NEC86_FIFOCTL_INTRFLG);
}
void
nec86hw_clear_intrflg(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data &= ~NEC86_FIFOCTL_INTRFLG;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
data |= NEC86_FIFOCTL_INTRFLG;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_reset_fifo(struct nec86hw_softc *sc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data |= NEC86_FIFOCTL_INIT;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
data &= ~NEC86_FIFOCTL_INIT;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_set_watermark(struct nec86hw_softc *sc, int wm)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
#ifdef DIAGNOSTIC
if ((wm < NEC86_INTRBLK_UNIT) || (wm > NEC86_BUFFSIZE)
|| ((wm % NEC86_INTRBLK_UNIT) != 0))
printf("nec86hw_set_watermark: invalid watermark %d\n", wm);
#endif
bus_space_write_1(iot, ioh, NEC86_FIFOINTRBLK,
(wm / NEC86_INTRBLK_UNIT) - 1);
}
void
nec86hw_set_precision_real(struct nec86hw_softc *sc, u_int prec)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_CTRL);
data &= ~NEC86_CTRL_8BITS;
if (prec == 8)
data |= NEC86_CTRL_8BITS;
bus_space_write_1(iot, ioh, NEC86_CTRL, data);
}
void
nec86hw_set_rate_real(struct nec86hw_softc *sc, u_int8_t bits)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t data;
data = bus_space_read_1(iot, ioh, NEC86_FIFOCTL);
data &= ~NEC86_FIFOCTL_MASK_RATE;
data |= bits & NEC86_FIFOCTL_MASK_RATE;
bus_space_write_1(iot, ioh, NEC86_FIFOCTL, data);
}
void
nec86hw_output_chunk(struct nec86hw_softc *sc)
{
int cc, nbyte;
if (sc->pdma_nchunk > 0) {
cc = sc->pdma_count / sc->pdma_nchunk;
nbyte = (*sc->func_fifo_output)(sc, cc);
DPRINTF2(3, ("nec86hw_output_chunk: sc->pdma_count=%d "
"sc->pdma_nchunk=%d cc=%d nbyte=%d ptr=%p\n",
sc->pdma_count, sc->pdma_nchunk, cc, nbyte, sc->pdma_ptr));
sc->pdma_nchunk--;
sc->pdma_count -= cc;
sc->pdma_ptr += nbyte;
} else {
nbyte = 0;
DPRINTF(("nec86hw_output_chunk: sc->pdma_nchunk=%d\n",
sc->pdma_nchunk));
}
if (nbyte <= sc->pdma_watermark) {
nec86fifo_padding(sc, sc->pdma_watermark);
sc->pdma_padded = 1;
DPRINTF(("nec86hw_output_chunk: write padding zero's\n"));
}
}
int
nec86fifo_output_mono_8_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
register u_int8_t d;
for (i = 0; i < cc; i++) {
d = *p++;
d ^= 0x80;
bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
}
return cc * 2;
}
int
nec86fifo_output_mono_16_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
for (i = 0; i < cc; i++) {
#if BYTE_ORDER == BIG_ENDIAN
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
#else
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
#endif
p += 2;
}
return cc * 4;
}
int
nec86fifo_output_stereo_8_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
for (i = 0; i < cc; i++) {
bus_space_write_1(iot, ioh, NEC86_FIFODATA, (*p++) ^ 0x80);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, (*p++) ^ 0x80);
}
return cc * 2;
}
int
nec86fifo_output_stereo_16_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
for (i = 0; i < cc; i++) {
#if BYTE_ORDER == BIG_ENDIAN
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
p += 2;
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
p += 2;
#else
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
p += 2;
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *(p + 1));
bus_space_write_1(iot, ioh, NEC86_FIFODATA, *p);
p += 2;
#endif
}
return cc * 4;
}
int
nec86fifo_output_mono_8_resamp(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
register int rval;
register u_int8_t d;
register u_int8_t d0, d1;
register u_long acc, orate, hw_orate;
rval = 0;
orate = sc->sc_orate;
hw_orate = sc->hw_orate;
acc = sc->conv_acc;
d0 = (u_int8_t) sc->conv_last0;
d1 = (u_int8_t) sc->conv_last1;
for (i = 0; i < cc; i++) {
d0 = d1;
d1 = *p++;
while (acc <= hw_orate) {
d = ((d0 * (hw_orate - acc)) + (d1 * acc)) / hw_orate;
d ^= 0x80;
bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, d);
acc += orate;
rval += 2;
}
acc -= hw_orate;
}
sc->conv_acc = acc;
sc->conv_last0 = (u_short) d0;
sc->conv_last1 = (u_short) d1;
return rval;
}
int
nec86fifo_output_mono_16_resamp(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
register int rval;
register u_short d, d0, d1;
register u_long acc, orate, hw_orate;
rval = 0;
orate = sc->sc_orate;
hw_orate = sc->hw_orate;
acc = sc->conv_acc;
d0 = sc->conv_last0;
d1 = sc->conv_last1;
for (i = 0; i < cc; i++) {
d0 = d1;
d1 = (*p | (*(p + 1) << 8)) ^ 0x8000;
p += 2;
while (acc <= hw_orate) {
d = ((d0 * (hw_orate - acc)) + (d1 * acc)) / hw_orate;
d ^= 0x8000;
bus_space_write_1(iot, ioh, NEC86_FIFODATA,
(d >> 8) & 0xff);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, d & 0xff);
bus_space_write_1(iot, ioh, NEC86_FIFODATA,
(d >> 8) & 0xff);
bus_space_write_1(iot, ioh, NEC86_FIFODATA, d & 0xff);
acc += orate;
rval += 4;
}
acc -= hw_orate;
}
sc->conv_acc = acc;
sc->conv_last0 = d0;
sc->conv_last1 = d1;
return rval;
}
void
nec86hw_input_chunk(struct nec86hw_softc *sc)
{
int cc, bpf;
bpf = (sc->channels * sc->precision) / NBBY;
if (sc->pdma_nchunk > 0) {
cc = sc->pdma_count / sc->pdma_nchunk;
(*sc->func_fifo_input)(sc, cc);
DPRINTF2(3, ("nec86hw_input_chunk: sc->pdma_count=%d "
"sc->pdma_nchunk=%d cc=%d ptr=%p\n",
sc->pdma_count, sc->pdma_nchunk, cc, sc->pdma_ptr));
sc->pdma_nchunk--;
sc->pdma_count -= cc;
sc->pdma_ptr += (cc * bpf);
} else {
cc = 0;
DPRINTF(("nec86hw_input_chunk: ??? sc->pdma_nchunk=%d ???",
sc->pdma_nchunk));
}
}
void
nec86fifo_input_mono_8_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
register u_int8_t d_l, d_r;
for (i = 0; i < cc; i++) {
d_l = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
d_r = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
*p++ = (d_l + d_r) / 2;
}
}
void
nec86fifo_input_mono_16_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
register u_short d, d_l, d_r;
for (i = 0; i < cc; i++) {
d_l = (bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80) << 8;
d_l |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
d_r = (bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80) << 8;
d_r |= bus_space_read_1(iot, ioh, NEC86_FIFODATA);
d = (d_l + d_r) / 2;
#if BYTE_ORDER == BIG_ENDIAN
*p++ = ((d >> 8) & 0xff) ^ 0x80;
*p++ = d & 0xff;
#else
*p++ = d & 0xff;
*p++ = ((d >> 8) & 0xff) ^ 0x80;
#endif
}
}
void
nec86fifo_input_stereo_8_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
for (i = 0; i < cc; i++) {
*p++ = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
*p++ = bus_space_read_1(iot, ioh, NEC86_FIFODATA) ^ 0x80;
}
}
void
nec86fifo_input_stereo_16_direct(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
u_int8_t *p = sc->pdma_ptr;
int i;
for (i = 0; i < cc; i++) {
#if BYTE_ORDER == BIG_ENDIAN
*p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
*(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
p += 2;
*p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
*(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
p += 2;
#else
*(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
*p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
p += 2;
*(p + 1) = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
*p = bus_space_read_1(iot, ioh, NEC86_FIFODATA);
p += 2;
#endif
}
}
void
nec86fifo_padding(struct nec86hw_softc *sc, int cc)
{
bus_space_tag_t iot = sc->sc_iot;
bus_space_handle_t ioh = sc->sc_ioh;
register int i;
DPRINTF2(2, ("nec86fifo_padding: %d\n", cc));
for (i = 0; i < cc; i++)
bus_space_write_1(iot, ioh, NEC86_FIFODATA, 0);
}
int
nec86hw_intr(void *arg)
{
struct nec86hw_softc *sc = (struct nec86hw_softc *) arg;
if (!nec86hw_seeif_intrflg(sc)) {
DPRINTF(("nec86hw_intr: ??? FM sound interrupt ???\n"));
return 0;
}
mtx_enter(&audio_lock);
nec86hw_clear_intrflg(sc);
switch(sc->pdma_mode) {
case PDMA_MODE_OUTPUT:
if (sc->pdma_padded) {
nec86hw_reset_fifo(sc);
sc->pdma_padded = 0;
DPRINTF(("nec86hw_intr: clear padding zero's\n"));
}
if (sc->pdma_count > 0) {
nec86hw_disable_fifointr(sc);
nec86hw_output_chunk(sc);
nec86hw_enable_fifointr(sc);
} else
(*sc->sc_intr)(sc->sc_arg);
break;
case PDMA_MODE_INPUT:
if (sc->pdma_count > 0) {
nec86hw_disable_fifointr(sc);
nec86hw_input_chunk(sc);
nec86hw_enable_fifointr(sc);
}
if (sc->pdma_count <= 0)
(*sc->sc_intr)(sc->sc_arg);
break;
default:
nec86hw_stop_fifo(sc);
nec86hw_disable_fifointr(sc);
sc->intr_busy = 0;
DPRINTF(("nec86hw_intr: ??? unexpected interrupt ???\n"));
mtx_leave(&audio_lock);
return 0;
}
mtx_leave(&audio_lock);
return 1;
}