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