xref: /netbsd-src/sys/dev/sbus/dbri.c (revision 2d5b96b7a61e75f05948bce65def3d50d47cf2d5)
1*2d5b96b7Sisaki /*	$NetBSD: dbri.c,v 1.45 2021/02/06 13:02:28 isaki Exp $	*/
271b0921aSmacallan 
371b0921aSmacallan /*
45d5d363cSmacallan  * Copyright (C) 1997 Rudolf Koenig (rfkoenig@immd4.informatik.uni-erlangen.de)
55d5d363cSmacallan  * Copyright (c) 1998, 1999 Brent Baccala (baccala@freesoft.org)
65d5d363cSmacallan  * Copyright (c) 2001, 2002 Jared D. McNeill <jmcneill@netbsd.org>
7042c09adSmacallan  * Copyright (c) 2005 Michael Lorenz <macallan@netbsd.org>
871b0921aSmacallan  * All rights reserved.
971b0921aSmacallan  *
105d5d363cSmacallan  * This driver is losely based on a Linux driver written by Rudolf Koenig and
115d5d363cSmacallan  * Brent Baccala who kindly gave their permission to use their code in a
125d5d363cSmacallan  * BSD-licensed driver.
135d5d363cSmacallan  *
1471b0921aSmacallan  * Redistribution and use in source and binary forms, with or without
1571b0921aSmacallan  * modification, are permitted provided that the following conditions
1671b0921aSmacallan  * are met:
1771b0921aSmacallan  * 1. Redistributions of source code must retain the above copyright
1871b0921aSmacallan  *    notice, this list of conditions and the following disclaimer.
1971b0921aSmacallan  * 2. Redistributions in binary form must reproduce the above copyright
2071b0921aSmacallan  *    notice, this list of conditions and the following disclaimer in the
2171b0921aSmacallan  *    documentation and/or other materials provided with the distribution.
2271b0921aSmacallan  *
23042c09adSmacallan  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
24042c09adSmacallan  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25042c09adSmacallan  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26042c09adSmacallan  * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27042c09adSmacallan  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28042c09adSmacallan  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
29042c09adSmacallan  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30042c09adSmacallan  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31042c09adSmacallan  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32042c09adSmacallan  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3371b0921aSmacallan  *
3471b0921aSmacallan  */
3571b0921aSmacallan 
3671b0921aSmacallan #include <sys/cdefs.h>
37*2d5b96b7Sisaki __KERNEL_RCSID(0, "$NetBSD: dbri.c,v 1.45 2021/02/06 13:02:28 isaki Exp $");
3871b0921aSmacallan 
3971b0921aSmacallan #include "audio.h"
4071b0921aSmacallan #if NAUDIO > 0
4171b0921aSmacallan 
4271b0921aSmacallan #include <sys/param.h>
4371b0921aSmacallan #include <sys/systm.h>
4471b0921aSmacallan #include <sys/errno.h>
4571b0921aSmacallan #include <sys/device.h>
4671b0921aSmacallan #include <sys/proc.h>
470d2dd3f5Smacallan #include <sys/kernel.h>
48a2a38285Sad #include <sys/bus.h>
49a2a38285Sad #include <sys/intr.h>
508a962f23Sjmcneill #include <sys/kmem.h>
5171b0921aSmacallan 
5271b0921aSmacallan #include <dev/sbus/sbusvar.h>
5371b0921aSmacallan #include <sparc/sparc/auxreg.h>
5471b0921aSmacallan #include <machine/autoconf.h>
5571b0921aSmacallan 
5671b0921aSmacallan #include <sys/audioio.h>
57e622eac4Sisaki #include <dev/audio/audio_if.h>
5871b0921aSmacallan 
5971b0921aSmacallan #include <dev/ic/cs4215reg.h>
6071b0921aSmacallan #include <dev/ic/cs4215var.h>
6171b0921aSmacallan #include <dev/sbus/dbrireg.h>
6271b0921aSmacallan #include <dev/sbus/dbrivar.h>
6371b0921aSmacallan 
64b4978607Smacallan #include "opt_sbus_dbri.h"
65b4978607Smacallan 
6671b0921aSmacallan #define DBRI_ROM_NAME_PREFIX		"SUNW,DBRI"
6771b0921aSmacallan 
68b4978607Smacallan #ifdef DBRI_DEBUG
692f1817f6Smacallan # define DPRINTF aprint_normal
70b4978607Smacallan #else
71964f61e4Smacallan # define DPRINTF while (0) printf
72b4978607Smacallan #endif
7371b0921aSmacallan 
7471b0921aSmacallan static const char *dbri_supported[] = {
7571b0921aSmacallan 	"e",
7671b0921aSmacallan 	"s3",
7771b0921aSmacallan 	""
7871b0921aSmacallan };
7971b0921aSmacallan 
8071b0921aSmacallan enum ms {
8171b0921aSmacallan 	CHImaster,
8271b0921aSmacallan 	CHIslave
8371b0921aSmacallan };
8471b0921aSmacallan 
8571b0921aSmacallan enum io {
8671b0921aSmacallan 	PIPEinput,
8771b0921aSmacallan 	PIPEoutput
8871b0921aSmacallan };
8971b0921aSmacallan 
9071b0921aSmacallan /*
9171b0921aSmacallan  * Function prototypes
9271b0921aSmacallan  */
9371b0921aSmacallan 
9471b0921aSmacallan /* softc stuff */
95e3c6619bSmacallan static void	dbri_attach_sbus(device_t, device_t, void *);
96d16a259fScegger static int	dbri_match_sbus(device_t, cfdata_t, void *);
9771b0921aSmacallan 
98bcebe4e0Smacallan static int	dbri_config_interrupts(device_t);
9971b0921aSmacallan 
10071b0921aSmacallan /* interrupt handler */
10171b0921aSmacallan static int	dbri_intr(void *);
1024b293a84Sad static void	dbri_softint(void *);
10371b0921aSmacallan 
10471b0921aSmacallan /* supporting subroutines */
10571b0921aSmacallan static int	dbri_init(struct dbri_softc *);
10671b0921aSmacallan static int	dbri_reset(struct dbri_softc *);
1076e067fe9Stsutsui static volatile uint32_t *dbri_command_lock(struct dbri_softc *);
1086e067fe9Stsutsui static void	dbri_command_send(struct dbri_softc *, volatile uint32_t *);
10971b0921aSmacallan static void	dbri_process_interrupt_buffer(struct dbri_softc *);
11071b0921aSmacallan static void	dbri_process_interrupt(struct dbri_softc *, int32_t);
11171b0921aSmacallan 
11271b0921aSmacallan /* mmcodec subroutines */
11371b0921aSmacallan static int	mmcodec_init(struct dbri_softc *);
11471b0921aSmacallan static void	mmcodec_init_data(struct dbri_softc *);
11571b0921aSmacallan static void	mmcodec_pipe_init(struct dbri_softc *);
11671b0921aSmacallan static void	mmcodec_default(struct dbri_softc *);
11771b0921aSmacallan static void	mmcodec_setgain(struct dbri_softc *, int);
11871b0921aSmacallan static int	mmcodec_setcontrol(struct dbri_softc *);
11971b0921aSmacallan 
12071b0921aSmacallan /* chi subroutines */
12171b0921aSmacallan static void	chi_reset(struct dbri_softc *, enum ms, int);
12271b0921aSmacallan 
12371b0921aSmacallan /* pipe subroutines */
12471b0921aSmacallan static void	pipe_setup(struct dbri_softc *, int, int);
12571b0921aSmacallan static void	pipe_reset(struct dbri_softc *, int);
12671b0921aSmacallan static void	pipe_receive_fixed(struct dbri_softc *, int,
1276e067fe9Stsutsui     volatile uint32_t *);
1286e067fe9Stsutsui static void	pipe_transmit_fixed(struct dbri_softc *, int, uint32_t);
12971b0921aSmacallan 
13071b0921aSmacallan static void	pipe_ts_link(struct dbri_softc *, int, enum io, int, int, int);
13171b0921aSmacallan static int	pipe_active(struct dbri_softc *, int);
13271b0921aSmacallan 
13371b0921aSmacallan /* audio(9) stuff */
134e622eac4Sisaki static int	dbri_query_format(void *, audio_format_query_t *);
135e622eac4Sisaki static int	dbri_set_format(void *, int,
136e622eac4Sisaki     const audio_params_t *, const audio_params_t *,
137e622eac4Sisaki     audio_filter_reg_t *, audio_filter_reg_t *);
13871b0921aSmacallan static int	dbri_round_blocksize(void *, int, int, const audio_params_t *);
13971b0921aSmacallan static int	dbri_halt_output(void *);
1402f4ccf79Smacallan static int	dbri_halt_input(void *);
14171b0921aSmacallan static int	dbri_getdev(void *, struct audio_device *);
14271b0921aSmacallan static int	dbri_set_port(void *, mixer_ctrl_t *);
14371b0921aSmacallan static int	dbri_get_port(void *, mixer_ctrl_t *);
14471b0921aSmacallan static int	dbri_query_devinfo(void *, mixer_devinfo_t *);
14571b0921aSmacallan static int	dbri_get_props(void *);
146b4978607Smacallan static int	dbri_open(void *, int);
147b4978607Smacallan static void	dbri_close(void *);
14871b0921aSmacallan 
149002ad491Smacallan static void	setup_ring_xmit(struct dbri_softc *, int, int, int, int,
150002ad491Smacallan     void (*)(void *), void *);
151002ad491Smacallan static void	setup_ring_recv(struct dbri_softc *, int, int, int, int,
1522f1817f6Smacallan     void (*)(void *), void *);
15371b0921aSmacallan 
15471b0921aSmacallan static int	dbri_trigger_output(void *, void *, void *, int,
15571b0921aSmacallan     void (*)(void *), void *, const struct audio_params *);
1562f4ccf79Smacallan static int	dbri_trigger_input(void *, void *, void *, int,
1572f4ccf79Smacallan     void (*)(void *), void *, const struct audio_params *);
1588a962f23Sjmcneill static void	dbri_get_locks(void *, kmutex_t **, kmutex_t **);
15971b0921aSmacallan 
1608a962f23Sjmcneill static void	*dbri_malloc(void *, int, size_t);
1618a962f23Sjmcneill static void	dbri_free(void *, void *, size_t);
162b4978607Smacallan static void	dbri_set_power(struct dbri_softc *, int);
163b4978607Smacallan static void	dbri_bring_up(struct dbri_softc *);
164c1b390d4Sdyoung static bool	dbri_suspend(device_t, const pmf_qual_t *);
165c1b390d4Sdyoung static bool	dbri_resume(device_t, const pmf_qual_t *);
166bc471ab0Smacallan static int	dbri_commit(void *);
16771b0921aSmacallan 
16871b0921aSmacallan /* stupid support routines */
1696e067fe9Stsutsui static uint32_t	reverse_bytes(uint32_t, int);
17071b0921aSmacallan 
17171b0921aSmacallan struct audio_device dbri_device = {
17271b0921aSmacallan 	"CS4215",
17371b0921aSmacallan 	"",
17471b0921aSmacallan 	"dbri"
17571b0921aSmacallan };
17671b0921aSmacallan 
17771b0921aSmacallan struct audio_hw_if dbri_hw_if = {
1788a962f23Sjmcneill 	.open			= dbri_open,
1798a962f23Sjmcneill 	.close			= dbri_close,
180e622eac4Sisaki 	.query_format		= dbri_query_format,
181e622eac4Sisaki 	.set_format		= dbri_set_format,
1828a962f23Sjmcneill 	.round_blocksize	= dbri_round_blocksize,
1838a962f23Sjmcneill 	.halt_output		= dbri_halt_output,
1848a962f23Sjmcneill 	.halt_input		= dbri_halt_input,
1858a962f23Sjmcneill 	.getdev			= dbri_getdev,
1868a962f23Sjmcneill 	.set_port		= dbri_set_port,
1878a962f23Sjmcneill 	.get_port		= dbri_get_port,
1888a962f23Sjmcneill 	.query_devinfo		= dbri_query_devinfo,
1898a962f23Sjmcneill 	.allocm			= dbri_malloc,
1908a962f23Sjmcneill 	.freem			= dbri_free,
1918a962f23Sjmcneill 	.get_props		= dbri_get_props,
1928a962f23Sjmcneill 	.trigger_output		= dbri_trigger_output,
1938a962f23Sjmcneill 	.trigger_input		= dbri_trigger_input,
1948a962f23Sjmcneill 	.get_locks		= dbri_get_locks,
195bc471ab0Smacallan 	.commit_settings	= dbri_commit,
19671b0921aSmacallan };
19771b0921aSmacallan 
198e3c6619bSmacallan CFATTACH_DECL_NEW(dbri, sizeof(struct dbri_softc),
19971b0921aSmacallan     dbri_match_sbus, dbri_attach_sbus, NULL, NULL);
20071b0921aSmacallan 
201e622eac4Sisaki /* The HW actually supports more encodings/frequencies, but it's enough. */
202e622eac4Sisaki static const struct audio_format dbri_formats[] = {
203e622eac4Sisaki 	{
204e622eac4Sisaki 		.mode		= AUMODE_PLAY | AUMODE_RECORD,
205e622eac4Sisaki 		.encoding	= AUDIO_ENCODING_SLINEAR_BE,
206e622eac4Sisaki 		.validbits	= 16,
207e622eac4Sisaki 		.precision	= 16,
208e622eac4Sisaki 		.channels	= 2,
209e622eac4Sisaki 		.channel_mask	= AUFMT_STEREO,
210e622eac4Sisaki 		.frequency_type	= 8,
211e622eac4Sisaki 		.frequency	=
212e622eac4Sisaki 		    { 8000, 9600, 11025, 16000, 22050, 32000, 44100, 48000 },
213e622eac4Sisaki 	},
2142f1817f6Smacallan };
215e622eac4Sisaki #define DBRI_NFORMATS	__arraycount(dbri_formats)
2162f1817f6Smacallan 
21771b0921aSmacallan enum {
2182f4ccf79Smacallan 	DBRI_OUTPUT_CLASS,
21971b0921aSmacallan 	DBRI_VOL_OUTPUT,
22071b0921aSmacallan 	DBRI_ENABLE_MONO,
22171b0921aSmacallan 	DBRI_ENABLE_HEADPHONE,
2222f4ccf79Smacallan 	DBRI_ENABLE_LINE,
2232f4ccf79Smacallan 	DBRI_MONITOR_CLASS,
2242f4ccf79Smacallan 	DBRI_VOL_MONITOR,
22571b0921aSmacallan 	DBRI_INPUT_CLASS,
22671b0921aSmacallan 	DBRI_INPUT_GAIN,
22771b0921aSmacallan 	DBRI_INPUT_SELECT,
2282f4ccf79Smacallan 	DBRI_RECORD_CLASS,
22971b0921aSmacallan 	DBRI_ENUM_LAST
23071b0921aSmacallan };
23171b0921aSmacallan 
23271b0921aSmacallan /*
23371b0921aSmacallan  * Autoconfig routines
23471b0921aSmacallan  */
2352f1817f6Smacallan static int
dbri_match_sbus(device_t parent,cfdata_t match,void * aux)236d16a259fScegger dbri_match_sbus(device_t parent, cfdata_t match, void *aux)
23771b0921aSmacallan {
23871b0921aSmacallan 	struct sbus_attach_args *sa = aux;
23971b0921aSmacallan 	char *ver;
24071b0921aSmacallan 	int i;
24171b0921aSmacallan 
24271b0921aSmacallan 	if (strncmp(DBRI_ROM_NAME_PREFIX, sa->sa_name, 9))
24371b0921aSmacallan 		return (0);
24471b0921aSmacallan 
24571b0921aSmacallan 	ver = &sa->sa_name[9];
24671b0921aSmacallan 
24771b0921aSmacallan 	for (i = 0; dbri_supported[i][0] != '\0'; i++)
24871b0921aSmacallan 		if (strcmp(dbri_supported[i], ver) == 0)
24971b0921aSmacallan 			return (1);
25071b0921aSmacallan 
25171b0921aSmacallan 	return (0);
25271b0921aSmacallan }
25371b0921aSmacallan 
2542f1817f6Smacallan static void
dbri_attach_sbus(device_t parent,device_t self,void * aux)255e3c6619bSmacallan dbri_attach_sbus(device_t parent, device_t self, void *aux)
25671b0921aSmacallan {
257e3c6619bSmacallan 	struct dbri_softc *sc = device_private(self);
25871b0921aSmacallan 	struct sbus_attach_args *sa = aux;
25971b0921aSmacallan 	bus_space_handle_t ioh;
26071b0921aSmacallan 	bus_size_t size;
2614b293a84Sad 	int error, rseg, pwr, i;
26271b0921aSmacallan 	char *ver = &sa->sa_name[9];
26371b0921aSmacallan 
264e3c6619bSmacallan 	sc->sc_dev = self;
26571b0921aSmacallan 	sc->sc_iot = sa->sa_bustag;
26671b0921aSmacallan 	sc->sc_dmat = sa->sa_dmatag;
2670d2dd3f5Smacallan 	sc->sc_powerstate = 1;
26871b0921aSmacallan 
269bc471ab0Smacallan 	sc->sc_whack_codec = 0;
270bc471ab0Smacallan 
27171b0921aSmacallan 	pwr = prom_getpropint(sa->sa_node,"pwr-on-auxio",0);
2722f4ccf79Smacallan 	aprint_normal(": rev %s\n", ver);
273964f61e4Smacallan 
27471b0921aSmacallan 	if (pwr) {
275b4978607Smacallan 		/*
276b4978607Smacallan 		 * we can control DBRI power via auxio and we're initially
277b4978607Smacallan 		 * powered down
278b4978607Smacallan 		 */
279b4978607Smacallan 
280b4978607Smacallan 		sc->sc_have_powerctl = 1;
281b4978607Smacallan 		sc->sc_powerstate = 0;
282b4978607Smacallan 		dbri_set_power(sc, 1);
283641f5484Schristos 		if (!pmf_device_register(self, dbri_suspend, dbri_resume)) {
284641f5484Schristos 			aprint_error_dev(self,
285641f5484Schristos 			    "cannot set power mgmt handler\n");
286641f5484Schristos 		}
287b4978607Smacallan 	} else {
288b4978607Smacallan 		/* we can't control power so we're always up */
289b4978607Smacallan 		sc->sc_have_powerctl = 0;
290b4978607Smacallan 		sc->sc_powerstate = 1;
291b4978607Smacallan 	}
29271b0921aSmacallan 
2934b293a84Sad 	for (i = 0; i < DBRI_NUM_DESCRIPTORS; i++) {
2944b293a84Sad 		sc->sc_desc[i].softint = softint_establish(SOFTINT_SERIAL,
2954b293a84Sad 		    dbri_softint, &sc->sc_desc[i]);
296e622eac4Sisaki 		sc->sc_desc[i].sc = sc;
2974b293a84Sad 	}
2984b293a84Sad 
29971b0921aSmacallan 	if (sa->sa_npromvaddrs)
30071b0921aSmacallan 		ioh = (bus_space_handle_t)sa->sa_promvaddrs[0];
30171b0921aSmacallan 	else {
30271b0921aSmacallan 		if (sbus_bus_map(sa->sa_bustag, sa->sa_slot,
30371b0921aSmacallan 				 sa->sa_offset, sa->sa_size,
30471b0921aSmacallan 				 BUS_SPACE_MAP_LINEAR, /*0,*/ &ioh) != 0) {
305964f61e4Smacallan 			aprint_error("%s @ sbus: cannot map registers\n",
3064c5fa20dScegger 				device_xname(self));
30771b0921aSmacallan 			return;
30871b0921aSmacallan 		}
30971b0921aSmacallan 	}
31071b0921aSmacallan 
31171b0921aSmacallan 	sc->sc_ioh = ioh;
31271b0921aSmacallan 
31371b0921aSmacallan 	size = sizeof(struct dbri_dma);
31471b0921aSmacallan 
31571b0921aSmacallan 	/* get a DMA handle */
31671b0921aSmacallan 	if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
31771b0921aSmacallan 				       BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) {
3184c5fa20dScegger 		aprint_error_dev(self, "DMA map create error %d\n",
319002ad491Smacallan 		    error);
32071b0921aSmacallan 		return;
32171b0921aSmacallan 	}
32271b0921aSmacallan 
32371b0921aSmacallan 	/* allocate DMA buffer */
32471b0921aSmacallan 	if ((error = bus_dmamem_alloc(sc->sc_dmat, size, 0, 0, &sc->sc_dmaseg,
32571b0921aSmacallan 				      1, &rseg, BUS_DMA_NOWAIT)) != 0) {
3264c5fa20dScegger 		aprint_error_dev(self, "DMA buffer alloc error %d\n",
3274c5fa20dScegger 		    error);
32871b0921aSmacallan 		return;
32971b0921aSmacallan 	}
33071b0921aSmacallan 
33171b0921aSmacallan 	/* map DMA buffer into CPU addressable space */
33271b0921aSmacallan 	if ((error = bus_dmamem_map(sc->sc_dmat, &sc->sc_dmaseg, rseg, size,
33371b0921aSmacallan 				    &sc->sc_membase,
33471b0921aSmacallan 				    BUS_DMA_NOWAIT|BUS_DMA_COHERENT)) != 0) {
3354c5fa20dScegger 		aprint_error_dev(self, "DMA buffer map error %d\n",
3364c5fa20dScegger 		    error);
33771b0921aSmacallan 		return;
33871b0921aSmacallan 	}
33971b0921aSmacallan 
34071b0921aSmacallan 	/* load the buffer */
34171b0921aSmacallan 	if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap,
34271b0921aSmacallan 				     sc->sc_membase, size, NULL,
34371b0921aSmacallan 				     BUS_DMA_NOWAIT)) != 0) {
3444c5fa20dScegger 		aprint_error_dev(self, "DMA buffer map load error %d\n",
3454c5fa20dScegger 		    error);
34671b0921aSmacallan 		bus_dmamem_unmap(sc->sc_dmat, sc->sc_membase, size);
34771b0921aSmacallan 		bus_dmamem_free(sc->sc_dmat, &sc->sc_dmaseg, rseg);
34871b0921aSmacallan 		return;
34971b0921aSmacallan 	}
35071b0921aSmacallan 
35171b0921aSmacallan 	/* map the registers into memory */
35271b0921aSmacallan 
353749b17a7Smacallan 	/* kernel virtual address of DMA buffer */
354749b17a7Smacallan 	sc->sc_dma = (struct dbri_dma *)sc->sc_membase;
355749b17a7Smacallan 	/* physical address of DMA buffer */
356749b17a7Smacallan 	sc->sc_dmabase = sc->sc_dmamap->dm_segs[0].ds_addr;
35771b0921aSmacallan 	sc->sc_bufsiz = size;
35871b0921aSmacallan 
3598a962f23Sjmcneill 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
360bc471ab0Smacallan 	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
3618a962f23Sjmcneill 
362c973b3b2Smrg #ifndef DBRI_SPIN
363c973b3b2Smrg 	cv_init(&sc->sc_cv, "dbricv");
364c973b3b2Smrg #endif
365c973b3b2Smrg 
366bc471ab0Smacallan 	bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_AUDIO, dbri_intr,
3675b56050bSmacallan 	    sc);
36871b0921aSmacallan 
36971b0921aSmacallan 	sc->sc_locked = 0;
37071b0921aSmacallan 	sc->sc_desc_used = 0;
3712f4ccf79Smacallan 	sc->sc_playing = 0;
372002ad491Smacallan 	sc->sc_recording = 0;
373bcebe4e0Smacallan 	sc->sc_init_done = 0;
374deb70e39Sskrll 	config_finalize_register(self, dbri_config_interrupts);
37571b0921aSmacallan 
37671b0921aSmacallan 	return;
37771b0921aSmacallan }
37871b0921aSmacallan 
379b4978607Smacallan /*
380b4978607Smacallan  * lowlevel routine to switch power for the DBRI chip
381b4978607Smacallan  */
382b4978607Smacallan static void
dbri_set_power(struct dbri_softc * sc,int state)383b4978607Smacallan dbri_set_power(struct dbri_softc *sc, int state)
384b4978607Smacallan {
385b4978607Smacallan 	int s;
386b4978607Smacallan 
387b4978607Smacallan 	if (sc->sc_have_powerctl == 0)
388b4978607Smacallan 		return;
389b4978607Smacallan 	if (sc->sc_powerstate == state)
390b4978607Smacallan 		return;
391b4978607Smacallan 
392b4978607Smacallan 	if (state) {
393e3c6619bSmacallan 		DPRINTF("%s: waiting to power up... ",
394e3c6619bSmacallan 		    device_xname(sc->sc_dev));
395b4978607Smacallan 		s = splhigh();
396b4978607Smacallan 		*AUXIO4M_REG |= (AUXIO4M_MMX);
397b4978607Smacallan 		splx(s);
398749b17a7Smacallan 		delay(10000);
3992f4ccf79Smacallan 		DPRINTF("done (%02x)\n", *AUXIO4M_REG);
400b4978607Smacallan 	} else {
401e3c6619bSmacallan 		DPRINTF("%s: powering down\n", device_xname(sc->sc_dev));
402b4978607Smacallan 		s = splhigh();
403b4978607Smacallan 		*AUXIO4M_REG &= ~AUXIO4M_MMX;
404b4978607Smacallan 		splx(s);
405964f61e4Smacallan 		DPRINTF("done (%02x})\n", *AUXIO4M_REG);
406b4978607Smacallan 	}
407b4978607Smacallan 	sc->sc_powerstate = state;
408b4978607Smacallan }
409b4978607Smacallan 
410b4978607Smacallan /*
411b4978607Smacallan  * power up and re-initialize the chip
412b4978607Smacallan  */
413b4978607Smacallan static void
dbri_bring_up(struct dbri_softc * sc)414b4978607Smacallan dbri_bring_up(struct dbri_softc *sc)
415b4978607Smacallan {
416b4978607Smacallan 
417b4978607Smacallan 	if (sc->sc_have_powerctl == 0)
418b4978607Smacallan 		return;
4190d2dd3f5Smacallan 
420b4978607Smacallan 	if (sc->sc_powerstate == 1)
421b4978607Smacallan 		return;
422b4978607Smacallan 
423b4978607Smacallan 	/* ok, we really need to do something */
424b4978607Smacallan 	dbri_set_power(sc, 1);
425b4978607Smacallan 
426b4978607Smacallan 	/*
427b4978607Smacallan 	 * re-initialize the chip but skip all the probing, don't overwrite
428b4978607Smacallan 	 * any other settings either
429b4978607Smacallan 	 */
430b4978607Smacallan 	dbri_init(sc);
431b4978607Smacallan 	mmcodec_setgain(sc, 1);
432b4978607Smacallan 	mmcodec_pipe_init(sc);
433b4978607Smacallan 	mmcodec_init_data(sc);
434b4978607Smacallan 	mmcodec_setgain(sc, 0);
435b4978607Smacallan }
436b4978607Smacallan 
437bcebe4e0Smacallan static int
dbri_config_interrupts(device_t dev)438e3c6619bSmacallan dbri_config_interrupts(device_t dev)
43971b0921aSmacallan {
440e3c6619bSmacallan 	struct dbri_softc *sc = device_private(dev);
441749b17a7Smacallan 
442c973b3b2Smrg 	mutex_spin_enter(&sc->sc_intr_lock);
443c973b3b2Smrg 	if (sc->sc_init_done != 0) {
444c973b3b2Smrg 		mutex_spin_exit(&sc->sc_intr_lock);
445bcebe4e0Smacallan 		return 0;
446c973b3b2Smrg 	}
447bcebe4e0Smacallan 	sc->sc_init_done = 1;
448bcebe4e0Smacallan 
44971b0921aSmacallan 	dbri_init(sc);
450bc471ab0Smacallan 
451bc471ab0Smacallan 	/* talking to the codec needs working interrupts */
452490ba2f1Smacallan 	if (mmcodec_init(sc) == -1) {
453f6826716Smrg 		mutex_spin_exit(&sc->sc_intr_lock);
454490ba2f1Smacallan 		printf("%s: no codec detected, aborting\n",
455490ba2f1Smacallan 		    device_xname(dev));
456bcebe4e0Smacallan 		return 0;
457490ba2f1Smacallan 	}
458f6826716Smrg 	mutex_spin_exit(&sc->sc_intr_lock);
459749b17a7Smacallan 
46071b0921aSmacallan 	/* Attach ourselves to the high level audio interface */
461e3c6619bSmacallan 	audio_attach_mi(&dbri_hw_if, sc, sc->sc_dev);
46271b0921aSmacallan 
463b4978607Smacallan 	/* power down until open() */
464c973b3b2Smrg 	mutex_spin_enter(&sc->sc_intr_lock);
465b4978607Smacallan 	dbri_set_power(sc, 0);
466c973b3b2Smrg 	mutex_spin_exit(&sc->sc_intr_lock);
467c973b3b2Smrg 
468bcebe4e0Smacallan 	return 0;
46971b0921aSmacallan }
47071b0921aSmacallan 
4712f1817f6Smacallan static int
dbri_intr(void * hdl)47271b0921aSmacallan dbri_intr(void *hdl)
47371b0921aSmacallan {
47471b0921aSmacallan 	struct dbri_softc *sc = hdl;
47571b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
47671b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
47771b0921aSmacallan 	int x;
47871b0921aSmacallan 
4798a962f23Sjmcneill 	mutex_spin_enter(&sc->sc_intr_lock);
4808a962f23Sjmcneill 
48171b0921aSmacallan 	/* clear interrupt */
48271b0921aSmacallan 	x = bus_space_read_4(iot, ioh, DBRI_REG1);
48371b0921aSmacallan 	if (x & (DBRI_MRR | DBRI_MLE | DBRI_LBG | DBRI_MBE)) {
4846e067fe9Stsutsui 		uint32_t tmp;
48571b0921aSmacallan 
48671b0921aSmacallan 		if (x & DBRI_MRR)
487e3c6619bSmacallan 			aprint_debug_dev(sc->sc_dev,
488e3c6619bSmacallan 			     "multiple ack error on sbus\n");
48971b0921aSmacallan 		if (x & DBRI_MLE)
490e3c6619bSmacallan 			aprint_debug_dev(sc->sc_dev,
491e3c6619bSmacallan 			    "multiple late error on sbus\n");
49271b0921aSmacallan 		if (x & DBRI_LBG)
493e3c6619bSmacallan 			aprint_debug_dev(sc->sc_dev,
494e3c6619bSmacallan 			    "lost bus grant on sbus\n");
49571b0921aSmacallan 		if (x & DBRI_MBE)
496e3c6619bSmacallan 			aprint_debug_dev(sc->sc_dev, "burst error on sbus\n");
49771b0921aSmacallan 
49871b0921aSmacallan 		/*
49971b0921aSmacallan 		 * Some of these errors disable the chip's circuitry.
50071b0921aSmacallan 		 * Re-enable the circuitry and keep on going.
50171b0921aSmacallan 		 */
50271b0921aSmacallan 
50371b0921aSmacallan 		tmp = bus_space_read_4(iot, ioh, DBRI_REG0);
50471b0921aSmacallan 		tmp &= ~(DBRI_DISABLE_MASTER);
50571b0921aSmacallan 		bus_space_write_4(iot, ioh, DBRI_REG0, tmp);
50671b0921aSmacallan 	}
50771b0921aSmacallan 
50871b0921aSmacallan #if 0
50971b0921aSmacallan 	if (!x & 1)	/* XXX: DBRI_INTR_REQ */
51071b0921aSmacallan 		return (1);
51171b0921aSmacallan #endif
51271b0921aSmacallan 
51371b0921aSmacallan 	dbri_process_interrupt_buffer(sc);
51471b0921aSmacallan 
5158a962f23Sjmcneill 	mutex_spin_exit(&sc->sc_intr_lock);
5168a962f23Sjmcneill 
51771b0921aSmacallan 	return (1);
51871b0921aSmacallan }
51971b0921aSmacallan 
5204b293a84Sad static void
dbri_softint(void * cookie)5214b293a84Sad dbri_softint(void *cookie)
5224b293a84Sad {
5234b293a84Sad 	struct dbri_desc *dd = cookie;
524e622eac4Sisaki 	struct dbri_softc *sc = dd->sc;
5254b293a84Sad 
526e622eac4Sisaki 	mutex_spin_enter(&sc->sc_intr_lock);
5274b293a84Sad 	if (dd->callback != NULL)
5284b293a84Sad 		dd->callback(dd->callback_args);
529e622eac4Sisaki 	mutex_spin_exit(&sc->sc_intr_lock);
5304b293a84Sad }
5314b293a84Sad 
5322f1817f6Smacallan static int
dbri_init(struct dbri_softc * sc)53371b0921aSmacallan dbri_init(struct dbri_softc *sc)
53471b0921aSmacallan {
53571b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
53671b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
5376e067fe9Stsutsui 	uint32_t reg;
5386e067fe9Stsutsui 	volatile uint32_t *cmd;
53971b0921aSmacallan 	bus_addr_t dmaaddr;
54071b0921aSmacallan 	int n;
54171b0921aSmacallan 
542f6826716Smrg 	KASSERT(mutex_owned(&sc->sc_intr_lock));
543c973b3b2Smrg 
54471b0921aSmacallan 	dbri_reset(sc);
545bc471ab0Smacallan 	sc->sc_mm.status = 0;
54671b0921aSmacallan 
54771b0921aSmacallan 	cmd = dbri_command_lock(sc);
54871b0921aSmacallan 
54971b0921aSmacallan 	/* XXX: Initialize interrupt ring buffer */
5506e067fe9Stsutsui 	sc->sc_dma->intr[0] = (uint32_t)sc->sc_dmabase + dbri_dma_off(intr, 0);
55171b0921aSmacallan 	sc->sc_irqp = 1;
55271b0921aSmacallan 
55371b0921aSmacallan 	/* Initialize pipes */
55471b0921aSmacallan 	for (n = 0; n < DBRI_PIPE_MAX; n++)
55571b0921aSmacallan 		sc->sc_pipe[n].desc = sc->sc_pipe[n].next = -1;
55671b0921aSmacallan 
55771b0921aSmacallan 	for (n = 1; n < DBRI_INT_BLOCKS; n++) {
55871b0921aSmacallan 		sc->sc_dma->intr[n] = 0;
55971b0921aSmacallan 	}
56071b0921aSmacallan 
56171b0921aSmacallan 	/* XXX 16 byte bursts cause errors, the rest works */
56271b0921aSmacallan 	reg = bus_space_read_4(iot, ioh, DBRI_REG0);
563749b17a7Smacallan 
56471b0921aSmacallan 	/*reg &= ~(DBRI_BURST_4 | DBRI_BURST_8 | DBRI_BURST_16);*/
56571b0921aSmacallan 	reg |= (DBRI_BURST_4 | DBRI_BURST_8);
56671b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, reg);
56771b0921aSmacallan 
56871b0921aSmacallan 	/* setup interrupt queue */
5696e067fe9Stsutsui 	dmaaddr = (uint32_t)sc->sc_dmabase + dbri_dma_off(intr, 0);
57071b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_IIQ, 0, 0);
57171b0921aSmacallan 	*(cmd++) = dmaaddr;
57271b0921aSmacallan 
57371b0921aSmacallan 	dbri_command_send(sc, cmd);
574c973b3b2Smrg 
57571b0921aSmacallan 	return (0);
57671b0921aSmacallan }
57771b0921aSmacallan 
5782f1817f6Smacallan static int
dbri_reset(struct dbri_softc * sc)57971b0921aSmacallan dbri_reset(struct dbri_softc *sc)
58071b0921aSmacallan {
58171b0921aSmacallan 	int bail = 0;
5825b56050bSmacallan 
58371b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
58471b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
58571b0921aSmacallan 
58671b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, DBRI_SOFT_RESET);
58771b0921aSmacallan 	while ((bus_space_read_4(iot, ioh, DBRI_REG0) & DBRI_SOFT_RESET) &&
58871b0921aSmacallan 	    (bail < 100000)) {
58971b0921aSmacallan 		bail++;
59071b0921aSmacallan 		delay(10);
59171b0921aSmacallan 	}
5924c5fa20dScegger 	if (bail == 100000)
593e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "reset timed out\n");
59471b0921aSmacallan 	return (0);
59571b0921aSmacallan }
59671b0921aSmacallan 
5976e067fe9Stsutsui static volatile uint32_t *
dbri_command_lock(struct dbri_softc * sc)59871b0921aSmacallan dbri_command_lock(struct dbri_softc *sc)
59971b0921aSmacallan {
60071b0921aSmacallan 
60171b0921aSmacallan 	if (sc->sc_locked)
602e3c6619bSmacallan 		aprint_debug_dev(sc->sc_dev, "command buffer locked\n");
60371b0921aSmacallan 
60471b0921aSmacallan 	sc->sc_locked++;
60571b0921aSmacallan 
60671b0921aSmacallan 	return (&sc->sc_dma->command[0]);
60771b0921aSmacallan }
60871b0921aSmacallan 
6092f1817f6Smacallan static void
dbri_command_send(struct dbri_softc * sc,volatile uint32_t * cmd)6106e067fe9Stsutsui dbri_command_send(struct dbri_softc *sc, volatile uint32_t *cmd)
61171b0921aSmacallan {
61271b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
61371b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
61471b0921aSmacallan 	int maxloops = 1000000;
61571b0921aSmacallan 
616f6826716Smrg 	KASSERT(mutex_owned(&sc->sc_intr_lock));
61771b0921aSmacallan 
61871b0921aSmacallan 	sc->sc_locked--;
61971b0921aSmacallan 
62071b0921aSmacallan 	if (sc->sc_locked != 0) {
621e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev,
622e3c6619bSmacallan 		    "command buffer improperly locked\n");
62371b0921aSmacallan 	} else if ((cmd - &sc->sc_dma->command[0]) >= DBRI_NUM_COMMANDS - 1) {
624e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "command buffer overflow\n");
62571b0921aSmacallan 	} else {
62671b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_PAUSE, 0, 0);
62771b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_WAIT, 1, 0);
62871b0921aSmacallan 		sc->sc_waitseen = 0;
62971b0921aSmacallan 		bus_space_write_4(iot, ioh, DBRI_REG8, sc->sc_dmabase);
63071b0921aSmacallan 		while ((--maxloops) > 0 &&
63171b0921aSmacallan 		    (bus_space_read_4(iot, ioh, DBRI_REG0)
63271b0921aSmacallan 		     & DBRI_COMMAND_VALID)) {
63371b0921aSmacallan 			bus_space_barrier(iot, ioh, DBRI_REG0, 4,
63471b0921aSmacallan 					  BUS_SPACE_BARRIER_READ);
63571b0921aSmacallan 			delay(1000);
63671b0921aSmacallan 		}
63771b0921aSmacallan 
63871b0921aSmacallan 		if (maxloops == 0) {
639e3c6619bSmacallan 			aprint_error_dev(sc->sc_dev,
6404c5fa20dScegger 			    "chip never completed command buffer\n");
64171b0921aSmacallan 		} else {
642749b17a7Smacallan 
643964f61e4Smacallan 			DPRINTF("%s: command completed\n",
644e3c6619bSmacallan 			    device_xname(sc->sc_dev));
645749b17a7Smacallan 
64671b0921aSmacallan 			while ((--maxloops) > 0 && (!sc->sc_waitseen))
64771b0921aSmacallan 				dbri_process_interrupt_buffer(sc);
64871b0921aSmacallan 			if (maxloops == 0) {
649e3c6619bSmacallan 				aprint_error_dev(sc->sc_dev, "chip never acked WAIT\n");
65071b0921aSmacallan 			}
65171b0921aSmacallan 		}
65271b0921aSmacallan 	}
65371b0921aSmacallan 
65471b0921aSmacallan 	return;
65571b0921aSmacallan }
65671b0921aSmacallan 
6572f1817f6Smacallan static void
dbri_process_interrupt_buffer(struct dbri_softc * sc)65871b0921aSmacallan dbri_process_interrupt_buffer(struct dbri_softc *sc)
65971b0921aSmacallan {
66071b0921aSmacallan 	int32_t i;
661c973b3b2Smrg 	int orig_irqp = sc->sc_irqp;
662c973b3b2Smrg 
663f6826716Smrg 	KASSERT(mutex_owned(&sc->sc_intr_lock));
66471b0921aSmacallan 
66571b0921aSmacallan 	while ((i = sc->sc_dma->intr[sc->sc_irqp]) != 0) {
66671b0921aSmacallan 		sc->sc_dma->intr[sc->sc_irqp] = 0;
66771b0921aSmacallan 		sc->sc_irqp++;
66871b0921aSmacallan 
66971b0921aSmacallan 		if (sc->sc_irqp == DBRI_INT_BLOCKS)
67071b0921aSmacallan 			sc->sc_irqp = 1;
67171b0921aSmacallan 		else if ((sc->sc_irqp & (DBRI_INT_BLOCKS - 1)) == 0)
67271b0921aSmacallan 			sc->sc_irqp++;
67371b0921aSmacallan 
67471b0921aSmacallan 		dbri_process_interrupt(sc, i);
675c973b3b2Smrg 
676c973b3b2Smrg 		/* don't loop more than once. */
677c973b3b2Smrg 		if (orig_irqp == sc->sc_irqp)
678c973b3b2Smrg 			break;
67971b0921aSmacallan 	}
68071b0921aSmacallan 
68171b0921aSmacallan 	return;
68271b0921aSmacallan }
68371b0921aSmacallan 
6842f1817f6Smacallan static void
dbri_process_interrupt(struct dbri_softc * sc,int32_t i)68571b0921aSmacallan dbri_process_interrupt(struct dbri_softc *sc, int32_t i)
68671b0921aSmacallan {
68771b0921aSmacallan #if 0
68871b0921aSmacallan 	const int liu_states[] = { 1, 0, 8, 3, 4, 5, 6, 7 };
68971b0921aSmacallan #endif
69071b0921aSmacallan 	int val = DBRI_INTR_GETVAL(i);
69171b0921aSmacallan 	int channel = DBRI_INTR_GETCHAN(i);
69271b0921aSmacallan 	int command = DBRI_INTR_GETCMD(i);
69371b0921aSmacallan 	int code = DBRI_INTR_GETCODE(i);
69471b0921aSmacallan #if 0
69571b0921aSmacallan 	int rval = DBRI_INTR_GETRVAL(i);
69671b0921aSmacallan #endif
69771b0921aSmacallan 	if (channel == DBRI_INTR_CMD && command == DBRI_COMMAND_WAIT)
69871b0921aSmacallan 		sc->sc_waitseen++;
69971b0921aSmacallan 
70071b0921aSmacallan 	switch (code) {
70171b0921aSmacallan 	case DBRI_INTR_XCMP:	/* transmission complete */
70271b0921aSmacallan 	{
70371b0921aSmacallan 		int td;
70471b0921aSmacallan 		struct dbri_desc *dd;
70571b0921aSmacallan 
706c973b3b2Smrg 		DPRINTF("%s:%d tx complete\n", __func__, channel);
70771b0921aSmacallan 		td = sc->sc_pipe[channel].desc;
70871b0921aSmacallan 		dd = &sc->sc_desc[td];
70971b0921aSmacallan 
7104b293a84Sad 		if (dd->callback != NULL)
7114b293a84Sad 			softint_schedule(dd->softint);
71271b0921aSmacallan 		break;
71371b0921aSmacallan 	}
71471b0921aSmacallan 	case DBRI_INTR_FXDT:		/* fixed data change */
715c973b3b2Smrg 		DPRINTF("%s:%d: Fixed data change: %x\n", __func__, channel,
716964f61e4Smacallan 		    val);
71771b0921aSmacallan 		if (sc->sc_pipe[channel].sdp & DBRI_SDP_MSB)
71871b0921aSmacallan 			val = reverse_bytes(val, sc->sc_pipe[channel].length);
71971b0921aSmacallan 		if (sc->sc_pipe[channel].prec)
72071b0921aSmacallan 			*(sc->sc_pipe[channel].prec) = val;
7212f4ccf79Smacallan #ifndef DBRI_SPIN
722c973b3b2Smrg 		DPRINTF("%s: cv_broadcast %p\n", device_xname(sc->sc_dev), sc);
723c973b3b2Smrg 		cv_broadcast(&sc->sc_cv);
72471b0921aSmacallan #endif
72571b0921aSmacallan 		break;
72671b0921aSmacallan 	case DBRI_INTR_SBRI:
727964f61e4Smacallan 		DPRINTF("dbri_intr: SBRI\n");
72871b0921aSmacallan 		break;
72971b0921aSmacallan 	case DBRI_INTR_BRDY:
73071b0921aSmacallan 	{
731002ad491Smacallan 		int td;
732002ad491Smacallan 		struct dbri_desc *dd;
73371b0921aSmacallan 
734c973b3b2Smrg 		DPRINTF("dbri_intr: buffer ready (%d)\n", channel);
735002ad491Smacallan 		td = sc->sc_pipe[channel].desc;
736002ad491Smacallan 		dd = &sc->sc_desc[td];
73771b0921aSmacallan 
738002ad491Smacallan 		if (dd->callback != NULL)
7394b293a84Sad 			softint_schedule(dd->softint);
74071b0921aSmacallan 		break;
74171b0921aSmacallan 	}
74271b0921aSmacallan 	case DBRI_INTR_UNDR:
74371b0921aSmacallan 	{
7446e067fe9Stsutsui 		volatile uint32_t *cmd;
74571b0921aSmacallan 		int td = sc->sc_pipe[channel].desc;
74671b0921aSmacallan 
747e3c6619bSmacallan 		DPRINTF("%s: DBRI_INTR_UNDR\n", device_xname(sc->sc_dev));
74871b0921aSmacallan 
749002ad491Smacallan 		sc->sc_dma->xmit[td].status = 0;
75071b0921aSmacallan 
75171b0921aSmacallan 		cmd = dbri_command_lock(sc);
75271b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_SDP, 0,
75371b0921aSmacallan 				    sc->sc_pipe[channel].sdp |
75471b0921aSmacallan 				    DBRI_SDP_VALID_POINTER |
75571b0921aSmacallan 				    DBRI_SDP_CLEAR |
75671b0921aSmacallan 				    DBRI_SDP_2SAME);
757002ad491Smacallan 		*(cmd++) = sc->sc_dmabase + dbri_dma_off(xmit, td);
75871b0921aSmacallan 		dbri_command_send(sc, cmd);
75971b0921aSmacallan 		break;
76071b0921aSmacallan 	}
761749b17a7Smacallan 	case DBRI_INTR_CMDI:
762002ad491Smacallan 		DPRINTF("ok");
763749b17a7Smacallan 		break;
76471b0921aSmacallan 	default:
765749b17a7Smacallan 
766e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "unknown interrupt code %d\n",
7674c5fa20dScegger 		    code);
76871b0921aSmacallan 		break;
76971b0921aSmacallan 	}
77071b0921aSmacallan 
77171b0921aSmacallan 	return;
77271b0921aSmacallan }
77371b0921aSmacallan 
77471b0921aSmacallan /*
77571b0921aSmacallan  * mmcodec stuff
77671b0921aSmacallan  */
77771b0921aSmacallan 
7782f1817f6Smacallan static int
mmcodec_init(struct dbri_softc * sc)77971b0921aSmacallan mmcodec_init(struct dbri_softc *sc)
78071b0921aSmacallan {
78171b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
78271b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
7836e067fe9Stsutsui 	uint32_t reg2;
784749b17a7Smacallan 	int bail;
78571b0921aSmacallan 
78671b0921aSmacallan 	reg2 = bus_space_read_4(iot, ioh, DBRI_REG2);
787964f61e4Smacallan 	DPRINTF("mmcodec_init: PIO reads %x\n", reg2);
788b4978607Smacallan 
78971b0921aSmacallan 	if (reg2 & DBRI_PIO2) {
790e3c6619bSmacallan 		aprint_normal_dev(sc->sc_dev, " onboard CS4215 detected\n");
79171b0921aSmacallan 		sc->sc_mm.onboard = 1;
79271b0921aSmacallan 	}
79371b0921aSmacallan 
79471b0921aSmacallan 	if (reg2 & DBRI_PIO0) {
795e3c6619bSmacallan 		aprint_normal_dev(sc->sc_dev, "speakerbox detected\n");
7960d2dd3f5Smacallan 		bus_space_write_4(iot, ioh, DBRI_REG2, DBRI_PIO2_ENABLE);
79771b0921aSmacallan 		sc->sc_mm.onboard = 0;
79871b0921aSmacallan 	}
79971b0921aSmacallan 
80071b0921aSmacallan 	if ((reg2 & DBRI_PIO2) && (reg2 & DBRI_PIO0)) {
801e3c6619bSmacallan 		aprint_normal_dev(sc->sc_dev, "using speakerbox\n");
80271b0921aSmacallan 		bus_space_write_4(iot, ioh, DBRI_REG2, DBRI_PIO2_ENABLE);
80371b0921aSmacallan 		sc->sc_mm.onboard = 0;
80471b0921aSmacallan 	}
80571b0921aSmacallan 
80671b0921aSmacallan 	if (!(reg2 & (DBRI_PIO0|DBRI_PIO2))) {
807e3c6619bSmacallan 		aprint_normal_dev(sc->sc_dev, "no mmcodec found\n");
80871b0921aSmacallan 		return -1;
80971b0921aSmacallan 	}
81071b0921aSmacallan 
81171b0921aSmacallan 	sc->sc_version = 0xff;
81271b0921aSmacallan 
81371b0921aSmacallan 	mmcodec_pipe_init(sc);
81471b0921aSmacallan 	mmcodec_default(sc);
81571b0921aSmacallan 
81671b0921aSmacallan 	sc->sc_mm.offset = sc->sc_mm.onboard ? 0 : 8;
81771b0921aSmacallan 
818749b17a7Smacallan 	/*
819749b17a7Smacallan 	 * mmcodec_setcontrol() sometimes fails right after powerup
820749b17a7Smacallan 	 * so we just try again until we either get a useful response or run
821749b17a7Smacallan 	 * out of time
822749b17a7Smacallan 	 */
823749b17a7Smacallan 	bail = 0;
824749b17a7Smacallan 	while (mmcodec_setcontrol(sc) == -1 || sc->sc_version == 0xff) {
825749b17a7Smacallan 
826749b17a7Smacallan 		bail++;
827749b17a7Smacallan 		if (bail > 100) {
828964f61e4Smacallan 			DPRINTF("%s: cs4215 probe failed at offset %d\n",
829e3c6619bSmacallan 		    	    device_xname(sc->sc_dev), sc->sc_mm.offset);
83071b0921aSmacallan 			return (-1);
83171b0921aSmacallan 		}
832749b17a7Smacallan 		delay(10000);
833749b17a7Smacallan 	}
83471b0921aSmacallan 
835e3c6619bSmacallan 	aprint_normal_dev(sc->sc_dev, "cs4215 rev %c found at offset %d\n",
8364c5fa20dScegger 	    0x43 + (sc->sc_version & 0xf), sc->sc_mm.offset);
83771b0921aSmacallan 
83871b0921aSmacallan 	/* set some sane defaults for mmcodec_init_data */
83971b0921aSmacallan 	sc->sc_params.channels = 2;
84071b0921aSmacallan 	sc->sc_params.precision = 16;
84171b0921aSmacallan 
84271b0921aSmacallan 	mmcodec_init_data(sc);
84371b0921aSmacallan 
84471b0921aSmacallan 	return (0);
84571b0921aSmacallan }
84671b0921aSmacallan 
8472f1817f6Smacallan static void
mmcodec_init_data(struct dbri_softc * sc)84871b0921aSmacallan mmcodec_init_data(struct dbri_softc *sc)
84971b0921aSmacallan {
85071b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
85171b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
8526e067fe9Stsutsui 	uint32_t tmp;
85371b0921aSmacallan 	int data_width;
85471b0921aSmacallan 
85571b0921aSmacallan 	tmp = bus_space_read_4(iot, ioh, DBRI_REG0);
85671b0921aSmacallan 	tmp &= ~(DBRI_CHI_ACTIVATE);	/* disable CHI */
85771b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, tmp);
85871b0921aSmacallan 
85971b0921aSmacallan 	/* switch CS4215 to data mode - set PIO3 to 1 */
86071b0921aSmacallan 	tmp = DBRI_PIO_ENABLE_ALL | DBRI_PIO1 | DBRI_PIO3;
8615b56050bSmacallan 
86271b0921aSmacallan 	/* XXX */
86371b0921aSmacallan 	tmp |= (sc->sc_mm.onboard ? DBRI_PIO0 : DBRI_PIO2);
86471b0921aSmacallan 
86571b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG2, tmp);
86671b0921aSmacallan 	chi_reset(sc, CHIslave, 128);
86771b0921aSmacallan 
8685b56050bSmacallan 	data_width = sc->sc_params.channels * sc->sc_params.precision;
869002ad491Smacallan 
870002ad491Smacallan 	if ((data_width != 32) && (data_width != 8))
871002ad491Smacallan 		aprint_error("%s: data_width is %d\n", __func__, data_width);
872002ad491Smacallan 
87371b0921aSmacallan 	pipe_ts_link(sc, 20, PIPEoutput, 16, 32, sc->sc_mm.offset + 32);
87471b0921aSmacallan 	pipe_ts_link(sc, 4, PIPEoutput, 16, data_width, sc->sc_mm.offset);
87571b0921aSmacallan 	pipe_ts_link(sc, 6, PIPEinput, 16, data_width, sc->sc_mm.offset);
876bc471ab0Smacallan #if 0
877bc471ab0Smacallan 	/* readback for the mixer registers - we don't use that */
878002ad491Smacallan 	pipe_ts_link(sc, 21, PIPEinput, 16, 32, sc->sc_mm.offset + 32);
879002ad491Smacallan 
880bc471ab0Smacallan 	pipe_receive_fixed(sc, 21, &sc->sc_mm.d.ldata);
881bc471ab0Smacallan #endif
88271b0921aSmacallan 	mmcodec_setgain(sc, 0);
88371b0921aSmacallan 
88471b0921aSmacallan 	tmp = bus_space_read_4(iot, ioh, DBRI_REG0);
88571b0921aSmacallan 	tmp |= DBRI_CHI_ACTIVATE;
88671b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, tmp);
88771b0921aSmacallan 
88871b0921aSmacallan 	return;
88971b0921aSmacallan }
89071b0921aSmacallan 
8912f1817f6Smacallan static void
mmcodec_pipe_init(struct dbri_softc * sc)89271b0921aSmacallan mmcodec_pipe_init(struct dbri_softc *sc)
89371b0921aSmacallan {
89471b0921aSmacallan 
89571b0921aSmacallan 	pipe_setup(sc, 4, DBRI_SDP_MEM | DBRI_SDP_TO_SER | DBRI_SDP_MSB);
89671b0921aSmacallan 	pipe_setup(sc, 20, DBRI_SDP_FIXED | DBRI_SDP_TO_SER | DBRI_SDP_MSB);
89771b0921aSmacallan 	pipe_setup(sc, 6, DBRI_SDP_MEM | DBRI_SDP_FROM_SER | DBRI_SDP_MSB);
898bc471ab0Smacallan #if 0
89971b0921aSmacallan 	pipe_setup(sc, 21, DBRI_SDP_FIXED | DBRI_SDP_FROM_SER | DBRI_SDP_MSB);
900bc471ab0Smacallan #endif
90171b0921aSmacallan 	pipe_setup(sc, 17, DBRI_SDP_FIXED | DBRI_SDP_TO_SER | DBRI_SDP_MSB);
90271b0921aSmacallan 	pipe_setup(sc, 18, DBRI_SDP_FIXED | DBRI_SDP_FROM_SER | DBRI_SDP_MSB);
90371b0921aSmacallan 	pipe_setup(sc, 19, DBRI_SDP_FIXED | DBRI_SDP_FROM_SER | DBRI_SDP_MSB);
90471b0921aSmacallan 
90571b0921aSmacallan 	pipe_receive_fixed(sc, 18, &sc->sc_mm.status);
90671b0921aSmacallan 	pipe_receive_fixed(sc, 19, &sc->sc_mm.version);
90771b0921aSmacallan 
90871b0921aSmacallan 	return;
90971b0921aSmacallan }
91071b0921aSmacallan 
9112f1817f6Smacallan static void
mmcodec_default(struct dbri_softc * sc)91271b0921aSmacallan mmcodec_default(struct dbri_softc *sc)
91371b0921aSmacallan {
91471b0921aSmacallan 	struct cs4215_state *mm = &sc->sc_mm;
91571b0921aSmacallan 
91671b0921aSmacallan 	/*
91771b0921aSmacallan 	 * no action, memory resetting only
91871b0921aSmacallan 	 *
91971b0921aSmacallan 	 * data time slots 5-8
92071b0921aSmacallan 	 * speaker, line and headphone enable. set gain to half.
921002ad491Smacallan 	 * input is line
92271b0921aSmacallan 	 */
923749b17a7Smacallan 	mm->d.bdata[0] = sc->sc_latt = 0x20 | CS4215_HE | CS4215_LE;
924749b17a7Smacallan 	mm->d.bdata[1] = sc->sc_ratt = 0x20 | CS4215_SE;
9252f4ccf79Smacallan 	sc->sc_linp = 128;
9262f4ccf79Smacallan 	sc->sc_rinp = 128;
9272f4ccf79Smacallan 	sc->sc_monitor = 0;
9282f4ccf79Smacallan 	sc->sc_input = 1;	/* line */
9292f4ccf79Smacallan 	mm->d.bdata[2] = (CS4215_LG((sc->sc_linp >> 4)) & 0x0f) |
9302f4ccf79Smacallan 	    ((sc->sc_input == 2) ? CS4215_IS : 0) | CS4215_PIO0 | CS4215_PIO1;
9312f4ccf79Smacallan 	mm->d.bdata[3] = (CS4215_RG((sc->sc_rinp >> 4) & 0x0f)) |
9322f4ccf79Smacallan 	    CS4215_MA(15 - ((sc->sc_monitor >> 4) & 0x0f));
9332f4ccf79Smacallan 
93471b0921aSmacallan 
93571b0921aSmacallan 	/*
93671b0921aSmacallan 	 * control time slots 1-4
93771b0921aSmacallan 	 *
93871b0921aSmacallan 	 * 0: default I/O voltage scale
93971b0921aSmacallan 	 * 1: 8 bit ulaw, 8kHz, mono, high pass filter disabled
94071b0921aSmacallan 	 * 2: serial enable, CHI master, 128 bits per frame, clock 1
94171b0921aSmacallan 	 * 3: tests disabled
94271b0921aSmacallan 	 */
943bc471ab0Smacallan 	mm->c.bcontrol[0] = CS4215_ONE | CS4215_MLB;
944749b17a7Smacallan 	mm->c.bcontrol[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval;
945749b17a7Smacallan 	mm->c.bcontrol[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal;
946749b17a7Smacallan 	mm->c.bcontrol[3] = 0;
94771b0921aSmacallan 
94871b0921aSmacallan 	return;
94971b0921aSmacallan }
95071b0921aSmacallan 
9512f1817f6Smacallan static void
mmcodec_setgain(struct dbri_softc * sc,int mute)95271b0921aSmacallan mmcodec_setgain(struct dbri_softc *sc, int mute)
95371b0921aSmacallan {
95471b0921aSmacallan 	if (mute) {
95571b0921aSmacallan 		/* disable all outputs, max. attenuation */
956749b17a7Smacallan 		sc->sc_mm.d.bdata[0] = sc->sc_latt | 63;
957749b17a7Smacallan 		sc->sc_mm.d.bdata[1] = sc->sc_ratt | 63;
95871b0921aSmacallan 	} else {
9592f4ccf79Smacallan 
960749b17a7Smacallan 		sc->sc_mm.d.bdata[0] = sc->sc_latt;
961749b17a7Smacallan 		sc->sc_mm.d.bdata[1] = sc->sc_ratt;
96271b0921aSmacallan 	}
96371b0921aSmacallan 
9642f4ccf79Smacallan 	/* input stuff */
9652f4ccf79Smacallan 	sc->sc_mm.d.bdata[2] = CS4215_LG((sc->sc_linp >> 4) & 0x0f) |
9662f4ccf79Smacallan 	    ((sc->sc_input == 2) ? CS4215_IS : 0) | CS4215_PIO0 | CS4215_PIO1;
9672f4ccf79Smacallan 	sc->sc_mm.d.bdata[3] = (CS4215_RG((sc->sc_rinp >> 4)) & 0x0f) |
9682f4ccf79Smacallan 	    (CS4215_MA(15 - ((sc->sc_monitor >> 4) & 0x0f)));
9692f4ccf79Smacallan 
970b4978607Smacallan 	if (sc->sc_powerstate == 0)
971b4978607Smacallan 		return;
972749b17a7Smacallan 	pipe_transmit_fixed(sc, 20, sc->sc_mm.d.ldata);
97371b0921aSmacallan 
9742f4ccf79Smacallan 	DPRINTF("mmcodec_setgain: %08x\n", sc->sc_mm.d.ldata);
9752f4ccf79Smacallan 	/* give the chip some time to execute the command */
97671b0921aSmacallan 	delay(250);
97771b0921aSmacallan 
97871b0921aSmacallan 	return;
97971b0921aSmacallan }
98071b0921aSmacallan 
9812f1817f6Smacallan static int
mmcodec_setcontrol(struct dbri_softc * sc)98271b0921aSmacallan mmcodec_setcontrol(struct dbri_softc *sc)
98371b0921aSmacallan {
98471b0921aSmacallan 	bus_space_tag_t iot = sc->sc_iot;
98571b0921aSmacallan 	bus_space_handle_t ioh = sc->sc_ioh;
9866e067fe9Stsutsui 	uint32_t val;
9876e067fe9Stsutsui 	uint32_t tmp;
988bc471ab0Smacallan 	int ret = 0;
989bc471ab0Smacallan #ifdef DBRI_SPIN
99071b0921aSmacallan 	int i;
991c973b3b2Smrg #else
992bc471ab0Smacallan 	int error, bail = 0;
99371b0921aSmacallan #endif
99471b0921aSmacallan 
995f6826716Smrg 	KASSERT(mutex_owned(&sc->sc_intr_lock));
996f6826716Smrg 
99771b0921aSmacallan 	/*
99871b0921aSmacallan 	 * Temporarily mute outputs and wait 125 us to make sure that it
99971b0921aSmacallan 	 * happens. This avoids clicking noises.
100071b0921aSmacallan 	 */
100171b0921aSmacallan 	mmcodec_setgain(sc, 1);
1002749b17a7Smacallan 	delay(125);
100371b0921aSmacallan 
1004bc471ab0Smacallan 	tmp = bus_space_read_4(iot, ioh, DBRI_REG0);
1005bc471ab0Smacallan 	tmp &= ~(DBRI_CHI_ACTIVATE);	/* disable CHI */
1006bc471ab0Smacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, tmp);
1007bc471ab0Smacallan 
10080d2dd3f5Smacallan 	bus_space_write_4(iot, ioh, DBRI_REG2, 0);
10090d2dd3f5Smacallan 	delay(125);
10100d2dd3f5Smacallan 
101171b0921aSmacallan 	/* enable control mode */
101271b0921aSmacallan 	val = DBRI_PIO_ENABLE_ALL | DBRI_PIO1;	/* was PIO1 */
101371b0921aSmacallan 
101471b0921aSmacallan 	/* XXX */
101571b0921aSmacallan 	val |= (sc->sc_mm.onboard ? DBRI_PIO0 : DBRI_PIO2);
101671b0921aSmacallan 
101771b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG2, val);
1018749b17a7Smacallan 	delay(34);
101971b0921aSmacallan 
102071b0921aSmacallan 	/*
102171b0921aSmacallan 	 * in control mode, the cs4215 is the slave device, so the
102271b0921aSmacallan 	 * DBRI must act as the CHI master.
102371b0921aSmacallan 	 *
102471b0921aSmacallan 	 * in data mode, the cs4215 must be the CHI master to insure
102571b0921aSmacallan 	 * that the data stream is in sync with its codec
102671b0921aSmacallan 	 */
102771b0921aSmacallan 	tmp = bus_space_read_4(iot, ioh, DBRI_REG0);
102871b0921aSmacallan 	tmp &= ~DBRI_COMMAND_CHI;
102971b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, tmp);
103071b0921aSmacallan 
103171b0921aSmacallan 	chi_reset(sc, CHImaster, 128);
103271b0921aSmacallan 
103371b0921aSmacallan 	/* control mode */
103471b0921aSmacallan 	pipe_ts_link(sc, 17, PIPEoutput, 16, 32, sc->sc_mm.offset);
103571b0921aSmacallan 	pipe_ts_link(sc, 18, PIPEinput, 16, 8, sc->sc_mm.offset);
103671b0921aSmacallan 	pipe_ts_link(sc, 19, PIPEinput, 16, 8, sc->sc_mm.offset + 48);
103771b0921aSmacallan 
1038bc471ab0Smacallan 	pipe_receive_fixed(sc, 18, &sc->sc_mm.status);
1039bc471ab0Smacallan 
104071b0921aSmacallan 	/* wait for the chip to echo back CLB as zero */
1041749b17a7Smacallan 	sc->sc_mm.c.bcontrol[0] &= ~CS4215_CLB;
1042749b17a7Smacallan 	pipe_transmit_fixed(sc, 17, sc->sc_mm.c.lcontrol);
104371b0921aSmacallan 
104471b0921aSmacallan 	tmp = bus_space_read_4(iot, ioh, DBRI_REG0);
104571b0921aSmacallan 	tmp |= DBRI_CHI_ACTIVATE;
104671b0921aSmacallan 	bus_space_write_4(iot, ioh, DBRI_REG0, tmp);
104771b0921aSmacallan 
1048bc471ab0Smacallan #ifdef DBRI_SPIN
104971b0921aSmacallan 	i = 1024;
1050bc471ab0Smacallan 	while (((sc->sc_mm.status & 0xe4) != CS4215_ONE) && (i > 0)) {
1051bc471ab0Smacallan 		i--;
105271b0921aSmacallan 		delay(125);
105371b0921aSmacallan 	}
105471b0921aSmacallan 
105571b0921aSmacallan 	if (i == 0) {
1056964f61e4Smacallan 		DPRINTF("%s: cs4215 didn't respond to CLB (0x%02x)\n",
1057e3c6619bSmacallan 		    device_xname(sc->sc_dev), sc->sc_mm.status);
1058bc471ab0Smacallan 		ret = -1;
1059bc471ab0Smacallan 		goto fail;
106071b0921aSmacallan 	}
106171b0921aSmacallan #else
1062bc471ab0Smacallan 	while (((sc->sc_mm.status & 0xe4) != CS4215_ONE) && (bail < 10)) {
1063c973b3b2Smrg 		DPRINTF("%s: cv_wait_sig %p\n", device_xname(sc->sc_dev), sc);
1064c973b3b2Smrg 		error = cv_timedwait_sig(&sc->sc_cv, &sc->sc_intr_lock, hz);
1065c973b3b2Smrg 		if (error == EINTR) {
1066c973b3b2Smrg 			DPRINTF("%s: interrupted\n", device_xname(sc->sc_dev));
1067bc471ab0Smacallan 			ret = -1;
1068bc471ab0Smacallan 			goto fail;
1069c973b3b2Smrg 		}
10700d2dd3f5Smacallan 		bail++;
107171b0921aSmacallan 	}
10720d2dd3f5Smacallan 	if (bail >= 10) {
1073bc471ab0Smacallan 		aprint_error("%s: switching to control mode timed out (%x %x)\n",
1074e3c6619bSmacallan 		    device_xname(sc->sc_dev), sc->sc_mm.status,
10750d2dd3f5Smacallan 		    bus_space_read_4(iot, ioh, DBRI_REG2));
1076bc471ab0Smacallan 		ret = -1;
1077bc471ab0Smacallan 		goto fail;
10780d2dd3f5Smacallan 	}
1079bc471ab0Smacallan #endif
108071b0921aSmacallan 
108171b0921aSmacallan 	/* copy the version information before it becomes unreadable again */
108271b0921aSmacallan 	sc->sc_version = sc->sc_mm.version;
1083bc471ab0Smacallan 	sc->sc_whack_codec = 0;
108471b0921aSmacallan 
1085bc471ab0Smacallan fail:
108671b0921aSmacallan 	/* terminate cs4215 control mode */
1087749b17a7Smacallan 	sc->sc_mm.c.bcontrol[0] |= CS4215_CLB;
1088749b17a7Smacallan 	pipe_transmit_fixed(sc, 17, sc->sc_mm.c.lcontrol);
108971b0921aSmacallan 
109071b0921aSmacallan 	/* two frames of control info @ 8kHz frame rate = 250us delay */
1091749b17a7Smacallan 	delay(250);
109271b0921aSmacallan 
109371b0921aSmacallan 	mmcodec_setgain(sc, 0);
109471b0921aSmacallan 
1095bc471ab0Smacallan 	return ret;
109671b0921aSmacallan 
109771b0921aSmacallan }
109871b0921aSmacallan 
109971b0921aSmacallan /*
110071b0921aSmacallan  * CHI combo
110171b0921aSmacallan  */
11022f1817f6Smacallan static void
chi_reset(struct dbri_softc * sc,enum ms ms,int bpf)110371b0921aSmacallan chi_reset(struct dbri_softc *sc, enum ms ms, int bpf)
110471b0921aSmacallan {
11056e067fe9Stsutsui 	volatile uint32_t *cmd;
110671b0921aSmacallan 	int val;
110771b0921aSmacallan 	int clockrate, divisor;
110871b0921aSmacallan 
110971b0921aSmacallan 	cmd = dbri_command_lock(sc);
111071b0921aSmacallan 
111171b0921aSmacallan 	/* set CHI anchor: pipe 16 */
111271b0921aSmacallan 	val = DBRI_DTS_VI | DBRI_DTS_INS | DBRI_DTS_PRVIN(16) | DBRI_PIPE(16);
111371b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_DTS, 0, val);
111471b0921aSmacallan 	*(cmd++) = DBRI_TS_ANCHOR | DBRI_TS_NEXT(16);
111571b0921aSmacallan 	*(cmd++) = 0;
111671b0921aSmacallan 
111771b0921aSmacallan 	val = DBRI_DTS_VO | DBRI_DTS_INS | DBRI_DTS_PRVOUT(16) | DBRI_PIPE(16);
111871b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_DTS, 0, val);
111971b0921aSmacallan 	*(cmd++) = 0;
112071b0921aSmacallan 	*(cmd++) = DBRI_TS_ANCHOR | DBRI_TS_NEXT(16);
112171b0921aSmacallan 
112271b0921aSmacallan 	sc->sc_pipe[16].sdp = 1;
112371b0921aSmacallan 	sc->sc_pipe[16].next = 16;
112471b0921aSmacallan 	sc->sc_chi_pipe_in = 16;
112571b0921aSmacallan 	sc->sc_chi_pipe_out = 16;
112671b0921aSmacallan 
112771b0921aSmacallan 	switch (ms) {
112871b0921aSmacallan 	case CHIslave:
112971b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_CHI, 0, DBRI_CHI_CHICM(0));
113071b0921aSmacallan 		break;
113171b0921aSmacallan 	case CHImaster:
113271b0921aSmacallan 		clockrate = bpf * 8;
113371b0921aSmacallan 		divisor = 12288 / clockrate;
113471b0921aSmacallan 
113571b0921aSmacallan 		if (divisor > 255 || divisor * clockrate != 12288)
1136e3c6619bSmacallan 			aprint_error_dev(sc->sc_dev,
1137e3c6619bSmacallan 			    "illegal bits-per-frame %d\n", bpf);
113871b0921aSmacallan 
113971b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_CHI, 0,
114071b0921aSmacallan 		    DBRI_CHI_CHICM(divisor) | DBRI_CHI_FD | DBRI_CHI_BPF(bpf));
114171b0921aSmacallan 		break;
114271b0921aSmacallan 	default:
1143e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "unknown value for ms!\n");
114471b0921aSmacallan 		break;
114571b0921aSmacallan 	}
114671b0921aSmacallan 
114771b0921aSmacallan 	sc->sc_chi_bpf = bpf;
114871b0921aSmacallan 
114971b0921aSmacallan 	/* CHI data mode */
115071b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_PAUSE, 0, 0);
115171b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_CDM, 0,
115271b0921aSmacallan 	    DBRI_CDM_XCE | DBRI_CDM_XEN | DBRI_CDM_REN);
115371b0921aSmacallan 
115471b0921aSmacallan 	dbri_command_send(sc, cmd);
115571b0921aSmacallan 
115671b0921aSmacallan 	return;
115771b0921aSmacallan }
115871b0921aSmacallan 
115971b0921aSmacallan /*
116071b0921aSmacallan  * pipe stuff
116171b0921aSmacallan  */
11622f1817f6Smacallan static void
pipe_setup(struct dbri_softc * sc,int pipe,int sdp)116371b0921aSmacallan pipe_setup(struct dbri_softc *sc, int pipe, int sdp)
116471b0921aSmacallan {
1165964f61e4Smacallan 	DPRINTF("pipe setup: %d\n", pipe);
116671b0921aSmacallan 	if (pipe < 0 || pipe >= DBRI_PIPE_MAX) {
1167e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "illegal pipe number %d\n",
11684c5fa20dScegger 		    pipe);
116971b0921aSmacallan 		return;
117071b0921aSmacallan 	}
117171b0921aSmacallan 
117271b0921aSmacallan 	if ((sdp & 0xf800) != sdp)
1173e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "strange SDP value %d\n",
1174002ad491Smacallan 		    sdp);
117571b0921aSmacallan 
117671b0921aSmacallan 	if (DBRI_SDP_MODE(sdp) == DBRI_SDP_FIXED &&
117771b0921aSmacallan 	    !(sdp & DBRI_SDP_TO_SER))
117871b0921aSmacallan 		sdp |= DBRI_SDP_CHANGE;
117971b0921aSmacallan 
118071b0921aSmacallan 	sdp |= DBRI_PIPE(pipe);
118171b0921aSmacallan 
118271b0921aSmacallan 	sc->sc_pipe[pipe].sdp = sdp;
118371b0921aSmacallan 	sc->sc_pipe[pipe].desc = -1;
118471b0921aSmacallan 
118571b0921aSmacallan 	pipe_reset(sc, pipe);
118671b0921aSmacallan 
118771b0921aSmacallan 	return;
118871b0921aSmacallan }
118971b0921aSmacallan 
11902f1817f6Smacallan static void
pipe_reset(struct dbri_softc * sc,int pipe)119171b0921aSmacallan pipe_reset(struct dbri_softc *sc, int pipe)
119271b0921aSmacallan {
119371b0921aSmacallan 	struct dbri_desc *dd;
119471b0921aSmacallan 	int sdp;
119571b0921aSmacallan 	int desc;
11966e067fe9Stsutsui 	volatile uint32_t *cmd;
119771b0921aSmacallan 
119871b0921aSmacallan 	if (pipe < 0 || pipe >= DBRI_PIPE_MAX) {
1199e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "illegal pipe number %d\n",
12004c5fa20dScegger 		    pipe);
120171b0921aSmacallan 		return;
120271b0921aSmacallan 	}
120371b0921aSmacallan 
120471b0921aSmacallan 	sdp = sc->sc_pipe[pipe].sdp;
120571b0921aSmacallan 	if (sdp == 0) {
1206e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "can not reset uninitialized pipe %d\n",
12074c5fa20dScegger 		    pipe);
120871b0921aSmacallan 		return;
120971b0921aSmacallan 	}
121071b0921aSmacallan 
121171b0921aSmacallan 	cmd = dbri_command_lock(sc);
121271b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_SDP, 0,
121371b0921aSmacallan 	    sdp | DBRI_SDP_CLEAR | DBRI_SDP_VALID_POINTER);
121471b0921aSmacallan 	*(cmd++) = 0;
121571b0921aSmacallan 	dbri_command_send(sc, cmd);
121671b0921aSmacallan 
121771b0921aSmacallan 	desc = sc->sc_pipe[pipe].desc;
121871b0921aSmacallan 
121971b0921aSmacallan 	dd = &sc->sc_desc[desc];
122071b0921aSmacallan 
122171b0921aSmacallan 	dd->busy = 0;
122271b0921aSmacallan 
122371b0921aSmacallan #if 0
122471b0921aSmacallan 	if (dd->callback)
12254b293a84Sad 		softint_schedule(dd->softint);
122671b0921aSmacallan #endif
122771b0921aSmacallan 
122871b0921aSmacallan 	sc->sc_pipe[pipe].desc = -1;
122971b0921aSmacallan 
123071b0921aSmacallan 	return;
123171b0921aSmacallan }
123271b0921aSmacallan 
12332f1817f6Smacallan static void
pipe_receive_fixed(struct dbri_softc * sc,int pipe,volatile uint32_t * prec)12346e067fe9Stsutsui pipe_receive_fixed(struct dbri_softc *sc, int pipe, volatile uint32_t *prec)
123571b0921aSmacallan {
123671b0921aSmacallan 
123771b0921aSmacallan 	if (pipe < DBRI_PIPE_MAX / 2 || pipe >= DBRI_PIPE_MAX) {
1238e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "illegal pipe number %d\n",
12394c5fa20dScegger 		    pipe);
124071b0921aSmacallan 		return;
124171b0921aSmacallan 	}
124271b0921aSmacallan 
124371b0921aSmacallan 	if (DBRI_SDP_MODE(sc->sc_pipe[pipe].sdp) != DBRI_SDP_FIXED) {
1244e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "non-fixed pipe %d\n",
124571b0921aSmacallan 		    pipe);
124671b0921aSmacallan 		return;
124771b0921aSmacallan 	}
124871b0921aSmacallan 
124971b0921aSmacallan 	if (sc->sc_pipe[pipe].sdp & DBRI_SDP_TO_SER) {
1250e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "can not receive on transmit pipe %d\b",
12514c5fa20dScegger 		    pipe);
125271b0921aSmacallan 		return;
125371b0921aSmacallan 	}
125471b0921aSmacallan 
125571b0921aSmacallan 	sc->sc_pipe[pipe].prec = prec;
125671b0921aSmacallan 
125771b0921aSmacallan 	return;
125871b0921aSmacallan }
125971b0921aSmacallan 
12602f1817f6Smacallan static void
pipe_transmit_fixed(struct dbri_softc * sc,int pipe,uint32_t data)12616e067fe9Stsutsui pipe_transmit_fixed(struct dbri_softc *sc, int pipe, uint32_t data)
126271b0921aSmacallan {
12636e067fe9Stsutsui 	volatile uint32_t *cmd;
126471b0921aSmacallan 
126571b0921aSmacallan 	if (pipe < DBRI_PIPE_MAX / 2 || pipe >= DBRI_PIPE_MAX) {
1266e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "illegal pipe number %d\n",
12674c5fa20dScegger 		    pipe);
126871b0921aSmacallan 		return;
126971b0921aSmacallan 	}
127071b0921aSmacallan 
127171b0921aSmacallan 	if (DBRI_SDP_MODE(sc->sc_pipe[pipe].sdp) == 0) {
1272e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "uninitialized pipe %d\n",
12734c5fa20dScegger 		    pipe);
127471b0921aSmacallan 		return;
127571b0921aSmacallan 	}
127671b0921aSmacallan 
127771b0921aSmacallan 	if (DBRI_SDP_MODE(sc->sc_pipe[pipe].sdp) != DBRI_SDP_FIXED) {
1278e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "non-fixed pipe %d\n",
12795b56050bSmacallan 		    pipe);
128071b0921aSmacallan 		return;
128171b0921aSmacallan 	}
128271b0921aSmacallan 
128371b0921aSmacallan 	if (!(sc->sc_pipe[pipe].sdp & DBRI_SDP_TO_SER)) {
1284e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "called on receive pipe %d\n",
12854c5fa20dScegger 		    pipe);
128671b0921aSmacallan 		return;
128771b0921aSmacallan 	}
128871b0921aSmacallan 
128971b0921aSmacallan 	if (sc->sc_pipe[pipe].sdp & DBRI_SDP_MSB)
129071b0921aSmacallan 		data = reverse_bytes(data, sc->sc_pipe[pipe].length);
129171b0921aSmacallan 
129271b0921aSmacallan 	cmd = dbri_command_lock(sc);
129371b0921aSmacallan 	*(cmd++) = DBRI_CMD(DBRI_COMMAND_SSP, 0, pipe);
129471b0921aSmacallan 	*(cmd++) = data;
129571b0921aSmacallan 
129671b0921aSmacallan 	dbri_command_send(sc, cmd);
129771b0921aSmacallan 
129871b0921aSmacallan 	return;
129971b0921aSmacallan }
130071b0921aSmacallan 
13012f1817f6Smacallan static void
setup_ring_xmit(struct dbri_softc * sc,int pipe,int which,int num,int blksz,void (* callback)(void *),void * callback_args)1302002ad491Smacallan setup_ring_xmit(struct dbri_softc *sc, int pipe, int which, int num, int blksz,
130371b0921aSmacallan 		void (*callback)(void *), void *callback_args)
130471b0921aSmacallan {
13056e067fe9Stsutsui 	volatile uint32_t *cmd;
13068a962f23Sjmcneill 	int i;
130741e64636Smrg #if 0
130871b0921aSmacallan 	int td;
130971b0921aSmacallan 	int td_first, td_last;
131041e64636Smrg #endif
131171b0921aSmacallan 	bus_addr_t dmabuf, dmabase;
131271b0921aSmacallan 	struct dbri_desc *dd = &sc->sc_desc[which];
131371b0921aSmacallan 
1314002ad491Smacallan 	switch (pipe) {
1315002ad491Smacallan 		case 4:
1316002ad491Smacallan 			/* output, offset 0 */
1317002ad491Smacallan 			break;
1318002ad491Smacallan 		default:
1319002ad491Smacallan 			aprint_error("%s: illegal pipe number (%d)\n",
1320002ad491Smacallan 			    __func__, pipe);
132171b0921aSmacallan 			return;
132271b0921aSmacallan 	}
132371b0921aSmacallan 
132441e64636Smrg #if 0
1325002ad491Smacallan 	td = 0;
1326002ad491Smacallan 	td_first = td_last = -1;
132741e64636Smrg #endif
1328002ad491Smacallan 
132971b0921aSmacallan 	if (sc->sc_pipe[pipe].sdp == 0) {
1330e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "uninitialized pipe %d\n",
13314c5fa20dScegger 		    pipe);
133271b0921aSmacallan 		return;
133371b0921aSmacallan 	}
133471b0921aSmacallan 
133571b0921aSmacallan 	dmabuf = dd->dmabase;
133671b0921aSmacallan 	dmabase = sc->sc_dmabase;
133771b0921aSmacallan 
133871b0921aSmacallan 	for (i = 0; i < (num - 1); i++) {
133971b0921aSmacallan 
1340002ad491Smacallan 		sc->sc_dma->xmit[i].flags = TX_BCNT(blksz)
134171b0921aSmacallan 		    | TX_EOF | TX_BINT;
1342002ad491Smacallan 		sc->sc_dma->xmit[i].ba = dmabuf;
1343002ad491Smacallan 		sc->sc_dma->xmit[i].nda = dmabase + dbri_dma_off(xmit, i + 1);
1344002ad491Smacallan 		sc->sc_dma->xmit[i].status = 0;
134571b0921aSmacallan 
134641e64636Smrg #if 0
134771b0921aSmacallan 		td_last = td;
134841e64636Smrg #endif
134971b0921aSmacallan 		dmabuf += blksz;
135071b0921aSmacallan 	}
135171b0921aSmacallan 
1352002ad491Smacallan 	sc->sc_dma->xmit[i].flags = TX_BCNT(blksz) | TX_EOF | TX_BINT;
1353002ad491Smacallan 
1354002ad491Smacallan 	sc->sc_dma->xmit[i].ba = dmabuf;
1355002ad491Smacallan 	sc->sc_dma->xmit[i].nda = dmabase + dbri_dma_off(xmit, 0);
1356002ad491Smacallan 	sc->sc_dma->xmit[i].status = 0;
135771b0921aSmacallan 
13585b56050bSmacallan 	dd->callback = callback;
13595b56050bSmacallan 	dd->callback_args = callback_args;
136071b0921aSmacallan 
136171b0921aSmacallan 	/* the pipe shouldn't be active */
136271b0921aSmacallan 	if (pipe_active(sc, pipe)) {
1363964f61e4Smacallan 		aprint_error("pipe active (CDP)\n");
136471b0921aSmacallan 		/* pipe is already active */
136571b0921aSmacallan #if 0
136671b0921aSmacallan 		td_last = sc->sc_pipe[pipe].desc;
136771b0921aSmacallan 		while (sc->sc_desc[td_last].next != -1)
136871b0921aSmacallan 			td_last = sc->sc_desc[td_last].next;
136971b0921aSmacallan 
137071b0921aSmacallan 		sc->sc_desc[td_last].next = td_first;
137171b0921aSmacallan 		sc->sc_dma->desc[td_last].nda =
137271b0921aSmacallan 		    sc->sc_dmabase + dbri_dma_off(desc, td_first);
137371b0921aSmacallan 
137471b0921aSmacallan 		cmd = dbri_command_lock(sc);
137571b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_CDP, 0, pipe);
137671b0921aSmacallan 		dbri_command_send(sc, cmd);
137771b0921aSmacallan #endif
137871b0921aSmacallan 	} else {
137971b0921aSmacallan 		/*
138071b0921aSmacallan 		 * pipe isn't active - issue an SDP command to start our
138171b0921aSmacallan 		 * chain of TDs running
138271b0921aSmacallan 		 */
138371b0921aSmacallan 		sc->sc_pipe[pipe].desc = which;
138471b0921aSmacallan 		cmd = dbri_command_lock(sc);
138571b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_SDP, 0,
138671b0921aSmacallan 					sc->sc_pipe[pipe].sdp |
138771b0921aSmacallan 					DBRI_SDP_VALID_POINTER |
138871b0921aSmacallan 					DBRI_SDP_EVERY |
138971b0921aSmacallan 					DBRI_SDP_CLEAR);
1390002ad491Smacallan 		*(cmd++) = sc->sc_dmabase + dbri_dma_off(xmit, 0);
139171b0921aSmacallan 		dbri_command_send(sc, cmd);
1392002ad491Smacallan 		DPRINTF("%s: starting DMA\n", __func__);
1393002ad491Smacallan 	}
1394002ad491Smacallan 
1395002ad491Smacallan 	return;
1396002ad491Smacallan }
1397002ad491Smacallan 
1398002ad491Smacallan static void
setup_ring_recv(struct dbri_softc * sc,int pipe,int which,int num,int blksz,void (* callback)(void *),void * callback_args)1399002ad491Smacallan setup_ring_recv(struct dbri_softc *sc, int pipe, int which, int num, int blksz,
1400002ad491Smacallan 		void (*callback)(void *), void *callback_args)
1401002ad491Smacallan {
14026e067fe9Stsutsui 	volatile uint32_t *cmd;
14038a962f23Sjmcneill 	int i;
140441e64636Smrg #if 0
1405002ad491Smacallan 	int td_first, td_last;
140641e64636Smrg #endif
1407002ad491Smacallan 	bus_addr_t dmabuf, dmabase;
1408002ad491Smacallan 	struct dbri_desc *dd = &sc->sc_desc[which];
1409002ad491Smacallan 
1410002ad491Smacallan 	switch (pipe) {
1411002ad491Smacallan 		case 6:
1412002ad491Smacallan 			break;
1413002ad491Smacallan 		default:
1414002ad491Smacallan 			aprint_error("%s: illegal pipe number (%d)\n",
1415002ad491Smacallan 			    __func__, pipe);
1416002ad491Smacallan 			return;
1417002ad491Smacallan 	}
1418002ad491Smacallan 
141941e64636Smrg #if 0
1420002ad491Smacallan 	td_first = td_last = -1;
142141e64636Smrg #endif
1422002ad491Smacallan 
1423002ad491Smacallan 	if (sc->sc_pipe[pipe].sdp == 0) {
1424e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "uninitialized pipe %d\n",
14254c5fa20dScegger 		    pipe);
1426002ad491Smacallan 		return;
1427002ad491Smacallan 	}
1428002ad491Smacallan 
1429002ad491Smacallan 	dmabuf = dd->dmabase;
1430002ad491Smacallan 	dmabase = sc->sc_dmabase;
1431002ad491Smacallan 
1432002ad491Smacallan 	for (i = 0; i < (num - 1); i++) {
1433002ad491Smacallan 
1434002ad491Smacallan 		sc->sc_dma->recv[i].flags = RX_BSIZE(blksz) | RX_FINAL;
1435002ad491Smacallan 		sc->sc_dma->recv[i].ba = dmabuf;
1436002ad491Smacallan 		sc->sc_dma->recv[i].nda = dmabase + dbri_dma_off(recv, i + 1);
1437002ad491Smacallan 		sc->sc_dma->recv[i].status = RX_EOF;
1438002ad491Smacallan 
143941e64636Smrg #if 0
1440002ad491Smacallan 		td_last = i;
144141e64636Smrg #endif
1442002ad491Smacallan 		dmabuf += blksz;
1443002ad491Smacallan 	}
1444002ad491Smacallan 
1445002ad491Smacallan 	sc->sc_dma->recv[i].flags = RX_BSIZE(blksz) | RX_FINAL;
1446002ad491Smacallan 
1447002ad491Smacallan 	sc->sc_dma->recv[i].ba = dmabuf;
1448002ad491Smacallan 	sc->sc_dma->recv[i].nda = dmabase + dbri_dma_off(recv, 0);
1449002ad491Smacallan 	sc->sc_dma->recv[i].status = RX_EOF;
1450002ad491Smacallan 
1451002ad491Smacallan 	dd->callback = callback;
1452002ad491Smacallan 	dd->callback_args = callback_args;
1453002ad491Smacallan 
1454002ad491Smacallan 	/* the pipe shouldn't be active */
1455002ad491Smacallan 	if (pipe_active(sc, pipe)) {
1456002ad491Smacallan 		aprint_error("pipe active (CDP)\n");
1457002ad491Smacallan 		/* pipe is already active */
1458002ad491Smacallan #if 0
1459002ad491Smacallan 		td_last = sc->sc_pipe[pipe].desc;
1460002ad491Smacallan 		while (sc->sc_desc[td_last].next != -1)
1461002ad491Smacallan 			td_last = sc->sc_desc[td_last].next;
1462002ad491Smacallan 
1463002ad491Smacallan 		sc->sc_desc[td_last].next = td_first;
1464002ad491Smacallan 		sc->sc_dma->desc[td_last].nda =
1465002ad491Smacallan 		    sc->sc_dmabase + dbri_dma_off(desc, td_first);
1466002ad491Smacallan 
1467002ad491Smacallan 		cmd = dbri_command_lock(sc);
1468002ad491Smacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_CDP, 0, pipe);
1469002ad491Smacallan 		dbri_command_send(sc, cmd);
1470002ad491Smacallan #endif
1471002ad491Smacallan 	} else {
1472002ad491Smacallan 		/*
1473002ad491Smacallan 		 * pipe isn't active - issue an SDP command to start our
1474002ad491Smacallan 		 * chain of TDs running
1475002ad491Smacallan 		 */
1476002ad491Smacallan 		sc->sc_pipe[pipe].desc = which;
1477002ad491Smacallan 		cmd = dbri_command_lock(sc);
1478002ad491Smacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_SDP, 0,
1479002ad491Smacallan 					sc->sc_pipe[pipe].sdp |
1480002ad491Smacallan 					DBRI_SDP_VALID_POINTER |
1481002ad491Smacallan 					DBRI_SDP_EVERY |
1482002ad491Smacallan 					DBRI_SDP_CLEAR);
1483002ad491Smacallan 		*(cmd++) = sc->sc_dmabase + dbri_dma_off(recv, 0);
1484002ad491Smacallan 		dbri_command_send(sc, cmd);
1485002ad491Smacallan 		DPRINTF("%s: starting DMA\n", __func__);
148671b0921aSmacallan 	}
148771b0921aSmacallan 
148871b0921aSmacallan 	return;
148971b0921aSmacallan }
149071b0921aSmacallan 
14912f1817f6Smacallan static void
pipe_ts_link(struct dbri_softc * sc,int pipe,enum io dir,int basepipe,int len,int cycle)149271b0921aSmacallan pipe_ts_link(struct dbri_softc *sc, int pipe, enum io dir, int basepipe,
149371b0921aSmacallan 		int len, int cycle)
149471b0921aSmacallan {
14956e067fe9Stsutsui 	volatile uint32_t *cmd;
149671b0921aSmacallan 	int prevpipe, nextpipe;
149771b0921aSmacallan 	int val;
149871b0921aSmacallan 
1499002ad491Smacallan 	DPRINTF("%s: %d\n", __func__, pipe);
150071b0921aSmacallan 	if (pipe < 0 || pipe >= DBRI_PIPE_MAX ||
150171b0921aSmacallan 	    basepipe < 0 || basepipe >= DBRI_PIPE_MAX) {
1502e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "illegal pipe numbers (%d, %d)\n",
15034c5fa20dScegger 		    pipe, basepipe);
150471b0921aSmacallan 		return;
150571b0921aSmacallan 	}
150671b0921aSmacallan 
150771b0921aSmacallan 	if (sc->sc_pipe[pipe].sdp == 0 || sc->sc_pipe[basepipe].sdp == 0) {
1508e3c6619bSmacallan 		aprint_error_dev(sc->sc_dev, "uninitialized pipe (%d, %d)\n",
15094c5fa20dScegger 		    pipe, basepipe);
151071b0921aSmacallan 		return;
151171b0921aSmacallan 	}
151271b0921aSmacallan 
151371b0921aSmacallan 	if (basepipe == 16 && dir == PIPEoutput && cycle == 0)
151471b0921aSmacallan 		cycle = sc->sc_chi_bpf;
151571b0921aSmacallan 
151671b0921aSmacallan 	if (basepipe == pipe)
151771b0921aSmacallan 		prevpipe = nextpipe = pipe;
151871b0921aSmacallan 	else {
151971b0921aSmacallan 		if (basepipe == 16) {
152071b0921aSmacallan 			if (dir == PIPEinput) {
152171b0921aSmacallan 				prevpipe = sc->sc_chi_pipe_in;
152271b0921aSmacallan 			} else {
152371b0921aSmacallan 				prevpipe = sc->sc_chi_pipe_out;
152471b0921aSmacallan 			}
152571b0921aSmacallan 		} else
152671b0921aSmacallan 			prevpipe = basepipe;
152771b0921aSmacallan 
152871b0921aSmacallan 		nextpipe = sc->sc_pipe[prevpipe].next;
152971b0921aSmacallan 
153071b0921aSmacallan 		while (sc->sc_pipe[nextpipe].cycle < cycle &&
153171b0921aSmacallan 		    sc->sc_pipe[nextpipe].next != basepipe) {
153271b0921aSmacallan 			prevpipe = nextpipe;
153371b0921aSmacallan 			nextpipe = sc->sc_pipe[nextpipe].next;
153471b0921aSmacallan 		}
153571b0921aSmacallan 	}
153671b0921aSmacallan 
153771b0921aSmacallan 	if (prevpipe == 16) {
153871b0921aSmacallan 		if (dir == PIPEinput) {
153971b0921aSmacallan 			sc->sc_chi_pipe_in = pipe;
154071b0921aSmacallan 		} else {
154171b0921aSmacallan 			sc->sc_chi_pipe_out = pipe;
154271b0921aSmacallan 		}
154371b0921aSmacallan 	} else
154471b0921aSmacallan 		sc->sc_pipe[prevpipe].next = pipe;
154571b0921aSmacallan 
154671b0921aSmacallan 	sc->sc_pipe[pipe].next = nextpipe;
154771b0921aSmacallan 	sc->sc_pipe[pipe].cycle = cycle;
154871b0921aSmacallan 	sc->sc_pipe[pipe].length = len;
154971b0921aSmacallan 
155071b0921aSmacallan 	cmd = dbri_command_lock(sc);
155171b0921aSmacallan 
155271b0921aSmacallan 	switch (dir) {
155371b0921aSmacallan 	case PIPEinput:
155471b0921aSmacallan 		val = DBRI_DTS_VI | DBRI_DTS_INS | DBRI_DTS_PRVIN(prevpipe);
155571b0921aSmacallan 		val |= pipe;
155671b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_DTS, 0, val);
155771b0921aSmacallan 		*(cmd++) = DBRI_TS_LEN(len) | DBRI_TS_CYCLE(cycle) |
155871b0921aSmacallan 		    DBRI_TS_NEXT(nextpipe);
155971b0921aSmacallan 		*(cmd++) = 0;
156071b0921aSmacallan 		break;
156171b0921aSmacallan 	case PIPEoutput:
156271b0921aSmacallan 		val = DBRI_DTS_VO | DBRI_DTS_INS | DBRI_DTS_PRVOUT(prevpipe);
156371b0921aSmacallan 		val |= pipe;
156471b0921aSmacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_DTS, 0, val);
156571b0921aSmacallan 		*(cmd++) = 0;
156671b0921aSmacallan 		*(cmd++) = DBRI_TS_LEN(len) | DBRI_TS_CYCLE(cycle) |
156771b0921aSmacallan 		    DBRI_TS_NEXT(nextpipe);
156871b0921aSmacallan 		break;
156971b0921aSmacallan 	default:
1570964f61e4Smacallan 		DPRINTF("%s: should not have happened!\n",
1571e3c6619bSmacallan 		    device_xname(sc->sc_dev));
157271b0921aSmacallan 		break;
157371b0921aSmacallan 	}
157471b0921aSmacallan 
157571b0921aSmacallan 	dbri_command_send(sc, cmd);
157671b0921aSmacallan 
157771b0921aSmacallan 	return;
157871b0921aSmacallan }
157971b0921aSmacallan 
15802f1817f6Smacallan static int
pipe_active(struct dbri_softc * sc,int pipe)158171b0921aSmacallan pipe_active(struct dbri_softc *sc, int pipe)
158271b0921aSmacallan {
158371b0921aSmacallan 
158471b0921aSmacallan 	return (sc->sc_pipe[pipe].desc != -1);
158571b0921aSmacallan }
158671b0921aSmacallan 
158771b0921aSmacallan /*
158871b0921aSmacallan  * subroutines required to interface with audio(9)
158971b0921aSmacallan  */
159071b0921aSmacallan 
15912f1817f6Smacallan static int
dbri_query_format(void * hdl,audio_format_query_t * afp)1592e622eac4Sisaki dbri_query_format(void *hdl, audio_format_query_t *afp)
159371b0921aSmacallan {
159471b0921aSmacallan 
1595e622eac4Sisaki 	return audio_query_format(dbri_formats, DBRI_NFORMATS, afp);
159671b0921aSmacallan }
159771b0921aSmacallan 
15982f1817f6Smacallan static int
dbri_set_format(void * hdl,int setmode,const audio_params_t * play,const audio_params_t * rec,audio_filter_reg_t * pfil,audio_filter_reg_t * rfil)1599e622eac4Sisaki dbri_set_format(void *hdl, int setmode,
1600e622eac4Sisaki 		const audio_params_t *play, const audio_params_t *rec,
1601e622eac4Sisaki 		audio_filter_reg_t *pfil, audio_filter_reg_t *rfil)
160271b0921aSmacallan {
160371b0921aSmacallan 	struct dbri_softc *sc = hdl;
16042f1817f6Smacallan 	int rate;
160571b0921aSmacallan 
1606e622eac4Sisaki 	/* *play and *rec are the identical because !AUDIO_PROP_INDEPENDENT. */
16072f1817f6Smacallan 
16082f1817f6Smacallan 	for (rate = 0; CS4215_FREQ[rate].freq; rate++)
1609e622eac4Sisaki 		if (CS4215_FREQ[rate].freq == play->sample_rate)
161071b0921aSmacallan 			break;
161171b0921aSmacallan 
16122f1817f6Smacallan 	if (CS4215_FREQ[rate].freq == 0)
1613e622eac4Sisaki 		return EINVAL;
161471b0921aSmacallan 
161571b0921aSmacallan 	/* set frequency */
1616749b17a7Smacallan 	sc->sc_mm.c.bcontrol[1] &= ~0x38;
16172f1817f6Smacallan 	sc->sc_mm.c.bcontrol[1] |= CS4215_FREQ[rate].csval;
1618749b17a7Smacallan 	sc->sc_mm.c.bcontrol[2] &= ~0x70;
16192f1817f6Smacallan 	sc->sc_mm.c.bcontrol[2] |= CS4215_FREQ[rate].xtal;
162071b0921aSmacallan 
1621e622eac4Sisaki 	/* set encoding */
1622749b17a7Smacallan 	sc->sc_mm.c.bcontrol[1] &= ~3;
1623749b17a7Smacallan 	sc->sc_mm.c.bcontrol[1] |= CS4215_DFR_LINEAR16;
162471b0921aSmacallan 
1625e622eac4Sisaki 	/* set channel */
1626749b17a7Smacallan 	sc->sc_mm.c.bcontrol[1] |= CS4215_DFR_STEREO;
1627e622eac4Sisaki 
1628bc471ab0Smacallan 	sc->sc_whack_codec = 1;
1629e622eac4Sisaki 	return 0;
163071b0921aSmacallan }
163171b0921aSmacallan 
16322f1817f6Smacallan static int
dbri_round_blocksize(void * hdl,int bs,int mode,const audio_params_t * param)163371b0921aSmacallan dbri_round_blocksize(void *hdl, int bs, int mode,
163471b0921aSmacallan 			const audio_params_t *param)
163571b0921aSmacallan {
163671b0921aSmacallan 
16376e396296Sisaki 	if (bs > 0x1ffc)
16386e396296Sisaki 		return 0x1ffc;
1639e622eac4Sisaki 	return bs;
164071b0921aSmacallan }
164171b0921aSmacallan 
16422f1817f6Smacallan static int
dbri_halt_output(void * hdl)164371b0921aSmacallan dbri_halt_output(void *hdl)
164471b0921aSmacallan {
164571b0921aSmacallan 	struct dbri_softc *sc = hdl;
164671b0921aSmacallan 
1647002ad491Smacallan 	if (!sc->sc_playing)
1648002ad491Smacallan 		return 0;
1649002ad491Smacallan 
16502f4ccf79Smacallan 	sc->sc_playing = 0;
165171b0921aSmacallan 	pipe_reset(sc, 4);
165271b0921aSmacallan 	return (0);
165371b0921aSmacallan }
165471b0921aSmacallan 
16552f1817f6Smacallan static int
dbri_getdev(void * hdl,struct audio_device * ret)165671b0921aSmacallan dbri_getdev(void *hdl, struct audio_device *ret)
165771b0921aSmacallan {
165871b0921aSmacallan 
165971b0921aSmacallan 	*ret = dbri_device;
166071b0921aSmacallan 	return (0);
166171b0921aSmacallan }
166271b0921aSmacallan 
16632f1817f6Smacallan static int
dbri_set_port(void * hdl,mixer_ctrl_t * mc)166471b0921aSmacallan dbri_set_port(void *hdl, mixer_ctrl_t *mc)
166571b0921aSmacallan {
166671b0921aSmacallan 	struct dbri_softc *sc = hdl;
166771b0921aSmacallan 	int latt = sc->sc_latt, ratt = sc->sc_ratt;
166871b0921aSmacallan 
166971b0921aSmacallan 	switch (mc->dev) {
167071b0921aSmacallan 	    case DBRI_VOL_OUTPUT:	/* master volume */
167171b0921aSmacallan 		latt = (latt & 0xc0) | (63 -
1672d1579b2dSriastradh 		    uimin(mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> 2, 63));
167371b0921aSmacallan 		ratt = (ratt & 0xc0) | (63 -
1674d1579b2dSriastradh 		    uimin(mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] >> 2, 63));
167571b0921aSmacallan 		break;
167671b0921aSmacallan 	    case DBRI_ENABLE_MONO:	/* built-in speaker */
167771b0921aSmacallan 	    	if (mc->un.ord == 1) {
167871b0921aSmacallan 			ratt |= CS4215_SE;
167971b0921aSmacallan 		} else
168071b0921aSmacallan 			ratt &= ~CS4215_SE;
168171b0921aSmacallan 		break;
168271b0921aSmacallan 	    case DBRI_ENABLE_HEADPHONE:	/* headphones output */
168371b0921aSmacallan 	    	if (mc->un.ord == 1) {
168471b0921aSmacallan 			latt |= CS4215_HE;
168571b0921aSmacallan 		} else
168671b0921aSmacallan 			latt &= ~CS4215_HE;
168771b0921aSmacallan 		break;
168871b0921aSmacallan 	    case DBRI_ENABLE_LINE:	/* line out */
168971b0921aSmacallan 	    	if (mc->un.ord == 1) {
169071b0921aSmacallan 			latt |= CS4215_LE;
169171b0921aSmacallan 		} else
169271b0921aSmacallan 			latt &= ~CS4215_LE;
169371b0921aSmacallan 		break;
16942f4ccf79Smacallan 	    case DBRI_VOL_MONITOR:
16952f4ccf79Smacallan 		if (mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] ==
16962f4ccf79Smacallan 		    sc->sc_monitor)
16972f4ccf79Smacallan 			return 0;
16982f4ccf79Smacallan 		sc->sc_monitor = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
16992f4ccf79Smacallan 		break;
17002f4ccf79Smacallan 	    case DBRI_INPUT_GAIN:
17012f4ccf79Smacallan 		sc->sc_linp = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
17022f4ccf79Smacallan 		sc->sc_rinp = mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
17032f4ccf79Smacallan 		break;
17042f4ccf79Smacallan 	    case DBRI_INPUT_SELECT:
17052f4ccf79Smacallan 	    	if (mc->un.mask == sc->sc_input)
17062f4ccf79Smacallan 	    		return 0;
17072f4ccf79Smacallan 	    	sc->sc_input =  mc->un.mask;
17082f4ccf79Smacallan 	    	break;
170971b0921aSmacallan 	}
171071b0921aSmacallan 
171171b0921aSmacallan 	sc->sc_latt = latt;
171271b0921aSmacallan 	sc->sc_ratt = ratt;
171371b0921aSmacallan 
1714e622eac4Sisaki 	mutex_spin_enter(&sc->sc_intr_lock);
171571b0921aSmacallan 	mmcodec_setgain(sc, 0);
1716e622eac4Sisaki 	mutex_spin_exit(&sc->sc_intr_lock);
171771b0921aSmacallan 
171871b0921aSmacallan 	return (0);
171971b0921aSmacallan }
172071b0921aSmacallan 
17212f1817f6Smacallan static int
dbri_get_port(void * hdl,mixer_ctrl_t * mc)172271b0921aSmacallan dbri_get_port(void *hdl, mixer_ctrl_t *mc)
172371b0921aSmacallan {
172471b0921aSmacallan 	struct dbri_softc *sc = hdl;
172571b0921aSmacallan 
172671b0921aSmacallan 	switch (mc->dev) {
172771b0921aSmacallan 	    case DBRI_VOL_OUTPUT:	/* master volume */
172871b0921aSmacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
172971b0921aSmacallan 		    (63 - (sc->sc_latt & 0x3f)) << 2;
173071b0921aSmacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
173171b0921aSmacallan 		    (63 - (sc->sc_ratt & 0x3f)) << 2;
173271b0921aSmacallan 		return (0);
173371b0921aSmacallan 	    case DBRI_ENABLE_MONO:	/* built-in speaker */
173471b0921aSmacallan 	    	mc->un.ord = (sc->sc_ratt & CS4215_SE) ? 1 : 0;
173571b0921aSmacallan 		return 0;
173671b0921aSmacallan 	    case DBRI_ENABLE_HEADPHONE:	/* headphones output */
173771b0921aSmacallan 	    	mc->un.ord = (sc->sc_latt & CS4215_HE) ? 1 : 0;
173871b0921aSmacallan 		return 0;
173971b0921aSmacallan 	    case DBRI_ENABLE_LINE:	/* line out */
174071b0921aSmacallan 	    	mc->un.ord = (sc->sc_latt & CS4215_LE) ? 1 : 0;
174171b0921aSmacallan 		return 0;
17422f4ccf79Smacallan 	    case DBRI_VOL_MONITOR:
17432f4ccf79Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_monitor;
17442f4ccf79Smacallan 		return 0;
17452f4ccf79Smacallan 	    case DBRI_INPUT_GAIN:
17462f4ccf79Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = sc->sc_linp;
17472f4ccf79Smacallan 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = sc->sc_rinp;
17482f4ccf79Smacallan 		return 0;
17492f4ccf79Smacallan 	    case DBRI_INPUT_SELECT:
17502f4ccf79Smacallan 	    	mc->un.mask = sc->sc_input;
17512f4ccf79Smacallan 	    	return 0;
175271b0921aSmacallan 	}
175371b0921aSmacallan 	return (EINVAL);
175471b0921aSmacallan }
175571b0921aSmacallan 
17562f1817f6Smacallan static int
dbri_query_devinfo(void * hdl,mixer_devinfo_t * di)175771b0921aSmacallan dbri_query_devinfo(void *hdl, mixer_devinfo_t *di)
175871b0921aSmacallan {
175971b0921aSmacallan 
176071b0921aSmacallan 	switch (di->index) {
176171b0921aSmacallan 	case DBRI_MONITOR_CLASS:
176271b0921aSmacallan 		di->mixer_class = DBRI_MONITOR_CLASS;
176371b0921aSmacallan 		strcpy(di->label.name, AudioCmonitor);
176471b0921aSmacallan 		di->type = AUDIO_MIXER_CLASS;
176571b0921aSmacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
176671b0921aSmacallan 		return 0;
17672f4ccf79Smacallan 	case DBRI_OUTPUT_CLASS:
17682f4ccf79Smacallan 		di->mixer_class = DBRI_OUTPUT_CLASS;
17692f4ccf79Smacallan 		strcpy(di->label.name, AudioCoutputs);
17702f4ccf79Smacallan 		di->type = AUDIO_MIXER_CLASS;
17712f4ccf79Smacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
17722f4ccf79Smacallan 		return 0;
17732f4ccf79Smacallan 	case DBRI_INPUT_CLASS:
17742f4ccf79Smacallan 		di->mixer_class = DBRI_INPUT_CLASS;
17752f4ccf79Smacallan 		strcpy(di->label.name, AudioCinputs);
17762f4ccf79Smacallan 		di->type = AUDIO_MIXER_CLASS;
17772f4ccf79Smacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
17782f4ccf79Smacallan 		return 0;
177971b0921aSmacallan 	case DBRI_VOL_OUTPUT:	/* master volume */
17802f4ccf79Smacallan 		di->mixer_class = DBRI_OUTPUT_CLASS;
178171b0921aSmacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
178271b0921aSmacallan 		strcpy(di->label.name, AudioNmaster);
178371b0921aSmacallan 		di->type = AUDIO_MIXER_VALUE;
178471b0921aSmacallan 		di->un.v.num_channels = 2;
1785351d4f5eSmacallan 		di->un.v.delta = 16;
178671b0921aSmacallan 		strcpy(di->un.v.units.name, AudioNvolume);
178771b0921aSmacallan 		return (0);
17882f4ccf79Smacallan 	case DBRI_INPUT_GAIN:	/* input gain */
17892f4ccf79Smacallan 		di->mixer_class = DBRI_INPUT_CLASS;
17902f4ccf79Smacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
17912f4ccf79Smacallan 		strcpy(di->label.name, AudioNrecord);
17922f4ccf79Smacallan 		di->type = AUDIO_MIXER_VALUE;
17932f4ccf79Smacallan 		di->un.v.num_channels = 2;
17942f4ccf79Smacallan 		strcpy(di->un.v.units.name, AudioNvolume);
17952f4ccf79Smacallan 		return (0);
17962f4ccf79Smacallan 	case DBRI_VOL_MONITOR:	/* monitor volume */
179771b0921aSmacallan 		di->mixer_class = DBRI_MONITOR_CLASS;
179871b0921aSmacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
17992f4ccf79Smacallan 		strcpy(di->label.name, AudioNmonitor);
18002f4ccf79Smacallan 		di->type = AUDIO_MIXER_VALUE;
18012f4ccf79Smacallan 		di->un.v.num_channels = 1;
18022f4ccf79Smacallan 		strcpy(di->un.v.units.name, AudioNvolume);
18032f4ccf79Smacallan 		return (0);
18042f4ccf79Smacallan 	case DBRI_ENABLE_MONO:	/* built-in speaker */
18052f4ccf79Smacallan 		di->mixer_class = DBRI_OUTPUT_CLASS;
18062f4ccf79Smacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
180771b0921aSmacallan 		strcpy(di->label.name, AudioNmono);
180871b0921aSmacallan 		di->type = AUDIO_MIXER_ENUM;
180971b0921aSmacallan 		di->un.e.num_mem = 2;
181071b0921aSmacallan 		strcpy(di->un.e.member[0].label.name, AudioNoff);
181171b0921aSmacallan 		di->un.e.member[0].ord = 0;
181271b0921aSmacallan 		strcpy(di->un.e.member[1].label.name, AudioNon);
181371b0921aSmacallan 		di->un.e.member[1].ord = 1;
181471b0921aSmacallan 		return (0);
181571b0921aSmacallan 	case DBRI_ENABLE_HEADPHONE:	/* headphones output */
18162f4ccf79Smacallan 		di->mixer_class = DBRI_OUTPUT_CLASS;
181771b0921aSmacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
181871b0921aSmacallan 		strcpy(di->label.name, AudioNheadphone);
181971b0921aSmacallan 		di->type = AUDIO_MIXER_ENUM;
182071b0921aSmacallan 		di->un.e.num_mem = 2;
182171b0921aSmacallan 		strcpy(di->un.e.member[0].label.name, AudioNoff);
182271b0921aSmacallan 		di->un.e.member[0].ord = 0;
182371b0921aSmacallan 		strcpy(di->un.e.member[1].label.name, AudioNon);
182471b0921aSmacallan 		di->un.e.member[1].ord = 1;
182571b0921aSmacallan 		return (0);
182671b0921aSmacallan 	case DBRI_ENABLE_LINE:	/* line out */
18272f4ccf79Smacallan 		di->mixer_class = DBRI_OUTPUT_CLASS;
182871b0921aSmacallan 		di->next = di->prev = AUDIO_MIXER_LAST;
182971b0921aSmacallan 		strcpy(di->label.name, AudioNline);
183071b0921aSmacallan 		di->type = AUDIO_MIXER_ENUM;
183171b0921aSmacallan 		di->un.e.num_mem = 2;
183271b0921aSmacallan 		strcpy(di->un.e.member[0].label.name, AudioNoff);
183371b0921aSmacallan 		di->un.e.member[0].ord = 0;
183471b0921aSmacallan 		strcpy(di->un.e.member[1].label.name, AudioNon);
183571b0921aSmacallan 		di->un.e.member[1].ord = 1;
183671b0921aSmacallan 		return (0);
18372f4ccf79Smacallan 	case DBRI_INPUT_SELECT:
18382f4ccf79Smacallan 		di->mixer_class = DBRI_INPUT_CLASS;
18392f4ccf79Smacallan 		strcpy(di->label.name, AudioNsource);
18402f4ccf79Smacallan 		di->type = AUDIO_MIXER_SET;
18412f4ccf79Smacallan 		di->prev = di->next = AUDIO_MIXER_LAST;
18422f4ccf79Smacallan 		di->un.s.num_mem = 2;
18432f4ccf79Smacallan 		strcpy(di->un.s.member[0].label.name, AudioNline);
18442f4ccf79Smacallan 		di->un.s.member[0].mask = 1 << 0;
18452f4ccf79Smacallan 		strcpy(di->un.s.member[1].label.name, AudioNmicrophone);
18462f4ccf79Smacallan 		di->un.s.member[1].mask = 1 << 1;
18472f4ccf79Smacallan 		return 0;
184871b0921aSmacallan 	}
184971b0921aSmacallan 
185071b0921aSmacallan 	return (ENXIO);
185171b0921aSmacallan }
185271b0921aSmacallan 
18532f1817f6Smacallan static int
dbri_get_props(void * hdl)185471b0921aSmacallan dbri_get_props(void *hdl)
185571b0921aSmacallan {
185671b0921aSmacallan 
1857ede47d01Sisaki 	return AUDIO_PROP_PLAYBACK | AUDIO_PROP_CAPTURE |
1858ede47d01Sisaki 	    AUDIO_PROP_FULLDUPLEX;
185971b0921aSmacallan }
186071b0921aSmacallan 
18612f1817f6Smacallan static int
dbri_commit(void * hdl)1862bc471ab0Smacallan dbri_commit(void *hdl)
1863bc471ab0Smacallan {
1864bc471ab0Smacallan 	struct dbri_softc *sc = hdl;
1865bc471ab0Smacallan 	int ret = 0;
1866bc471ab0Smacallan 
1867bc471ab0Smacallan 	/*
1868bc471ab0Smacallan 	 * we only need to whack the codec if things like sample format or
1869bc471ab0Smacallan 	 * frequency changed, not for mixer stuff
1870bc471ab0Smacallan 	 */
1871bc471ab0Smacallan 	if (sc->sc_whack_codec == 0)
1872bc471ab0Smacallan 		return 0;
1873bc471ab0Smacallan 
1874f6826716Smrg 	mutex_spin_enter(&sc->sc_intr_lock);
1875bc471ab0Smacallan 	ret = mmcodec_setcontrol(sc);
1876bc471ab0Smacallan 	if (ret) {
1877bc471ab0Smacallan 		DPRINTF("%s: control mode failed. Mutex %s PIL %x\n", __func__,
1878bc471ab0Smacallan 		    mutex_owned(&sc->sc_intr_lock) ? "held" : "free",
1879bc471ab0Smacallan 		    (getpsr() & PSR_PIL) >> 8);
1880bc471ab0Smacallan 	} else
1881bc471ab0Smacallan 		DPRINTF("%s: control mode ok\n", __func__);
1882bc471ab0Smacallan 	mmcodec_init_data(sc);
1883f6826716Smrg 	mutex_spin_exit(&sc->sc_intr_lock);
1884bc471ab0Smacallan 	return 0;
1885bc471ab0Smacallan }
1886bc471ab0Smacallan 
1887bc471ab0Smacallan static int
dbri_trigger_output(void * hdl,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const struct audio_params * param)188871b0921aSmacallan dbri_trigger_output(void *hdl, void *start, void *end, int blksize,
188971b0921aSmacallan 		    void (*intr)(void *), void *intrarg,
189071b0921aSmacallan 		    const struct audio_params *param)
189171b0921aSmacallan {
189271b0921aSmacallan 	struct dbri_softc *sc = hdl;
1893002ad491Smacallan 	unsigned long count, num;
1894002ad491Smacallan 
1895e5d4bf89Sisaki 	KASSERT(sc->sc_playing == 0);
189671b0921aSmacallan 
1897a4960a24Smrg 	count = (unsigned long)(((char *)end - (char *)start));
189871b0921aSmacallan 	num = count / blksize;
1899b4978607Smacallan 
1900964f61e4Smacallan 	DPRINTF("trigger_output(%lx %lx) : %d %ld %ld\n",
190171b0921aSmacallan 	    (unsigned long)intr,
1902964f61e4Smacallan 	    (unsigned long)intrarg, blksize, count, num);
1903b4978607Smacallan 
190471b0921aSmacallan 	sc->sc_params = *param;
190571b0921aSmacallan 
1906002ad491Smacallan 	/*
1907002ad491Smacallan 	 * always use DMA descriptor 0 for output
1908002ad491Smacallan 	 * no need to allocate them dynamically since we only ever have
1909002ad491Smacallan 	 * exactly one input stream and exactly one output stream
1910002ad491Smacallan 	 */
1911002ad491Smacallan 	setup_ring_xmit(sc, 4, 0, num, blksize, intr, intrarg);
19122f4ccf79Smacallan 	sc->sc_playing = 1;
191371b0921aSmacallan 	return 0;
191471b0921aSmacallan }
191571b0921aSmacallan 
19162f4ccf79Smacallan static int
dbri_halt_input(void * cookie)19172f4ccf79Smacallan dbri_halt_input(void *cookie)
19182f4ccf79Smacallan {
1919002ad491Smacallan 	struct dbri_softc *sc = cookie;
1920002ad491Smacallan 
1921002ad491Smacallan 	if (!sc->sc_recording)
1922002ad491Smacallan 		return 0;
1923002ad491Smacallan 
1924002ad491Smacallan 	sc->sc_recording = 0;
1925002ad491Smacallan 	pipe_reset(sc, 6);
19262f4ccf79Smacallan 	return 0;
19272f4ccf79Smacallan }
19282f4ccf79Smacallan 
19292f4ccf79Smacallan static int
dbri_trigger_input(void * hdl,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,const struct audio_params * param)19302f4ccf79Smacallan dbri_trigger_input(void *hdl, void *start, void *end, int blksize,
19312f4ccf79Smacallan 		    void (*intr)(void *), void *intrarg,
19322f4ccf79Smacallan 		    const struct audio_params *param)
19332f4ccf79Smacallan {
19342f4ccf79Smacallan 	struct dbri_softc *sc = hdl;
1935002ad491Smacallan 	unsigned long count, num;
1936002ad491Smacallan 
1937e5d4bf89Sisaki 	KASSERT(sc->sc_recording == 0);
19382f4ccf79Smacallan 
19392f4ccf79Smacallan 	count = (unsigned long)(((char *)end - (char *)start));
19402f4ccf79Smacallan 	num = count / blksize;
19412f4ccf79Smacallan 
19422f4ccf79Smacallan 	DPRINTF("trigger_input(%lx %lx) : %d %ld %ld\n",
19432f4ccf79Smacallan 	    (unsigned long)intr,
19442f4ccf79Smacallan 	    (unsigned long)intrarg, blksize, count, num);
19452f4ccf79Smacallan 
19462f4ccf79Smacallan 	sc->sc_params = *param;
19472f4ccf79Smacallan 
1948002ad491Smacallan 	sc->sc_recording = 1;
1949002ad491Smacallan 	setup_ring_recv(sc, 6, 1, num, blksize, intr, intrarg);
1950002ad491Smacallan 	return 0;
19512f4ccf79Smacallan }
19522f4ccf79Smacallan 
19538a962f23Sjmcneill static void
dbri_get_locks(void * opaque,kmutex_t ** intr,kmutex_t ** thread)19548a962f23Sjmcneill dbri_get_locks(void *opaque, kmutex_t **intr, kmutex_t **thread)
19558a962f23Sjmcneill {
19568a962f23Sjmcneill 	struct dbri_softc *sc = opaque;
19578a962f23Sjmcneill 
19588a962f23Sjmcneill 	*intr = &sc->sc_intr_lock;
19598a962f23Sjmcneill 	*thread = &sc->sc_lock;
19608a962f23Sjmcneill }
19612f4ccf79Smacallan 
19626e067fe9Stsutsui static uint32_t
reverse_bytes(uint32_t b,int len)19636e067fe9Stsutsui reverse_bytes(uint32_t b, int len)
196471b0921aSmacallan {
196571b0921aSmacallan 	switch (len) {
196671b0921aSmacallan 	case 32:
196771b0921aSmacallan 		b = ((b & 0xffff0000) >> 16) | ((b & 0x0000ffff) << 16);
196871b0921aSmacallan 	case 16:
196971b0921aSmacallan 		b = ((b & 0xff00ff00) >>  8) | ((b & 0x00ff00ff) <<  8);
197071b0921aSmacallan 	case 8:
197171b0921aSmacallan 		b = ((b & 0xf0f0f0f0) >>  4) | ((b & 0x0f0f0f0f) <<  4);
197271b0921aSmacallan 	case 4:
197371b0921aSmacallan 		b = ((b & 0xcccccccc) >>  2) | ((b & 0x33333333) <<  2);
197471b0921aSmacallan 	case 2:
197571b0921aSmacallan 		b = ((b & 0xaaaaaaaa) >>  1) | ((b & 0x55555555) <<  1);
197671b0921aSmacallan 	case 1:
197771b0921aSmacallan 	case 0:
197871b0921aSmacallan 		break;
197971b0921aSmacallan 	default:
1980964f61e4Smacallan 		DPRINTF("reverse_bytes: unsupported length\n");
198171b0921aSmacallan 	};
198271b0921aSmacallan 
198371b0921aSmacallan 	return (b);
198471b0921aSmacallan }
198571b0921aSmacallan 
1986002ad491Smacallan static void *
dbri_malloc(void * v,int dir,size_t s)19878a962f23Sjmcneill dbri_malloc(void *v, int dir, size_t s)
198871b0921aSmacallan {
198971b0921aSmacallan 	struct dbri_softc *sc = v;
199071b0921aSmacallan 	struct dbri_desc *dd = &sc->sc_desc[sc->sc_desc_used];
199171b0921aSmacallan 	int rseg;
199271b0921aSmacallan 
199371b0921aSmacallan 	if (bus_dmamap_create(sc->sc_dmat, s, 1, s, 0, BUS_DMA_NOWAIT,
199471b0921aSmacallan 	    &dd->dmamap) == 0) {
199571b0921aSmacallan 		if (bus_dmamem_alloc(sc->sc_dmat, s, 0, 0, &dd->dmaseg,
199671b0921aSmacallan 		    1, &rseg, BUS_DMA_NOWAIT) == 0) {
199771b0921aSmacallan 			if (bus_dmamem_map(sc->sc_dmat, &dd->dmaseg, rseg, s,
199871b0921aSmacallan 			    &dd->buf, BUS_DMA_NOWAIT|BUS_DMA_COHERENT) == 0) {
199971b0921aSmacallan 				if (dd->buf != NULL) {
200071b0921aSmacallan 					if (bus_dmamap_load(sc->sc_dmat,
200171b0921aSmacallan 					    dd->dmamap, dd->buf, s, NULL,
200271b0921aSmacallan 					    BUS_DMA_NOWAIT) == 0) {
200371b0921aSmacallan 						dd->len = s;
200471b0921aSmacallan 						dd->busy = 0;
200571b0921aSmacallan 						dd->callback = NULL;
200671b0921aSmacallan 						dd->dmabase =
200771b0921aSmacallan 						 dd->dmamap->dm_segs[0].ds_addr;
2008002ad491Smacallan 						DPRINTF("dbri_malloc: using buffer %d %08x\n",
2009002ad491Smacallan 						    sc->sc_desc_used, (uint32_t)dd->buf);
201071b0921aSmacallan 						sc->sc_desc_used++;
201171b0921aSmacallan 						return dd->buf;
201271b0921aSmacallan 					} else
2013964f61e4Smacallan 						aprint_error("dbri_malloc: load failed\n");
201471b0921aSmacallan 				} else
2015964f61e4Smacallan 					aprint_error("dbri_malloc: map returned NULL\n");
201671b0921aSmacallan 			} else
2017964f61e4Smacallan 				aprint_error("dbri_malloc: map failed\n");
201871b0921aSmacallan 			bus_dmamem_free(sc->sc_dmat, &dd->dmaseg, rseg);
201971b0921aSmacallan 		} else
2020964f61e4Smacallan 			aprint_error("dbri_malloc: malloc() failed\n");
202171b0921aSmacallan 		bus_dmamap_destroy(sc->sc_dmat, dd->dmamap);
202271b0921aSmacallan 	} else
2023964f61e4Smacallan 		aprint_error("dbri_malloc: bus_dmamap_create() failed\n");
202471b0921aSmacallan 	return NULL;
202571b0921aSmacallan }
202671b0921aSmacallan 
202771b0921aSmacallan static void
dbri_free(void * v,void * p,size_t size)20288a962f23Sjmcneill dbri_free(void *v, void *p, size_t size)
202971b0921aSmacallan {
2030a6eabcaeSmartin 	struct dbri_softc *sc = v;
2031a6eabcaeSmartin 	struct dbri_desc *dd;
2032a6eabcaeSmartin 	int i;
2033a6eabcaeSmartin 
2034a6eabcaeSmartin 	for (i = 0; i < sc->sc_desc_used; i++) {
2035a6eabcaeSmartin 		dd = &sc->sc_desc[i];
2036a6eabcaeSmartin 		if (dd->buf == p)
2037a6eabcaeSmartin 			break;
2038a6eabcaeSmartin 	}
2039a6eabcaeSmartin 	if (i >= sc->sc_desc_used)
2040a6eabcaeSmartin 		return;
2041a6eabcaeSmartin 	bus_dmamap_unload(sc->sc_dmat, dd->dmamap);
2042a6eabcaeSmartin 	bus_dmamap_destroy(sc->sc_dmat, dd->dmamap);
204371b0921aSmacallan }
204471b0921aSmacallan 
2045b4978607Smacallan static int
dbri_open(void * cookie,int flags)2046b4978607Smacallan dbri_open(void *cookie, int flags)
2047b4978607Smacallan {
2048b4978607Smacallan 	struct dbri_softc *sc = cookie;
2049b4978607Smacallan 
2050e5d4bf89Sisaki 	DPRINTF("%s\n", __func__);
2051002ad491Smacallan 
2052b4978607Smacallan 	dbri_bring_up(sc);
2053b4978607Smacallan 	return 0;
2054b4978607Smacallan }
2055b4978607Smacallan 
2056b4978607Smacallan static void
dbri_close(void * cookie)2057b4978607Smacallan dbri_close(void *cookie)
2058b4978607Smacallan {
2059b4978607Smacallan 	struct dbri_softc *sc = cookie;
2060b4978607Smacallan 
2061e5d4bf89Sisaki 	DPRINTF("%s\n", __func__);
2062e5d4bf89Sisaki 	KASSERT(sc->sc_playing == 0);
2063e5d4bf89Sisaki 	KASSERT(sc->sc_recording == 0);
2064002ad491Smacallan 
2065b4978607Smacallan 	dbri_set_power(sc, 0);
2066b4978607Smacallan }
2067b4978607Smacallan 
2068641f5484Schristos static bool
dbri_suspend(device_t self,const pmf_qual_t * qual)2069c1b390d4Sdyoung dbri_suspend(device_t self, const pmf_qual_t *qual)
2070b4978607Smacallan {
2071641f5484Schristos 	struct dbri_softc *sc = device_private(self);
2072b4978607Smacallan 
2073c973b3b2Smrg 	mutex_spin_enter(&sc->sc_intr_lock);
2074b4978607Smacallan 	dbri_set_power(sc, 0);
2075c973b3b2Smrg 	mutex_spin_exit(&sc->sc_intr_lock);
2076641f5484Schristos 	return true;
2077641f5484Schristos }
2078641f5484Schristos 
2079641f5484Schristos static bool
dbri_resume(device_t self,const pmf_qual_t * qual)2080c1b390d4Sdyoung dbri_resume(device_t self, const pmf_qual_t *qual)
2081641f5484Schristos {
20827a44913bStsutsui 	struct dbri_softc *sc = device_private(self);
20837a44913bStsutsui 
2084002ad491Smacallan 	if (sc->sc_powerstate != 0)
20857a44913bStsutsui 		return true;
2086e5d4bf89Sisaki 	aprint_verbose("resume\n");
20872f4ccf79Smacallan 	if (sc->sc_playing) {
20886e067fe9Stsutsui 		volatile uint32_t *cmd;
20892f4ccf79Smacallan 
20908a962f23Sjmcneill 		mutex_spin_enter(&sc->sc_intr_lock);
2091c973b3b2Smrg 		dbri_bring_up(sc);
20922f4ccf79Smacallan 		cmd = dbri_command_lock(sc);
20932f4ccf79Smacallan 		*(cmd++) = DBRI_CMD(DBRI_COMMAND_SDP,
20942f4ccf79Smacallan 		    0, sc->sc_pipe[4].sdp |
20952f4ccf79Smacallan 		    DBRI_SDP_VALID_POINTER |
20962f4ccf79Smacallan 		    DBRI_SDP_EVERY | DBRI_SDP_CLEAR);
20972f4ccf79Smacallan 		*(cmd++) = sc->sc_dmabase +
2098002ad491Smacallan 		    dbri_dma_off(xmit, 0);
20992f4ccf79Smacallan 		dbri_command_send(sc, cmd);
21008a962f23Sjmcneill 		mutex_spin_exit(&sc->sc_intr_lock);
2101b4978607Smacallan 	}
2102641f5484Schristos 	return true;
21032f4ccf79Smacallan }
2104b4978607Smacallan 
2105b4978607Smacallan #endif /* NAUDIO > 0 */
2106