#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: toccata.c,v 1.22 2023/08/27 18:36:55 andvar Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/device.h>
#include <sys/fcntl.h>
#include <sys/bus.h>
#include <sys/audioio.h>
#include <dev/audio/audio_if.h>
#include <dev/ic/ad1848reg.h>
#include <dev/ic/ad1848var.h>
#include <amiga/dev/zbusvar.h>
#include <amiga/amiga/isr.h>
#ifdef AUDIO_DEBUG
#define DPRINTF(x) if (ad1848debug) printf x
extern int ad1848debug;
#else
#define DPRINTF(x)
#endif
#define TOCC_FIFO_STAT 0x1ffe
#define TOCC_FIFO_DATA 0x2000
#define TOCC_CODEC_ADDR 0x67FF
#define TOCC_CODEC_STAT TOCC_CODEC_ADDR
#define TOCC_CODEC_REG 0x6801
#define TOCC_FIFO_INT 0x80
#define TOCC_FIFO_PBHE 0x08
#define TOCC_FIFO_CPHF 0x04
#define TOCC_RST 0x02
#define TOCC_ACT 0x01
#define TOCC_MAGIC 0x04
#define TOCC_PB_INTENA 0x80
#define TOCC_PB_FILL 0x10
#define TOCC_PB_PREP (TOCC_ACT + TOCC_PB_FILL)
#define TOCC_PB_TAIL (TOCC_PB_PREP + TOCC_MAGIC)
#define TOCC_PB_MAIN (TOCC_PB_TAIL + TOCC_PB_INTENA)
#define TOCC_CP_INTENA 0x40
#define TOCC_CP_RUN 0x08
#define TOCC_CP_TAIL (TOCC_ACT + TOCC_CP_RUN)
#define TOCC_CP_MAIN (TOCC_CP_TAIL + TOCC_CP_INTENA + TOCC_MAGIC)
#define TOCCATA_INPUT_CLASS 0
#define TOCCATA_OUTPUT_CLASS 1
#define TOCCATA_MONITOR_CLASS 2
#define TOCCATA_RECORD_CLASS 3
#define TOCCATA_RECORD_SOURCE 4
#define TOCCATA_REC_LVL 5
#define TOCCATA_MIC_IN_LVL 6
#define TOCCATA_AUX1_LVL 7
#define TOCCATA_AUX1_MUTE 8
#define TOCCATA_AUX2_LVL 9
#define TOCCATA_AUX2_MUTE 10
#define TOCCATA_MONITOR_LVL 11
#define TOCCATA_MONITOR_MUTE 12
#define TOCCATA_OUTPUT_LVL 13
#define TOCCATA_LINE_IN_LVL 14
#define TOCCATA_LINE_IN_MUTE 15
#define TOCCATA_MIC_LVL 16
#define TOCCATA_MIC_MUTE 17
int toccata_intr(void *);
int toccata_readreg(struct ad1848_softc *, int);
void toccata_writereg(struct ad1848_softc *, int, int);
int toccata_round_blocksize(void *, int, int, const audio_params_t *);
size_t toccata_round_buffersize(void *, int, size_t);
int toccata_open(void *, int);
void toccata_close(void *);
int toccata_getdev(void *, struct audio_device *);
int toccata_get_props(void *);
int toccata_halt_input(void *);
int toccata_halt_output(void *);
int toccata_start_input(void *, void *, int, void (*)(void *), void *);
int toccata_start_output(void *, void *, int, void (*)(void *), void *);
int toccata_set_port(void *, mixer_ctrl_t *);
int toccata_get_port(void *, mixer_ctrl_t *);
int toccata_query_devinfo(void *, mixer_devinfo_t *);
void toccata_get_locks(void *, kmutex_t **, kmutex_t **);
const struct audio_hw_if audiocs_hw_if = {
.open = toccata_open,
.close = toccata_close,
.query_format = ad1848_query_format,
.set_format = ad1848_set_format,
.round_blocksize = toccata_round_blocksize,
.commit_settings = ad1848_commit_settings,
.init_output = NULL,
.init_input = NULL,
.start_output = toccata_start_output,
.start_input = toccata_start_input,
.halt_output = toccata_halt_output,
.halt_input = toccata_halt_input,
.getdev = toccata_getdev,
.set_port = toccata_set_port,
.get_port = toccata_get_port,
.query_devinfo = toccata_query_devinfo,
.round_buffersize = toccata_round_buffersize,
.get_props = toccata_get_props,
.get_locks = toccata_get_locks,
};
struct toccata_softc {
struct ad1848_softc sc_ad;
struct isr sc_isr;
volatile uint8_t *sc_boardp;
void (*sc_captmore)(void *);
void *sc_captarg;
void *sc_captbuf;
int sc_captbufsz;
void (*sc_playmore)(void *);
void *sc_playarg;
kmutex_t sc_lock;
kmutex_t sc_intr_lock;
};
int toccata_match(device_t, cfdata_t, void *);
void toccata_attach(device_t, device_t, void *);
CFATTACH_DECL_NEW(toccata, sizeof(struct toccata_softc),
toccata_match, toccata_attach, NULL, NULL);
int
toccata_match(device_t parent, cfdata_t cfp, void *aux)
{
struct zbus_args *zap;
zap = aux;
if (zap->manid != 18260)
return (0);
if (zap->prodid != 12)
return (0);
return (1);
}
void
toccata_attach(device_t parent, device_t self, void *aux)
{
struct toccata_softc *sc;
struct ad1848_softc *asc;
struct zbus_args *zap;
volatile uint8_t *boardp;
sc = device_private(self);
asc = &sc->sc_ad;
asc->sc_dev = self;
zap = aux;
boardp = (volatile uint8_t *)zap->va;
sc->sc_boardp = boardp;
*boardp = TOCC_RST;
delay(500000);
*boardp = TOCC_ACT;
asc->parent = sc;
asc->sc_readreg = toccata_readreg;
asc->sc_writereg = toccata_writereg;
asc->chip_name = "ad1848";
asc->mode = 1;
ad1848_attach(asc);
printf("\n");
sc->sc_captbuf = 0;
sc->sc_playmore = 0;
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED);
sc->sc_isr.isr_ipl = 6;
sc->sc_isr.isr_arg = sc;
sc->sc_isr.isr_intr = toccata_intr;
add_isr(&sc->sc_isr);
audio_attach_mi(&audiocs_hw_if, sc, self);
}
int
toccata_intr(void *tag) {
struct toccata_softc *sc;
uint8_t *buf;
volatile uint8_t *fifo;
uint8_t status;
int i;
sc = tag;
mutex_spin_enter(&sc->sc_intr_lock);
status = *(sc->sc_boardp);
if (status & TOCC_FIFO_INT) {
mutex_spin_exit(&sc->sc_intr_lock);
return 0;
}
if (status & TOCC_FIFO_PBHE) {
if (sc->sc_playmore) {
(*sc->sc_playmore)(sc->sc_playarg);
mutex_spin_exit(&sc->sc_intr_lock);
return 1;
}
} else if (status & TOCC_FIFO_CPHF) {
if (sc->sc_captbuf) {
buf = sc->sc_captbuf;
fifo = sc->sc_boardp + TOCC_FIFO_DATA;
for (i = sc->sc_captbufsz/4 - 1; i>=0; --i) {
*buf++ = *fifo;
*buf++ = *fifo;
*buf++ = *fifo;
*buf++ = *fifo;
}
(*sc->sc_captmore)(sc->sc_captarg);
mutex_spin_exit(&sc->sc_intr_lock);
return 1;
}
}
#ifdef DEBUG
printf("%s: got unexpected interrupt %x\n",
device_xname(sc->sc_ad.sc_dev), status);
#endif
*sc->sc_boardp = TOCC_ACT;
mutex_spin_exit(&sc->sc_intr_lock);
return 1;
}
int
toccata_readreg(struct ad1848_softc *asc, int offset)
{
struct toccata_softc *sc;
sc = (struct toccata_softc *)asc;
return *(sc->sc_boardp + TOCC_CODEC_ADDR +
offset * (TOCC_CODEC_REG - TOCC_CODEC_ADDR));
}
void
toccata_writereg(struct ad1848_softc *asc, int offset, int value)
{
struct toccata_softc *sc;
sc = (struct toccata_softc *)asc;
*(sc->sc_boardp + TOCC_CODEC_ADDR +
offset * (TOCC_CODEC_REG - TOCC_CODEC_ADDR)) = value;
}
int
toccata_open(void *addr, int flags)
{
struct toccata_softc *sc;
struct ad1848_softc *asc;
sc = addr;
asc = &sc->sc_ad;
asc->open_mode = flags;
if (flags & FREAD && asc->mute[AD1848_MONITOR_CHANNEL] == 0)
ad1848_mute_wave_output(asc, WAVE_UNMUTE1, 1);
#ifdef AUDIO_DEBUG
if (ad1848debug)
ad1848_dump_regs(asc);
#endif
return 0;
}
void
toccata_close(void *addr)
{
struct toccata_softc *sc;
struct ad1848_softc *asc;
unsigned reg;
sc = addr;
asc = &sc->sc_ad;
asc->open_mode = 0;
ad1848_mute_wave_output(asc, WAVE_UNMUTE1, 0);
reg = ad_read(&sc->sc_ad, SP_INTERFACE_CONFIG);
ad_write(&sc->sc_ad, SP_INTERFACE_CONFIG,
(reg & ~(CAPTURE_ENABLE|PLAYBACK_ENABLE)));
*sc->sc_boardp = TOCC_ACT;
#ifdef AUDIO_DEBUG
if (ad1848debug)
ad1848_dump_regs(asc);
#endif
}
int
toccata_round_blocksize(void *addr, int blk,
int mode, const audio_params_t *param)
{
if (blk > 512)
blk = 512;
return blk;
}
size_t
toccata_round_buffersize(void *addr, int direction, size_t suggested)
{
return suggested & -4;
}
struct audio_device toccata_device = {
"toccata", "x", "audio"
};
int
toccata_getdev(void *addr, struct audio_device *retp)
{
*retp = toccata_device;
return 0;
}
int
toccata_get_props(void *addr)
{
return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE;
}
void
toccata_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
{
struct toccata_softc *sc = opaque;
*intr = &sc->sc_intr_lock;
*thread = &sc->sc_lock;
}
int
toccata_halt_input(void *addr)
{
struct toccata_softc *sc;
unsigned reg;
sc = addr;
*sc->sc_boardp = TOCC_CP_TAIL;
sc->sc_captmore = 0;
sc->sc_captbuf = 0;
reg = ad_read(&sc->sc_ad, SP_INTERFACE_CONFIG);
ad_write(&sc->sc_ad, SP_INTERFACE_CONFIG, (reg & ~CAPTURE_ENABLE));
return 0;
}
int
toccata_start_input(void *addr, void *block, int blksize,
void (*intr)(void *), void *intrarg)
{
struct toccata_softc *sc;
unsigned int reg;
volatile uint8_t *cmd;
sc = addr;
cmd = sc->sc_boardp;
if (sc->sc_captmore == 0) {
*cmd = TOCC_ACT;
*cmd = TOCC_CP_MAIN;
reg = ad_read(&sc->sc_ad, SP_INTERFACE_CONFIG);
ad_write(&sc->sc_ad, SP_INTERFACE_CONFIG,
(reg | CAPTURE_ENABLE));
}
sc->sc_captarg = intrarg;
sc->sc_captmore = intr;
sc->sc_captbuf = (uint8_t *)block;
sc->sc_captbufsz = blksize;
return 0;
}
int
toccata_halt_output(void *addr)
{
struct toccata_softc *sc;
unsigned int reg;
sc = addr;
*sc->sc_boardp = TOCC_PB_TAIL;
sc->sc_playmore = 0;
reg = ad_read(&sc->sc_ad, SP_INTERFACE_CONFIG);
ad_write(&sc->sc_ad, SP_INTERFACE_CONFIG, (reg & ~PLAYBACK_ENABLE));
return 0;
}
int
toccata_start_output(void *addr, void *block, int blksize,
void (*intr)(void*), void *intrarg)
{
struct toccata_softc *sc;
unsigned reg;
int i;
volatile uint8_t *cmd, *fifo;
uint8_t *buf;
sc = addr;
buf = block;
cmd = sc->sc_boardp;
fifo = sc->sc_boardp + TOCC_FIFO_DATA;
if (sc->sc_playmore == 0) {
*cmd = TOCC_ACT;
*cmd = TOCC_PB_PREP;
}
for (i = blksize/4 - 1; i>=0; --i) {
*fifo = *buf++;
*fifo = *buf++;
*fifo = *buf++;
*fifo = *buf++;
}
if (sc->sc_playmore == 0) {
reg = ad_read(&sc->sc_ad, SP_INTERFACE_CONFIG);
ad_write(&sc->sc_ad, SP_INTERFACE_CONFIG,
(reg | PLAYBACK_ENABLE));
*sc->sc_boardp = TOCC_PB_MAIN;
}
sc->sc_playarg = intrarg;
sc->sc_playmore = intr;
return 0;
}
static ad1848_devmap_t csmapping[] = {
{ TOCCATA_MIC_IN_LVL, AD1848_KIND_MICGAIN, -1 },
{ TOCCATA_AUX1_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
{ TOCCATA_AUX1_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
{ TOCCATA_AUX2_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
{ TOCCATA_AUX2_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
{ TOCCATA_OUTPUT_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL },
{ TOCCATA_MONITOR_LVL, AD1848_KIND_LVL, AD1848_MONITOR_CHANNEL },
{ TOCCATA_MONITOR_MUTE, AD1848_KIND_MUTE, AD1848_MONITOR_CHANNEL },
{ TOCCATA_REC_LVL, AD1848_KIND_RECORDGAIN, -1 },
{ TOCCATA_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1 },
{ TOCCATA_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_LINE_CHANNEL },
{ TOCCATA_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_LINE_CHANNEL },
};
#define nummap (sizeof(csmapping) / sizeof(csmapping[0]))
int
toccata_set_port(void *addr, mixer_ctrl_t *cp)
{
struct ad1848_softc *ac;
ac = addr;
return ad1848_mixer_set_port(ac, csmapping,
ac->mode == 2 ? nummap : nummap - 2, cp);
}
int
toccata_get_port(void *addr, mixer_ctrl_t *cp)
{
struct ad1848_softc *ac;
ac = addr;
return ad1848_mixer_get_port(ac, csmapping,
ac->mode == 2 ? nummap : nummap - 2, cp);
}
int
toccata_query_devinfo(void *addr, mixer_devinfo_t *dip)
{
switch(dip->index) {
case TOCCATA_MIC_IN_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->prev = dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNmicrophone);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
#if 0
case TOCCATA_MONO_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = TOCCATA_MONO_MUTE;
strcpy(dip->label.name, AudioNmicrophone);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
#endif
case TOCCATA_AUX1_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = TOCCATA_AUX1_MUTE;
strcpy(dip->label.name, "aux1");
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case TOCCATA_AUX1_MUTE:
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = TOCCATA_AUX1_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case TOCCATA_AUX2_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = TOCCATA_AUX2_MUTE;
strcpy(dip->label.name, "aux2");
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case TOCCATA_AUX2_MUTE:
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = TOCCATA_AUX2_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
case TOCCATA_MONITOR_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_MONITOR_CLASS;
dip->next = TOCCATA_MONITOR_MUTE;
dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNmonitor);
dip->un.v.num_channels = 1;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case TOCCATA_OUTPUT_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_OUTPUT_CLASS;
dip->prev = dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNmaster);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
#if 0
case TOCCATA_LINE_IN_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = TOCCATA_LINE_IN_MUTE;
strcpy(dip->label.name, AudioNline);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case TOCCATA_LINE_IN_MUTE:
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = TOCCATA_LINE_IN_LVL;
dip->next = AUDIO_MIXER_LAST;
goto mute;
#endif
case TOCCATA_MONITOR_MUTE:
dip->mixer_class = TOCCATA_MONITOR_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = TOCCATA_MONITOR_LVL;
dip->next = AUDIO_MIXER_LAST;
mute:
strcpy(dip->label.name, AudioNmute);
dip->un.e.num_mem = 2;
strcpy(dip->un.e.member[0].label.name, AudioNoff);
dip->un.e.member[0].ord = 0;
strcpy(dip->un.e.member[1].label.name, AudioNon);
dip->un.e.member[1].ord = 1;
break;
case TOCCATA_REC_LVL:
dip->type = AUDIO_MIXER_VALUE;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->prev = AUDIO_MIXER_LAST;
dip->next = TOCCATA_RECORD_SOURCE;
strcpy(dip->label.name, AudioNrecord);
dip->un.v.num_channels = 2;
strcpy(dip->un.v.units.name, AudioNvolume);
break;
case TOCCATA_RECORD_SOURCE:
dip->mixer_class = TOCCATA_RECORD_CLASS;
dip->type = AUDIO_MIXER_ENUM;
dip->prev = TOCCATA_REC_LVL;
dip->next = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioNsource);
dip->un.e.num_mem = 4;
strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
dip->un.e.member[1].ord = MIC_IN_PORT;
strcpy(dip->un.e.member[1].label.name, AudioNline);
dip->un.e.member[3].ord = LINE_IN_PORT;
strcpy(dip->un.e.member[2].label.name, "aux1");
dip->un.e.member[2].ord = AUX1_IN_PORT;
strcpy(dip->un.e.member[3].label.name, AudioNoutput);
dip->un.e.member[0].ord = DAC_IN_PORT;
break;
case TOCCATA_INPUT_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = TOCCATA_INPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCinputs);
break;
case TOCCATA_OUTPUT_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = TOCCATA_OUTPUT_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCoutputs);
break;
case TOCCATA_MONITOR_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = TOCCATA_MONITOR_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCmonitor);
break;
case TOCCATA_RECORD_CLASS:
dip->type = AUDIO_MIXER_CLASS;
dip->mixer_class = TOCCATA_RECORD_CLASS;
dip->next = dip->prev = AUDIO_MIXER_LAST;
strcpy(dip->label.name, AudioCrecord);
break;
default:
return ENXIO;
}
return 0;
}