#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/endian.h>
#include <sys/fcntl.h>
#include <sys/audioio.h>
#include <dev/audio_if.h>
#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <dev/ic/arcofivar.h>
#if 0
#define ARCOFI_DEBUG
#endif
#define CMDR_AD 0x80
#define CMDR_READ 0x40
#define CMDR_WRITE 0x00
#define CMDR_PU 0x20
#define CMDR_RCS 0x10
#define CMDR_MASK 0x0f
#define SOP_0 0x00
#define COP_1 0x01
#define COP_2 0x02
#define COP_3 0x03
#define SOP_4 0x04
#define SOP_5 0x05
#define SOP_6 0x06
#define SOP_7 0x07
#define COP_8 0x08
#define COP_9 0x09
#define COP_A 0x0a
#define COP_B 0x0b
#define COP_C 0x0c
#define COP_D 0x0d
#define COP_E 0x0e
#define CR1_GR 0x80
#define CR1_GZ 0x40
#define CR1_FX 0x20
#define CR1_FR 0x10
#define CR1_GX 0x08
#define CR1_T_MASK 0x07
#define CR1_DLP 0x07
#define CR1_DLM 0x06
#define CR1_DLS 0x05
#define CR1_IDR 0x04
#define CR1_BYP 0x03
#define CR1_ALM 0x02
#define CR1_ALS 0x01
#define CR2_SD 0x80
#define CR2_SC 0x40
#define CR2_SB 0x20
#define CR2_SA 0x10
#define CR2_ELS 0x08
#define CR2_AM 0x04
#define CR2_TR 0x02
#define CR2_EFC 0x01
#define CR3_MIC_G_MASK 0xe0
#define CR3_MIC_X_INPUT 0xe0
#define CR3_MIC_G_17 0xc0
#define CR3_MIC_G_22 0xa0
#define CR3_MIC_G_28 0x80
#define CR3_MIC_G_34 0x60
#define CR3_MIC_G_40 0x40
#define CR3_MIC_G_46 0x20
#define CR3_MIC_G_52 0x00
#define CR3_AFEC_MASK 0x1c
#define CR3_AFEC_MUTE 0x18
#define CR3_AFEC_HFS 0x14
#define CR3_AFEC_LH3 0x10
#define CR3_AFEC_LH2 0x0c
#define CR3_AFEC_LH1 0x08
#define CR3_AFEC_RDY 0x04
#define CR3_AFEC_POR 0x00
#define CR3_OPMODE_MASK 0x03
#define CR3_OPMODE_LINEAR 0x02
#define CR3_OPMODE_MIXED 0x01
#define CR3_OPMODE_NORMAL 0x00
#define CR4_DHF 0x80
#define CR4_DTMF 0x40
#define CR4_TG 0x20
#define CR4_BT 0x10
#define CR4_TM 0x08
#define CR4_BM 0x04
#define CR4_PM 0x02
#define CR4_ULAW 0x01
#define ARCOFI_ID 0
#define ARCOFI_CSR 1
#define CSR_INTR_ENABLE 0x80
#define CSR_INTR_REQUEST 0x40
#define CSR_WIDTH_16 0x08
#define CSR_CTRL_FIFO_ENABLE 0x04
#define CSR_DATA_FIFO_ENABLE 0x01
#define ARCOFI_FIFO_IR 2
#define FIFO_IR_ENABLE(ev) ((ev) << 4)
#define FIFO_IR_EVENT(ev) (ev)
#define FIFO_IR_OUT_EMPTY 0x08
#define FIFO_IR_CTRL_EMPTY 0x04
#define FIFO_IR_OUT_HALF_EMPTY 0x02
#define FIFO_IR_IN_HALF_EMPTY 0x01
#define ARCOFI_FIFO_SR 3
#define FIFO_SR_CTRL_FULL 0x20
#define FIFO_SR_CTRL_EMPTY 0x10
#define FIFO_SR_OUT_FULL 0x08
#define FIFO_SR_OUT_EMPTY 0x04
#define FIFO_SR_IN_FULL 0x02
#define FIFO_SR_IN_EMPTY 0x01
#define ARCOFI_FIFO_DATA 4
#define ARCOFI_FIFO_CTRL 5
#define ARCOFI_FIFO_SIZE 128
struct cfdriver arcofi_cd = {
NULL, "arcofi", DV_DULL
};
#define arcofi_read(sc, r) \
bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r])
#define arcofi_write(sc, r, v) \
bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r], v)
int arcofi_cmd(struct arcofi_softc *, uint8_t, const uint8_t *);
int arcofi_cr3_to_portmask(uint, int);
int arcofi_gain_to_mi(uint);
uint arcofi_mi_to_gain(int);
uint arcofi_portmask_to_cr3(int);
int arcofi_set_param(struct arcofi_softc *, int, int, int,
struct audio_params *);
void arcofi_close(void *);
int arcofi_commit_settings(void *);
int arcofi_get_port(void *, mixer_ctrl_t *);
int arcofi_halt_input(void *);
int arcofi_halt_output(void *);
int arcofi_open(void *, int);
int arcofi_query_devinfo(void *, mixer_devinfo_t *);
int arcofi_round_blocksize(void *, int);
int arcofi_set_params(void *, int, int, struct audio_params *,
struct audio_params *);
int arcofi_set_port(void *, mixer_ctrl_t *);
int arcofi_start_input(void *, void *, int, void (*)(void *), void *);
int arcofi_start_output(void *, void *, int, void (*)(void *), void *);
const struct audio_hw_if arcofi_hw_if = {
.open = arcofi_open,
.close = arcofi_close,
.set_params = arcofi_set_params,
.round_blocksize = arcofi_round_blocksize,
.commit_settings = arcofi_commit_settings,
.start_output = arcofi_start_output,
.start_input = arcofi_start_input,
.halt_output = arcofi_halt_output,
.halt_input = arcofi_halt_input,
.set_port = arcofi_set_port,
.get_port = arcofi_get_port,
.query_devinfo = arcofi_query_devinfo,
};
#define ARCOFI_PORT_AUDIO_IN_VOLUME 0
#define ARCOFI_PORT_AUDIO_OUT_VOLUME 1
#define ARCOFI_PORT_AUDIO_SPKR_VOLUME 2
#define ARCOFI_PORT_AUDIO_IN_MUTE 3
#define ARCOFI_PORT_AUDIO_OUT_MUTE 4
#define ARCOFI_PORT_AUDIO_SPKR_MUTE 5
#define ARCOFI_CLASS_INPUT 6
#define ARCOFI_CLASS_OUTPUT 7
#define NEGATIVE_GAINS 60
#define POSITIVE_GAINS 14
static const uint16_t arcofi_gains[1 + NEGATIVE_GAINS + 1 + POSITIVE_GAINS] = {
0x0988,
0xf8b8, 0xf8b8, 0xf8b8, 0xf8b8, 0x099f, 0x099f, 0x099f, 0x099f,
0x09af, 0x09af, 0x09af, 0x09cf, 0x09cf, 0x09cf, 0xf8a9, 0xf83a,
0xf83a, 0xf82b, 0xf82d, 0xf8a3, 0xf8b2, 0xf8a1, 0xe8aa, 0xe84b,
0xe89e, 0xe8d3, 0xe891, 0xe8b1, 0xd8aa, 0xd8cb, 0xd8a6, 0xd8b3,
0xd842, 0xd8b1, 0xc8aa, 0xc8bb, 0xc888, 0xc853, 0xc852, 0xc8b1,
0xb8aa, 0xb8ab, 0xb896, 0xb892, 0xb842, 0xb8b1, 0xa8aa, 0xa8bb,
0x199f, 0x195b, 0x29c1, 0x2923, 0x29aa, 0x392b, 0xf998, 0xb988,
0x1aac, 0x3aa1, 0xbaa1, 0xbb88,
0x8888,
0xd388, 0x5288, 0xb1a1, 0x31a1, 0x1192, 0x11d0, 0x30c0, 0x2050,
0x1021, 0x1020, 0x1000, 0x0001, 0x0010, 0x0000
};
int
arcofi_open(void *v, int flags)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
return ENXIO;
if (sc->sc_open)
return EBUSY;
sc->sc_open = 1;
KASSERT(sc->sc_mode == 0);
return 0;
}
void
arcofi_close(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
arcofi_halt_input(v);
arcofi_halt_output(v);
sc->sc_open = 0;
}
int
arcofi_set_param(struct arcofi_softc *sc, int set, int use, int mode,
struct audio_params *ap)
{
if ((set & mode) == 0)
return 0;
#ifdef ARCOFI_DEBUG
printf("%s: set_param, mode %d encoding %d precision %d\n",
sc->sc_dev.dv_xname, mode, ap->encoding, ap->precision);
#endif
switch (ap->precision) {
case 8:
switch (ap->encoding) {
case AUDIO_ENCODING_ULAW:
sc->sc_shadow.cr4 |= CR4_ULAW;
break;
case AUDIO_ENCODING_ALAW:
sc->sc_shadow.cr4 &= ~CR4_ULAW;
break;
default:
return EINVAL;
}
sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
CR3_OPMODE_NORMAL;
break;
case 16:
switch (ap->encoding) {
case AUDIO_ENCODING_SLINEAR_BE:
break;
default:
return EINVAL;
}
sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
CR3_OPMODE_LINEAR;
break;
default:
return EINVAL;
}
ap->bps = AUDIO_BPS(ap->precision);
ap->msb = 1;
ap->channels = 1;
ap->sample_rate = 8000;
return 0;
}
int
arcofi_set_params(void *v, int set, int use, struct audio_params *p,
struct audio_params *r)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int rc;
if (r != NULL) {
rc = arcofi_set_param(sc, set, use, AUMODE_RECORD, r);
if (rc != 0)
return rc;
}
if (p != NULL) {
rc = arcofi_set_param(sc, set, use, AUMODE_PLAY, p);
if (rc != 0)
return rc;
}
return 0;
}
int
arcofi_round_blocksize(void *v, int blksz)
{
return roundup(blksz, ARCOFI_FIFO_SIZE / 2);
}
int
arcofi_commit_settings(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int rc;
uint8_t cmd[2], csr, ocsr;
#ifdef ARCOFI_DEBUG
printf("%s: commit_settings, gr %04x gx %04x cr3 %02x cr4 %02x mute %d\n",
sc->sc_dev.dv_xname,
arcofi_gains[sc->sc_shadow.gr_idx],
arcofi_gains[sc->sc_shadow.gx_idx],
sc->sc_shadow.cr3, sc->sc_shadow.cr4, sc->sc_shadow.output_mute);
#endif
if (bcmp(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active)) == 0)
return 0;
mtx_enter(&audio_lock);
if (sc->sc_active.gr_idx != sc->sc_shadow.gr_idx) {
cmd[0] = arcofi_gains[sc->sc_shadow.gr_idx] >> 8;
cmd[1] = arcofi_gains[sc->sc_shadow.gr_idx];
if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0)
goto error;
sc->sc_active.gr_idx = sc->sc_shadow.gr_idx;
}
if (sc->sc_active.gx_idx != sc->sc_shadow.gx_idx ||
sc->sc_active.output_mute != sc->sc_shadow.output_mute) {
if (sc->sc_shadow.output_mute) {
cmd[0] = arcofi_gains[0] >> 8;
cmd[1] = arcofi_gains[0];
} else {
cmd[0] = arcofi_gains[sc->sc_shadow.gx_idx] >> 8;
cmd[1] = arcofi_gains[sc->sc_shadow.gx_idx];
}
if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0)
goto error;
sc->sc_active.gx_idx = sc->sc_shadow.gx_idx;
sc->sc_active.output_mute = sc->sc_shadow.output_mute;
}
if (sc->sc_active.cr3 != sc->sc_shadow.cr3) {
cmd[0] = sc->sc_shadow.cr3;
if ((rc = arcofi_cmd(sc, SOP_6, cmd)) != 0)
goto error;
sc->sc_active.cr3 = sc->sc_shadow.cr3;
ocsr = arcofi_read(sc, ARCOFI_CSR);
if ((sc->sc_active.cr3 & CR3_OPMODE_MASK) != CR3_OPMODE_NORMAL)
csr = ocsr | CSR_WIDTH_16;
else
csr = ocsr & ~CSR_WIDTH_16;
if (csr != ocsr)
arcofi_write(sc, ARCOFI_CSR, csr);
}
if (sc->sc_active.cr4 != sc->sc_shadow.cr4) {
cmd[0] = sc->sc_shadow.cr4;
if ((rc = arcofi_cmd(sc, SOP_7, cmd)) != 0)
goto error;
sc->sc_active.cr4 = sc->sc_shadow.cr4;
}
rc = 0;
error:
mtx_leave(&audio_lock);
return rc;
}
int
arcofi_start_input(void *v, void *rbuf, int rsz, void (*cb)(void *),
void *cbarg)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
#ifdef ARCOFI_DEBUG
printf("%s: start_input, mode %d\n",
sc->sc_dev.dv_xname, sc->sc_mode);
#endif
if (sc->sc_mode == 0)
arcofi_write(sc, ARCOFI_CSR,
arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE);
sc->sc_mode |= AUMODE_RECORD;
sc->sc_recv.buf = (uint8_t *)rbuf;
sc->sc_recv.past = (uint8_t *)rbuf + rsz;
sc->sc_recv.cb = cb;
sc->sc_recv.cbarg = cbarg;
arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
return 0;
}
int
arcofi_start_output(void *v, void *wbuf, int wsz, void (*cb)(void *),
void *cbarg)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
#ifdef ARCOFI_DEBUG
printf("%s: start_output, mode %d\n",
sc->sc_dev.dv_xname, sc->sc_mode);
#endif
if (sc->sc_mode == 0)
arcofi_write(sc, ARCOFI_CSR,
arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE);
sc->sc_mode |= AUMODE_PLAY;
sc->sc_xmit.buf = (uint8_t *)wbuf;
sc->sc_xmit.past = (uint8_t *)wbuf + wsz;
sc->sc_xmit.cb = cb;
sc->sc_xmit.cbarg = cbarg;
arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
return 0;
}
int
arcofi_halt_input(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
mtx_enter(&audio_lock);
arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
sc->sc_mode &= ~AUMODE_RECORD;
if (sc->sc_mode == 0)
arcofi_write(sc, ARCOFI_CSR,
arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE);
mtx_leave(&audio_lock);
return 0;
}
int
arcofi_halt_output(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
mtx_enter(&audio_lock);
arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
sc->sc_mode &= ~AUMODE_PLAY;
if (sc->sc_mode == 0)
arcofi_write(sc, ARCOFI_CSR,
arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE);
mtx_leave(&audio_lock);
return 0;
}
int
arcofi_gain_to_mi(uint idx)
{
if (idx == 0)
return AUDIO_MIN_GAIN;
if (idx == nitems(arcofi_gains) - 1)
return AUDIO_MAX_GAIN;
return ((idx - 1) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) /
(nitems(arcofi_gains) - 1) + AUDIO_MIN_GAIN + 1;
}
uint
arcofi_mi_to_gain(int lvl)
{
if (lvl <= AUDIO_MIN_GAIN)
return 0;
if (lvl >= AUDIO_MAX_GAIN)
return nitems(arcofi_gains) - 1;
return ((lvl - AUDIO_MIN_GAIN - 1) * (nitems(arcofi_gains) - 1)) /
(AUDIO_MAX_GAIN - AUDIO_MIN_GAIN);
}
#define AUDIO_SPEAKER 0x01
#define AUDIO_LINE_IN 0x02
#define AUDIO_LINE_OUT 0x04
uint
arcofi_portmask_to_cr3(int mask)
{
switch (mask) {
default:
case 0:
return CR3_MIC_G_17 | CR3_AFEC_POR;
case AUDIO_LINE_OUT:
return CR3_MIC_G_17 | CR3_AFEC_MUTE;
case AUDIO_SPEAKER:
return CR3_MIC_G_17 | CR3_AFEC_LH1;
case AUDIO_SPEAKER | AUDIO_LINE_OUT:
return CR3_MIC_X_INPUT | CR3_AFEC_LH3;
case AUDIO_LINE_IN:
case AUDIO_LINE_IN | AUDIO_LINE_OUT:
return CR3_MIC_G_17 | CR3_AFEC_RDY;
case AUDIO_LINE_IN | AUDIO_SPEAKER:
return CR3_MIC_G_17 | CR3_AFEC_LH2;
case AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT:
return CR3_MIC_G_17 | CR3_AFEC_LH3;
}
}
int
arcofi_cr3_to_portmask(uint cr3, int output_mute)
{
switch (cr3 & CR3_AFEC_MASK) {
default:
case CR3_AFEC_POR:
return 0;
case CR3_AFEC_RDY:
return output_mute ?
AUDIO_LINE_IN : AUDIO_LINE_IN | AUDIO_LINE_OUT;
case CR3_AFEC_HFS:
case CR3_AFEC_LH1:
return AUDIO_SPEAKER;
case CR3_AFEC_LH2:
return AUDIO_LINE_IN | AUDIO_SPEAKER;
case CR3_AFEC_LH3:
if ((cr3 & CR3_MIC_G_MASK) == CR3_MIC_X_INPUT)
return AUDIO_SPEAKER | AUDIO_LINE_OUT;
else
return AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT;
case CR3_AFEC_MUTE:
return AUDIO_LINE_OUT;
}
}
int
arcofi_set_port(void *v, mixer_ctrl_t *mc)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int portmask;
switch (mc->dev) {
case ARCOFI_PORT_AUDIO_IN_VOLUME:
case ARCOFI_PORT_AUDIO_OUT_VOLUME:
case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
if (mc->un.value.num_channels != 1)
return EINVAL;
break;
case ARCOFI_PORT_AUDIO_IN_MUTE:
case ARCOFI_PORT_AUDIO_OUT_MUTE:
case ARCOFI_PORT_AUDIO_SPKR_MUTE:
if (mc->type != AUDIO_MIXER_ENUM)
return EINVAL;
portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3,
sc->sc_shadow.output_mute);
#ifdef ARCOFI_DEBUG
printf("%s: set_port cr3 %02x -> mask %02x\n",
sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask);
#endif
break;
default:
return EINVAL;
}
switch (mc->dev) {
case ARCOFI_PORT_AUDIO_IN_VOLUME:
sc->sc_shadow.gr_idx =
arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
return 0;
case ARCOFI_PORT_AUDIO_OUT_VOLUME:
case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
sc->sc_shadow.gx_idx =
arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
return 0;
case ARCOFI_PORT_AUDIO_IN_MUTE:
if (mc->un.ord)
portmask &= ~AUDIO_LINE_IN;
else
portmask |= AUDIO_LINE_IN;
break;
case ARCOFI_PORT_AUDIO_OUT_MUTE:
if (mc->un.ord)
portmask &= ~AUDIO_LINE_OUT;
else
portmask |= AUDIO_LINE_OUT;
break;
case ARCOFI_PORT_AUDIO_SPKR_MUTE:
if (mc->un.ord)
portmask &= ~AUDIO_SPEAKER;
else
portmask |= AUDIO_SPEAKER;
break;
}
sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & CR3_OPMODE_MASK) |
arcofi_portmask_to_cr3(portmask);
sc->sc_shadow.output_mute = (portmask == AUDIO_LINE_IN);
#ifdef ARCOFI_DEBUG
printf("%s: set_port mask %02x -> cr3 %02x m %d\n",
sc->sc_dev.dv_xname, portmask,
sc->sc_shadow.cr3, sc->sc_shadow.output_mute);
#endif
return 0;
}
int
arcofi_get_port(void *v, mixer_ctrl_t *mc)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int portmask;
switch (mc->dev) {
case ARCOFI_PORT_AUDIO_IN_VOLUME:
case ARCOFI_PORT_AUDIO_OUT_VOLUME:
case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
if (mc->un.value.num_channels != 1)
return EINVAL;
break;
case ARCOFI_PORT_AUDIO_IN_MUTE:
case ARCOFI_PORT_AUDIO_OUT_MUTE:
case ARCOFI_PORT_AUDIO_SPKR_MUTE:
if (mc->type != AUDIO_MIXER_ENUM)
return EINVAL;
portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3,
sc->sc_shadow.output_mute);
#ifdef ARCOFI_DEBUG
printf("%s: get_port cr3 %02x -> mask %02x\n",
sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask);
#endif
break;
default:
return EINVAL;
}
switch (mc->dev) {
case ARCOFI_PORT_AUDIO_IN_VOLUME:
mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
arcofi_gain_to_mi(sc->sc_shadow.gr_idx);
break;
case ARCOFI_PORT_AUDIO_OUT_VOLUME:
case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
arcofi_gain_to_mi(sc->sc_shadow.gx_idx);
break;
case ARCOFI_PORT_AUDIO_IN_MUTE:
mc->un.ord = portmask & AUDIO_LINE_IN ? 0 : 1;
break;
case ARCOFI_PORT_AUDIO_OUT_MUTE:
mc->un.ord = portmask & AUDIO_LINE_OUT ? 0 : 1;
break;
case ARCOFI_PORT_AUDIO_SPKR_MUTE:
mc->un.ord = portmask & AUDIO_SPEAKER ? 0 : 1;
break;
}
return 0;
}
int
arcofi_query_devinfo(void *v, mixer_devinfo_t *md)
{
switch (md->index) {
default:
return ENXIO;
case ARCOFI_PORT_AUDIO_IN_VOLUME:
md->type = AUDIO_MIXER_VALUE;
md->mixer_class = ARCOFI_CLASS_INPUT;
md->prev = AUDIO_MIXER_LAST;
md->next = ARCOFI_PORT_AUDIO_IN_MUTE;
strlcpy(md->label.name, AudioNline,
sizeof md->label.name);
goto mono_volume;
case ARCOFI_PORT_AUDIO_OUT_VOLUME:
md->type = AUDIO_MIXER_VALUE;
md->mixer_class = ARCOFI_CLASS_OUTPUT;
md->prev = AUDIO_MIXER_LAST;
md->next = ARCOFI_PORT_AUDIO_OUT_MUTE;
strlcpy(md->label.name, AudioNline,
sizeof md->label.name);
goto mono_volume;
case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
md->type = AUDIO_MIXER_VALUE;
md->mixer_class = ARCOFI_CLASS_OUTPUT;
md->prev = AUDIO_MIXER_LAST;
md->next = ARCOFI_PORT_AUDIO_SPKR_MUTE;
strlcpy(md->label.name, AudioNspeaker,
sizeof md->label.name);
mono_volume:
md->un.v.num_channels = 1;
strlcpy(md->un.v.units.name, AudioNvolume,
sizeof md->un.v.units.name);
break;
case ARCOFI_PORT_AUDIO_IN_MUTE:
md->type = AUDIO_MIXER_ENUM;
md->mixer_class = ARCOFI_CLASS_INPUT;
md->prev = ARCOFI_PORT_AUDIO_IN_VOLUME;
md->next = AUDIO_MIXER_LAST;
goto mute;
case ARCOFI_PORT_AUDIO_OUT_MUTE:
md->type = AUDIO_MIXER_ENUM;
md->mixer_class = ARCOFI_CLASS_OUTPUT;
md->prev = ARCOFI_PORT_AUDIO_OUT_VOLUME;
md->next = AUDIO_MIXER_LAST;
goto mute;
case ARCOFI_PORT_AUDIO_SPKR_MUTE:
md->type = AUDIO_MIXER_ENUM;
md->mixer_class = ARCOFI_CLASS_OUTPUT;
md->prev = ARCOFI_PORT_AUDIO_SPKR_VOLUME;
md->next = AUDIO_MIXER_LAST;
mute:
strlcpy(md->label.name, AudioNmute, sizeof md->label.name);
md->un.e.num_mem = 2;
strlcpy(md->un.e.member[0].label.name, AudioNoff,
sizeof md->un.e.member[0].label.name);
md->un.e.member[0].ord = 0;
strlcpy(md->un.e.member[1].label.name, AudioNon,
sizeof md->un.e.member[1].label.name);
md->un.e.member[1].ord = 1;
break;
case ARCOFI_CLASS_INPUT:
md->type = AUDIO_MIXER_CLASS;
md->mixer_class = ARCOFI_CLASS_INPUT;
md->prev = AUDIO_MIXER_LAST;
md->next = AUDIO_MIXER_LAST;
strlcpy(md->label.name, AudioCinputs,
sizeof md->label.name);
break;
case ARCOFI_CLASS_OUTPUT:
md->type = AUDIO_MIXER_CLASS;
md->mixer_class = ARCOFI_CLASS_OUTPUT;
md->prev = AUDIO_MIXER_LAST;
md->next = AUDIO_MIXER_LAST;
strlcpy(md->label.name, AudioCoutputs,
sizeof md->label.name);
break;
}
return 0;
}
int
arcofi_hwintr(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
uint8_t *cur, *past;
uint8_t csr, fir, data;
int rc = 0;
csr = arcofi_read(sc, ARCOFI_CSR);
if ((csr & CSR_INTR_REQUEST) == 0)
return 0;
fir = arcofi_read(sc, ARCOFI_FIFO_IR);
if (fir & FIFO_IR_EVENT(FIFO_IR_IN_HALF_EMPTY)) {
rc = 1;
cur = sc->sc_recv.buf;
past = sc->sc_recv.past;
while ((arcofi_read(sc, ARCOFI_FIFO_SR) &
FIFO_SR_IN_EMPTY) == 0) {
data = arcofi_read(sc, ARCOFI_FIFO_DATA);
if (cur != NULL && cur != past) {
*cur++ = data;
if (cur == past) {
softintr_schedule(sc->sc_sih);
break;
}
}
}
sc->sc_recv.buf = cur;
if (cur == NULL || cur == past) {
arcofi_write(sc, ARCOFI_FIFO_IR,
arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
}
}
if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_HALF_EMPTY)) {
rc = 1;
cur = sc->sc_xmit.buf;
past = sc->sc_xmit.past;
if (cur != NULL) {
while ((arcofi_read(sc, ARCOFI_FIFO_SR) &
FIFO_SR_OUT_FULL) == 0) {
if (cur != past)
arcofi_write(sc, ARCOFI_FIFO_DATA,
*cur++);
if (cur == past) {
softintr_schedule(sc->sc_sih);
break;
}
}
}
if (cur == NULL || cur == past) {
arcofi_write(sc, ARCOFI_FIFO_IR,
arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
}
sc->sc_xmit.buf = cur;
}
if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_EMPTY)) {
rc = 1;
arcofi_write(sc, ARCOFI_FIFO_IR,
arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_OUT_EMPTY));
wakeup(&sc->sc_xmit);
}
#ifdef ARCOFI_DEBUG
if (rc == 0)
printf("%s: unclaimed interrupt, csr %02x fir %02x fsr %02x\n",
sc->sc_dev.dv_xname, csr, fir,
arcofi_read(sc, ARCOFI_FIFO_SR));
#endif
return rc;
}
void
arcofi_swintr(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int action;
action = 0;
mtx_enter(&audio_lock);
if (sc->sc_recv.buf != NULL && sc->sc_recv.buf == sc->sc_recv.past)
action |= AUMODE_RECORD;
if (sc->sc_xmit.buf != NULL && sc->sc_xmit.buf == sc->sc_xmit.past)
action |= AUMODE_PLAY;
if (action & AUMODE_RECORD) {
if (sc->sc_recv.cb)
sc->sc_recv.cb(sc->sc_recv.cbarg);
}
if (action & AUMODE_PLAY) {
if (sc->sc_xmit.cb)
sc->sc_xmit.cb(sc->sc_xmit.cbarg);
}
mtx_leave(&audio_lock);
}
int
arcofi_cmd(struct arcofi_softc *sc, uint8_t cmd, const uint8_t *data)
{
size_t len;
uint8_t csr;
int cnt;
static const uint8_t cmdlen[] = {
[SOP_0] = 4,
[COP_1] = 4,
[COP_2] = 2,
[COP_3] = 2,
[SOP_4] = 1,
[SOP_5] = 1,
[SOP_6] = 1,
[SOP_7] = 1,
[COP_8] = 2,
[COP_9] = 4,
[COP_A] = 8,
[COP_B] = 2,
[COP_C] = 8,
[COP_D] = 4,
[COP_E] = 4
};
if (cmd >= nitems(cmdlen))
return EINVAL;
len = cmdlen[cmd];
mtx_enter(&audio_lock);
csr = arcofi_read(sc, ARCOFI_CSR);
arcofi_write(sc, ARCOFI_CSR,
csr & ~(CSR_DATA_FIFO_ENABLE | CSR_CTRL_FIFO_ENABLE));
arcofi_write(sc, ARCOFI_FIFO_CTRL, CMDR_PU | CMDR_WRITE | cmd);
for (; len != 0; len--)
arcofi_write(sc, ARCOFI_FIFO_CTRL, *data++);
arcofi_write(sc, ARCOFI_CSR,
(csr & ~CSR_DATA_FIFO_ENABLE) | CSR_CTRL_FIFO_ENABLE);
cnt = 100;
while ((arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_CTRL_EMPTY) == 0) {
if (cnt-- == 0) {
mtx_leave(&audio_lock);
return EBUSY;
}
delay(10);
}
arcofi_write(sc, ARCOFI_CSR, csr);
mtx_leave(&audio_lock);
return 0;
}
void
arcofi_attach(struct arcofi_softc *sc, const char *version)
{
int rc;
uint8_t cmd[4];
arcofi_write(sc, ARCOFI_ID, 0);
delay(100000);
arcofi_write(sc, ARCOFI_CSR, 0);
sc->sc_active.cr3 =
arcofi_portmask_to_cr3(AUDIO_SPEAKER) | CR3_OPMODE_NORMAL;
sc->sc_active.cr4 = CR4_TM | CR4_ULAW;
sc->sc_active.gr_idx = sc->sc_active.gx_idx = 1 + NEGATIVE_GAINS;
sc->sc_active.output_mute = 0;
bcopy(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active));
cmd[0] = CR1_IDR;
if ((rc = arcofi_cmd(sc, SOP_4, cmd)) != 0)
goto error;
delay(1000);
cmd[0] = arcofi_gains[sc->sc_active.gr_idx] >> 8;
cmd[1] = arcofi_gains[sc->sc_active.gr_idx];
if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0)
goto error;
if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0)
goto error;
cmd[0] = sc->sc_active.cr4;
cmd[1] = sc->sc_active.cr3;
cmd[2] = CR2_SD | CR2_SC | CR2_SB | CR2_SA | CR2_ELS | CR2_AM | CR2_EFC;
cmd[3] = CR1_GR | CR1_GX;
if ((rc = arcofi_cmd(sc, SOP_0, cmd)) != 0)
goto error;
arcofi_write(sc, ARCOFI_FIFO_IR, 0);
arcofi_write(sc, ARCOFI_CSR, CSR_INTR_ENABLE);
audio_attach_mi(&arcofi_hw_if, sc, NULL, &sc->sc_dev);
return;
error:
arcofi_write(sc, ARCOFI_ID, 0);
printf("%s: command failed, error %d\n", __func__, rc);
}