Module Name: src Committed By: martin Date: Fri Aug 29 11:42:15 UTC 2014
Modified Files: src/distrib/sets/lists/man [netbsd-7]: mi src/share/man/man4 [netbsd-7]: Makefile src/sys/arch/hp300/conf [netbsd-7]: GENERIC files.hp300 majors.hp300 src/sys/arch/hp300/hp300 [netbsd-7]: intr.c locore.s src/sys/conf [netbsd-7]: files Added Files: src/share/man/man4 [netbsd-7]: arcofi.4 src/sys/arch/hp300/dev [netbsd-7]: arcofi_dio.c src/sys/dev/ic [netbsd-7]: arcofi.c arcofivar.h Log Message: Pull up following revision(s) (requested by tsutsui in ticket #62): sys/arch/hp300/conf/files.hp300: revision 1.89 share/man/man4/arcofi.4: revision 1.1 share/man/man4/arcofi.4: revision 1.2 share/man/man4/Makefile: revision 1.617 sys/arch/hp300/hp300/intr.c: revision 1.41 sys/conf/files: revision 1.1100 sys/arch/hp300/hp300/locore.s: revision 1.171 distrib/sets/lists/man/mi: revision 1.1486 sys/dev/ic/arcofivar.h: revision 1.1 sys/arch/hp300/conf/majors.hp300: revision 1.26 sys/arch/hp300/dev/arcofi_dio.c: revision 1.1 sys/arch/hp300/conf/GENERIC: revision 1.188 sys/dev/ic/arcofi.c: revision 1.1 Add new arcofi(4) audio driver for NetBSD/hp300, ported from OpenBSD. The arcofi(4) is a driver for the HP "Audio1" device (Siemens PSB 2160 "ARCOFI" phone quality audio chip) found on the HP9000/425e and HP9000/{705,710,745,747} models (but only hp300 attachment is ported for now). The chip supports 8-bit mono 8kHz U-law, A-law and 16-bit mono slinear_be formats. The old HP9000/425e playing tunes with this new arcofi(4) audio driver was also demonstrated at Open Source Conference 2014 Shimane. Add a man page for arcofi(4) driver. From OpenBSD. Fix date. To generate a diff of this commit: cvs rdiff -u -r1.1485 -r1.1485.2.1 src/distrib/sets/lists/man/mi cvs rdiff -u -r1.616 -r1.616.2.1 src/share/man/man4/Makefile cvs rdiff -u -r0 -r1.2.2.2 src/share/man/man4/arcofi.4 cvs rdiff -u -r1.184 -r1.184.2.1 src/sys/arch/hp300/conf/GENERIC cvs rdiff -u -r1.88 -r1.88.2.1 src/sys/arch/hp300/conf/files.hp300 cvs rdiff -u -r1.25 -r1.25.28.1 src/sys/arch/hp300/conf/majors.hp300 cvs rdiff -u -r0 -r1.1.2.2 src/sys/arch/hp300/dev/arcofi_dio.c cvs rdiff -u -r1.40 -r1.40.34.1 src/sys/arch/hp300/hp300/intr.c cvs rdiff -u -r1.170 -r1.170.4.1 src/sys/arch/hp300/hp300/locore.s cvs rdiff -u -r1.1096 -r1.1096.2.1 src/sys/conf/files cvs rdiff -u -r0 -r1.1.2.2 src/sys/dev/ic/arcofi.c src/sys/dev/ic/arcofivar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/man/mi diff -u src/distrib/sets/lists/man/mi:1.1485 src/distrib/sets/lists/man/mi:1.1485.2.1 --- src/distrib/sets/lists/man/mi:1.1485 Sat Aug 9 11:33:53 2014 +++ src/distrib/sets/lists/man/mi Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1485 2014/08/09 11:33:53 apb Exp $ +# $NetBSD: mi,v 1.1485.2.1 2014/08/29 11:42:15 martin Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -826,6 +826,7 @@ ./usr/share/man/cat4/aps.0 man-sys-catman .cat ./usr/share/man/cat4/arc/intro.0 man-sys-catman .cat ./usr/share/man/cat4/arcmsr.0 man-sys-catman .cat +./usr/share/man/cat4/arcofi.0 man-sys-catman .cat ./usr/share/man/cat4/aria.0 man-sys-catman .cat ./usr/share/man/cat4/arm26/arckbd.0 man-obsolete obsolete ./usr/share/man/cat4/arm26/arcwskbd.0 man-obsolete obsolete @@ -3911,6 +3912,7 @@ ./usr/share/man/html4/aps.html man-sys-htmlman html ./usr/share/man/html4/arc/intro.html man-sys-htmlman html ./usr/share/man/html4/arcmsr.html man-sys-htmlman html +./usr/share/man/html4/arcofi.html man-sys-htmlman html ./usr/share/man/html4/aria.html man-sys-htmlman html ./usr/share/man/html4/arp.html man-sys-htmlman html ./usr/share/man/html4/artsata.html man-sys-htmlman html @@ -6691,6 +6693,7 @@ ./usr/share/man/man4/aps.4 man-sys-man .man ./usr/share/man/man4/arc/intro.4 man-sys-man .man ./usr/share/man/man4/arcmsr.4 man-sys-man .man +./usr/share/man/man4/arcofi.4 man-sys-man .man ./usr/share/man/man4/aria.4 man-sys-man .man ./usr/share/man/man4/arm26/arckbd.4 man-obsolete obsolete ./usr/share/man/man4/arm26/arcwskbd.4 man-obsolete obsolete Index: src/share/man/man4/Makefile diff -u src/share/man/man4/Makefile:1.616 src/share/man/man4/Makefile:1.616.2.1 --- src/share/man/man4/Makefile:1.616 Thu Jul 24 21:08:50 2014 +++ src/share/man/man4/Makefile Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.616 2014/07/24 21:08:50 alnsn Exp $ +# $NetBSD: Makefile,v 1.616.2.1 2014/08/29 11:42:15 martin Exp $ # @(#)Makefile 8.1 (Berkeley) 6/18/93 MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ @@ -7,7 +7,7 @@ MAN= aac.4 ac97.4 acardide.4 aceride.4 a ahcisata.4 ahd.4 \ aibs.4 alc.4 ale.4 alipm.4 altmem.4 altq.4 amdpm.4 amdtemp.4 amhphy.4 \ amr.4 aps.4 asus.4 \ - an.4 arcmsr.4 aria.4 artsata.4 ata.4 atalk.4 ataraid.4 \ + an.4 arcmsr.4 arcofi.4 aria.4 artsata.4 ata.4 atalk.4 ataraid.4 \ ath.4 athn.4 atphy.4 atppc.4 attimer.4 atw.4 \ auacer.4 audio.4 audiocs.4 auich.4 \ auixp.4 autri.4 auvia.4 awi.4 azalia.4 \ Index: src/sys/arch/hp300/conf/GENERIC diff -u src/sys/arch/hp300/conf/GENERIC:1.184 src/sys/arch/hp300/conf/GENERIC:1.184.2.1 --- src/sys/arch/hp300/conf/GENERIC:1.184 Fri Jul 18 17:59:12 2014 +++ src/sys/arch/hp300/conf/GENERIC Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -# $NetBSD: GENERIC,v 1.184 2014/07/18 17:59:12 tsutsui Exp $ +# $NetBSD: GENERIC,v 1.184.2.1 2014/08/29 11:42:15 martin Exp $ # # GENERIC machine description file # @@ -22,7 +22,7 @@ include "arch/hp300/conf/std.hp300" options INCLUDE_CONFIG_FILE # embed config file in kernel binary -#ident "GENERIC-$Revision: 1.184 $" +#ident "GENERIC-$Revision: 1.184.2.1 $" makeoptions COPTS="-O2 -fno-reorder-blocks" # see share/mk/sys.mk @@ -279,6 +279,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: src/sys/arch/hp300/conf/files.hp300 diff -u src/sys/arch/hp300/conf/files.hp300:1.88 src/sys/arch/hp300/conf/files.hp300:1.88.2.1 --- src/sys/arch/hp300/conf/files.hp300:1.88 Sun Apr 20 04:12:54 2014 +++ src/sys/arch/hp300/conf/files.hp300 Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files.hp300,v 1.88 2014/04/20 04:12:54 tsutsui Exp $ +# $NetBSD: files.hp300,v 1.88.2.1 2014/08/29 11:42:15 martin Exp $ # # hp300-specific configuration info @@ -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: src/sys/arch/hp300/conf/majors.hp300 diff -u src/sys/arch/hp300/conf/majors.hp300:1.25 src/sys/arch/hp300/conf/majors.hp300:1.25.28.1 --- src/sys/arch/hp300/conf/majors.hp300:1.25 Thu Jun 30 20:09:30 2011 +++ src/sys/arch/hp300/conf/majors.hp300 Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -# $NetBSD: majors.hp300,v 1.25 2011/06/30 20:09:30 wiz Exp $ +# $NetBSD: majors.hp300,v 1.25.28.1 2014/08/29 11:42:15 martin Exp $ # # Device majors for hp300 # @@ -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: src/sys/arch/hp300/hp300/intr.c diff -u src/sys/arch/hp300/hp300/intr.c:1.40 src/sys/arch/hp300/hp300/intr.c:1.40.34.1 --- src/sys/arch/hp300/hp300/intr.c:1.40 Mon Dec 20 00:25:33 2010 +++ src/sys/arch/hp300/hp300/intr.c Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: intr.c,v 1.40 2010/12/20 00:25:33 matt Exp $ */ +/* $NetBSD: intr.c,v 1.40.34.1 2014/08/29 11:42:15 martin Exp $ */ /*- * Copyright (c) 1996, 1997, 1999 The NetBSD Foundation, Inc. @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.40 2010/12/20 00:25:33 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.40.34.1 2014/08/29 11:42:15 martin Exp $"); #define _HP300_INTR_H_PRIVATE @@ -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: src/sys/arch/hp300/hp300/locore.s diff -u src/sys/arch/hp300/hp300/locore.s:1.170 src/sys/arch/hp300/hp300/locore.s:1.170.4.1 --- src/sys/arch/hp300/hp300/locore.s:1.170 Sat Mar 15 09:26:36 2014 +++ src/sys/arch/hp300/hp300/locore.s Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -/* $NetBSD: locore.s,v 1.170 2014/03/15 09:26:36 tsutsui Exp $ */ +/* $NetBSD: locore.s,v 1.170.4.1 2014/08/29 11:42:15 martin Exp $ */ /* * Copyright (c) 1980, 1990, 1993 @@ -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 Index: src/sys/conf/files diff -u src/sys/conf/files:1.1096 src/sys/conf/files:1.1096.2.1 --- src/sys/conf/files:1.1096 Sun Aug 10 16:44:35 2014 +++ src/sys/conf/files Fri Aug 29 11:42:15 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1096 2014/08/10 16:44:35 tls Exp $ +# $NetBSD: files,v 1.1096.2.1 2014/08/29 11:42:15 martin Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20100430 @@ -911,6 +911,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 Added files: Index: src/share/man/man4/arcofi.4 diff -u /dev/null src/share/man/man4/arcofi.4:1.2.2.2 --- /dev/null Fri Aug 29 11:42:15 2014 +++ src/share/man/man4/arcofi.4 Fri Aug 29 11:42:15 2014 @@ -0,0 +1,102 @@ +.\" $NetBSD: arcofi.4,v 1.2.2.2 2014/08/29 11:42:15 martin Exp $ +.\" $OpenBSD: arcofi.4,v 1.4 2011/12/27 10:28:35 jmc 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. +.\" +.Dd August 25, 2014 +.Dt ARCOFI 4 +.Os +.Sh NAME +.Nm arcofi +.Nd Siemens PSB2160 audio codec +.Sh SYNOPSIS +.Cd "arcofi* at dio?" +.\" .Cd "arcofi* at gsc?" +.Cd "audio* at audiobus?" +.Sh DESCRIPTION +The +.Nm +driver supports the HP +.Dq Audio1 +audio devices, based upon the Siemens PSB2160 +.Dq ARCOFI +codec, to implement the audio device interface described in +.Xr audio 4 . +.Pp +This device is found onboard HP 9000 workstations models 425e, 705 and 710. +.Pp +The +.Nm +is limited to a phone-quality mono, 8000 Hz sound. +.Ss AUDIOCTL SETTINGS +The following encodings are supported: +.Pp +.Bl -tag -width AUDIO_ENCODING_SLINEAR_BE -offset indent -compact +.It Li AUDIO_ENCODING_ULAW +.It Li AUDIO_ENCODING_ALAW +.It Li AUDIO_ENCODING_SLINEAR_BE +Natively supported. +.Pp +.It Li AUDIO_ENCODING_SLINEAR +.It Li AUDIO_ENCODING_SLINEAR_LE +.It Li AUDIO_ENCODING_ULINEAR_LE +.It Li AUDIO_ENCODING_ULINEAR_BE +Software converted to +.Li AUDIO_ENCODING_SLINEAR_BE +encoding. +.El +.Ss MIXERCTL SETTINGS +The +.Nm +has three audio ports: +.Pp +.Bl -tag -width "outputs.lineXXX" -offset indent -compact +.It Cm inputs.line +The +.Sq line in +jack connector. +.It Cm outputs.line +The +.Sq line out +jack connector. +.It Cm outputs.speaker +The built-in speaker. +.El +.Pp +Each port has a volume control, and can be muted. +.Pp +The +.Cm outputs.line +and +.Cm outputs.speaker +volume settings are tied to the same hardware setting. +.Sh SEE ALSO +.Xr audioctl 1 , +.Xr mixerctl 1 , +.Xr ioctl 2 , +.Xr audio 4 , +.Xr dio 4 , +.\" .Xr gsc 4 , +.Xr intro 4 +.Sh HISTORY +The +.Nm +driver was written for +.Ox +and first appeared in +.Ox 5.1 , +and was ported to +.Nx . Index: src/sys/arch/hp300/dev/arcofi_dio.c diff -u /dev/null src/sys/arch/hp300/dev/arcofi_dio.c:1.1.2.2 --- /dev/null Fri Aug 29 11:42:15 2014 +++ src/sys/arch/hp300/dev/arcofi_dio.c Fri Aug 29 11:42:15 2014 @@ -0,0 +1,94 @@ +/* $NetBSD: arcofi_dio.c,v 1.1.2.2 2014/08/29 11:42:15 martin Exp $ */ +/* $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"); +} Index: src/sys/dev/ic/arcofi.c diff -u /dev/null src/sys/dev/ic/arcofi.c:1.1.2.2 --- /dev/null Fri Aug 29 11:42:16 2014 +++ src/sys/dev/ic/arcofi.c Fri Aug 29 11:42:15 2014 @@ -0,0 +1,1274 @@ +/* $NetBSD: arcofi.c,v 1.1.2.2 2014/08/29 11:42:15 martin Exp $ */ +/* $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); +} Index: src/sys/dev/ic/arcofivar.h diff -u /dev/null src/sys/dev/ic/arcofivar.h:1.1.2.2 --- /dev/null Fri Aug 29 11:42:16 2014 +++ src/sys/dev/ic/arcofivar.h Fri Aug 29 11:42:15 2014 @@ -0,0 +1,56 @@ +/* $NetBSD: arcofivar.h,v 1.1.2.2 2014/08/29 11:42:15 martin Exp $ */ +/* $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 *);