Index: sys/arch/arm/broadcom/bcm2835_vcaudio.c =================================================================== RCS file: /cvsroot/src/sys/arch/arm/broadcom/bcm2835_vcaudio.c,v retrieving revision 1.3 diff -u -p -r1.3 bcm2835_vcaudio.c --- sys/arch/arm/broadcom/bcm2835_vcaudio.c 5 May 2014 08:13:31 -0000 1.3 +++ sys/arch/arm/broadcom/bcm2835_vcaudio.c 30 Aug 2014 12:06:11 -0000 @@ -1,4 +1,4 @@ -/* $NetBSD: bcm2835_vcaudio.c,v 1.3 2014/05/05 08:13:31 skrll Exp $ */ +/* $NetBSD: bcm2835_vcaudio.c,v 1.3 2014/05/05 08:13:31 skrll Exp $ */ /*- * Copyright (c) 2013 Jared D. McNeill @@ -40,10 +40,11 @@ __KERNEL_RCSID(0, "$NetBSD: bcm2835_vcau #include #include #include -#include +#include #include #include +#include #include #include @@ -54,6 +55,11 @@ __KERNEL_RCSID(0, "$NetBSD: bcm2835_vcau #define vol2pct(vol) (((vol) * 100) / 255) +#define VCAUDIO_MSGSIZE 4000 +#define VCAUDIO_NUMMSGS 16 +#define VCAUDIO_BLOCKSIZE (VCAUDIO_MSGSIZE * VCAUDIO_NUMMSGS) +#define VCAUDIO_BUFFERSIZE 128000 + enum { VCAUDIO_OUTPUT_CLASS, VCAUDIO_INPUT_CLASS, @@ -68,16 +74,15 @@ enum vcaudio_dest { VCAUDIO_DEST_HDMI = 2, }; -struct vcaudio_work { - struct work vw_wk; -}; - struct vcaudio_softc { device_t sc_dev; device_t sc_audiodev; + lwp_t *sc_lwp; + kmutex_t sc_lock; kmutex_t sc_intr_lock; + kcondvar_t sc_datacv; kmutex_t sc_msglock; kcondvar_t sc_msgcv; @@ -89,11 +94,15 @@ struct vcaudio_softc { void *sc_pintarg; audio_params_t sc_pparam; bool sc_started; - int sc_pbytes; + int sc_ibytes; // inflight bytes + int sc_abytes; // available bytes + int sc_pbytes; // played bytes off_t sc_ppos; void *sc_pstart; void *sc_pend; int sc_pblksize; +// callout_t sc_ptimer; +// bool sc_preset; bool sc_msgdone; int sc_success; @@ -102,8 +111,7 @@ struct vcaudio_softc { VCHI_CONNECTION_T sc_connection; VCHI_SERVICE_HANDLE_T sc_service; - struct workqueue *sc_wq; - struct vcaudio_work sc_work; + void *sc_silence; int sc_volume; enum vcaudio_dest sc_dest; @@ -118,11 +126,13 @@ static int vcaudio_init(struct vcaudio_s static void vcaudio_service_callback(void *, const VCHI_CALLBACK_REASON_T, void *); -static int vcaudio_msg_sync(struct vcaudio_softc *, VC_AUDIO_MSG_T *, size_t); -static void vcaudio_worker(struct work *, void *); +static int vcaudio_msg_sync(struct vcaudio_softc *, VC_AUDIO_MSG_T *, + size_t); +static void vcaudio_worker(void *); static int vcaudio_open(void *, int); static void vcaudio_close(void *); +static int vcaudio_drain(void *); static int vcaudio_query_encoding(void *, struct audio_encoding *); static int vcaudio_set_params(void *, int, int, audio_params_t *, audio_params_t *, @@ -135,7 +145,8 @@ static int vcaudio_get_port(void *, mixe static int vcaudio_query_devinfo(void *, mixer_devinfo_t *); static int vcaudio_getdev(void *, struct audio_device *); static int vcaudio_get_props(void *); -static int vcaudio_round_blocksize(void *, int, int, const audio_params_t *); +static int vcaudio_round_blocksize(void *, int, int, + const audio_params_t *); static size_t vcaudio_round_buffersize(void *, int, size_t); static int vcaudio_trigger_output(void *, void *, void *, int, void (*)(void *), void *, @@ -148,6 +159,7 @@ static void vcaudio_get_locks(void *, km static const struct audio_hw_if vcaudio_hw_if = { .open = vcaudio_open, .close = vcaudio_close, + .drain = vcaudio_drain, .query_encoding = vcaudio_query_encoding, .set_params = vcaudio_set_params, .halt_output = vcaudio_halt_output, @@ -165,7 +177,8 @@ static const struct audio_hw_if vcaudio_ }; CFATTACH_DECL2_NEW(vcaudio, sizeof(struct vcaudio_softc), - vcaudio_match, vcaudio_attach, NULL, NULL, vcaudio_rescan, vcaudio_childdet); + vcaudio_match, vcaudio_attach, NULL, NULL, vcaudio_rescan, + vcaudio_childdet); static int vcaudio_match(device_t parent, cfdata_t match, void *aux) @@ -185,17 +198,22 @@ vcaudio_attach(device_t parent, device_t mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&sc->sc_msglock, MUTEX_DEFAULT, IPL_NONE); - cv_init(&sc->sc_msgcv, "vcaudiocv"); + cv_init(&sc->sc_msgcv, "vcaudsm"); + cv_init(&sc->sc_datacv, "vcaudsd"); + + sc->sc_started = false; sc->sc_success = -1; - error = workqueue_create(&sc->sc_wq, "vcaudiowq", vcaudio_worker, - sc, PRI_BIO, IPL_SCHED, WQ_MPSAFE); + /* XXX PRI_BIO??? */ + error = kthread_create(PRI_BIO, KTHREAD_MPSAFE, NULL, vcaudio_worker, + sc, &sc->sc_lwp, "vcaudio"); + if (error) { - aprint_error(": couldn't create workqueue (%d)\n", error); + aprint_error(": couldn't create thread (%d)\n", error); return; } aprint_naive("\n"); - aprint_normal(": AUDS\n"); + aprint_normal(": auds\n"); if (vcaudio_init(sc) != 0) { aprint_error_dev(self, "not configured\n"); @@ -203,6 +221,12 @@ vcaudio_attach(device_t parent, device_t } vcaudio_rescan(self, NULL, NULL); + + /* Where is the right place? */ + mutex_enter(&sc->sc_intr_lock); + cv_signal(&sc->sc_datacv); + mutex_exit(&sc->sc_intr_lock); + } static int @@ -214,6 +238,7 @@ vcaudio_rescan(device_t self, const char sc->sc_audiodev = audio_attach_mi(&vcaudio_hw_if, sc, sc->sc_dev); } + return 0; } @@ -232,6 +257,12 @@ vcaudio_init(struct vcaudio_softc *sc) VC_AUDIO_MSG_T msg; int error; + /* XXX error handling */ + sc->sc_silence = kmem_zalloc(VCAUDIO_MSGSIZE, KM_SLEEP); + if (sc->sc_silence == NULL) + return ENOMEM; + + /* volume handling */ sc->sc_volume = 128; sc->sc_dest = VCAUDIO_DEST_AUTO; @@ -242,7 +273,7 @@ vcaudio_init(struct vcaudio_softc *sc) sc->sc_format.channels = 2; sc->sc_format.channel_mask = AUFMT_STEREO; sc->sc_format.frequency_type = 0; - sc->sc_format.frequency[0] = 8000; + sc->sc_format.frequency[0] = 48000; sc->sc_format.frequency[1] = 48000; error = auconv_create_encodings(&sc->sc_format, 1, &sc->sc_encodings); @@ -269,7 +300,7 @@ vcaudio_init(struct vcaudio_softc *sc) SERVICE_CREATION_T setup = { .version = VCHI_VERSION(VC_AUDIOSERV_VER), .service_id = VC_AUDIO_SERVER_NAME, - .connection = &sc->sc_connection, + .connection = &sc->sc_connection, /* NULL? */ .rx_fifo_size = 0, .tx_fifo_size = 0, .callback = vcaudio_service_callback, @@ -298,6 +329,17 @@ vcaudio_init(struct vcaudio_softc *sc) } memset(&msg, 0, sizeof(msg)); + msg.type = VC_AUDIO_MSG_TYPE_CONFIG; + msg.u.config.channels = 2; + msg.u.config.samplerate = 48000; + msg.u.config.bps = 16; + error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); + if (error) { + device_printf(sc->sc_dev, "couldn't send CONFIG message (%d)\n", + error); + } + + memset(&msg, 0, sizeof(msg)); msg.type = VC_AUDIO_MSG_TYPE_CONTROL; msg.u.control.volume = vol2pct(sc->sc_volume); msg.u.control.dest = VCAUDIO_DEST_AUTO; @@ -305,6 +347,23 @@ vcaudio_init(struct vcaudio_softc *sc) if (error) { device_printf(sc->sc_dev, "couldn't send CONTROL message (%d)\n", error); } + +#ifdef VCAUDIO_DEBUG + device_printf(sc->sc_dev, "starting output\n"); +#endif + + memset(&msg, 0, sizeof(msg)); + msg.type = VC_AUDIO_MSG_TYPE_START; + error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); + if (error) { + printf("%s: failed to start (%d)\n", __func__, error); + vchi_service_release(sc->sc_service); + return EIO; + } + + sc->sc_started = true; + vchi_service_release(sc->sc_service); return 0; @@ -321,8 +380,11 @@ vcaudio_service_callback(void *priv, con void (*intr)(void *) = NULL; void *intrarg = NULL; - if (sc == NULL || reason != VCHI_CALLBACK_MSG_AVAILABLE) + if (sc == NULL || reason != VCHI_CALLBACK_MSG_AVAILABLE) { + if (sc) + device_printf(sc->sc_dev, "reason = %d\n", reason); return; + } memset(&msg, 0, sizeof(msg)); error = vchi_msg_dequeue(sc->sc_service, &msg, sizeof(msg), &msglen, @@ -344,136 +406,128 @@ vcaudio_service_callback(void *priv, con case VC_AUDIO_MSG_TYPE_COMPLETE: intr = msg.u.complete.callback; intrarg = msg.u.complete.cookie; - if (intr && intrarg) { - mutex_enter(&sc->sc_intr_lock); - if (msg.u.complete.count > 0 && msg.u.complete.count <= sc->sc_pblksize) { - sc->sc_pbytes += msg.u.complete.count; - } else { - if (sc->sc_started) { - device_printf(sc->sc_dev, "WARNING: count = %d\n", msg.u.complete.count); - } - } - if (sc->sc_pbytes >= sc->sc_pblksize) { - sc->sc_pbytes -= sc->sc_pblksize; +#ifdef VCAUDIO_DEBUG +// device_printf(sc->sc_dev, "played %08x bytes intr %p\n", msg.u.complete.count, intr); +#endif + + unsigned int count = msg.u.complete.count & 0xffff; + bool more = false; + + mutex_enter(&sc->sc_intr_lock); + sc->sc_pbytes += count; + sc->sc_ibytes -= count; + if (sc->sc_ibytes <= VCAUDIO_BLOCKSIZE) + more = true; + + while (sc->sc_pbytes >= VCAUDIO_BLOCKSIZE) { + sc->sc_pbytes -= VCAUDIO_BLOCKSIZE; + if (intr && intrarg) { intr(intrarg); - workqueue_enqueue(sc->sc_wq, (struct work *)&sc->sc_work, NULL); + sc->sc_abytes += VCAUDIO_BLOCKSIZE; } - mutex_exit(&sc->sc_intr_lock); + more = true; + } + if (more) { + cv_signal(&sc->sc_datacv); } + mutex_exit(&sc->sc_intr_lock); break; default: + device_printf(sc->sc_dev, "unknown msg type %d\n", msg.type); break; } } static void -vcaudio_worker(struct work *wk, void *priv) +vcaudio_worker(void *priv) { struct vcaudio_softc *sc = priv; + int error, resid, nb, count; VC_AUDIO_MSG_T msg; void (*intr)(void *); void *intrarg; - void *block; - int error, resid, off, nb, count; + uint8_t *block, *bstart, *bend; + int invoke = 0; mutex_enter(&sc->sc_intr_lock); - intr = sc->sc_pint; - intrarg = sc->sc_pintarg; - - if (intr == NULL || intrarg == NULL) { - mutex_exit(&sc->sc_intr_lock); - return; - } - - vchi_service_use(sc->sc_service); - if (sc->sc_started == false) { -#ifdef VCAUDIO_DEBUG - device_printf(sc->sc_dev, "starting output\n"); -#endif + while (true) { + if (!sc->sc_started) { + cv_wait/*_sig*/(&sc->sc_datacv, &sc->sc_intr_lock); + continue; + } - memset(&msg, 0, sizeof(msg)); - msg.type = VC_AUDIO_MSG_TYPE_CONFIG; - msg.u.config.channels = sc->sc_pparam.channels; - msg.u.config.samplerate = sc->sc_pparam.sample_rate; - msg.u.config.bps = sc->sc_pparam.precision; - error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); - if (error) { - printf("%s: failed to config (%d)\n", __func__, error); - goto done; + if (sc->sc_ibytes >= VCAUDIO_BLOCKSIZE) { + cv_wait/*_sig*/(&sc->sc_datacv, &sc->sc_intr_lock); + invoke++; + continue; } - memset(&msg, 0, sizeof(msg)); - msg.type = VC_AUDIO_MSG_TYPE_START; - error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), - VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); - if (error) { - printf("%s: failed to start (%d)\n", __func__, error); - goto done; + bool silence = true; + /* Grab sc state required */ + intr = sc->sc_pint; + intrarg = sc->sc_pintarg; + + block = sc->sc_silence; + bstart = bend = NULL; + + if (sc->sc_abytes >= VCAUDIO_BLOCKSIZE) { + silence = false; + + block = (uint8_t *)sc->sc_pstart + sc->sc_ppos; + bstart = sc->sc_pstart; + bend = sc->sc_pend; + count = resid = VCAUDIO_BLOCKSIZE; + } else { + count = resid = 2 * VCAUDIO_BLOCKSIZE - sc->sc_ibytes; } - sc->sc_started = true; - sc->sc_pbytes = 0; - sc->sc_ppos = 0; - sc->sc_pblksize = sc->sc_pblksize; - count = (uintptr_t)sc->sc_pend - (uintptr_t)sc->sc_pstart; + mutex_exit(&sc->sc_intr_lock); + + vchi_service_use(sc->sc_service); - /* initial silence */ memset(&msg, 0, sizeof(msg)); msg.type = VC_AUDIO_MSG_TYPE_WRITE; - msg.u.write.count = PAGE_SIZE * 3; - msg.u.write.callback = NULL; - msg.u.write.cookie = NULL; - msg.u.write.silence = 1; - msg.u.write.max_packet = 0; + msg.u.write.max_packet = VCAUDIO_MSGSIZE; + msg.u.write.count = count; + msg.u.write.callback = intr; + msg.u.write.cookie = intrarg; + msg.u.write.silence = 0; error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); if (error) { printf("%s: failed to write (%d)\n", __func__, error); goto done; } - } else { - count = sc->sc_pblksize; - } - memset(&msg, 0, sizeof(msg)); - msg.type = VC_AUDIO_MSG_TYPE_WRITE; - msg.u.write.max_packet = 4000; - msg.u.write.count = count; - msg.u.write.callback = intr; - msg.u.write.cookie = intrarg; - msg.u.write.silence = 0; + while (resid > 0) { + nb = min(resid, msg.u.write.max_packet); + error = vchi_msg_queue(sc->sc_service, block, nb, + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); + if (error) { + printf("%s: failed to queue data (%d)\n", + __func__, error); + goto done; + } + resid -= nb; - error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), - VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); - if (error) { - printf("%s: failed to write (%d)\n", __func__, error); - goto done; - } + if (intr && intrarg) { + block += nb; + if (block >= bend) { + block = bstart; + } + } + } +done: /* XXX */ + vchi_service_release(sc->sc_service); - block = (uint8_t *)sc->sc_pstart + sc->sc_ppos; - resid = count; - off = 0; - while (resid > 0) { - nb = min(resid, msg.u.write.max_packet); - error = vchi_msg_queue(sc->sc_service, - (char *)block + off, nb, - VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); - if (error) { - printf("%s: failed to queue data (%d)\n", __func__, error); - goto done; + mutex_enter(&sc->sc_intr_lock); + sc->sc_ibytes += count; + if (!silence) { + sc->sc_ppos = block - bstart; + sc->sc_abytes -= count; } - off += nb; - resid -= nb; } - - sc->sc_ppos += count; - if ((uint8_t *)sc->sc_pstart + sc->sc_ppos >= (uint8_t *)sc->sc_pend) - sc->sc_ppos = 0; - -done: - mutex_exit(&sc->sc_intr_lock); - vchi_service_release(sc->sc_service); } static int @@ -509,6 +563,7 @@ done: static int vcaudio_open(void *priv, int flags) { + return 0; } @@ -518,6 +573,33 @@ vcaudio_close(void *priv) } static int +vcaudio_drain(void *priv) +{ +#if 0 + while (sc->sc_abytes != 0) { + cv_wait_sig(&sc->datacv, &sc->sc_intr_lock); + } + struct vcaudio_softc *sc = priv; + audio_params_t *pp = &sc->sc_pparam; + int bps, delay; + + if (sc->sc_started == true) { + sc->sc_draining = true; + bps = pp->channels * pp->sample_rate * pp->precision / 8; + if (bps > 0) { + delay = sc->sc_nblks * sc->sc_pblksize * 1000 / bps; + if (delay > 3000) + delay = 3000; + kpause("vcaudiodr", false, mstohz(delay), +&sc->sc_intr_lock); + } + } +#endif + + return 0; +} + +static int vcaudio_query_encoding(void *priv, struct audio_encoding *ae) { struct vcaudio_softc *sc = priv; @@ -547,13 +629,13 @@ static int vcaudio_halt_output(void *priv) { struct vcaudio_softc *sc = priv; - VC_AUDIO_MSG_T msg; +// VC_AUDIO_MSG_T msg; int error = 0; - +#if 0 vchi_service_use(sc->sc_service); memset(&msg, 0, sizeof(msg)); msg.type = VC_AUDIO_MSG_TYPE_STOP; - msg.u.stop.draining = 1; + msg.u.stop.draining = 0; error = vchi_msg_queue(sc->sc_service, &msg, sizeof(msg), VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); if (error) { @@ -561,10 +643,13 @@ vcaudio_halt_output(void *priv) error); } vchi_service_release(sc->sc_service); +#endif sc->sc_pint = NULL; sc->sc_pintarg = NULL; - sc->sc_started = false; + sc->sc_abytes = 0; + + cv_signal(&sc->sc_datacv); #ifdef VCAUDIO_DEBUG device_printf(sc->sc_dev, "halting output\n"); @@ -586,6 +671,8 @@ vcaudio_set_port(void *priv, mixer_ctrl_ VC_AUDIO_MSG_T msg; int error; + printf("set port dev = %d\n", mc->dev); + switch (mc->dev) { case VCAUDIO_OUTPUT_MASTER_VOLUME: case VCAUDIO_INPUT_DAC_VOLUME: @@ -597,12 +684,16 @@ vcaudio_set_port(void *priv, mixer_ctrl_ vchi_service_use(sc->sc_service); error = vcaudio_msg_sync(sc, &msg, sizeof(msg)); if (error) { - device_printf(sc->sc_dev, "couldn't send CONTROL message (%d)\n", error); + device_printf(sc->sc_dev, "couldn't send CONTROL" + "message (%d)\n", error); } vchi_service_release(sc->sc_service); + printf(" err = %d\n", error); + return error; } + printf(" err = ENXIO\n"); return ENXIO; } @@ -678,19 +769,14 @@ static int vcaudio_round_blocksize(void *priv, int bs, int mode, const audio_params_t *params) { - return PAGE_SIZE; + return VCAUDIO_BLOCKSIZE; } static size_t vcaudio_round_buffersize(void *priv, int direction, size_t bufsize) { - size_t sz; - sz = (bufsize + 0x3ff) & ~0x3ff; - if (sz > 32768) - sz = 32768; - - return sz; + return VCAUDIO_BUFFERSIZE; } static int @@ -699,6 +785,8 @@ vcaudio_trigger_output(void *priv, void { struct vcaudio_softc *sc = priv; + KASSERT(mutex_owned(&sc->sc_intr_lock)); + sc->sc_pparam = *params; sc->sc_pint = intr; sc->sc_pintarg = intrarg; @@ -706,7 +794,9 @@ vcaudio_trigger_output(void *priv, void sc->sc_pstart = start; sc->sc_pend = end; sc->sc_pblksize = blksize; - workqueue_enqueue(sc->sc_wq, (struct work *)&sc->sc_work, NULL); + sc->sc_abytes = blksize; + + cv_signal(&sc->sc_datacv); return 0; }