1*7eff0e4eSkn /* $OpenBSD: arcofi.c,v 1.21 2022/10/28 15:09:45 kn Exp $ */
25538dba8Smiod
35538dba8Smiod /*
45538dba8Smiod * Copyright (c) 2011 Miodrag Vallat.
55538dba8Smiod *
65538dba8Smiod * Permission to use, copy, modify, and distribute this software for any
75538dba8Smiod * purpose with or without fee is hereby granted, provided that the above
85538dba8Smiod * copyright notice and this permission notice appear in all copies.
95538dba8Smiod *
105538dba8Smiod * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115538dba8Smiod * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125538dba8Smiod * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135538dba8Smiod * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145538dba8Smiod * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155538dba8Smiod * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165538dba8Smiod * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175538dba8Smiod */
185538dba8Smiod
195538dba8Smiod /*
205538dba8Smiod * Driver for the HP ``Audio1'' device, which is a FIFO layer around a
215538dba8Smiod * Siemens PSB 2160 ``ARCOFI'' phone quality audio chip.
225538dba8Smiod *
235538dba8Smiod * It is known to exist in two flavours: on-board the HP9000/425e as a DIO
245538dba8Smiod * device, an on-board the HP9000/{705,710,745,747} as a GIO device.
255538dba8Smiod *
265538dba8Smiod * The FIFO logic buffers up to 128 bytes. When using 8 bit samples and
275538dba8Smiod * the logic set to interrupt every half FIFO, the device will interrupt
285538dba8Smiod * 125 times per second.
295538dba8Smiod */
305538dba8Smiod
315538dba8Smiod #include <sys/param.h>
325538dba8Smiod #include <sys/systm.h>
335538dba8Smiod #include <sys/conf.h>
345538dba8Smiod #include <sys/device.h>
355538dba8Smiod #include <sys/kernel.h>
365538dba8Smiod #include <sys/proc.h>
379b18ffb8Sguenther #include <sys/endian.h>
38*7eff0e4eSkn #include <sys/fcntl.h>
395538dba8Smiod
405538dba8Smiod #include <sys/audioio.h>
415538dba8Smiod #include <dev/audio_if.h>
425538dba8Smiod
435538dba8Smiod #include <machine/autoconf.h>
445538dba8Smiod #include <machine/bus.h>
455538dba8Smiod #include <machine/intr.h>
465538dba8Smiod
475538dba8Smiod #include <dev/ic/arcofivar.h>
485538dba8Smiod
495538dba8Smiod #if 0
505538dba8Smiod #define ARCOFI_DEBUG
515538dba8Smiod #endif
525538dba8Smiod
535538dba8Smiod /*
545538dba8Smiod * Siemens PSB2160 registers
555538dba8Smiod */
565538dba8Smiod
575538dba8Smiod /* CMDR */
58e5128cdcSmiod #define CMDR_AD 0x80 /* SP1/SP2 address convention */
595538dba8Smiod #define CMDR_READ 0x40
605538dba8Smiod #define CMDR_WRITE 0x00
615538dba8Smiod #define CMDR_PU 0x20 /* Power Up */
625538dba8Smiod #define CMDR_RCS 0x10 /* Receive and transmit in CH B2 */
635538dba8Smiod #define CMDR_MASK 0x0f
645538dba8Smiod
655538dba8Smiod /* command length data */
665538dba8Smiod #define SOP_0 0x00 /* 5 CR4 CR3 CR2 CR1 */
675538dba8Smiod #define COP_1 0x01 /* 5 t1_hi t1_lo f1_hi f1_lo */
685538dba8Smiod #define COP_2 0x02 /* 3 gr1 gr2 */
695538dba8Smiod #define COP_3 0x03 /* 3 t2_hi t2_lo f2_hi f2_lo */
705538dba8Smiod #define SOP_4 0x04 /* 2 CR1 */
715538dba8Smiod #define SOP_5 0x05 /* 2 CR2 */
725538dba8Smiod #define SOP_6 0x06 /* 2 CR3 */
735538dba8Smiod #define SOP_7 0x07 /* 2 CR4 */
745538dba8Smiod #define COP_8 0x08 /* 3 dtmf_hi dtmf_lo */
755538dba8Smiod #define COP_9 0x09 /* 5 gz a3 a2 a1 */
765538dba8Smiod #define COP_A 0x0a /* 9 fx1 to fx8 */
775538dba8Smiod #define COP_B 0x0b /* 3 gx1 gx2 */
785538dba8Smiod #define COP_C 0x0c /* 9 fr1 to fr 8 */
795538dba8Smiod #define COP_D 0x0d /* 5 fr9 fr10 fx9 fx10 */
805538dba8Smiod #define COP_E 0x0e /* 5 t3_hi t3_lo f3_hi f3_lo */
815538dba8Smiod
825538dba8Smiod /* CR1 */
835538dba8Smiod #define CR1_GR 0x80 /* GR gain loaded from CRAM vs 0dB */
845538dba8Smiod #define CR1_GZ 0x40 /* Z gain loaded from CRAM vs -18dB */
855538dba8Smiod #define CR1_FX 0x20 /* X filter loaded from CRAM vs 0dB flat */
865538dba8Smiod #define CR1_FR 0x10 /* R filter loaded from CRAM vs 0dB flat */
875538dba8Smiod #define CR1_GX 0x08 /* GX gain loaded from CRAM vs 0dB */
885538dba8Smiod #define CR1_T_MASK 0x07 /* test mode */
895538dba8Smiod #define CR1_DLP 0x07 /* digital loopback via PCM registers */
905538dba8Smiod #define CR1_DLM 0x06 /* D/A output looped back to A/D input */
915538dba8Smiod #define CR1_DLS 0x05 /* digital loopback via converter registers */
925538dba8Smiod #define CR1_IDR 0x04 /* data RAM initialization */
935538dba8Smiod #define CR1_BYP 0x03 /* bypass analog frontend */
945538dba8Smiod #define CR1_ALM 0x02 /* analog loopback via MUX */
955538dba8Smiod #define CR1_ALS 0x01 /* analog loopback via converter registers */
965538dba8Smiod
975538dba8Smiod /* CR2 */
985538dba8Smiod #define CR2_SD 0x80 /* SD pin set to input vs output */
995538dba8Smiod #define CR2_SC 0x40 /* SC pin set to input vs output */
1005538dba8Smiod #define CR2_SB 0x20 /* SB pin set to input vs output */
1015538dba8Smiod #define CR2_SA 0x10 /* SA pin set to input vs output */
1025538dba8Smiod #define CR2_ELS 0x08 /* non-input S pins tristate SIP vs sending 0 */
1035538dba8Smiod #define CR2_AM 0x04 /* only one device on the SLD bus */
1045538dba8Smiod #define CR2_TR 0x02 /* three party conferencing */
1055538dba8Smiod #define CR2_EFC 0x01 /* enable feature control */
1065538dba8Smiod
1075538dba8Smiod /* CR3 */
1085538dba8Smiod #define CR3_MIC_G_MASK 0xe0 /* MIC input analog gain */
1095538dba8Smiod #define CR3_MIC_X_INPUT 0xe0 /* MIC disabled, X input 15.1 dB */
1105538dba8Smiod #define CR3_MIC_G_17 0xc0 /* 17 dB */
1115538dba8Smiod #define CR3_MIC_G_22 0xa0 /* 22 dB */
1125538dba8Smiod #define CR3_MIC_G_28 0x80 /* 28 dB */
1135538dba8Smiod #define CR3_MIC_G_34 0x60 /* 34 dB */
1145538dba8Smiod #define CR3_MIC_G_40 0x40 /* 40 dB */
1155538dba8Smiod #define CR3_MIC_G_46 0x20 /* 46 dB */
1165538dba8Smiod #define CR3_MIC_G_52 0x00 /* 52 dB (reset default) */
1175538dba8Smiod #define CR3_AFEC_MASK 0x1c
1185538dba8Smiod #define CR3_AFEC_MUTE 0x18 /* mute: Hout */
1195538dba8Smiod #define CR3_AFEC_HFS 0x14 /* hands free: FHM, LS out */
1205538dba8Smiod #define CR3_AFEC_LH3 0x10 /* loud hearing 3: MIC, H out, LS out */
1215538dba8Smiod #define CR3_AFEC_LH2 0x0c /* loud hearing 2: MIC, LS out */
1225538dba8Smiod #define CR3_AFEC_LH1 0x08 /* loud hearing 1: LS out */
1235538dba8Smiod #define CR3_AFEC_RDY 0x04 /* ready: MIC, H out */
1245538dba8Smiod #define CR3_AFEC_POR 0x00 /* power on reset: all off */
1255538dba8Smiod #define CR3_OPMODE_MASK 0x03
1265538dba8Smiod #define CR3_OPMODE_LINEAR 0x02 /* linear (16 bit) */
1275538dba8Smiod #define CR3_OPMODE_MIXED 0x01 /* mixed */
1285538dba8Smiod #define CR3_OPMODE_NORMAL 0x00 /* normal (A/u-Law) */
1295538dba8Smiod
1305538dba8Smiod /* CR4 */
1315538dba8Smiod #define CR4_DHF 0x80 /* TX digital high frequency enable */
1325538dba8Smiod #define CR4_DTMF 0x40 /* DTMF generator enable */
1335538dba8Smiod #define CR4_TG 0x20 /* tone ring enable */
1345538dba8Smiod #define CR4_BT 0x10 /* beat tone generator enable */
1355538dba8Smiod #define CR4_TM 0x08 /* incoming voice enable */
1365538dba8Smiod #define CR4_BM 0x04 /* beat mode (3 tone vs 2 tone) */
1375538dba8Smiod #define CR4_PM 0x02 /* tone sent to piezo vs loudspeaker */
1385538dba8Smiod #define CR4_ULAW 0x01 /* u-Law vs A-Law */
1395538dba8Smiod
1405538dba8Smiod
1415538dba8Smiod /*
1425538dba8Smiod * Glue logic registers
1435538dba8Smiod * Note the register values here are symbolic, as actual addresses
1445538dba8Smiod * depend upon the particular bus the device is connected to.
1455538dba8Smiod */
1465538dba8Smiod
1475538dba8Smiod #define ARCOFI_ID 0 /* id (r) and reset (w) register */
1485538dba8Smiod
1495538dba8Smiod #define ARCOFI_CSR 1 /* status and control register */
1505538dba8Smiod #define CSR_INTR_ENABLE 0x80
1515538dba8Smiod #define CSR_INTR_REQUEST 0x40 /* unacknowledged interrupt */
1525538dba8Smiod /* 0x20 and 0x10 used in DIO flavours, to provide IPL */
1535538dba8Smiod #define CSR_WIDTH_16 0x08 /* 16-bit samples */
1545538dba8Smiod #define CSR_CTRL_FIFO_ENABLE 0x04 /* connect FIFO to CMDR */
1555538dba8Smiod #define CSR_DATA_FIFO_ENABLE 0x01 /* connect FIFO to DU/DD */
1565538dba8Smiod
1575538dba8Smiod #define ARCOFI_FIFO_IR 2 /* FIFO interrupt register */
1585538dba8Smiod #define FIFO_IR_ENABLE(ev) ((ev) << 4)
1595538dba8Smiod #define FIFO_IR_EVENT(ev) (ev)
1605538dba8Smiod #define FIFO_IR_OUT_EMPTY 0x08
1615538dba8Smiod #define FIFO_IR_CTRL_EMPTY 0x04
1625538dba8Smiod #define FIFO_IR_OUT_HALF_EMPTY 0x02
1635538dba8Smiod #define FIFO_IR_IN_HALF_EMPTY 0x01
1645538dba8Smiod
1655538dba8Smiod #define ARCOFI_FIFO_SR 3 /* FIFO status register (ro) */
1665538dba8Smiod #define FIFO_SR_CTRL_FULL 0x20
1675538dba8Smiod #define FIFO_SR_CTRL_EMPTY 0x10
1685538dba8Smiod #define FIFO_SR_OUT_FULL 0x08
1695538dba8Smiod #define FIFO_SR_OUT_EMPTY 0x04
1705538dba8Smiod #define FIFO_SR_IN_FULL 0x02
1715538dba8Smiod #define FIFO_SR_IN_EMPTY 0x01
1725538dba8Smiod
1735538dba8Smiod #define ARCOFI_FIFO_DATA 4 /* data FIFO port */
1745538dba8Smiod
1755538dba8Smiod #define ARCOFI_FIFO_CTRL 5 /* control FIFO port (wo) */
1765538dba8Smiod
1775538dba8Smiod #define ARCOFI_FIFO_SIZE 128
1785538dba8Smiod
1795538dba8Smiod
1805538dba8Smiod struct cfdriver arcofi_cd = {
1815538dba8Smiod NULL, "arcofi", DV_DULL
1825538dba8Smiod };
1835538dba8Smiod
1845538dba8Smiod #define arcofi_read(sc, r) \
1855538dba8Smiod bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r])
1865538dba8Smiod #define arcofi_write(sc, r, v) \
1875538dba8Smiod bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (sc)->sc_reg[r], v)
1885538dba8Smiod
1895538dba8Smiod int arcofi_cmd(struct arcofi_softc *, uint8_t, const uint8_t *);
1901129e472Smiod int arcofi_cr3_to_portmask(uint, int);
1915538dba8Smiod int arcofi_gain_to_mi(uint);
1925538dba8Smiod uint arcofi_mi_to_gain(int);
1935538dba8Smiod uint arcofi_portmask_to_cr3(int);
1945538dba8Smiod int arcofi_set_param(struct arcofi_softc *, int, int, int,
1955538dba8Smiod struct audio_params *);
1965538dba8Smiod
1975538dba8Smiod void arcofi_close(void *);
1985538dba8Smiod int arcofi_commit_settings(void *);
1995538dba8Smiod int arcofi_get_port(void *, mixer_ctrl_t *);
2005538dba8Smiod int arcofi_halt_input(void *);
2015538dba8Smiod int arcofi_halt_output(void *);
2025538dba8Smiod int arcofi_open(void *, int);
2035538dba8Smiod int arcofi_query_devinfo(void *, mixer_devinfo_t *);
2045538dba8Smiod int arcofi_round_blocksize(void *, int);
2055538dba8Smiod int arcofi_set_params(void *, int, int, struct audio_params *,
2065538dba8Smiod struct audio_params *);
2075538dba8Smiod int arcofi_set_port(void *, mixer_ctrl_t *);
2085538dba8Smiod int arcofi_start_input(void *, void *, int, void (*)(void *), void *);
2095538dba8Smiod int arcofi_start_output(void *, void *, int, void (*)(void *), void *);
2105538dba8Smiod
2110d6a2fdeSmiod const struct audio_hw_if arcofi_hw_if = {
2125538dba8Smiod .open = arcofi_open,
2135538dba8Smiod .close = arcofi_close,
2145538dba8Smiod .set_params = arcofi_set_params,
2155538dba8Smiod .round_blocksize = arcofi_round_blocksize,
2165538dba8Smiod .commit_settings = arcofi_commit_settings,
2175538dba8Smiod .start_output = arcofi_start_output,
2185538dba8Smiod .start_input = arcofi_start_input,
2195538dba8Smiod .halt_output = arcofi_halt_output,
2205538dba8Smiod .halt_input = arcofi_halt_input,
2215538dba8Smiod .set_port = arcofi_set_port,
2225538dba8Smiod .get_port = arcofi_get_port,
2235538dba8Smiod .query_devinfo = arcofi_query_devinfo,
2245538dba8Smiod };
2255538dba8Smiod
2265538dba8Smiod /* mixer items */
2275538dba8Smiod #define ARCOFI_PORT_AUDIO_IN_VOLUME 0 /* line in volume (GR) */
2285538dba8Smiod #define ARCOFI_PORT_AUDIO_OUT_VOLUME 1 /* line out volume (GX) */
2295538dba8Smiod #define ARCOFI_PORT_AUDIO_SPKR_VOLUME 2 /* speaker volume (GX) */
2305538dba8Smiod #define ARCOFI_PORT_AUDIO_IN_MUTE 3 /* line in mute (MIC) */
2315538dba8Smiod #define ARCOFI_PORT_AUDIO_OUT_MUTE 4 /* line out mute (H out) */
2325538dba8Smiod #define ARCOFI_PORT_AUDIO_SPKR_MUTE 5 /* line in mute (LS out) */
2335538dba8Smiod /* mixer classes */
2345538dba8Smiod #define ARCOFI_CLASS_INPUT 6
2355538dba8Smiod #define ARCOFI_CLASS_OUTPUT 7
2365538dba8Smiod
2375538dba8Smiod /*
2385538dba8Smiod * Gain programming formulae are a complete mystery to me, and of course
2395538dba8Smiod * no two chips are compatible - not even the PSB 2163 and PSB 2165
2405538dba8Smiod * later ARCOFI chips, from the same manufacturer as the PSB 2160!
2415538dba8Smiod *
2425538dba8Smiod * Of course, the PSB 2160 datasheet does not give any set of values.
243184d26b4Smiod * The following table is taken from the HP-UX audio driver (audio_shared.o
244184d26b4Smiod * private_audio_gain_tab).
2455538dba8Smiod */
2465538dba8Smiod
247184d26b4Smiod #define NEGATIVE_GAINS 60
248184d26b4Smiod #define POSITIVE_GAINS 14
2495538dba8Smiod static const uint16_t arcofi_gains[1 + NEGATIVE_GAINS + 1 + POSITIVE_GAINS] = {
2505538dba8Smiod /* minus infinity */
251184d26b4Smiod 0x0988,
252184d26b4Smiod
253184d26b4Smiod 0xf8b8, 0xf8b8, 0xf8b8, 0xf8b8, 0x099f, 0x099f, 0x099f, 0x099f,
254184d26b4Smiod 0x09af, 0x09af, 0x09af, 0x09cf, 0x09cf, 0x09cf, 0xf8a9, 0xf83a,
255184d26b4Smiod 0xf83a, 0xf82b, 0xf82d, 0xf8a3, 0xf8b2, 0xf8a1, 0xe8aa, 0xe84b,
256184d26b4Smiod 0xe89e, 0xe8d3, 0xe891, 0xe8b1, 0xd8aa, 0xd8cb, 0xd8a6, 0xd8b3,
257184d26b4Smiod 0xd842, 0xd8b1, 0xc8aa, 0xc8bb, 0xc888, 0xc853, 0xc852, 0xc8b1,
258184d26b4Smiod 0xb8aa, 0xb8ab, 0xb896, 0xb892, 0xb842, 0xb8b1, 0xa8aa, 0xa8bb,
259184d26b4Smiod 0x199f, 0x195b, 0x29c1, 0x2923, 0x29aa, 0x392b, 0xf998, 0xb988,
260184d26b4Smiod 0x1aac, 0x3aa1, 0xbaa1, 0xbb88,
261184d26b4Smiod
2625538dba8Smiod /* 0 */
263184d26b4Smiod 0x8888,
264184d26b4Smiod
265184d26b4Smiod 0xd388, 0x5288, 0xb1a1, 0x31a1, 0x1192, 0x11d0, 0x30c0, 0x2050,
266184d26b4Smiod 0x1021, 0x1020, 0x1000, 0x0001, 0x0010, 0x0000
2675538dba8Smiod };
2685538dba8Smiod
2695538dba8Smiod int
arcofi_open(void * v,int flags)2705538dba8Smiod arcofi_open(void *v, int flags)
2715538dba8Smiod {
2725538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
2735538dba8Smiod
274*7eff0e4eSkn if ((flags & (FWRITE | FREAD)) == (FWRITE | FREAD))
275*7eff0e4eSkn return ENXIO;
2765538dba8Smiod if (sc->sc_open)
2775538dba8Smiod return EBUSY;
2785538dba8Smiod sc->sc_open = 1;
2795538dba8Smiod KASSERT(sc->sc_mode == 0);
2805538dba8Smiod
2815538dba8Smiod return 0;
2825538dba8Smiod }
2835538dba8Smiod
2845538dba8Smiod void
arcofi_close(void * v)2855538dba8Smiod arcofi_close(void *v)
2865538dba8Smiod {
2875538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
2885538dba8Smiod
2895538dba8Smiod arcofi_halt_input(v);
2905538dba8Smiod arcofi_halt_output(v);
2915538dba8Smiod sc->sc_open = 0;
2925538dba8Smiod }
2935538dba8Smiod
2945538dba8Smiod /*
2955538dba8Smiod * Compute proper sample and hardware settings. Invoked both for record
2965538dba8Smiod * and playback, as we don't support independent settings.
2975538dba8Smiod */
2985538dba8Smiod int
arcofi_set_param(struct arcofi_softc * sc,int set,int use,int mode,struct audio_params * ap)2995538dba8Smiod arcofi_set_param(struct arcofi_softc *sc, int set, int use, int mode,
3005538dba8Smiod struct audio_params *ap)
3015538dba8Smiod {
3025538dba8Smiod if ((set & mode) == 0)
3035538dba8Smiod return 0;
3045538dba8Smiod
3055538dba8Smiod #ifdef ARCOFI_DEBUG
306eff9a77cSmiod printf("%s: set_param, mode %d encoding %d precision %d\n",
307eff9a77cSmiod sc->sc_dev.dv_xname, mode, ap->encoding, ap->precision);
3085538dba8Smiod #endif
309eff9a77cSmiod switch (ap->precision) {
310eff9a77cSmiod case 8:
3115538dba8Smiod switch (ap->encoding) {
3125538dba8Smiod case AUDIO_ENCODING_ULAW:
313eff9a77cSmiod sc->sc_shadow.cr4 |= CR4_ULAW;
3145538dba8Smiod break;
3155538dba8Smiod case AUDIO_ENCODING_ALAW:
3165538dba8Smiod sc->sc_shadow.cr4 &= ~CR4_ULAW;
3175538dba8Smiod break;
3185538dba8Smiod default:
3195538dba8Smiod return EINVAL;
3205538dba8Smiod }
321eff9a77cSmiod sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
322eff9a77cSmiod CR3_OPMODE_NORMAL;
323eff9a77cSmiod break;
324eff9a77cSmiod case 16:
325eff9a77cSmiod switch (ap->encoding) {
326eff9a77cSmiod case AUDIO_ENCODING_SLINEAR_BE:
327eff9a77cSmiod break;
328eff9a77cSmiod default:
329eff9a77cSmiod return EINVAL;
330eff9a77cSmiod }
331eff9a77cSmiod sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & ~CR3_OPMODE_MASK) |
332eff9a77cSmiod CR3_OPMODE_LINEAR;
333eff9a77cSmiod break;
334eff9a77cSmiod default:
335eff9a77cSmiod return EINVAL;
336eff9a77cSmiod }
3375538dba8Smiod
338eff9a77cSmiod ap->bps = AUDIO_BPS(ap->precision);
3395538dba8Smiod ap->msb = 1;
3405538dba8Smiod ap->channels = 1;
3415538dba8Smiod ap->sample_rate = 8000;
3425538dba8Smiod
3435538dba8Smiod return 0;
3445538dba8Smiod }
3455538dba8Smiod
3465538dba8Smiod int
arcofi_set_params(void * v,int set,int use,struct audio_params * p,struct audio_params * r)3475538dba8Smiod arcofi_set_params(void *v, int set, int use, struct audio_params *p,
3485538dba8Smiod struct audio_params *r)
3495538dba8Smiod {
3505538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
3515538dba8Smiod int rc;
3525538dba8Smiod
3535538dba8Smiod if (r != NULL) {
3545538dba8Smiod rc = arcofi_set_param(sc, set, use, AUMODE_RECORD, r);
3555538dba8Smiod if (rc != 0)
3565538dba8Smiod return rc;
3575538dba8Smiod }
3585538dba8Smiod
3595538dba8Smiod if (p != NULL) {
3605538dba8Smiod rc = arcofi_set_param(sc, set, use, AUMODE_PLAY, p);
3615538dba8Smiod if (rc != 0)
3625538dba8Smiod return rc;
3635538dba8Smiod }
3645538dba8Smiod
3655538dba8Smiod return 0;
3665538dba8Smiod }
3675538dba8Smiod
3685538dba8Smiod int
arcofi_round_blocksize(void * v,int blksz)3695538dba8Smiod arcofi_round_blocksize(void *v, int blksz)
3705538dba8Smiod {
3715538dba8Smiod /*
3725538dba8Smiod * Round the size up to a multiple of half the FIFO, to favour
3735538dba8Smiod * smooth interrupt operation.
3745538dba8Smiod */
3755538dba8Smiod return roundup(blksz, ARCOFI_FIFO_SIZE / 2);
3765538dba8Smiod }
3775538dba8Smiod
3785538dba8Smiod int
arcofi_commit_settings(void * v)3795538dba8Smiod arcofi_commit_settings(void *v)
3805538dba8Smiod {
3815538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
3825538dba8Smiod int rc;
383eff9a77cSmiod uint8_t cmd[2], csr, ocsr;
3845538dba8Smiod
3855538dba8Smiod #ifdef ARCOFI_DEBUG
3861129e472Smiod printf("%s: commit_settings, gr %04x gx %04x cr3 %02x cr4 %02x mute %d\n",
3875538dba8Smiod sc->sc_dev.dv_xname,
3881129e472Smiod arcofi_gains[sc->sc_shadow.gr_idx],
3891129e472Smiod arcofi_gains[sc->sc_shadow.gx_idx],
3901129e472Smiod sc->sc_shadow.cr3, sc->sc_shadow.cr4, sc->sc_shadow.output_mute);
3915538dba8Smiod #endif
3925538dba8Smiod
3935538dba8Smiod if (bcmp(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active)) == 0)
3945538dba8Smiod return 0;
3955538dba8Smiod
396886882aaSratchov mtx_enter(&audio_lock);
3975538dba8Smiod
3985538dba8Smiod if (sc->sc_active.gr_idx != sc->sc_shadow.gr_idx) {
3995538dba8Smiod cmd[0] = arcofi_gains[sc->sc_shadow.gr_idx] >> 8;
4005538dba8Smiod cmd[1] = arcofi_gains[sc->sc_shadow.gr_idx];
4015538dba8Smiod if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0)
4025538dba8Smiod goto error;
4035538dba8Smiod sc->sc_active.gr_idx = sc->sc_shadow.gr_idx;
4045538dba8Smiod }
4055538dba8Smiod
4061129e472Smiod if (sc->sc_active.gx_idx != sc->sc_shadow.gx_idx ||
4071129e472Smiod sc->sc_active.output_mute != sc->sc_shadow.output_mute) {
4081129e472Smiod if (sc->sc_shadow.output_mute) {
4091129e472Smiod cmd[0] = arcofi_gains[0] >> 8;
4101129e472Smiod cmd[1] = arcofi_gains[0];
4111129e472Smiod } else {
4125538dba8Smiod cmd[0] = arcofi_gains[sc->sc_shadow.gx_idx] >> 8;
4135538dba8Smiod cmd[1] = arcofi_gains[sc->sc_shadow.gx_idx];
4141129e472Smiod }
4155538dba8Smiod if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0)
4165538dba8Smiod goto error;
4175538dba8Smiod sc->sc_active.gx_idx = sc->sc_shadow.gx_idx;
4181129e472Smiod sc->sc_active.output_mute = sc->sc_shadow.output_mute;
4195538dba8Smiod }
4205538dba8Smiod
4215538dba8Smiod if (sc->sc_active.cr3 != sc->sc_shadow.cr3) {
4225538dba8Smiod cmd[0] = sc->sc_shadow.cr3;
4235538dba8Smiod if ((rc = arcofi_cmd(sc, SOP_6, cmd)) != 0)
4245538dba8Smiod goto error;
4255538dba8Smiod sc->sc_active.cr3 = sc->sc_shadow.cr3;
426eff9a77cSmiod
427eff9a77cSmiod ocsr = arcofi_read(sc, ARCOFI_CSR);
428eff9a77cSmiod if ((sc->sc_active.cr3 & CR3_OPMODE_MASK) != CR3_OPMODE_NORMAL)
429eff9a77cSmiod csr = ocsr | CSR_WIDTH_16;
430eff9a77cSmiod else
431eff9a77cSmiod csr = ocsr & ~CSR_WIDTH_16;
432eff9a77cSmiod if (csr != ocsr)
433eff9a77cSmiod arcofi_write(sc, ARCOFI_CSR, csr);
4345538dba8Smiod }
4355538dba8Smiod
4365538dba8Smiod if (sc->sc_active.cr4 != sc->sc_shadow.cr4) {
4375538dba8Smiod cmd[0] = sc->sc_shadow.cr4;
4385538dba8Smiod if ((rc = arcofi_cmd(sc, SOP_7, cmd)) != 0)
4395538dba8Smiod goto error;
4405538dba8Smiod sc->sc_active.cr4 = sc->sc_shadow.cr4;
4415538dba8Smiod }
4425538dba8Smiod
443eff9a77cSmiod rc = 0;
4445538dba8Smiod error:
445886882aaSratchov mtx_leave(&audio_lock);
4465538dba8Smiod return rc;
4475538dba8Smiod }
4485538dba8Smiod
4495538dba8Smiod int
arcofi_start_input(void * v,void * rbuf,int rsz,void (* cb)(void *),void * cbarg)4505538dba8Smiod arcofi_start_input(void *v, void *rbuf, int rsz, void (*cb)(void *),
4515538dba8Smiod void *cbarg)
4525538dba8Smiod {
4535538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
4545538dba8Smiod
4555538dba8Smiod #ifdef ARCOFI_DEBUG
4565538dba8Smiod printf("%s: start_input, mode %d\n",
4575538dba8Smiod sc->sc_dev.dv_xname, sc->sc_mode);
4585538dba8Smiod #endif
4595538dba8Smiod
4605538dba8Smiod /* enable data FIFO if becoming active */
4615538dba8Smiod if (sc->sc_mode == 0)
4625538dba8Smiod arcofi_write(sc, ARCOFI_CSR,
4635538dba8Smiod arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE);
4645538dba8Smiod sc->sc_mode |= AUMODE_RECORD;
4655538dba8Smiod
4665538dba8Smiod sc->sc_recv.buf = (uint8_t *)rbuf;
4675538dba8Smiod sc->sc_recv.past = (uint8_t *)rbuf + rsz;
4685538dba8Smiod sc->sc_recv.cb = cb;
4695538dba8Smiod sc->sc_recv.cbarg = cbarg;
4705538dba8Smiod
4715538dba8Smiod /* enable input FIFO interrupts */
472eff9a77cSmiod arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
4735538dba8Smiod FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
4745538dba8Smiod
4755538dba8Smiod return 0;
4765538dba8Smiod }
4775538dba8Smiod
4785538dba8Smiod int
arcofi_start_output(void * v,void * wbuf,int wsz,void (* cb)(void *),void * cbarg)4795538dba8Smiod arcofi_start_output(void *v, void *wbuf, int wsz, void (*cb)(void *),
4805538dba8Smiod void *cbarg)
4815538dba8Smiod {
4825538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
4835538dba8Smiod
4845538dba8Smiod #ifdef ARCOFI_DEBUG
4855538dba8Smiod printf("%s: start_output, mode %d\n",
4865538dba8Smiod sc->sc_dev.dv_xname, sc->sc_mode);
4875538dba8Smiod #endif
4885538dba8Smiod
4895538dba8Smiod /* enable data FIFO if becoming active */
4905538dba8Smiod if (sc->sc_mode == 0)
4915538dba8Smiod arcofi_write(sc, ARCOFI_CSR,
4925538dba8Smiod arcofi_read(sc, ARCOFI_CSR) | CSR_DATA_FIFO_ENABLE);
4935538dba8Smiod sc->sc_mode |= AUMODE_PLAY;
4945538dba8Smiod
4955538dba8Smiod sc->sc_xmit.buf = (uint8_t *)wbuf;
4965538dba8Smiod sc->sc_xmit.past = (uint8_t *)wbuf + wsz;
4975538dba8Smiod sc->sc_xmit.cb = cb;
4985538dba8Smiod sc->sc_xmit.cbarg = cbarg;
4995538dba8Smiod
5005538dba8Smiod /* enable output FIFO interrupts */
501eff9a77cSmiod arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) |
5025538dba8Smiod FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
5035538dba8Smiod
5045538dba8Smiod return 0;
5055538dba8Smiod }
5065538dba8Smiod
5075538dba8Smiod int
arcofi_halt_input(void * v)5085538dba8Smiod arcofi_halt_input(void *v)
5095538dba8Smiod {
5105538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
5115538dba8Smiod
512886882aaSratchov mtx_enter(&audio_lock);
5135538dba8Smiod
5145538dba8Smiod /* disable input FIFO interrupts */
515eff9a77cSmiod arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) &
5165538dba8Smiod ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
5175538dba8Smiod /* disable data FIFO if becoming idle */
5185538dba8Smiod sc->sc_mode &= ~AUMODE_RECORD;
5195538dba8Smiod if (sc->sc_mode == 0)
5205538dba8Smiod arcofi_write(sc, ARCOFI_CSR,
5215538dba8Smiod arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE);
5225538dba8Smiod
523886882aaSratchov mtx_leave(&audio_lock);
5245538dba8Smiod return 0;
5255538dba8Smiod }
5265538dba8Smiod
5275538dba8Smiod int
arcofi_halt_output(void * v)5285538dba8Smiod arcofi_halt_output(void *v)
5295538dba8Smiod {
5305538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
5315538dba8Smiod
532886882aaSratchov mtx_enter(&audio_lock);
5335538dba8Smiod
5345538dba8Smiod /* disable output FIFO interrupts */
535eff9a77cSmiod arcofi_write(sc, ARCOFI_FIFO_IR, arcofi_read(sc, ARCOFI_FIFO_IR) &
5365538dba8Smiod ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
5375538dba8Smiod /* disable data FIFO if becoming idle */
5385538dba8Smiod sc->sc_mode &= ~AUMODE_PLAY;
5395538dba8Smiod if (sc->sc_mode == 0)
5405538dba8Smiod arcofi_write(sc, ARCOFI_CSR,
5415538dba8Smiod arcofi_read(sc, ARCOFI_CSR) & ~CSR_DATA_FIFO_ENABLE);
5425538dba8Smiod
543886882aaSratchov mtx_leave(&audio_lock);
5445538dba8Smiod return 0;
5455538dba8Smiod }
5465538dba8Smiod
5475538dba8Smiod /*
5485538dba8Smiod * Convert gain table index to AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale.
5495538dba8Smiod */
5505538dba8Smiod int
arcofi_gain_to_mi(uint idx)5515538dba8Smiod arcofi_gain_to_mi(uint idx)
5525538dba8Smiod {
5535538dba8Smiod if (idx == 0)
5545538dba8Smiod return AUDIO_MIN_GAIN;
5555538dba8Smiod if (idx == nitems(arcofi_gains) - 1)
5565538dba8Smiod return AUDIO_MAX_GAIN;
5575538dba8Smiod
5581129e472Smiod return ((idx - 1) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) /
5591129e472Smiod (nitems(arcofi_gains) - 1) + AUDIO_MIN_GAIN + 1;
5605538dba8Smiod }
5615538dba8Smiod
5625538dba8Smiod /*
563eff9a77cSmiod * Convert AUDIO_MIN_GAIN..AUDIO_MAX_GAIN scale to gain table index.
5645538dba8Smiod */
5655538dba8Smiod uint
arcofi_mi_to_gain(int lvl)5665538dba8Smiod arcofi_mi_to_gain(int lvl)
5675538dba8Smiod {
5685538dba8Smiod if (lvl <= AUDIO_MIN_GAIN)
5695538dba8Smiod return 0;
5705538dba8Smiod if (lvl >= AUDIO_MAX_GAIN)
5715538dba8Smiod return nitems(arcofi_gains) - 1;
5725538dba8Smiod
5731129e472Smiod return ((lvl - AUDIO_MIN_GAIN - 1) * (nitems(arcofi_gains) - 1)) /
5741129e472Smiod (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN);
5755538dba8Smiod }
5765538dba8Smiod
5775538dba8Smiod /*
578b5445af2Smiod * Input and output ports definition (used to be in <audioio.h>)
5795538dba8Smiod */
58086757eeeSmiod #define AUDIO_SPEAKER 0x01 /* built-in speaker */
58186757eeeSmiod #define AUDIO_LINE_IN 0x02 /* line in */
58286757eeeSmiod #define AUDIO_LINE_OUT 0x04 /* line out */
5835538dba8Smiod
5845538dba8Smiod /*
5855538dba8Smiod * The mapping between the available inputs and outputs, and CR3, is as
5865538dba8Smiod * follows:
5875538dba8Smiod * - the `line in' connector is the `MIC' input.
5885538dba8Smiod * - the `line out' connector is the `H out' (heaphones) output.
5895538dba8Smiod * - the internal `speaker' is the `LS out' (loudspeaker) output.
5905538dba8Smiod *
591a349d0bbSmiod * Each of these can be enabled or disabled independently, except for
5925538dba8Smiod * MIC enabled with H out and LS out disabled, which is not allowed
5935538dba8Smiod * by the chip (and makes no sense for a chip which was intended to
5941129e472Smiod * be used in phones, not voice recorders); we cheat by keeping one
5951129e472Smiod * output source enabled, but with the output gain forced to minus
5961129e472Smiod * infinity to mute it.
5975538dba8Smiod *
5985538dba8Smiod * The truth table is thus:
5995538dba8Smiod *
6005538dba8Smiod * MIC LS out H out AFEC
6015538dba8Smiod * off off off POR
6025538dba8Smiod * off off on MUTE
6035538dba8Smiod * off on off LH1
6045538dba8Smiod * off on on LH3, X input enabled
6051129e472Smiod * on off off RDY, GX forced to minus infinity
6065538dba8Smiod * on off on RDY
6075538dba8Smiod * on on off LH2
6085538dba8Smiod * on on on LH3
6095538dba8Smiod */
6105538dba8Smiod
6115538dba8Smiod /*
6125538dba8Smiod * Convert logical port enable settings to a valid CR3 value.
6135538dba8Smiod */
6145538dba8Smiod uint
arcofi_portmask_to_cr3(int mask)6155538dba8Smiod arcofi_portmask_to_cr3(int mask)
6165538dba8Smiod {
6175538dba8Smiod switch (mask) {
6185538dba8Smiod default:
6195538dba8Smiod case 0:
620184d26b4Smiod return CR3_MIC_G_17 | CR3_AFEC_POR;
6215538dba8Smiod case AUDIO_LINE_OUT:
622184d26b4Smiod return CR3_MIC_G_17 | CR3_AFEC_MUTE;
6235538dba8Smiod case AUDIO_SPEAKER:
624184d26b4Smiod return CR3_MIC_G_17 | CR3_AFEC_LH1;
6255538dba8Smiod case AUDIO_SPEAKER | AUDIO_LINE_OUT:
6265538dba8Smiod return CR3_MIC_X_INPUT | CR3_AFEC_LH3;
6271129e472Smiod case AUDIO_LINE_IN:
6281129e472Smiod /* since we can't do this, just... */
6291129e472Smiod /* FALLTHROUGH */
6305538dba8Smiod case AUDIO_LINE_IN | AUDIO_LINE_OUT:
631184d26b4Smiod return CR3_MIC_G_17 | CR3_AFEC_RDY;
6325538dba8Smiod case AUDIO_LINE_IN | AUDIO_SPEAKER:
633184d26b4Smiod return CR3_MIC_G_17 | CR3_AFEC_LH2;
6345538dba8Smiod case AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT:
635184d26b4Smiod return CR3_MIC_G_17 | CR3_AFEC_LH3;
6365538dba8Smiod }
6375538dba8Smiod }
6385538dba8Smiod
6395538dba8Smiod /*
6405538dba8Smiod * Convert CR3 to an enabled ports mask.
6415538dba8Smiod */
6425538dba8Smiod int
arcofi_cr3_to_portmask(uint cr3,int output_mute)6431129e472Smiod arcofi_cr3_to_portmask(uint cr3, int output_mute)
6445538dba8Smiod {
6455538dba8Smiod switch (cr3 & CR3_AFEC_MASK) {
6465538dba8Smiod default:
6475538dba8Smiod case CR3_AFEC_POR:
6485538dba8Smiod return 0;
6495538dba8Smiod case CR3_AFEC_RDY:
6501129e472Smiod return output_mute ?
6511129e472Smiod AUDIO_LINE_IN : AUDIO_LINE_IN | AUDIO_LINE_OUT;
6525538dba8Smiod case CR3_AFEC_HFS:
6535538dba8Smiod case CR3_AFEC_LH1:
6545538dba8Smiod return AUDIO_SPEAKER;
6555538dba8Smiod case CR3_AFEC_LH2:
6565538dba8Smiod return AUDIO_LINE_IN | AUDIO_SPEAKER;
6575538dba8Smiod case CR3_AFEC_LH3:
6585538dba8Smiod if ((cr3 & CR3_MIC_G_MASK) == CR3_MIC_X_INPUT)
6595538dba8Smiod return AUDIO_SPEAKER | AUDIO_LINE_OUT;
6605538dba8Smiod else
6615538dba8Smiod return AUDIO_LINE_IN | AUDIO_SPEAKER | AUDIO_LINE_OUT;
6625538dba8Smiod case CR3_AFEC_MUTE:
6635538dba8Smiod return AUDIO_LINE_OUT;
6645538dba8Smiod }
6655538dba8Smiod }
6665538dba8Smiod
6675538dba8Smiod int
arcofi_set_port(void * v,mixer_ctrl_t * mc)6685538dba8Smiod arcofi_set_port(void *v, mixer_ctrl_t *mc)
6695538dba8Smiod {
6705538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
6715538dba8Smiod int portmask;
6725538dba8Smiod
6735538dba8Smiod /* check for proper type */
6745538dba8Smiod switch (mc->dev) {
6755538dba8Smiod /* volume settings */
6765538dba8Smiod case ARCOFI_PORT_AUDIO_IN_VOLUME:
6775538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_VOLUME:
6785538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
6795538dba8Smiod if (mc->un.value.num_channels != 1)
6805538dba8Smiod return EINVAL;
6815538dba8Smiod break;
6825538dba8Smiod /* mute settings */
6835538dba8Smiod case ARCOFI_PORT_AUDIO_IN_MUTE:
6845538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_MUTE:
6855538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_MUTE:
6865538dba8Smiod if (mc->type != AUDIO_MIXER_ENUM)
6875538dba8Smiod return EINVAL;
6881129e472Smiod portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3,
6891129e472Smiod sc->sc_shadow.output_mute);
6905538dba8Smiod #ifdef ARCOFI_DEBUG
6915538dba8Smiod printf("%s: set_port cr3 %02x -> mask %02x\n",
6925538dba8Smiod sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask);
6935538dba8Smiod #endif
6945538dba8Smiod break;
6955538dba8Smiod default:
6965538dba8Smiod return EINVAL;
6975538dba8Smiod }
6985538dba8Smiod
6995538dba8Smiod switch (mc->dev) {
700eff9a77cSmiod /* volume settings */
7015538dba8Smiod case ARCOFI_PORT_AUDIO_IN_VOLUME:
7025538dba8Smiod sc->sc_shadow.gr_idx =
7035538dba8Smiod arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
7045538dba8Smiod return 0;
7055538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_VOLUME:
7065538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
7075538dba8Smiod sc->sc_shadow.gx_idx =
7085538dba8Smiod arcofi_mi_to_gain(mc->un.value.level[AUDIO_MIXER_LEVEL_MONO]);
7095538dba8Smiod return 0;
7105538dba8Smiod
711eff9a77cSmiod /* mute settings */
7125538dba8Smiod case ARCOFI_PORT_AUDIO_IN_MUTE:
7135538dba8Smiod if (mc->un.ord)
7145538dba8Smiod portmask &= ~AUDIO_LINE_IN;
7155538dba8Smiod else
7165538dba8Smiod portmask |= AUDIO_LINE_IN;
7175538dba8Smiod break;
7185538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_MUTE:
7195538dba8Smiod if (mc->un.ord)
7205538dba8Smiod portmask &= ~AUDIO_LINE_OUT;
7215538dba8Smiod else
7225538dba8Smiod portmask |= AUDIO_LINE_OUT;
7235538dba8Smiod break;
7245538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_MUTE:
7255538dba8Smiod if (mc->un.ord)
7265538dba8Smiod portmask &= ~AUDIO_SPEAKER;
7275538dba8Smiod else
7285538dba8Smiod portmask |= AUDIO_SPEAKER;
7295538dba8Smiod break;
7305538dba8Smiod }
7315538dba8Smiod
7325538dba8Smiod sc->sc_shadow.cr3 = (sc->sc_shadow.cr3 & CR3_OPMODE_MASK) |
7335538dba8Smiod arcofi_portmask_to_cr3(portmask);
7341129e472Smiod sc->sc_shadow.output_mute = (portmask == AUDIO_LINE_IN);
7355538dba8Smiod #ifdef ARCOFI_DEBUG
7361129e472Smiod printf("%s: set_port mask %02x -> cr3 %02x m %d\n",
7371129e472Smiod sc->sc_dev.dv_xname, portmask,
7381129e472Smiod sc->sc_shadow.cr3, sc->sc_shadow.output_mute);
7395538dba8Smiod #endif
7405538dba8Smiod
7415538dba8Smiod return 0;
7425538dba8Smiod }
7435538dba8Smiod
7445538dba8Smiod int
arcofi_get_port(void * v,mixer_ctrl_t * mc)7455538dba8Smiod arcofi_get_port(void *v, mixer_ctrl_t *mc)
7465538dba8Smiod {
7475538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
7485538dba8Smiod int portmask;
7495538dba8Smiod
7505538dba8Smiod /* check for proper type */
7515538dba8Smiod switch (mc->dev) {
7525538dba8Smiod /* volume settings */
7535538dba8Smiod case ARCOFI_PORT_AUDIO_IN_VOLUME:
7545538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_VOLUME:
7555538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
7565538dba8Smiod if (mc->un.value.num_channels != 1)
7575538dba8Smiod return EINVAL;
7585538dba8Smiod break;
759eff9a77cSmiod
7605538dba8Smiod /* mute settings */
7615538dba8Smiod case ARCOFI_PORT_AUDIO_IN_MUTE:
7625538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_MUTE:
7635538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_MUTE:
7645538dba8Smiod if (mc->type != AUDIO_MIXER_ENUM)
7655538dba8Smiod return EINVAL;
7661129e472Smiod portmask = arcofi_cr3_to_portmask(sc->sc_shadow.cr3,
7671129e472Smiod sc->sc_shadow.output_mute);
7685538dba8Smiod #ifdef ARCOFI_DEBUG
7695538dba8Smiod printf("%s: get_port cr3 %02x -> mask %02x\n",
7705538dba8Smiod sc->sc_dev.dv_xname, sc->sc_shadow.cr3, portmask);
7715538dba8Smiod #endif
7725538dba8Smiod break;
7735538dba8Smiod default:
7745538dba8Smiod return EINVAL;
7755538dba8Smiod }
7765538dba8Smiod
7775538dba8Smiod switch (mc->dev) {
778eff9a77cSmiod /* volume settings */
7795538dba8Smiod case ARCOFI_PORT_AUDIO_IN_VOLUME:
7805538dba8Smiod mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
7815538dba8Smiod arcofi_gain_to_mi(sc->sc_shadow.gr_idx);
7825538dba8Smiod break;
7835538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_VOLUME:
7845538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
7855538dba8Smiod mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
7865538dba8Smiod arcofi_gain_to_mi(sc->sc_shadow.gx_idx);
7875538dba8Smiod break;
788eff9a77cSmiod
789eff9a77cSmiod /* mute settings */
7905538dba8Smiod case ARCOFI_PORT_AUDIO_IN_MUTE:
7915538dba8Smiod mc->un.ord = portmask & AUDIO_LINE_IN ? 0 : 1;
7925538dba8Smiod break;
7935538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_MUTE:
7945538dba8Smiod mc->un.ord = portmask & AUDIO_LINE_OUT ? 0 : 1;
7955538dba8Smiod break;
7965538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_MUTE:
7975538dba8Smiod mc->un.ord = portmask & AUDIO_SPEAKER ? 0 : 1;
7985538dba8Smiod break;
7995538dba8Smiod }
8005538dba8Smiod
8015538dba8Smiod return 0;
8025538dba8Smiod }
8035538dba8Smiod
8045538dba8Smiod int
arcofi_query_devinfo(void * v,mixer_devinfo_t * md)8055538dba8Smiod arcofi_query_devinfo(void *v, mixer_devinfo_t *md)
8065538dba8Smiod {
8075538dba8Smiod switch (md->index) {
8085538dba8Smiod default:
8095538dba8Smiod return ENXIO;
8105538dba8Smiod
8115538dba8Smiod /* items */
8125538dba8Smiod case ARCOFI_PORT_AUDIO_IN_VOLUME:
8135538dba8Smiod md->type = AUDIO_MIXER_VALUE;
8145538dba8Smiod md->mixer_class = ARCOFI_CLASS_INPUT;
8155538dba8Smiod md->prev = AUDIO_MIXER_LAST;
8165538dba8Smiod md->next = ARCOFI_PORT_AUDIO_IN_MUTE;
8175538dba8Smiod strlcpy(md->label.name, AudioNline,
8185538dba8Smiod sizeof md->label.name);
819eff9a77cSmiod goto mono_volume;
8205538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_VOLUME:
8215538dba8Smiod md->type = AUDIO_MIXER_VALUE;
8225538dba8Smiod md->mixer_class = ARCOFI_CLASS_OUTPUT;
8235538dba8Smiod md->prev = AUDIO_MIXER_LAST;
8245538dba8Smiod md->next = ARCOFI_PORT_AUDIO_OUT_MUTE;
8255538dba8Smiod strlcpy(md->label.name, AudioNline,
8265538dba8Smiod sizeof md->label.name);
827eff9a77cSmiod goto mono_volume;
8285538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_VOLUME:
8295538dba8Smiod md->type = AUDIO_MIXER_VALUE;
8305538dba8Smiod md->mixer_class = ARCOFI_CLASS_OUTPUT;
8315538dba8Smiod md->prev = AUDIO_MIXER_LAST;
8325538dba8Smiod md->next = ARCOFI_PORT_AUDIO_SPKR_MUTE;
8335538dba8Smiod strlcpy(md->label.name, AudioNspeaker,
8345538dba8Smiod sizeof md->label.name);
835eff9a77cSmiod /* goto mono_volume; */
836eff9a77cSmiod mono_volume:
8375538dba8Smiod md->un.v.num_channels = 1;
8385538dba8Smiod strlcpy(md->un.v.units.name, AudioNvolume,
8395538dba8Smiod sizeof md->un.v.units.name);
8405538dba8Smiod break;
8415538dba8Smiod
8425538dba8Smiod case ARCOFI_PORT_AUDIO_IN_MUTE:
8435538dba8Smiod md->type = AUDIO_MIXER_ENUM;
8445538dba8Smiod md->mixer_class = ARCOFI_CLASS_INPUT;
8455538dba8Smiod md->prev = ARCOFI_PORT_AUDIO_IN_VOLUME;
8465538dba8Smiod md->next = AUDIO_MIXER_LAST;
8475538dba8Smiod goto mute;
8485538dba8Smiod case ARCOFI_PORT_AUDIO_OUT_MUTE:
8495538dba8Smiod md->type = AUDIO_MIXER_ENUM;
8505538dba8Smiod md->mixer_class = ARCOFI_CLASS_OUTPUT;
8515538dba8Smiod md->prev = ARCOFI_PORT_AUDIO_OUT_VOLUME;
8525538dba8Smiod md->next = AUDIO_MIXER_LAST;
8535538dba8Smiod goto mute;
8545538dba8Smiod case ARCOFI_PORT_AUDIO_SPKR_MUTE:
8555538dba8Smiod md->type = AUDIO_MIXER_ENUM;
8565538dba8Smiod md->mixer_class = ARCOFI_CLASS_OUTPUT;
8575538dba8Smiod md->prev = ARCOFI_PORT_AUDIO_SPKR_VOLUME;
8585538dba8Smiod md->next = AUDIO_MIXER_LAST;
8595538dba8Smiod /* goto mute; */
8605538dba8Smiod mute:
8615538dba8Smiod strlcpy(md->label.name, AudioNmute, sizeof md->label.name);
8625538dba8Smiod md->un.e.num_mem = 2;
8635538dba8Smiod strlcpy(md->un.e.member[0].label.name, AudioNoff,
8645538dba8Smiod sizeof md->un.e.member[0].label.name);
8655538dba8Smiod md->un.e.member[0].ord = 0;
8665538dba8Smiod strlcpy(md->un.e.member[1].label.name, AudioNon,
8675538dba8Smiod sizeof md->un.e.member[1].label.name);
8685538dba8Smiod md->un.e.member[1].ord = 1;
8695538dba8Smiod break;
8705538dba8Smiod
8715538dba8Smiod /* classes */
8725538dba8Smiod case ARCOFI_CLASS_INPUT:
8735538dba8Smiod md->type = AUDIO_MIXER_CLASS;
8745538dba8Smiod md->mixer_class = ARCOFI_CLASS_INPUT;
8755538dba8Smiod md->prev = AUDIO_MIXER_LAST;
8765538dba8Smiod md->next = AUDIO_MIXER_LAST;
8775538dba8Smiod strlcpy(md->label.name, AudioCinputs,
8785538dba8Smiod sizeof md->label.name);
8795538dba8Smiod break;
8805538dba8Smiod case ARCOFI_CLASS_OUTPUT:
8815538dba8Smiod md->type = AUDIO_MIXER_CLASS;
8825538dba8Smiod md->mixer_class = ARCOFI_CLASS_OUTPUT;
8835538dba8Smiod md->prev = AUDIO_MIXER_LAST;
8845538dba8Smiod md->next = AUDIO_MIXER_LAST;
8855538dba8Smiod strlcpy(md->label.name, AudioCoutputs,
8865538dba8Smiod sizeof md->label.name);
8875538dba8Smiod break;
8885538dba8Smiod }
8895538dba8Smiod
8905538dba8Smiod return 0;
8915538dba8Smiod }
8925538dba8Smiod
8935538dba8Smiod int
arcofi_hwintr(void * v)8945538dba8Smiod arcofi_hwintr(void *v)
8955538dba8Smiod {
8965538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
8975538dba8Smiod uint8_t *cur, *past;
8985538dba8Smiod uint8_t csr, fir, data;
8995538dba8Smiod int rc = 0;
9005538dba8Smiod
9015538dba8Smiod csr = arcofi_read(sc, ARCOFI_CSR);
9025538dba8Smiod if ((csr & CSR_INTR_REQUEST) == 0)
9035538dba8Smiod return 0;
9045538dba8Smiod
9055538dba8Smiod fir = arcofi_read(sc, ARCOFI_FIFO_IR);
9065538dba8Smiod
9075538dba8Smiod /* receive */
9085538dba8Smiod if (fir & FIFO_IR_EVENT(FIFO_IR_IN_HALF_EMPTY)) {
9095538dba8Smiod rc = 1;
9105538dba8Smiod cur = sc->sc_recv.buf;
9115538dba8Smiod past = sc->sc_recv.past;
9125538dba8Smiod
9135538dba8Smiod while ((arcofi_read(sc, ARCOFI_FIFO_SR) &
9145538dba8Smiod FIFO_SR_IN_EMPTY) == 0) {
9155538dba8Smiod data = arcofi_read(sc, ARCOFI_FIFO_DATA);
9165538dba8Smiod if (cur != NULL && cur != past) {
9175538dba8Smiod *cur++ = data;
9185538dba8Smiod if (cur == past) {
9195538dba8Smiod softintr_schedule(sc->sc_sih);
9205538dba8Smiod break;
9215538dba8Smiod }
9225538dba8Smiod }
9235538dba8Smiod }
9245538dba8Smiod sc->sc_recv.buf = cur;
9255538dba8Smiod
9265538dba8Smiod if (cur == NULL || cur == past) {
9275538dba8Smiod /* underrun, disable further interrupts */
9285538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_IR,
9295538dba8Smiod arcofi_read(sc, ARCOFI_FIFO_IR) &
9305538dba8Smiod ~FIFO_IR_ENABLE(FIFO_IR_IN_HALF_EMPTY));
9315538dba8Smiod }
9325538dba8Smiod }
9335538dba8Smiod
9345538dba8Smiod /* xmit */
9355538dba8Smiod if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_HALF_EMPTY)) {
9365538dba8Smiod rc = 1;
9375538dba8Smiod cur = sc->sc_xmit.buf;
9385538dba8Smiod past = sc->sc_xmit.past;
9395538dba8Smiod if (cur != NULL) {
9405538dba8Smiod while ((arcofi_read(sc, ARCOFI_FIFO_SR) &
9415538dba8Smiod FIFO_SR_OUT_FULL) == 0) {
9425538dba8Smiod if (cur != past)
9435538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_DATA,
9445538dba8Smiod *cur++);
9455538dba8Smiod if (cur == past) {
9465538dba8Smiod softintr_schedule(sc->sc_sih);
9475538dba8Smiod break;
9485538dba8Smiod }
9495538dba8Smiod }
9505538dba8Smiod }
9515538dba8Smiod if (cur == NULL || cur == past) {
9525538dba8Smiod /* disable further interrupts */
9535538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_IR,
9545538dba8Smiod arcofi_read(sc, ARCOFI_FIFO_IR) &
9555538dba8Smiod ~FIFO_IR_ENABLE(FIFO_IR_OUT_HALF_EMPTY));
9565538dba8Smiod }
9575538dba8Smiod sc->sc_xmit.buf = cur;
9585538dba8Smiod }
9595538dba8Smiod
9605538dba8Smiod /* drain */
9615538dba8Smiod if (fir & FIFO_IR_EVENT(FIFO_IR_OUT_EMPTY)) {
9625538dba8Smiod rc = 1;
9635538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_IR,
9645538dba8Smiod arcofi_read(sc, ARCOFI_FIFO_IR) &
9655538dba8Smiod ~FIFO_IR_ENABLE(FIFO_IR_OUT_EMPTY));
9665538dba8Smiod wakeup(&sc->sc_xmit);
9675538dba8Smiod }
9685538dba8Smiod
9695538dba8Smiod #ifdef ARCOFI_DEBUG
9705538dba8Smiod if (rc == 0)
9715538dba8Smiod printf("%s: unclaimed interrupt, csr %02x fir %02x fsr %02x\n",
9725538dba8Smiod sc->sc_dev.dv_xname, csr, fir,
9735538dba8Smiod arcofi_read(sc, ARCOFI_FIFO_SR));
9745538dba8Smiod #endif
9755538dba8Smiod
9765538dba8Smiod return rc;
9775538dba8Smiod }
9785538dba8Smiod
9795538dba8Smiod void
arcofi_swintr(void * v)9805538dba8Smiod arcofi_swintr(void *v)
9815538dba8Smiod {
9825538dba8Smiod struct arcofi_softc *sc = (struct arcofi_softc *)v;
983886882aaSratchov int action;
9845538dba8Smiod
9855538dba8Smiod action = 0;
986886882aaSratchov mtx_enter(&audio_lock);
9875538dba8Smiod if (sc->sc_recv.buf != NULL && sc->sc_recv.buf == sc->sc_recv.past)
9885538dba8Smiod action |= AUMODE_RECORD;
9895538dba8Smiod if (sc->sc_xmit.buf != NULL && sc->sc_xmit.buf == sc->sc_xmit.past)
9905538dba8Smiod action |= AUMODE_PLAY;
9915538dba8Smiod
9925538dba8Smiod if (action & AUMODE_RECORD) {
9935538dba8Smiod if (sc->sc_recv.cb)
9945538dba8Smiod sc->sc_recv.cb(sc->sc_recv.cbarg);
9955538dba8Smiod }
9965538dba8Smiod if (action & AUMODE_PLAY) {
9975538dba8Smiod if (sc->sc_xmit.cb)
9985538dba8Smiod sc->sc_xmit.cb(sc->sc_xmit.cbarg);
9995538dba8Smiod }
10000cf84469Sratchov mtx_leave(&audio_lock);
10015538dba8Smiod }
10025538dba8Smiod
10035538dba8Smiod int
arcofi_cmd(struct arcofi_softc * sc,uint8_t cmd,const uint8_t * data)10045538dba8Smiod arcofi_cmd(struct arcofi_softc *sc, uint8_t cmd, const uint8_t *data)
10055538dba8Smiod {
10065538dba8Smiod size_t len;
10075538dba8Smiod uint8_t csr;
10085538dba8Smiod int cnt;
10095538dba8Smiod static const uint8_t cmdlen[] = {
10105538dba8Smiod [SOP_0] = 4,
10115538dba8Smiod [COP_1] = 4,
10125538dba8Smiod [COP_2] = 2,
10135538dba8Smiod [COP_3] = 2,
10145538dba8Smiod [SOP_4] = 1,
10155538dba8Smiod [SOP_5] = 1,
10165538dba8Smiod [SOP_6] = 1,
10175538dba8Smiod [SOP_7] = 1,
10185538dba8Smiod [COP_8] = 2,
10195538dba8Smiod [COP_9] = 4,
10205538dba8Smiod [COP_A] = 8,
10215538dba8Smiod [COP_B] = 2,
10225538dba8Smiod [COP_C] = 8,
10235538dba8Smiod [COP_D] = 4,
10245538dba8Smiod [COP_E] = 4
10255538dba8Smiod };
10265538dba8Smiod
10275538dba8Smiod /*
10285538dba8Smiod * Compute command length.
10295538dba8Smiod */
10305538dba8Smiod if (cmd >= nitems(cmdlen))
10315538dba8Smiod return EINVAL;
10325538dba8Smiod len = cmdlen[cmd];
10335538dba8Smiod
1034886882aaSratchov mtx_enter(&audio_lock);
10355538dba8Smiod
10365538dba8Smiod /*
10375538dba8Smiod * Disable all FIFO processing.
10385538dba8Smiod */
10395538dba8Smiod csr = arcofi_read(sc, ARCOFI_CSR);
10405538dba8Smiod arcofi_write(sc, ARCOFI_CSR,
10415538dba8Smiod csr & ~(CSR_DATA_FIFO_ENABLE | CSR_CTRL_FIFO_ENABLE));
10425538dba8Smiod
10435538dba8Smiod /*
10445538dba8Smiod * Fill the FIFO with the command bytes.
10455538dba8Smiod */
10465538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_CTRL, CMDR_PU | CMDR_WRITE | cmd);
10475538dba8Smiod for (; len != 0; len--)
10485538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_CTRL, *data++);
10495538dba8Smiod
10505538dba8Smiod /*
10515538dba8Smiod * Enable command processing.
10525538dba8Smiod */
10535538dba8Smiod arcofi_write(sc, ARCOFI_CSR,
10545538dba8Smiod (csr & ~CSR_DATA_FIFO_ENABLE) | CSR_CTRL_FIFO_ENABLE);
10555538dba8Smiod
10565538dba8Smiod /*
10575538dba8Smiod * Wait for the command FIFO to be empty.
10585538dba8Smiod */
10595538dba8Smiod cnt = 100;
10605538dba8Smiod while ((arcofi_read(sc, ARCOFI_FIFO_SR) & FIFO_SR_CTRL_EMPTY) == 0) {
1061886882aaSratchov if (cnt-- == 0) {
1062886882aaSratchov mtx_leave(&audio_lock);
10635538dba8Smiod return EBUSY;
1064886882aaSratchov }
10655538dba8Smiod delay(10);
10665538dba8Smiod }
10675538dba8Smiod
10685538dba8Smiod arcofi_write(sc, ARCOFI_CSR, csr);
10695538dba8Smiod
1070886882aaSratchov mtx_leave(&audio_lock);
10715538dba8Smiod return 0;
10725538dba8Smiod }
10735538dba8Smiod
10745538dba8Smiod void
arcofi_attach(struct arcofi_softc * sc,const char * version)10755538dba8Smiod arcofi_attach(struct arcofi_softc *sc, const char *version)
10765538dba8Smiod {
10775538dba8Smiod int rc;
10785538dba8Smiod uint8_t cmd[4];
10795538dba8Smiod
10805538dba8Smiod /*
10815538dba8Smiod * Reset logic.
10825538dba8Smiod */
10835538dba8Smiod arcofi_write(sc, ARCOFI_ID, 0);
10845538dba8Smiod delay(100000);
10855538dba8Smiod arcofi_write(sc, ARCOFI_CSR, 0);
10865538dba8Smiod
10875538dba8Smiod /*
10885538dba8Smiod * Initialize the chip to default settings (8 bit, u-Law).
10895538dba8Smiod */
10905538dba8Smiod sc->sc_active.cr3 =
10915538dba8Smiod arcofi_portmask_to_cr3(AUDIO_SPEAKER) | CR3_OPMODE_NORMAL;
10925538dba8Smiod sc->sc_active.cr4 = CR4_TM | CR4_ULAW;
10931129e472Smiod sc->sc_active.gr_idx = sc->sc_active.gx_idx = 1 + NEGATIVE_GAINS;
10941129e472Smiod sc->sc_active.output_mute = 0;
10955538dba8Smiod bcopy(&sc->sc_active, &sc->sc_shadow, sizeof(sc->sc_active));
10965538dba8Smiod
10975538dba8Smiod /* clear CRAM */
10985538dba8Smiod cmd[0] = CR1_IDR;
10995538dba8Smiod if ((rc = arcofi_cmd(sc, SOP_4, cmd)) != 0)
11005538dba8Smiod goto error;
11015538dba8Smiod delay(1000);
11025538dba8Smiod
11035538dba8Smiod /* set gain values before enabling them in CR1 */
11045538dba8Smiod cmd[0] = arcofi_gains[sc->sc_active.gr_idx] >> 8;
11055538dba8Smiod cmd[1] = arcofi_gains[sc->sc_active.gr_idx];
11065538dba8Smiod if ((rc = arcofi_cmd(sc, COP_2, cmd)) != 0)
11075538dba8Smiod goto error;
11085538dba8Smiod /* same value for gx... */
11095538dba8Smiod if ((rc = arcofi_cmd(sc, COP_B, cmd)) != 0)
11105538dba8Smiod goto error;
11115538dba8Smiod
11125538dba8Smiod /* set all CR registers at once */
11135538dba8Smiod cmd[0] = sc->sc_active.cr4;
11145538dba8Smiod cmd[1] = sc->sc_active.cr3;
11155538dba8Smiod cmd[2] = CR2_SD | CR2_SC | CR2_SB | CR2_SA | CR2_ELS | CR2_AM | CR2_EFC;
11165538dba8Smiod cmd[3] = CR1_GR | CR1_GX;
11175538dba8Smiod if ((rc = arcofi_cmd(sc, SOP_0, cmd)) != 0)
11185538dba8Smiod goto error;
11195538dba8Smiod
11205538dba8Smiod arcofi_write(sc, ARCOFI_FIFO_IR, 0);
11215538dba8Smiod arcofi_write(sc, ARCOFI_CSR, CSR_INTR_ENABLE);
11225538dba8Smiod
11232baa08e2Santon audio_attach_mi(&arcofi_hw_if, sc, NULL, &sc->sc_dev);
11245538dba8Smiod return;
11255538dba8Smiod
11265538dba8Smiod error:
11275538dba8Smiod arcofi_write(sc, ARCOFI_ID, 0);
1128c8b758f8Smiod printf("%s: command failed, error %d\n", __func__, rc);
11295538dba8Smiod }
1130