#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/mutex.h>
#include <sys/bus.h>
#include <sys/intr.h>
#include <sys/audioio.h>
#include <dev/audio/audio_if.h>
#include <dev/audio/mulaw.h>
#include <dev/ic/arcofivar.h>
#include "ioconf.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
#ifdef hp300
#define arcofi_read(sc, r) \
bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
#define arcofi_write(sc, r, v) \
bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
#else
#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))
#endif
static int arcofi_cmd(struct arcofi_softc *, uint8_t, const uint8_t *);
static int arcofi_cr3_to_portmask(uint, int);
static int arcofi_gain_to_mi(uint);
static uint arcofi_mi_to_gain(int);
static uint arcofi_portmask_to_cr3(int);
static int arcofi_recv_data(struct arcofi_softc *);
static int arcofi_xmit_data(struct arcofi_softc *);
static int arcofi_open(void *, int);
static int arcofi_query_format(void *, audio_format_query_t *);
static int arcofi_set_format(void *, int,
const audio_params_t *, const audio_params_t *,
audio_filter_reg_t *, audio_filter_reg_t *);
static int arcofi_round_blocksize(void *, int, int,
const audio_params_t *);
static int arcofi_commit_settings(void *);
static int arcofi_start_output(void *, void *, int, void (*)(void *),
void *);
static int arcofi_start_input(void *, void *, int, void (*)(void *),
void *);
static int arcofi_halt_output(void *);
static int arcofi_halt_input(void *);
static int arcofi_getdev(void *, struct audio_device *);
static int arcofi_set_port(void *, mixer_ctrl_t *);
static int arcofi_get_port(void *, mixer_ctrl_t *);
static int arcofi_query_devinfo(void *, mixer_devinfo_t *);
static int arcofi_get_props(void *);
static void arcofi_get_locks(void *, kmutex_t **, kmutex_t **);
static const struct audio_hw_if arcofi_hw_if = {
.open = arcofi_open,
.query_format = arcofi_query_format,
.set_format = arcofi_set_format,
.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,
.speaker_ctl = NULL,
.getdev = arcofi_getdev,
.set_port = arcofi_set_port,
.get_port = arcofi_get_port,
.query_devinfo = arcofi_query_devinfo,
.allocm = NULL,
.freem = NULL,
.round_buffersize = NULL,
.get_props = arcofi_get_props,
.trigger_output = NULL,
.trigger_input = NULL,
.dev_ioctl = NULL,
.get_locks = arcofi_get_locks,
};
#define ARCOFI_FORMAT(prio, enc, prec) \
{ \
.mode = AUMODE_PLAY | AUMODE_RECORD, \
.priority = (prio), \
.encoding = (enc), \
.validbits = (prec), \
.precision = (prec), \
.channels = 1, \
.channel_mask = AUFMT_MONAURAL, \
.frequency_type = 1, \
.frequency = { 8000 }, \
}
static const struct audio_format arcofi_formats[] = {
ARCOFI_FORMAT(1, AUDIO_ENCODING_ULAW, 8),
ARCOFI_FORMAT(0, AUDIO_ENCODING_ALAW, 8),
ARCOFI_FORMAT(0, AUDIO_ENCODING_SLINEAR_BE, 16),
};
#define ARCOFI_NFORMATS __arraycount(arcofi_formats)
#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
};
static int
arcofi_open(void *v, int flags)
{
struct arcofi_softc *sc __diagused = (struct arcofi_softc *)v;
KASSERT(sc->sc_mode == 0);
return 0;
}
static int
arcofi_query_format(void *v, audio_format_query_t *afp)
{
return audio_query_format(arcofi_formats, ARCOFI_NFORMATS, afp);
}
static int
arcofi_set_format(void *handle, int setmode,
const audio_params_t *play, const audio_params_t *rec,
audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
{
struct arcofi_softc *sc;
sc = handle;
if ((setmode & AUMODE_PLAY)) {
switch (play->encoding) {
case AUDIO_ENCODING_ULAW:
pfil->codec = audio_internal_to_mulaw;
break;
case AUDIO_ENCODING_ALAW:
pfil->codec = audio_internal_to_alaw;
break;
}
}
if ((setmode & AUMODE_RECORD)) {
switch (rec->encoding) {
case AUDIO_ENCODING_ULAW:
rfil->codec = audio_mulaw_to_internal;
break;
case AUDIO_ENCODING_ALAW:
rfil->codec = audio_alaw_to_internal;
break;
}
}
if (play->precision == 8) {
if (play->encoding == AUDIO_ENCODING_ULAW)
sc->sc_shadow.cr4 |= CR4_ULAW;
else
sc->sc_shadow.cr4 &= ~CR4_ULAW;
sc->sc_shadow.cr3 =
(sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
CR3_OPMODE_NORMAL;
} else {
sc->sc_shadow.cr3 =
(sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
CR3_OPMODE_LINEAR;
}
return 0;
}
static int
arcofi_round_blocksize(void *handle, int block, int mode,
const audio_params_t *param)
{
return roundup(block, ARCOFI_FIFO_SIZE / 2);
}
static 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: %s, gr %04x gx %04x cr3 %02x cr4 %02x mute %d\n",
device_xname(sc->sc_dev), __func__,
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 (memcmp(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active)) == 0)
return 0;
mutex_spin_enter(&sc->sc_intr_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:
mutex_spin_exit(&sc->sc_intr_lock);
return rc;
}
static int
arcofi_recv_data(struct arcofi_softc *sc)
{
uint8_t *cur;
uint8_t *past;
cur = sc->sc_recv.buf;
past = sc->sc_recv.past;
while (cur != past &&
(arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_IN_EMPTY) == 0) {
*cur++ = arcofi_read(sc, ARCOFI_FIFO_DATA);
}
sc->sc_recv.buf = cur;
return past - cur;
}
static int
arcofi_xmit_data(struct arcofi_softc *sc)
{
uint8_t *cur;
uint8_t *past;
cur = sc->sc_xmit.buf;
past = sc->sc_xmit.past;
while (cur != past &&
(arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_OUT_FULL) == 0) {
arcofi_write(sc, ARCOFI_FIFO_DATA, *cur++);
}
sc->sc_xmit.buf = cur;
return past - cur;
}
static 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: %s, mode %d\n",
device_xname(sc->sc_dev), __func__, 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;
}
static 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: %s, mode %d\n",
device_xname(sc->sc_dev), __func__, 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_xmit_data(sc);
arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
return 0;
}
static int
arcofi_halt_input(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
#ifdef ARCOFI_DEBUG
printf("%s: %s, mode %d\n",
device_xname(sc->sc_dev), __func__, sc->sc_mode);
#endif
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);
return 0;
}
static int
arcofi_halt_output(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
#ifdef ARCOFI_DEBUG
printf("%s: %s, mode %d\n",
device_xname(sc->sc_dev), __func__, sc->sc_mode);
#endif
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);
return 0;
}
static int
arcofi_getdev(void *v, struct audio_device *ad)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
*ad = sc->sc_audio_device;
return 0;
}
static int
arcofi_gain_to_mi(uint idx)
{
if (idx == 0)
return AUDIO_MIN_GAIN;
if (idx == __arraycount(arcofi_gains) - 1)
return AUDIO_MAX_GAIN;
return ((idx - 1) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) /
(__arraycount(arcofi_gains) - 1) + AUDIO_MIN_GAIN + 1;
}
static uint
arcofi_mi_to_gain(int lvl)
{
if (lvl <= AUDIO_MIN_GAIN)
return 0;
if (lvl >= AUDIO_MAX_GAIN)
return __arraycount(arcofi_gains) - 1;
return ((lvl - AUDIO_MIN_GAIN - 1) * (__arraycount(arcofi_gains) - 1)) /
(AUDIO_MAX_GAIN - AUDIO_MIN_GAIN);
}
#if (AUDIO_SPEAKER == AUDIO_LINE_IN) || (AUDIO_LINE_OUT == AUDIO_LINE_IN) || \
(AUDIO_SPEAKER == AUDIO_LINE_OUT)
#error Please rework the cr3 handling logic.
#endif
static 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;
}
}
static 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;
}
}
static int
arcofi_set_port(void *v, mixer_ctrl_t *mc)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int portmask = 0;
#ifdef ARCOFI_DEBUG
printf("%s: %s, mode %d\n",
device_xname(sc->sc_dev), __func__, sc->sc_mode);
#endif
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: %s cr3 %02x -> mask %02x\n",
device_xname(sc->sc_dev), __func__,
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: %s mask %02x -> cr3 %02x m %d\n",
device_xname(sc->sc_dev), __func__,
portmask, sc->sc_shadow.cr3, sc->sc_shadow.output_mute);
#endif
return 0;
}
static int
arcofi_get_port(void *v, mixer_ctrl_t *mc)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
int portmask = 0;
#ifdef ARCOFI_DEBUG
printf("%s: %s, mode %d\n",
device_xname(sc->sc_dev), __func__, sc->sc_mode);
#endif
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: %s cr3 %02x -> mask %02x\n",
device_xname(sc->sc_dev), __func__,
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;
}
static 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;
}
static int
arcofi_get_props(void *v)
{
return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE;
}
static void
arcofi_get_locks(void *v, kmutex_t **intr, kmutex_t **thread)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
*intr = &sc->sc_intr_lock;
*thread = &sc->sc_lock;
}
int
arcofi_hwintr(void *v)
{
struct arcofi_softc *sc = (struct arcofi_softc *)v;
uint8_t csr, fir;
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 ((sc->sc_mode & AUMODE_RECORD) &&
(fir & FIFO_IR_EVENT(FIFO_IR_IN_HALF_EMPTY))) {
rc = 1;
if (arcofi_recv_data(sc) == 0) {
arcofi_write(sc, ARCOFI_FIFO_IR,
arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
sc->sc_recv.cb(sc->sc_recv.cbarg);
}
}
if ((sc->sc_mode & AUMODE_PLAY) &&
(fir & FIFO_IR_EVENT(FIFO_IR_OUT_HALF_EMPTY))) {
rc = 1;
if (arcofi_xmit_data(sc) == 0) {
arcofi_write(sc, ARCOFI_FIFO_IR,
arcofi_read(sc, ARCOFI_FIFO_IR) &
~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
sc->sc_xmit.cb(sc->sc_xmit.cbarg);
}
}
#ifdef ARCOFI_DEBUG
if (rc == 0)
printf("%s: unclaimed interrupt, csr %02x fir %02x fsr %02x\n",
device_xname(sc->sc_dev), csr, fir,
arcofi_read(sc, ARCOFI_FIFO_SR));
#endif
return rc;
}
static 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 >= __arraycount(cmdlen))
return EINVAL;
len = cmdlen[cmd];
KASSERT(cold || mutex_owned(&sc->sc_intr_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) {
return EBUSY;
}
delay(10);
}
arcofi_write(sc, ARCOFI_CSR, csr);
return 0;
}
void
arcofi_attach(struct arcofi_softc *sc, const char *versionstr)
{
device_t self;
int rc;
uint8_t op, cmd[4];
self = sc->sc_dev;
mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
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;
memcpy(&sc->sc_shadow, &sc->sc_active, sizeof(sc->sc_active));
op = SOP_4;
cmd[0] = CR1_IDR;
if ((rc = arcofi_cmd(sc, op, cmd)) != 0)
goto error;
delay(1000);
op = COP_2;
cmd[0] = arcofi_gains[sc->sc_active.gr_idx] >> 8;
cmd[1] = arcofi_gains[sc->sc_active.gr_idx];
if ((rc = arcofi_cmd(sc, op, cmd)) != 0)
goto error;
op = COP_B;
if ((rc = arcofi_cmd(sc, op, cmd)) != 0)
goto error;
op = SOP_0;
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, op, cmd)) != 0)
goto error;
arcofi_write(sc, ARCOFI_FIFO_IR, 0);
arcofi_write(sc, ARCOFI_CSR, CSR_INTR_ENABLE);
strlcpy(sc->sc_audio_device.name, arcofi_cd.cd_name,
sizeof(sc->sc_audio_device.name));
strlcpy(sc->sc_audio_device.version, versionstr,
sizeof(sc->sc_audio_device.version));
strlcpy(sc->sc_audio_device.config, device_xname(self),
sizeof(sc->sc_audio_device.config));
audio_attach_mi(&arcofi_hw_if, sc, self);
return;
error:
arcofi_write(sc, ARCOFI_ID, 0);
aprint_error("%s: %02x command failed, error %d\n",
__func__, op, rc);
mutex_destroy(&sc->sc_intr_lock);
mutex_destroy(&sc->sc_lock);
}