Module Name: src Committed By: martin Date: Wed Mar 21 12:04:35 UTC 2018
Modified Files: src/sys/dev/ic [netbsd-7]: cs4215reg.h src/sys/dev/sbus [netbsd-7]: dbri.c dbrivar.h Log Message: Pull up following revision(s) (requested by mrg in ticket #1586): sys/dev/sbus/dbrivar.h: revision 1.14 sys/dev/sbus/dbrivar.h: revision 1.15 sys/dev/ic/cs4215reg.h: revision 1.5 sys/dev/sbus/dbri.c: revision 1.36 sys/dev/sbus/dbri.c: revision 1.37 sys/dev/sbus/dbri.c: revision 1.38 fix audiomp bugs: - switch from tsleep/wakeup to condvar - fix locking in a bunch of places. there were several locking against myself issues. also: - don't let dbri_process_interrupt_buffer() loop more than once over the array of intrs. this fixes hangs when using audio on ss20 in -current, but does not make audio work. it eventually times out with eg: dbri0: switching to control mode timed out (0 f6) and may leave a sample in the audio buffer repeating. overhaul the dbri driver and make it work again in the New Order Of Things - fix switching between control and data mode - make sure interrupts can happen in control mode - implement audioif.commit_settings() - switch to control mode only if needed - for changes in sample rate or format but not for things like volume control should fix PR 52786 fix several KASSERT()s and locking in a few places. fixes DIAGNOSTIC kernels and still plays. To generate a diff of this commit: cvs rdiff -u -r1.4 -r1.4.62.1 src/sys/dev/ic/cs4215reg.h cvs rdiff -u -r1.35 -r1.35.4.1 src/sys/dev/sbus/dbri.c cvs rdiff -u -r1.13 -r1.13.24.1 src/sys/dev/sbus/dbrivar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/ic/cs4215reg.h diff -u src/sys/dev/ic/cs4215reg.h:1.4 src/sys/dev/ic/cs4215reg.h:1.4.62.1 --- src/sys/dev/ic/cs4215reg.h:1.4 Mon May 5 00:21:47 2008 +++ src/sys/dev/ic/cs4215reg.h Wed Mar 21 12:04:35 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: cs4215reg.h,v 1.4 2008/05/05 00:21:47 jmcneill Exp $ */ +/* $NetBSD: cs4215reg.h,v 1.4.62.1 2018/03/21 12:04:35 martin Exp $ */ /* * Copyright (c) 2001 Jared D. McNeill <jmcne...@netbsd.org> @@ -30,7 +30,7 @@ /* time slot 1: status register */ #define CS4215_CLB (1<<2) /* control latch bit */ #define CS4215_MLB (1<<4) /* 1: mic: 20 dB gain disabled */ -#define CS4215_RSRVD_1 (1<<5) +#define CS4215_ONE (1<<5) /* always one */ /* time slot 2: data format register */ #define CS4215_DFR_LINEAR16 0 Index: src/sys/dev/sbus/dbri.c diff -u src/sys/dev/sbus/dbri.c:1.35 src/sys/dev/sbus/dbri.c:1.35.4.1 --- src/sys/dev/sbus/dbri.c:1.35 Sat Oct 19 21:00:32 2013 +++ src/sys/dev/sbus/dbri.c Wed Mar 21 12:04:35 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: dbri.c,v 1.35 2013/10/19 21:00:32 mrg Exp $ */ +/* $NetBSD: dbri.c,v 1.35.4.1 2018/03/21 12:04:35 martin Exp $ */ /* * Copyright (C) 1997 Rudolf Koenig (rfkoe...@immd4.informatik.uni-erlangen.de) @@ -34,7 +34,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: dbri.c,v 1.35 2013/10/19 21:00:32 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: dbri.c,v 1.35.4.1 2018/03/21 12:04:35 martin Exp $"); #include "audio.h" #if NAUDIO > 0 @@ -165,6 +165,7 @@ static void dbri_set_power(struct dbri_s static void dbri_bring_up(struct dbri_softc *); static bool dbri_suspend(device_t, const pmf_qual_t *); static bool dbri_resume(device_t, const pmf_qual_t *); +static int dbri_commit(void *); /* stupid support routines */ static uint32_t reverse_bytes(uint32_t, int); @@ -195,6 +196,7 @@ struct audio_hw_if dbri_hw_if = { .trigger_output = dbri_trigger_output, .trigger_input = dbri_trigger_input, .get_locks = dbri_get_locks, + .commit_settings = dbri_commit, }; CFATTACH_DECL_NEW(dbri, sizeof(struct dbri_softc), @@ -277,6 +279,8 @@ dbri_attach_sbus(device_t parent, device sc->sc_dmat = sa->sa_dmatag; sc->sc_powerstate = 1; + sc->sc_whack_codec = 0; + pwr = prom_getpropint(sa->sa_node,"pwr-on-auxio",0); aprint_normal(": rev %s\n", ver); @@ -365,9 +369,13 @@ dbri_attach_sbus(device_t parent, device sc->sc_bufsiz = size; mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); - mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_SCHED); + mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO); + +#ifndef DBRI_SPIN + cv_init(&sc->sc_cv, "dbricv"); +#endif - bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_SCHED, dbri_intr, + bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_AUDIO, dbri_intr, sc); sc->sc_locked = 0; @@ -444,23 +452,32 @@ dbri_config_interrupts(device_t dev) { struct dbri_softc *sc = device_private(dev); - if (sc->sc_init_done != 0) + mutex_spin_enter(&sc->sc_intr_lock); + if (sc->sc_init_done != 0) { + mutex_spin_exit(&sc->sc_intr_lock); return 0; - + } sc->sc_init_done = 1; dbri_init(sc); + + /* talking to the codec needs working interrupts */ if (mmcodec_init(sc) == -1) { + mutex_spin_exit(&sc->sc_intr_lock); printf("%s: no codec detected, aborting\n", device_xname(dev)); return 0; } + mutex_spin_exit(&sc->sc_intr_lock); /* Attach ourselves to the high level audio interface */ audio_attach_mi(&dbri_hw_if, sc, sc->sc_dev); /* power down until open() */ + mutex_spin_enter(&sc->sc_intr_lock); dbri_set_power(sc, 0); + mutex_spin_exit(&sc->sc_intr_lock); + return 0; } @@ -532,7 +549,10 @@ dbri_init(struct dbri_softc *sc) bus_addr_t dmaaddr; int n; + KASSERT(mutex_owned(&sc->sc_intr_lock)); + dbri_reset(sc); + sc->sc_mm.status = 0; cmd = dbri_command_lock(sc); @@ -548,7 +568,6 @@ dbri_init(struct dbri_softc *sc) sc->sc_dma->intr[n] = 0; } - /* Disable all SBus bursts */ /* XXX 16 byte bursts cause errors, the rest works */ reg = bus_space_read_4(iot, ioh, DBRI_REG0); @@ -562,6 +581,7 @@ dbri_init(struct dbri_softc *sc) *(cmd++) = dmaaddr; dbri_command_send(sc, cmd); + return (0); } @@ -603,7 +623,7 @@ dbri_command_send(struct dbri_softc *sc, bus_space_tag_t iot = sc->sc_iot; int maxloops = 1000000; - mutex_spin_enter(&sc->sc_intr_lock); + KASSERT(mutex_owned(&sc->sc_intr_lock)); sc->sc_locked--; @@ -641,8 +661,6 @@ dbri_command_send(struct dbri_softc *sc, } } - mutex_spin_exit(&sc->sc_intr_lock); - return; } @@ -650,6 +668,9 @@ static void dbri_process_interrupt_buffer(struct dbri_softc *sc) { int32_t i; + int orig_irqp = sc->sc_irqp; + + KASSERT(mutex_owned(&sc->sc_intr_lock)); while ((i = sc->sc_dma->intr[sc->sc_irqp]) != 0) { sc->sc_dma->intr[sc->sc_irqp] = 0; @@ -661,6 +682,10 @@ dbri_process_interrupt_buffer(struct dbr sc->sc_irqp++; dbri_process_interrupt(sc, i); + + /* don't loop more than once. */ + if (orig_irqp == sc->sc_irqp) + break; } return; @@ -688,6 +713,7 @@ dbri_process_interrupt(struct dbri_softc int td; struct dbri_desc *dd; + DPRINTF("%s:%d tx complete\n", __func__, channel); td = sc->sc_pipe[channel].desc; dd = &sc->sc_desc[td]; @@ -696,18 +722,15 @@ dbri_process_interrupt(struct dbri_softc break; } case DBRI_INTR_FXDT: /* fixed data change */ - DPRINTF("dbri_intr: Fixed data change (%d: %x)\n", channel, + DPRINTF("%s:%d: Fixed data change: %x\n", __func__, channel, val); -#if 0 - printf("reg: %08x\n", sc->sc_mm.status); -#endif if (sc->sc_pipe[channel].sdp & DBRI_SDP_MSB) val = reverse_bytes(val, sc->sc_pipe[channel].length); if (sc->sc_pipe[channel].prec) *(sc->sc_pipe[channel].prec) = val; #ifndef DBRI_SPIN - DPRINTF("%s: wakeup %p\n", device_xname(sc->sc_dev), sc); - wakeup(sc); + DPRINTF("%s: cv_broadcast %p\n", device_xname(sc->sc_dev), sc); + cv_broadcast(&sc->sc_cv); #endif break; case DBRI_INTR_SBRI: @@ -718,6 +741,7 @@ dbri_process_interrupt(struct dbri_softc int td; struct dbri_desc *dd; + DPRINTF("dbri_intr: buffer ready (%d)\n", channel); td = sc->sc_pipe[channel].desc; dd = &sc->sc_desc[td]; @@ -859,10 +883,12 @@ mmcodec_init_data(struct dbri_softc *sc) pipe_ts_link(sc, 20, PIPEoutput, 16, 32, sc->sc_mm.offset + 32); pipe_ts_link(sc, 4, PIPEoutput, 16, data_width, sc->sc_mm.offset); pipe_ts_link(sc, 6, PIPEinput, 16, data_width, sc->sc_mm.offset); +#if 0 + /* readback for the mixer registers - we don't use that */ pipe_ts_link(sc, 21, PIPEinput, 16, 32, sc->sc_mm.offset + 32); - pipe_receive_fixed(sc, 21, &sc->sc_mm.status); - + pipe_receive_fixed(sc, 21, &sc->sc_mm.d.ldata); +#endif mmcodec_setgain(sc, 0); tmp = bus_space_read_4(iot, ioh, DBRI_REG0); @@ -879,14 +905,13 @@ mmcodec_pipe_init(struct dbri_softc *sc) pipe_setup(sc, 4, DBRI_SDP_MEM | DBRI_SDP_TO_SER | DBRI_SDP_MSB); pipe_setup(sc, 20, DBRI_SDP_FIXED | DBRI_SDP_TO_SER | DBRI_SDP_MSB); pipe_setup(sc, 6, DBRI_SDP_MEM | DBRI_SDP_FROM_SER | DBRI_SDP_MSB); +#if 0 pipe_setup(sc, 21, DBRI_SDP_FIXED | DBRI_SDP_FROM_SER | DBRI_SDP_MSB); - +#endif pipe_setup(sc, 17, DBRI_SDP_FIXED | DBRI_SDP_TO_SER | DBRI_SDP_MSB); pipe_setup(sc, 18, DBRI_SDP_FIXED | DBRI_SDP_FROM_SER | DBRI_SDP_MSB); pipe_setup(sc, 19, DBRI_SDP_FIXED | DBRI_SDP_FROM_SER | DBRI_SDP_MSB); - sc->sc_mm.status = 0; - pipe_receive_fixed(sc, 18, &sc->sc_mm.status); pipe_receive_fixed(sc, 19, &sc->sc_mm.version); @@ -925,7 +950,7 @@ mmcodec_default(struct dbri_softc *sc) * 2: serial enable, CHI master, 128 bits per frame, clock 1 * 3: tests disabled */ - mm->c.bcontrol[0] = CS4215_RSRVD_1 | CS4215_MLB; + mm->c.bcontrol[0] = CS4215_ONE | CS4215_MLB; mm->c.bcontrol[1] = CS4215_DFR_ULAW | CS4215_FREQ[0].csval; mm->c.bcontrol[2] = CS4215_XCLK | CS4215_BSEL_128 | CS4215_FREQ[0].xtal; mm->c.bcontrol[3] = 0; @@ -970,11 +995,15 @@ mmcodec_setcontrol(struct dbri_softc *sc bus_space_handle_t ioh = sc->sc_ioh; uint32_t val; uint32_t tmp; - int bail = 0; -#if DBRI_SPIN + int ret = 0; +#ifdef DBRI_SPIN int i; +#else + int error, bail = 0; #endif + KASSERT(mutex_owned(&sc->sc_intr_lock)); + /* * Temporarily mute outputs and wait 125 us to make sure that it * happens. This avoids clicking noises. @@ -982,6 +1011,10 @@ mmcodec_setcontrol(struct dbri_softc *sc mmcodec_setgain(sc, 1); delay(125); + tmp = bus_space_read_4(iot, ioh, DBRI_REG0); + tmp &= ~(DBRI_CHI_ACTIVATE); /* disable CHI */ + bus_space_write_4(iot, ioh, DBRI_REG0, tmp); + bus_space_write_4(iot, ioh, DBRI_REG2, 0); delay(125); @@ -992,7 +1025,6 @@ mmcodec_setcontrol(struct dbri_softc *sc val |= (sc->sc_mm.onboard ? DBRI_PIO0 : DBRI_PIO2); bus_space_write_4(iot, ioh, DBRI_REG2, val); - delay(34); /* @@ -1013,6 +1045,8 @@ mmcodec_setcontrol(struct dbri_softc *sc pipe_ts_link(sc, 18, PIPEinput, 16, 8, sc->sc_mm.offset); pipe_ts_link(sc, 19, PIPEinput, 16, 8, sc->sc_mm.offset + 48); + pipe_receive_fixed(sc, 18, &sc->sc_mm.status); + /* wait for the chip to echo back CLB as zero */ sc->sc_mm.c.bcontrol[0] &= ~CS4215_CLB; pipe_transmit_fixed(sc, 17, sc->sc_mm.c.lcontrol); @@ -1021,34 +1055,45 @@ mmcodec_setcontrol(struct dbri_softc *sc tmp |= DBRI_CHI_ACTIVATE; bus_space_write_4(iot, ioh, DBRI_REG0, tmp); -#if DBRI_SPIN +#ifdef DBRI_SPIN i = 1024; - while (((sc->sc_mm.status & 0xe4) != 0x20) && --i) { + while (((sc->sc_mm.status & 0xe4) != CS4215_ONE) && (i > 0)) { + i--; delay(125); } if (i == 0) { DPRINTF("%s: cs4215 didn't respond to CLB (0x%02x)\n", device_xname(sc->sc_dev), sc->sc_mm.status); - return (-1); + ret = -1; + goto fail; } #else - while (((sc->sc_mm.status & 0xe4) != 0x20) && (bail < 10)) { - DPRINTF("%s: tsleep %p\n", device_xname(sc->sc_dev), sc); - tsleep(sc, PCATCH | PZERO, "dbrifxdt", hz); + while (((sc->sc_mm.status & 0xe4) != CS4215_ONE) && (bail < 10)) { + DPRINTF("%s: cv_wait_sig %p\n", device_xname(sc->sc_dev), sc); + error = cv_timedwait_sig(&sc->sc_cv, &sc->sc_intr_lock, hz); + if (error == EINTR) { + DPRINTF("%s: interrupted\n", device_xname(sc->sc_dev)); + ret = -1; + mutex_spin_exit(&sc->sc_intr_lock); + goto fail; + } bail++; } -#endif if (bail >= 10) { - DPRINTF("%s: switching to control mode timed out (%x %x)\n", + aprint_error("%s: switching to control mode timed out (%x %x)\n", device_xname(sc->sc_dev), sc->sc_mm.status, bus_space_read_4(iot, ioh, DBRI_REG2)); - return -1; + ret = -1; + goto fail; } +#endif /* copy the version information before it becomes unreadable again */ sc->sc_version = sc->sc_mm.version; + sc->sc_whack_codec = 0; +fail: /* terminate cs4215 control mode */ sc->sc_mm.c.bcontrol[0] |= CS4215_CLB; pipe_transmit_fixed(sc, 17, sc->sc_mm.c.lcontrol); @@ -1058,7 +1103,7 @@ mmcodec_setcontrol(struct dbri_softc *sc mmcodec_setgain(sc, 0); - return (0); + return ret; } @@ -1324,8 +1369,6 @@ setup_ring_xmit(struct dbri_softc *sc, i dd->callback = callback; dd->callback_args = callback_args; - mutex_spin_enter(&sc->sc_intr_lock); - /* the pipe shouldn't be active */ if (pipe_active(sc, pipe)) { aprint_error("pipe active (CDP)\n"); @@ -1360,8 +1403,6 @@ setup_ring_xmit(struct dbri_softc *sc, i DPRINTF("%s: starting DMA\n", __func__); } - mutex_spin_exit(&sc->sc_intr_lock); - return; } @@ -1421,8 +1462,6 @@ setup_ring_recv(struct dbri_softc *sc, i dd->callback = callback; dd->callback_args = callback_args; - mutex_spin_enter(&sc->sc_intr_lock); - /* the pipe shouldn't be active */ if (pipe_active(sc, pipe)) { aprint_error("pipe active (CDP)\n"); @@ -1457,8 +1496,6 @@ setup_ring_recv(struct dbri_softc *sc, i DPRINTF("%s: starting DMA\n", __func__); } - mutex_spin_exit(&sc->sc_intr_lock); - return; } @@ -1731,7 +1768,7 @@ dbri_set_params(void *hdl, int setmode, sc->sc_mm.c.bcontrol[1] |= CS4215_DFR_STEREO; break; } - + sc->sc_whack_codec = 1; return (0); } @@ -1975,6 +2012,32 @@ dbri_get_props(void *hdl) } static int +dbri_commit(void *hdl) +{ + struct dbri_softc *sc = hdl; + int ret = 0; + + /* + * we only need to whack the codec if things like sample format or + * frequency changed, not for mixer stuff + */ + if (sc->sc_whack_codec == 0) + return 0; + + mutex_spin_enter(&sc->sc_intr_lock); + ret = mmcodec_setcontrol(sc); + if (ret) { + DPRINTF("%s: control mode failed. Mutex %s PIL %x\n", __func__, + mutex_owned(&sc->sc_intr_lock) ? "held" : "free", + (getpsr() & PSR_PIL) >> 8); + } else + DPRINTF("%s: control mode ok\n", __func__); + mmcodec_init_data(sc); + mutex_spin_exit(&sc->sc_intr_lock); + return 0; +} + +static int dbri_trigger_output(void *hdl, void *start, void *end, int blksize, void (*intr)(void *), void *intrarg, const struct audio_params *param) @@ -1994,13 +2057,6 @@ dbri_trigger_output(void *hdl, void *sta sc->sc_params = *param; - if (sc->sc_recording == 0) { - /* do not muck with the codec when it's already in use */ - if (mmcodec_setcontrol(sc) != 0) - return -1; - mmcodec_init_data(sc); - } - /* * always use DMA descriptor 0 for output * no need to allocate them dynamically since we only ever have @@ -2044,17 +2100,6 @@ dbri_trigger_input(void *hdl, void *star sc->sc_params = *param; - if (sc->sc_playing == 0) { - - /* - * we don't support different parameters for playing and - * recording anyway so don't bother whacking the codec if - * it's already set up - */ - mmcodec_setcontrol(sc); - mmcodec_init_data(sc); - } - sc->sc_recording = 1; setup_ring_recv(sc, 6, 1, num, blksize, intr, intrarg); return 0; @@ -2181,8 +2226,9 @@ dbri_open(void *cookie, int flags) DPRINTF("%s: %d\n", __func__, sc->sc_refcount); - if (sc->sc_refcount == 0) + if (sc->sc_refcount == 0) { dbri_bring_up(sc); + } sc->sc_refcount++; @@ -2211,7 +2257,9 @@ dbri_suspend(device_t self, const pmf_qu { struct dbri_softc *sc = device_private(self); + mutex_spin_enter(&sc->sc_intr_lock); dbri_set_power(sc, 0); + mutex_spin_exit(&sc->sc_intr_lock); return true; } @@ -2226,8 +2274,8 @@ dbri_resume(device_t self, const pmf_qua if (sc->sc_playing) { volatile uint32_t *cmd; - dbri_bring_up(sc); mutex_spin_enter(&sc->sc_intr_lock); + dbri_bring_up(sc); cmd = dbri_command_lock(sc); *(cmd++) = DBRI_CMD(DBRI_COMMAND_SDP, 0, sc->sc_pipe[4].sdp | Index: src/sys/dev/sbus/dbrivar.h diff -u src/sys/dev/sbus/dbrivar.h:1.13 src/sys/dev/sbus/dbrivar.h:1.13.24.1 --- src/sys/dev/sbus/dbrivar.h:1.13 Wed Nov 23 23:07:36 2011 +++ src/sys/dev/sbus/dbrivar.h Wed Mar 21 12:04:35 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: dbrivar.h,v 1.13 2011/11/23 23:07:36 jmcneill Exp $ */ +/* $NetBSD: dbrivar.h,v 1.13.24.1 2018/03/21 12:04:35 martin Exp $ */ /* * Copyright (C) 1997 Rudolf Koenig (rfkoe...@immd4.informatik.uni-erlangen.de) @@ -153,6 +153,7 @@ struct dbri_softc { int sc_linp, sc_rinp; /* input volume */ int sc_monitor; /* monitor volume */ int sc_input; /* 0 - line, 1 - mic */ + int sc_whack_codec; /* 1 - codec needs control mode */ int sc_ctl_mode; @@ -169,6 +170,9 @@ struct dbri_softc { kmutex_t sc_lock; kmutex_t sc_intr_lock; +#ifndef DBRI_SPIN + kcondvar_t sc_cv; +#endif }; #define dbri_dma_off(member, elem) \