Last active
August 29, 2015 14:05
-
-
Save tsutsui/f1dec4c1a6dbf0417a4b to your computer and use it in GitHub Desktop.
arcofi(4) audio driver for NetBSD/hp300 ported from OpenBSD (updated)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
? dev/ic/arcofi.c | |
? dev/ic/arcofivar.h | |
Index: conf/files | |
=================================================================== | |
RCS file: /cvsroot/src/sys/conf/files,v | |
retrieving revision 1.1097 | |
diff -u -p -d -r1.1097 files | |
--- conf/files 16 Aug 2014 17:24:28 -0000 1.1097 | |
+++ conf/files 17 Aug 2014 09:13:06 -0000 | |
@@ -912,6 +912,10 @@ file dev/ic/ad1848.c ad1848 | |
define am7930 | |
file dev/ic/am7930.c am7930 | |
+# Siemens PSB2160 audio codec, as found in HP systems | |
+device arcofi: audiobus, auconv, mulaw | |
+file dev/ic/arcofi.c arcofi | |
+ | |
# SPARC `SUNW,audiocs' | |
# | |
device audiocs: audiobus, auconv, ad1848 | |
--- /dev/null 2014-08-18 16:26:40.000000000 +0000 | |
+++ dev/ic/arcofi.c 2014-08-18 16:31:23.000000000 +0000 | |
@@ -0,0 +1,1274 @@ | |
+/* $NetBSD$ */ | |
+/* $OpenBSD: arcofi.c,v 1.6 2013/05/15 08:29:24 ratchov Exp $ */ | |
+ | |
+/* | |
+ * Copyright (c) 2011 Miodrag Vallat. | |
+ * | |
+ * Permission to use, copy, modify, and distribute this software for any | |
+ * purpose with or without fee is hereby granted, provided that the above | |
+ * copyright notice and this permission notice appear in all copies. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
+ */ | |
+ | |
+/* | |
+ * Driver for the HP ``Audio1'' device, which is a FIFO layer around a | |
+ * Siemens PSB 2160 ``ARCOFI'' phone quality audio chip. | |
+ * | |
+ * It is known to exist in two flavours: on-board the HP9000/425e as a DIO | |
+ * device, an on-board the HP9000/{705,710,745,747} as a GIO device. | |
+ * | |
+ * The FIFO logic buffers up to 128 bytes. When using 8 bit samples and | |
+ * the logic set to interrupt every half FIFO, the device will interrupt | |
+ * 125 times per second. | |
+ */ | |
+ | |
+#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/condvar.h> | |
+#include <sys/bus.h> | |
+#include <sys/intr.h> | |
+ | |
+#include <sys/audioio.h> | |
+ | |
+#include <dev/audio_if.h> | |
+#include <dev/auconv.h> | |
+#include <dev/mulaw.h> | |
+ | |
+#include <dev/ic/arcofivar.h> | |
+ | |
+#include "ioconf.h" | |
+ | |
+#if 0 | |
+#define ARCOFI_DEBUG | |
+#endif | |
+ | |
+/* | |
+ * Siemens PSB2160 registers | |
+ */ | |
+ | |
+/* CMDR */ | |
+#define CMDR_AD 0x80 /* SP1/PS2 address convention */ | |
+#define CMDR_READ 0x40 | |
+#define CMDR_WRITE 0x00 | |
+#define CMDR_PU 0x20 /* Power Up */ | |
+#define CMDR_RCS 0x10 /* Receive and transmit in CH B2 */ | |
+#define CMDR_MASK 0x0f | |
+ | |
+ /* command length data */ | |
+#define SOP_0 0x00 /* 5 CR4 CR3 CR2 CR1 */ | |
+#define COP_1 0x01 /* 5 t1_hi t1_lo f1_hi f1_lo */ | |
+#define COP_2 0x02 /* 3 gr1 gr2 */ | |
+#define COP_3 0x03 /* 3 t2_hi t2_lo f2_hi f2_lo */ | |
+#define SOP_4 0x04 /* 2 CR1 */ | |
+#define SOP_5 0x05 /* 2 CR2 */ | |
+#define SOP_6 0x06 /* 2 CR3 */ | |
+#define SOP_7 0x07 /* 2 CR4 */ | |
+#define COP_8 0x08 /* 3 dtmf_hi dtmf_lo */ | |
+#define COP_9 0x09 /* 5 gz a3 a2 a1 */ | |
+#define COP_A 0x0a /* 9 fx1 to fx8 */ | |
+#define COP_B 0x0b /* 3 gx1 gx2 */ | |
+#define COP_C 0x0c /* 9 fr1 to fr 8 */ | |
+#define COP_D 0x0d /* 5 fr9 fr10 fx9 fx10 */ | |
+#define COP_E 0x0e /* 5 t3_hi t3_lo f3_hi f3_lo */ | |
+ | |
+/* CR1 */ | |
+#define CR1_GR 0x80 /* GR gain loaded from CRAM vs 0dB */ | |
+#define CR1_GZ 0x40 /* Z gain loaded from CRAM vs -18dB */ | |
+#define CR1_FX 0x20 /* X filter loaded from CRAM vs 0dB flat */ | |
+#define CR1_FR 0x10 /* R filter loaded from CRAM vs 0dB flat */ | |
+#define CR1_GX 0x08 /* GX gain loaded from CRAM vs 0dB */ | |
+#define CR1_T_MASK 0x07 /* test mode */ | |
+#define CR1_DLP 0x07 /* digital loopback via PCM registers */ | |
+#define CR1_DLM 0x06 /* D/A output looped back to A/D input */ | |
+#define CR1_DLS 0x05 /* digital loopback via converter registers */ | |
+#define CR1_IDR 0x04 /* data RAM initialization */ | |
+#define CR1_BYP 0x03 /* bypass analog frontend */ | |
+#define CR1_ALM 0x02 /* analog loopback via MUX */ | |
+#define CR1_ALS 0x01 /* analog loopback via converter registers */ | |
+ | |
+/* CR2 */ | |
+#define CR2_SD 0x80 /* SD pin set to input vs output */ | |
+#define CR2_SC 0x40 /* SC pin set to input vs output */ | |
+#define CR2_SB 0x20 /* SB pin set to input vs output */ | |
+#define CR2_SA 0x10 /* SA pin set to input vs output */ | |
+#define CR2_ELS 0x08 /* non-input S pins tristate SIP vs sending 0 */ | |
+#define CR2_AM 0x04 /* only one device on the SLD bus */ | |
+#define CR2_TR 0x02 /* three party conferencing */ | |
+#define CR2_EFC 0x01 /* enable feature control */ | |
+ | |
+/* CR3 */ | |
+#define CR3_MIC_G_MASK 0xe0 /* MIC input analog gain */ | |
+#define CR3_MIC_X_INPUT 0xe0 /* MIC disabled, X input 15.1 dB */ | |
+#define CR3_MIC_G_17 0xc0 /* 17 dB */ | |
+#define CR3_MIC_G_22 0xa0 /* 22 dB */ | |
+#define CR3_MIC_G_28 0x80 /* 28 dB */ | |
+#define CR3_MIC_G_34 0x60 /* 34 dB */ | |
+#define CR3_MIC_G_40 0x40 /* 40 dB */ | |
+#define CR3_MIC_G_46 0x20 /* 46 dB */ | |
+#define CR3_MIC_G_52 0x00 /* 52 dB (reset default) */ | |
+#define CR3_AFEC_MASK 0x1c | |
+#define CR3_AFEC_MUTE 0x18 /* mute: Hout */ | |
+#define CR3_AFEC_HFS 0x14 /* hands free: FHM, LS out */ | |
+#define CR3_AFEC_LH3 0x10 /* loud hearing 3: MIC, H out, LS out */ | |
+#define CR3_AFEC_LH2 0x0c /* loud hearing 2: MIC, LS out */ | |
+#define CR3_AFEC_LH1 0x08 /* loud hearing 1: LS out */ | |
+#define CR3_AFEC_RDY 0x04 /* ready: MIC, H out */ | |
+#define CR3_AFEC_POR 0x00 /* power on reset: all off */ | |
+#define CR3_OPMODE_MASK 0x03 | |
+#define CR3_OPMODE_LINEAR 0x02 /* linear (16 bit) */ | |
+#define CR3_OPMODE_MIXED 0x01 /* mixed */ | |
+#define CR3_OPMODE_NORMAL 0x00 /* normal (A/u-Law) */ | |
+ | |
+/* CR4 */ | |
+#define CR4_DHF 0x80 /* TX digital high frequency enable */ | |
+#define CR4_DTMF 0x40 /* DTMF generator enable */ | |
+#define CR4_TG 0x20 /* tone ring enable */ | |
+#define CR4_BT 0x10 /* beat tone generator enable */ | |
+#define CR4_TM 0x08 /* incoming voice enable */ | |
+#define CR4_BM 0x04 /* beat mode (3 tone vs 2 tone) */ | |
+#define CR4_PM 0x02 /* tone sent to piezo vs loudspeaker */ | |
+#define CR4_ULAW 0x01 /* u-Law vs A-Law */ | |
+ | |
+ | |
+/* | |
+ * Glue logic registers | |
+ * Note the register values here are symbolic, as actual addresses | |
+ * depend upon the particular bus the device is connected to. | |
+ */ | |
+ | |
+#define ARCOFI_ID 0 /* id (r) and reset (w) register */ | |
+ | |
+#define ARCOFI_CSR 1 /* status and control register */ | |
+#define CSR_INTR_ENABLE 0x80 | |
+#define CSR_INTR_REQUEST 0x40 /* unacknowledged interrupt */ | |
+/* 0x20 and 0x10 used in DIO flavours, to provide IPL */ | |
+#define CSR_WIDTH_16 0x08 /* 16-bit samples */ | |
+#define CSR_CTRL_FIFO_ENABLE 0x04 /* connect FIFO to CMDR */ | |
+#define CSR_DATA_FIFO_ENABLE 0x01 /* connect FIFO to DU/DD */ | |
+ | |
+#define ARCOFI_FIFO_IR 2 /* FIFO interrupt register */ | |
+#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 /* FIFO status register (ro) */ | |
+#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 /* data FIFO port */ | |
+ | |
+#define ARCOFI_FIFO_CTRL 5 /* control FIFO port (wo) */ | |
+ | |
+#define ARCOFI_FIFO_SIZE 128 | |
+ | |
+#ifdef hp300 /* XXX */ | |
+#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_open(void *, int); | |
+static void arcofi_close(void *); | |
+static int arcofi_drain(void *); | |
+static int arcofi_query_encoding(void *, struct audio_encoding *); | |
+static int arcofi_set_params(void *, int, int, | |
+ struct audio_params *, struct audio_params *, | |
+ stream_filter_list_t *, stream_filter_list_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, | |
+ .close = arcofi_close, | |
+ .drain = arcofi_drain, | |
+ .query_encoding = arcofi_query_encoding, | |
+ .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, | |
+ .speaker_ctl = NULL, | |
+ .getdev = arcofi_getdev, | |
+ .setfd = NULL, | |
+ .set_port = arcofi_set_port, | |
+ .get_port = arcofi_get_port, | |
+ .query_devinfo = arcofi_query_devinfo, | |
+ .allocm = NULL, | |
+ .freem = NULL, | |
+ .round_buffersize = NULL, | |
+ .mappage = NULL, | |
+ .get_props = arcofi_get_props, | |
+ .trigger_output = NULL, | |
+ .trigger_input = NULL, | |
+ .dev_ioctl = NULL, | |
+ .get_locks = arcofi_get_locks, | |
+}; | |
+ | |
+static const struct audio_format arcofi_formats[] = { | |
+ /* | |
+ * 8-bit encodings: | |
+ * - u-Law and A-Law are native | |
+ * - linear are converted to 16-bit by auconv | |
+ */ | |
+ {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULAW, 8, 8, | |
+ 1, AUFMT_MONAURAL, 1, {8000}}, | |
+ {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ALAW, 8, 8, | |
+ 1, AUFMT_MONAURAL, 1, {8000}}, | |
+ /* | |
+ * 16-bit encodings: | |
+ * - slinear big-endian is native | |
+ * - unsigned or little-endian are converted by auconv | |
+ */ | |
+ {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_BE, 16, 16, | |
+ 1, AUFMT_MONAURAL, 1, {8000}}, | |
+}; | |
+#define ARCOFI_NFORMATS __arraycount(arcofi_formats) | |
+ | |
+/* mixer items */ | |
+#define ARCOFI_PORT_AUDIO_IN_VOLUME 0 /* line in volume (GR) */ | |
+#define ARCOFI_PORT_AUDIO_OUT_VOLUME 1 /* line out volume (GX) */ | |
+#define ARCOFI_PORT_AUDIO_SPKR_VOLUME 2 /* speaker volume (GX) */ | |
+#define ARCOFI_PORT_AUDIO_IN_MUTE 3 /* line in mute (MIC) */ | |
+#define ARCOFI_PORT_AUDIO_OUT_MUTE 4 /* line out mute (H out) */ | |
+#define ARCOFI_PORT_AUDIO_SPKR_MUTE 5 /* line in mute (LS out) */ | |
+/* mixer classes */ | |
+#define ARCOFI_CLASS_INPUT 6 | |
+#define ARCOFI_CLASS_OUTPUT 7 | |
+ | |
+/* | |
+ * Gain programming formulae are a complete mystery to me, and of course | |
+ * no two chips are compatible - not even the PSB 2163 and PSB 2165 | |
+ * later ARCOFI chips, from the same manufacturer as the PSB 2160! | |
+ * | |
+ * Of course, the PSB 2160 datasheet does not give any set of values. | |
+ * The following table is taken from the HP-UX audio driver (audio_shared.o | |
+ * private_audio_gain_tab). | |
+ */ | |
+ | |
+#define NEGATIVE_GAINS 60 | |
+#define POSITIVE_GAINS 14 | |
+static const uint16_t arcofi_gains[1 + NEGATIVE_GAINS + 1 + POSITIVE_GAINS] = { | |
+ /* minus infinity */ | |
+ 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, | |
+ | |
+ /* 0 */ | |
+ 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 = (struct arcofi_softc *)v; | |
+ | |
+ if (sc->sc_open) | |
+ return EBUSY; | |
+ sc->sc_open = 1; | |
+ KASSERT(sc->sc_mode == 0); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static 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; | |
+} | |
+ | |
+static int | |
+arcofi_drain(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 | |
+ if ((arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_OUT_EMPTY) == 0) { | |
+ /* enable output FIFO empty interrupt... */ | |
+ arcofi_write(sc, ARCOFI_FIFO_IR, | |
+ arcofi_read(sc, ARCOFI_FIFO_IR) | | |
+ FIFO_IR_ENABLE(FIFO_IR_OUT_EMPTY)); | |
+ /* ...and wait for it to fire */ | |
+ if (cv_timedwait(&sc->sc_cv, &sc->sc_intr_lock, | |
+ ((ARCOFI_FIFO_SIZE * hz) / 8000) + 100) != 0) { | |
+ printf("%s: drain did not complete\n", | |
+ device_xname(sc->sc_dev)); | |
+ arcofi_write(sc, ARCOFI_FIFO_IR, | |
+ arcofi_read(sc, ARCOFI_FIFO_IR) & | |
+ ~FIFO_IR_ENABLE(FIFO_IR_OUT_EMPTY)); | |
+ } | |
+ } | |
+ return 0; | |
+} | |
+ | |
+static int | |
+arcofi_query_encoding(void *v, struct audio_encoding *aep) | |
+{ | |
+ struct arcofi_softc *sc = (struct arcofi_softc *)v; | |
+ | |
+ return auconv_query_encoding(sc->sc_encodings, aep); | |
+} | |
+ | |
+/* | |
+ * Compute proper sample and hardware settings. | |
+ */ | |
+static int | |
+arcofi_set_params(void *handle, int setmode, int usemode, | |
+ audio_params_t *play, audio_params_t *rec, | |
+ stream_filter_list_t *pfil, stream_filter_list_t *rfil) | |
+{ | |
+ struct arcofi_softc *sc; | |
+ int i; | |
+ | |
+ sc = handle; | |
+ for (i = 0; i < 2; i++) { | |
+ int mode; | |
+ audio_params_t *p; | |
+ stream_filter_list_t *fil; | |
+ int ind; | |
+ | |
+ switch (i) { | |
+ case 0: | |
+ mode = AUMODE_PLAY; | |
+ p = play; | |
+ fil = pfil; | |
+ break; | |
+ case 1: | |
+ mode = AUMODE_RECORD; | |
+ p = rec; | |
+ fil = rfil; | |
+ break; | |
+ default: | |
+ return EINVAL; | |
+ } | |
+ | |
+ if ((setmode & mode) == 0) | |
+ continue; | |
+ | |
+#ifdef ARCOFI_DEBUG | |
+ printf("%s: %s, mode %d encoding %d precision %d\n", | |
+ device_xname(sc->sc_dev), __func__, | |
+ mode, p->encoding, p->precision); | |
+#endif | |
+ | |
+ ind = auconv_set_converter(arcofi_formats, ARCOFI_NFORMATS, | |
+ mode, p, false, fil); | |
+ if (ind < 0) | |
+ return EINVAL; | |
+ if (fil->req_size > 0) | |
+ p = &fil->filters[0].param; | |
+ if (p->precision == 8) { | |
+ if (p->encoding == AUDIO_ENCODING_ALAW) | |
+ 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) | |
+{ | |
+ | |
+ /* | |
+ * Round the size up to a multiple of half the FIFO, to favour | |
+ * smooth interrupt operation. | |
+ */ | |
+ 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_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 | |
+ | |
+ /* enable data FIFO if becoming active */ | |
+ 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; | |
+ | |
+ /* enable input FIFO interrupts */ | |
+ 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 | |
+ | |
+ /* enable data FIFO if becoming active */ | |
+ 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; | |
+ | |
+ /* enable output FIFO interrupts */ | |
+ 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 | |
+ | |
+ /* disable input FIFO interrupts */ | |
+ arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) & | |
+ ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY)); | |
+ /* disable data FIFO if becoming idle */ | |
+ 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 | |
+ | |
+ /* disable output FIFO interrupts */ | |
+ arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) & | |
+ ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY)); | |
+ /* disable data FIFO if becoming idle */ | |
+ 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; | |
+} | |
+ | |
+/* | |
+ * Convert gain table index to AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale. | |
+ */ | |
+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; | |
+} | |
+ | |
+/* | |
+ * Convert AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale to gain table index. | |
+ */ | |
+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); | |
+} | |
+ | |
+/* | |
+ * The following routines rely upon this... | |
+ */ | |
+#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 | |
+ | |
+/* | |
+ * The mapping between the available inputs and outputs, and CR3, is as | |
+ * follows: | |
+ * - the `line in' connector is the `MIC' input. | |
+ * - the `line out' connector is the `H out' (heaphones) output. | |
+ * - the internal `speaker' is the `LS out' (loudspeaker) output. | |
+ * | |
+ * Each of these can be enabled or disabled indepently, except for | |
+ * MIC enabled with H out and LS out disabled, which is not allowed | |
+ * by the chip (and makes no sense for a chip which was intended to | |
+ * be used in phones, not voice recorders); we cheat by keeping one | |
+ * output source enabled, but with the output gain forced to minus | |
+ * infinity to mute it. | |
+ * | |
+ * The truth table is thus: | |
+ * | |
+ * MIC LS out H out AFEC | |
+ * off off off POR | |
+ * off off on MUTE | |
+ * off on off LH1 | |
+ * off on on LH3, X input enabled | |
+ * on off off RDY, GX forced to minus infinity | |
+ * on off on RDY | |
+ * on on off LH2 | |
+ * on on on LH3 | |
+ */ | |
+ | |
+/* | |
+ * Convert logical port enable settings to a valid CR3 value. | |
+ */ | |
+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: | |
+ /* since we can't do this, just... */ | |
+ /* FALLTHROUGH */ | |
+ 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; | |
+ } | |
+} | |
+ | |
+/* | |
+ * Convert CR3 to an enabled ports mask. | |
+ */ | |
+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 | |
+ /* check for proper type */ | |
+ switch (mc->dev) { | |
+ /* volume settings */ | |
+ 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; | |
+ /* mute settings */ | |
+ 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) { | |
+ /* volume settings */ | |
+ 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; | |
+ | |
+ /* mute settings */ | |
+ 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 | |
+ /* check for proper type */ | |
+ switch (mc->dev) { | |
+ /* volume settings */ | |
+ 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; | |
+ | |
+ /* mute settings */ | |
+ 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) { | |
+ /* volume settings */ | |
+ 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; | |
+ | |
+ /* mute settings */ | |
+ 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; | |
+ | |
+ /* items */ | |
+ 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); | |
+ /* goto mono_volume; */ | |
+ 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; | |
+ /* goto mute; */ | |
+ 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; | |
+ | |
+ /* classes */ | |
+ 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 0; | |
+} | |
+ | |
+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 *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); | |
+ | |
+ /* receive */ | |
+ 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) { | |
+ softint_schedule(sc->sc_sih); | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ sc->sc_recv.buf = cur; | |
+ | |
+ if (cur == NULL || cur == past) { | |
+ /* underrun, disable further interrupts */ | |
+ arcofi_write(sc, ARCOFI_FIFO_IR, | |
+ arcofi_read(sc, ARCOFI_FIFO_IR) & | |
+ ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY)); | |
+ } | |
+ } | |
+ | |
+ /* xmit */ | |
+ 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) { | |
+ softint_schedule(sc->sc_sih); | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ if (cur == NULL || cur == past) { | |
+ /* disable further interrupts */ | |
+ 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; | |
+ } | |
+ | |
+ /* drain */ | |
+ 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)); | |
+ mutex_spin_enter(&sc->sc_intr_lock); | |
+ cv_signal(&sc->sc_cv); | |
+ mutex_spin_exit(&sc->sc_intr_lock); | |
+ } | |
+ | |
+#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; | |
+} | |
+ | |
+void | |
+arcofi_swintr(void *v) | |
+{ | |
+ struct arcofi_softc *sc = (struct arcofi_softc *)v; | |
+ int action; | |
+ | |
+ action = 0; | |
+ mutex_spin_enter(&sc->sc_intr_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); | |
+ } | |
+ mutex_spin_exit(&sc->sc_intr_lock); | |
+} | |
+ | |
+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 | |
+ }; | |
+ | |
+ /* | |
+ * Compute command length. | |
+ */ | |
+ if (cmd >= __arraycount(cmdlen)) | |
+ return EINVAL; | |
+ len = cmdlen[cmd]; | |
+ | |
+ KASSERT(cold || mutex_owned(&sc->sc_intr_lock)); | |
+ | |
+ /* | |
+ * Disable all FIFO processing. | |
+ */ | |
+ csr = arcofi_read(sc, ARCOFI_CSR); | |
+ arcofi_write(sc, ARCOFI_CSR, | |
+ csr & ~(CSR_DATA_FIFO_ENABLE | CSR_CTRL_FIFO_ENABLE)); | |
+ | |
+ /* | |
+ * Fill the FIFO with the command bytes. | |
+ */ | |
+ arcofi_write(sc, ARCOFI_FIFO_CTRL, CMDR_PU | CMDR_WRITE | cmd); | |
+ for (; len != 0; len--) | |
+ arcofi_write(sc, ARCOFI_FIFO_CTRL, *data++); | |
+ | |
+ /* | |
+ * Enable command processing. | |
+ */ | |
+ arcofi_write(sc, ARCOFI_CSR, | |
+ (csr & ~CSR_DATA_FIFO_ENABLE) | CSR_CTRL_FIFO_ENABLE); | |
+ | |
+ /* | |
+ * Wait for the command FIFO to be empty. | |
+ */ | |
+ 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); | |
+ cv_init(&sc->sc_cv, device_xname(self)); | |
+ rc = auconv_create_encodings(arcofi_formats, ARCOFI_NFORMATS, | |
+ &sc->sc_encodings); | |
+ if (rc != 0) | |
+ goto out; | |
+ | |
+ /* | |
+ * Reset logic. | |
+ */ | |
+ arcofi_write(sc, ARCOFI_ID, 0); | |
+ delay(100000); | |
+ arcofi_write(sc, ARCOFI_CSR, 0); | |
+ | |
+ /* | |
+ * Initialize the chip to default settings (8 bit, u-Law). | |
+ */ | |
+ 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)); | |
+ | |
+ /* clear CRAM */ | |
+ op = SOP_4; | |
+ cmd[0] = CR1_IDR; | |
+ if ((rc = arcofi_cmd(sc, op, cmd)) != 0) | |
+ goto error; | |
+ delay(1000); | |
+ | |
+ /* set gain values before enabling them in CR1 */ | |
+ 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; | |
+ /* same value for gx... */ | |
+ op = COP_B; | |
+ if ((rc = arcofi_cmd(sc, op, cmd)) != 0) | |
+ goto error; | |
+ | |
+ /* set all CR registers at once */ | |
+ 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); | |
+ out: | |
+ cv_destroy(&sc->sc_cv); | |
+ mutex_destroy(&sc->sc_intr_lock); | |
+ mutex_destroy(&sc->sc_lock); | |
+} | |
--- /dev/null 2014-08-18 16:26:40.000000000 +0000 | |
+++ dev/ic/arcofivar.h 2014-08-18 12:41:50.000000000 +0000 | |
@@ -0,0 +1,56 @@ | |
+/* $NetBSD$ */ | |
+/* $OpenBSD: arcofivar.h,v 1.2 2011/12/25 00:07:27 miod Exp $ */ | |
+ | |
+/* | |
+ * Copyright (c) 2011 Miodrag Vallat. | |
+ * | |
+ * Permission to use, copy, modify, and distribute this software for any | |
+ * purpose with or without fee is hereby granted, provided that the above | |
+ * copyright notice and this permission notice appear in all copies. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
+ */ | |
+ | |
+#define ARCOFI_NREGS 6 | |
+ | |
+struct arcofi_softc { | |
+ device_t sc_dev; | |
+ bus_addr_t sc_reg[ARCOFI_NREGS]; | |
+ bus_space_tag_t sc_iot; | |
+ bus_space_handle_t sc_ioh; | |
+ | |
+ struct audio_device sc_audio_device; | |
+ void *sc_sih; | |
+ | |
+ int sc_open; | |
+ int sc_mode; | |
+ | |
+ struct { | |
+ uint8_t cr3, cr4; | |
+ uint gr_idx, gx_idx; | |
+ int output_mute; | |
+ } sc_active, | |
+ sc_shadow; | |
+ | |
+ struct { | |
+ uint8_t *buf; | |
+ uint8_t *past; | |
+ void (*cb)(void *); | |
+ void *cbarg; | |
+ } sc_recv, | |
+ sc_xmit; | |
+ kmutex_t sc_lock; | |
+ kmutex_t sc_intr_lock; | |
+ kcondvar_t sc_cv; | |
+ struct audio_encoding_set *sc_encodings; | |
+}; | |
+ | |
+void arcofi_attach(struct arcofi_softc *, const char *); | |
+int arcofi_hwintr(void *); | |
+void arcofi_swintr(void *); | |
? arch/hp300/dev/arcofi_dio.c | |
Index: arch/hp300/conf/GENERIC | |
=================================================================== | |
RCS file: /cvsroot/src/sys/arch/hp300/conf/GENERIC,v | |
retrieving revision 1.185 | |
diff -u -p -d -r1.185 GENERIC | |
--- arch/hp300/conf/GENERIC 16 Aug 2014 17:56:32 -0000 1.185 | |
+++ arch/hp300/conf/GENERIC 17 Aug 2014 09:14:01 -0000 | |
@@ -280,6 +280,10 @@ ch* at scsibus? target ? lun ? # SCSI c | |
ss* at scsibus? target ? lun ? # SCSI scanners | |
uk* at scsibus? target ? lun ? # unknown SCSI devices | |
+# 425e digital audio | |
+arcofi* at dio? scode ? | |
+audio* at arcofi? | |
+ | |
# | |
# Pseudo-devices | |
# | |
Index: arch/hp300/conf/files.hp300 | |
=================================================================== | |
RCS file: /cvsroot/src/sys/arch/hp300/conf/files.hp300,v | |
retrieving revision 1.88 | |
diff -u -p -d -r1.88 files.hp300 | |
--- arch/hp300/conf/files.hp300 20 Apr 2014 04:12:54 -0000 1.88 | |
+++ arch/hp300/conf/files.hp300 17 Aug 2014 09:14:01 -0000 | |
@@ -147,6 +147,10 @@ file arch/hp300/dev/dcm.c dcm needs-fla | |
attach le at dio: le24 | |
file arch/hp300/dev/if_le.c le | |
+# 425e digital audio | |
+attach arcofi at dio with arcofi_dio | |
+file arch/hp300/dev/arcofi_dio.c arcofi_dio | |
+ | |
# HP-IB interfaces | |
define hpibdev { } | |
Index: arch/hp300/conf/majors.hp300 | |
=================================================================== | |
RCS file: /cvsroot/src/sys/arch/hp300/conf/majors.hp300,v | |
retrieving revision 1.25 | |
diff -u -p -d -r1.25 majors.hp300 | |
--- arch/hp300/conf/majors.hp300 30 Jun 2011 20:09:30 -0000 1.25 | |
+++ arch/hp300/conf/majors.hp300 17 Aug 2014 09:14:01 -0000 | |
@@ -48,6 +48,7 @@ device-major ch char 48 ch | |
device-major ss char 49 ss | |
device-major uk char 50 uk | |
device-major ses char 51 ses | |
+device-major audio char 52 audio | |
device-major nsmb char 98 nsmb | |
Index: arch/hp300/hp300/intr.c | |
=================================================================== | |
RCS file: /cvsroot/src/sys/arch/hp300/hp300/intr.c,v | |
retrieving revision 1.40 | |
diff -u -p -d -r1.40 intr.c | |
--- arch/hp300/hp300/intr.c 20 Dec 2010 00:25:33 -0000 1.40 | |
+++ arch/hp300/hp300/intr.c 17 Aug 2014 09:14:02 -0000 | |
@@ -45,6 +45,8 @@ __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.4 | |
#include <sys/cpu.h> | |
#include <sys/intr.h> | |
+#include "audio.h" | |
+ | |
/* | |
* The location and size of the autovectored interrupt portion | |
* of the vector table. | |
@@ -209,6 +211,12 @@ intr_dispatch(int evec /* format | vecto | |
ih = LIST_NEXT(ih, ih_q)) | |
handled |= (*ih->ih_fn)(ih->ih_arg); | |
+#if NAUDIO > 0 | |
+ /* hardclock() on ipl 6 is already handled in locore.s */ | |
+ if (ipl == 6) | |
+ return; | |
+#endif | |
+ | |
if (handled) | |
straycount = 0; | |
else if (++straycount > 50) | |
Index: arch/hp300/hp300/locore.s | |
=================================================================== | |
RCS file: /cvsroot/src/sys/arch/hp300/hp300/locore.s,v | |
retrieving revision 1.170 | |
diff -u -p -d -r1.170 locore.s | |
--- arch/hp300/hp300/locore.s 15 Mar 2014 09:26:36 -0000 1.170 | |
+++ arch/hp300/hp300/locore.s 17 Aug 2014 09:14:02 -0000 | |
@@ -97,6 +97,7 @@ | |
#include <hp300/hp300/leds.h> | |
#endif | |
+#include "audio.h" | |
#include "ksyms.h" | |
#define MMUADDR(ar) movl _C_LABEL(MMUbase),ar | |
@@ -951,6 +952,12 @@ Lrecheck: | |
CLKADDR(%a0) | |
movb %a0@(CLKSR),%d0 | see if anything happened | |
jmi Lclkagain | while we were in hardclock/statintr | |
+#if NAUDIO >0 | |
+ movw %sp@(22),%sp@- | push exception vector info | |
+ clrw %sp@- | |
+ jbsr _C_LABEL(intr_dispatch) | call dispatch routine | |
+ addql #4,%sp | |
+#endif | |
INTERRUPT_RESTOREREG | |
subql #1,_C_LABEL(idepth) | exiting from interrupt | |
jra _ASM_LABEL(rei) | all done | |
--- /dev/null 2014-08-18 16:40:57.000000000 +0000 | |
+++ arch/hp300/dev/arcofi_dio.c 2014-08-17 08:57:45.000000000 +0000 | |
@@ -0,0 +1,94 @@ | |
+/* $NetBSD$ */ | |
+/* $OpenBSD: arcofi_dio.c,v 1.1 2011/12/21 23:12:03 miod Exp $ */ | |
+ | |
+/* | |
+ * Copyright (c) 2011 Miodrag Vallat. | |
+ * | |
+ * Permission to use, copy, modify, and distribute this software for any | |
+ * purpose with or without fee is hereby granted, provided that the above | |
+ * copyright notice and this permission notice appear in all copies. | |
+ * | |
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
+ */ | |
+ | |
+#include <sys/param.h> | |
+#include <sys/systm.h> | |
+#include <sys/conf.h> | |
+#include <sys/device.h> | |
+#include <sys/bus.h> | |
+#include <sys/intr.h> | |
+ | |
+#include <sys/audioio.h> | |
+#include <dev/audio_if.h> | |
+#include <dev/ic/arcofivar.h> | |
+ | |
+#include <hp300/dev/dioreg.h> | |
+#include <hp300/dev/diovar.h> | |
+ | |
+#include <hp300/dev/diodevs.h> | |
+ | |
+#define SOFTINT_AUDIO SOFTINT_SERIAL /* XXX */ | |
+ | |
+static void arcofi_dio_attach(device_t, device_t, void *); | |
+static int arcofi_dio_match(device_t, cfdata_t, void *); | |
+ | |
+struct arcofi_dio_softc { | |
+ struct arcofi_softc sc_arcofi; | |
+ | |
+ struct bus_space_tag sc_tag; | |
+}; | |
+ | |
+CFATTACH_DECL_NEW(arcofi_dio, sizeof(struct arcofi_dio_softc), | |
+ arcofi_dio_match, arcofi_dio_attach, NULL, NULL); | |
+ | |
+static int | |
+arcofi_dio_match(device_t parent, cfdata_t match, void *aux) | |
+{ | |
+ struct dio_attach_args *da = aux; | |
+ | |
+ if (da->da_id != DIO_DEVICE_ID_AUDIO) | |
+ return 0; | |
+ | |
+ return 1; | |
+} | |
+ | |
+static void | |
+arcofi_dio_attach(device_t parent, device_t self, void *aux) | |
+{ | |
+ struct arcofi_dio_softc *adsc = device_private(self); | |
+ struct arcofi_softc *sc = &adsc->sc_arcofi; | |
+ struct dio_attach_args *da = aux; | |
+ bus_space_tag_t iot = &adsc->sc_tag; | |
+ int ipl; | |
+ | |
+ sc->sc_dev = self; | |
+ | |
+ /* XXX is it better to use sc->sc_reg[] to handle odd sparse map? */ | |
+ memcpy(iot, da->da_bst, sizeof(struct bus_space_tag)); | |
+ dio_set_bus_space_oddbyte(iot); | |
+ sc->sc_iot = iot; | |
+ | |
+ if (bus_space_map(iot, da->da_addr, DIOII_SIZEOFF, 0, | |
+ &sc->sc_ioh) != 0) { | |
+ aprint_error(": can't map registers\n"); | |
+ return; | |
+ } | |
+ | |
+ sc->sc_sih = softint_establish(SOFTINT_AUDIO, arcofi_swintr, sc); | |
+ if (sc->sc_sih == NULL) { | |
+ aprint_error(": can't register soft interrupt\n"); | |
+ return; | |
+ } | |
+ ipl = da->da_ipl; | |
+ dio_intr_establish(arcofi_hwintr, sc, ipl, IPL_AUDIO); | |
+ | |
+ aprint_normal("\n"); | |
+ | |
+ arcofi_attach(sc, "dio"); | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment