1/*-
2 * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#ifdef HAVE_KERNEL_OPTION_HEADERS
27#include "opt_snd.h"
28#endif
29
30#include <dev/sound/pcm/sound.h>
31
32#include "mixer_if.h"
33
34#include "interface/compat/vchi_bsd.h"
35#include "interface/vchi/vchi.h"
36#include "interface/vchiq_arm/vchiq.h"
37
38#include "vc_vchi_audioserv_defs.h"
39
40/* Audio destination */
41#define	DEST_AUTO		0
42#define	DEST_HEADPHONES		1
43#define	DEST_HDMI		2
44
45/* Playback state */
46#define	PLAYBACK_IDLE		0
47#define	PLAYBACK_PLAYING	1
48#define	PLAYBACK_STOPPING	2
49
50/* Worker thread state */
51#define	WORKER_RUNNING		0
52#define	WORKER_STOPPING		1
53#define	WORKER_STOPPED		2
54
55/*
56 * Worker thread flags, set to 1 in flags_pending
57 * when driver requests one or another operation
58 * from worker. Cleared to 0 once worker performs
59 * the operations.
60 */
61#define	AUDIO_PARAMS		(1 << 0)
62#define	AUDIO_PLAY		(1 << 1)
63#define	AUDIO_STOP		(1 << 2)
64
65#define	VCHIQ_AUDIO_PACKET_SIZE	4000
66#define	VCHIQ_AUDIO_BUFFER_SIZE	10*VCHIQ_AUDIO_PACKET_SIZE
67
68#define	VCHIQ_AUDIO_MAX_VOLUME
69/* volume in terms of 0.01dB */
70#define VCHIQ_AUDIO_VOLUME_MIN -10239
71#define VCHIQ_AUDIO_VOLUME(db100) (uint32_t)(-((db100) << 8)/100)
72
73/* dB levels with 5% volume step */
74static int db_levels[] = {
75	VCHIQ_AUDIO_VOLUME_MIN, -4605, -3794, -3218, -2772,
76	-2407, -2099, -1832, -1597, -1386,
77	-1195, -1021, -861, -713, -575,
78	-446, -325, -210, -102, 0,
79};
80
81static uint32_t bcm2835_audio_playfmt[] = {
82	SND_FORMAT(AFMT_U8, 1, 0),
83	SND_FORMAT(AFMT_U8, 2, 0),
84	SND_FORMAT(AFMT_S8, 1, 0),
85	SND_FORMAT(AFMT_S8, 2, 0),
86	SND_FORMAT(AFMT_S16_LE, 1, 0),
87	SND_FORMAT(AFMT_S16_LE, 2, 0),
88	SND_FORMAT(AFMT_U16_LE, 1, 0),
89	SND_FORMAT(AFMT_U16_LE, 2, 0),
90	0
91};
92
93static struct pcmchan_caps bcm2835_audio_playcaps = {8000, 48000, bcm2835_audio_playfmt, 0};
94
95struct bcm2835_audio_info;
96
97struct bcm2835_audio_chinfo {
98	struct bcm2835_audio_info *parent;
99	struct pcm_channel *channel;
100	struct snd_dbuf *buffer;
101	uint32_t fmt, spd, blksz;
102
103	/* Pointer to first unsubmitted sample */
104	uint32_t unsubmittedptr;
105	/*
106	 * Number of bytes in "submitted but not played"
107	 * pseudo-buffer
108	 */
109	int available_space;
110	int playback_state;
111	uint64_t callbacks;
112	uint64_t submitted_samples;
113	uint64_t retrieved_samples;
114	uint64_t underruns;
115	int starved;
116};
117
118struct bcm2835_audio_info {
119	device_t dev;
120	unsigned int bufsz;
121    	struct bcm2835_audio_chinfo pch;
122	uint32_t dest, volume;
123	struct intr_config_hook intr_hook;
124
125	/* VCHI data */
126	VCHI_INSTANCE_T vchi_instance;
127	VCHI_CONNECTION_T *vchi_connection;
128	VCHI_SERVICE_HANDLE_T vchi_handle;
129
130	struct mtx lock;
131	struct cv worker_cv;
132
133	uint32_t flags_pending;
134
135	/* Worker thread state */
136	int worker_state;
137};
138
139#define BCM2835_AUDIO_LOCK(sc)		mtx_lock(&(sc)->lock)
140#define BCM2835_AUDIO_LOCKED(sc)	mtx_assert(&(sc)->lock, MA_OWNED)
141#define BCM2835_AUDIO_UNLOCK(sc)	mtx_unlock(&(sc)->lock)
142
143static const char *
144dest_description(uint32_t dest)
145{
146	switch (dest) {
147		case DEST_AUTO:
148			return "AUTO";
149			break;
150
151		case DEST_HEADPHONES:
152			return "HEADPHONES";
153			break;
154
155		case DEST_HDMI:
156			return "HDMI";
157			break;
158		default:
159			return "UNKNOWN";
160			break;
161	}
162}
163
164static void
165bcm2835_worker_update_params(struct bcm2835_audio_info *sc)
166{
167
168	BCM2835_AUDIO_LOCKED(sc);
169
170	sc->flags_pending |= AUDIO_PARAMS;
171	cv_signal(&sc->worker_cv);
172}
173
174static void
175bcm2835_worker_play_start(struct bcm2835_audio_info *sc)
176{
177	BCM2835_AUDIO_LOCK(sc);
178	sc->flags_pending &= ~(AUDIO_STOP);
179	sc->flags_pending |= AUDIO_PLAY;
180	cv_signal(&sc->worker_cv);
181	BCM2835_AUDIO_UNLOCK(sc);
182}
183
184static void
185bcm2835_worker_play_stop(struct bcm2835_audio_info *sc)
186{
187	BCM2835_AUDIO_LOCK(sc);
188	sc->flags_pending &= ~(AUDIO_PLAY);
189	sc->flags_pending |= AUDIO_STOP;
190	cv_signal(&sc->worker_cv);
191	BCM2835_AUDIO_UNLOCK(sc);
192}
193
194static void
195bcm2835_audio_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle)
196{
197	struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)param;
198	int32_t status;
199	uint32_t msg_len;
200	VC_AUDIO_MSG_T m;
201
202	if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
203		return;
204
205	status = vchi_msg_dequeue(sc->vchi_handle,
206	    &m, sizeof m, &msg_len, VCHI_FLAGS_NONE);
207	if (status != 0)
208		return;
209	if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
210		if (m.u.result.success) {
211			device_printf(sc->dev,
212			    "msg type %08x failed\n",
213			    m.type);
214		}
215	} else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
216		struct bcm2835_audio_chinfo *ch = m.u.complete.cookie;
217
218		int count = m.u.complete.count & 0xffff;
219		int perr = (m.u.complete.count & (1U << 30)) != 0;
220		ch->callbacks++;
221		if (perr)
222			ch->underruns++;
223
224		BCM2835_AUDIO_LOCK(sc);
225		if (ch->playback_state != PLAYBACK_IDLE) {
226			/* Prevent LOR */
227			BCM2835_AUDIO_UNLOCK(sc);
228			chn_intr(sc->pch.channel);
229			BCM2835_AUDIO_LOCK(sc);
230		}
231		/* We should check again, state might have changed */
232		if (ch->playback_state != PLAYBACK_IDLE) {
233			if (!perr) {
234				if ((ch->available_space + count)> VCHIQ_AUDIO_BUFFER_SIZE) {
235					device_printf(sc->dev, "inconsistent data in callback:\n");
236					device_printf(sc->dev, "available_space == %d, count = %d, perr=%d\n",
237					    ch->available_space, count, perr);
238					device_printf(sc->dev,
239					    "retrieved_samples = %lld, submitted_samples = %lld\n",
240					    ch->retrieved_samples, ch->submitted_samples);
241				}
242				ch->available_space += count;
243				ch->retrieved_samples += count;
244			}
245			if (perr || (ch->available_space >= VCHIQ_AUDIO_PACKET_SIZE))
246				cv_signal(&sc->worker_cv);
247		}
248		BCM2835_AUDIO_UNLOCK(sc);
249	} else
250		printf("%s: unknown m.type: %d\n", __func__, m.type);
251}
252
253/* VCHIQ stuff */
254static void
255bcm2835_audio_init(struct bcm2835_audio_info *sc)
256{
257	int status;
258
259	/* Initialize and create a VCHI connection */
260	status = vchi_initialise(&sc->vchi_instance);
261	if (status != 0) {
262		printf("vchi_initialise failed: %d\n", status);
263		return;
264	}
265
266	status = vchi_connect(NULL, 0, sc->vchi_instance);
267	if (status != 0) {
268		printf("vchi_connect failed: %d\n", status);
269		return;
270	}
271
272	SERVICE_CREATION_T params = {
273	    VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
274	    VC_AUDIO_SERVER_NAME,   /* 4cc service code */
275	    sc->vchi_connection,    /* passed in fn pointers */
276	    0,  /* rx fifo size */
277	    0,  /* tx fifo size */
278	    bcm2835_audio_callback,    /* service callback */
279	    sc,   /* service callback parameter */
280	    1,
281	    1,
282	    0   /* want crc check on bulk transfers */
283	};
284
285	status = vchi_service_open(sc->vchi_instance, &params,
286	    &sc->vchi_handle);
287
288	if (status != 0)
289		sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
290}
291
292static void
293bcm2835_audio_release(struct bcm2835_audio_info *sc)
294{
295	int success;
296
297	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
298		success = vchi_service_close(sc->vchi_handle);
299		if (success != 0)
300			printf("vchi_service_close failed: %d\n", success);
301		vchi_service_release(sc->vchi_handle);
302		sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
303	}
304
305	vchi_disconnect(sc->vchi_instance);
306}
307
308static void
309bcm2835_audio_reset_channel(struct bcm2835_audio_chinfo *ch)
310{
311
312	ch->available_space = VCHIQ_AUDIO_BUFFER_SIZE;
313	ch->unsubmittedptr = 0;
314	sndbuf_reset(ch->buffer);
315}
316
317static void
318bcm2835_audio_start(struct bcm2835_audio_chinfo *ch)
319{
320	VC_AUDIO_MSG_T m;
321	int ret;
322	struct bcm2835_audio_info *sc = ch->parent;
323
324	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
325		m.type = VC_AUDIO_MSG_TYPE_START;
326		ret = vchi_msg_queue(sc->vchi_handle,
327		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
328
329		if (ret != 0)
330			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
331	}
332}
333
334static void
335bcm2835_audio_stop(struct bcm2835_audio_chinfo *ch)
336{
337	VC_AUDIO_MSG_T m;
338	int ret;
339	struct bcm2835_audio_info *sc = ch->parent;
340
341	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
342		m.type = VC_AUDIO_MSG_TYPE_STOP;
343		m.u.stop.draining = 0;
344
345		ret = vchi_msg_queue(sc->vchi_handle,
346		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
347
348		if (ret != 0)
349			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
350	}
351}
352
353static void
354bcm2835_audio_open(struct bcm2835_audio_info *sc)
355{
356	VC_AUDIO_MSG_T m;
357	int ret;
358
359	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
360		m.type = VC_AUDIO_MSG_TYPE_OPEN;
361		ret = vchi_msg_queue(sc->vchi_handle,
362		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
363
364		if (ret != 0)
365			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
366	}
367}
368
369static void
370bcm2835_audio_update_controls(struct bcm2835_audio_info *sc, uint32_t volume, uint32_t dest)
371{
372	VC_AUDIO_MSG_T m;
373	int ret, db;
374
375	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
376		m.type = VC_AUDIO_MSG_TYPE_CONTROL;
377		m.u.control.dest = dest;
378		if (volume > 99)
379			volume = 99;
380		db = db_levels[volume/5];
381		m.u.control.volume = VCHIQ_AUDIO_VOLUME(db);
382
383		ret = vchi_msg_queue(sc->vchi_handle,
384		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
385
386		if (ret != 0)
387			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
388	}
389}
390
391static void
392bcm2835_audio_update_params(struct bcm2835_audio_info *sc, uint32_t fmt, uint32_t speed)
393{
394	VC_AUDIO_MSG_T m;
395	int ret;
396
397	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
398		m.type = VC_AUDIO_MSG_TYPE_CONFIG;
399		m.u.config.channels = AFMT_CHANNEL(fmt);
400		m.u.config.samplerate = speed;
401		m.u.config.bps = AFMT_BIT(fmt);
402
403		ret = vchi_msg_queue(sc->vchi_handle,
404		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
405
406		if (ret != 0)
407			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
408	}
409}
410
411static bool
412bcm2835_audio_buffer_should_sleep(struct bcm2835_audio_chinfo *ch)
413{
414
415	if (ch->playback_state != PLAYBACK_PLAYING)
416		return (true);
417
418	/* Not enough data */
419	if (sndbuf_getready(ch->buffer) < VCHIQ_AUDIO_PACKET_SIZE) {
420		printf("starve\n");
421		ch->starved++;
422		return (true);
423	}
424
425	/* Not enough free space */
426	if (ch->available_space < VCHIQ_AUDIO_PACKET_SIZE) {
427		return (true);
428	}
429
430	return (false);
431}
432
433static void
434bcm2835_audio_write_samples(struct bcm2835_audio_chinfo *ch, void *buf, uint32_t count)
435{
436	struct bcm2835_audio_info *sc = ch->parent;
437	VC_AUDIO_MSG_T m;
438	int ret;
439
440	if (sc->vchi_handle == VCHIQ_SERVICE_HANDLE_INVALID) {
441		return;
442	}
443
444	m.type = VC_AUDIO_MSG_TYPE_WRITE;
445	m.u.write.count = count;
446	m.u.write.max_packet = VCHIQ_AUDIO_PACKET_SIZE;
447	m.u.write.callback = NULL;
448	m.u.write.cookie = ch;
449	m.u.write.silence = 0;
450
451	ret = vchi_msg_queue(sc->vchi_handle,
452	    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
453
454	if (ret != 0)
455		printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
456
457	while (count > 0) {
458		int bytes = MIN((int)m.u.write.max_packet, (int)count);
459		ret = vchi_msg_queue(sc->vchi_handle,
460		    buf, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
461		if (ret != 0)
462			printf("%s: vchi_msg_queue failed: %d\n",
463			    __func__, ret);
464		buf = (char *)buf + bytes;
465		count -= bytes;
466	}
467}
468
469static void
470bcm2835_audio_worker(void *data)
471{
472	struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data;
473	struct bcm2835_audio_chinfo *ch = &sc->pch;
474	uint32_t speed, format;
475	uint32_t volume, dest;
476	uint32_t flags;
477	uint32_t count, size, readyptr;
478	uint8_t *buf;
479
480	ch->playback_state = PLAYBACK_IDLE;
481
482	while (1) {
483		if (sc->worker_state != WORKER_RUNNING)
484			break;
485
486		BCM2835_AUDIO_LOCK(sc);
487		/*
488		 * wait until there are flags set or buffer is ready
489		 * to consume more samples
490		 */
491		while ((sc->flags_pending == 0) &&
492		    bcm2835_audio_buffer_should_sleep(ch)) {
493			cv_wait_sig(&sc->worker_cv, &sc->lock);
494		}
495		flags = sc->flags_pending;
496		/* Clear pending flags */
497		sc->flags_pending = 0;
498		BCM2835_AUDIO_UNLOCK(sc);
499
500		/* Requested to change parameters */
501		if (flags & AUDIO_PARAMS) {
502			BCM2835_AUDIO_LOCK(sc);
503			speed = ch->spd;
504			format = ch->fmt;
505			volume = sc->volume;
506			dest = sc->dest;
507			BCM2835_AUDIO_UNLOCK(sc);
508			if (ch->playback_state == PLAYBACK_IDLE)
509				bcm2835_audio_update_params(sc, format, speed);
510			bcm2835_audio_update_controls(sc, volume, dest);
511		}
512
513		/* Requested to stop playback */
514		if ((flags & AUDIO_STOP) &&
515		    (ch->playback_state == PLAYBACK_PLAYING)) {
516			bcm2835_audio_stop(ch);
517			BCM2835_AUDIO_LOCK(sc);
518			bcm2835_audio_reset_channel(&sc->pch);
519			ch->playback_state = PLAYBACK_IDLE;
520			BCM2835_AUDIO_UNLOCK(sc);
521			continue;
522		}
523
524		/* Requested to start playback */
525		if ((flags & AUDIO_PLAY) &&
526		    (ch->playback_state == PLAYBACK_IDLE)) {
527			BCM2835_AUDIO_LOCK(sc);
528			ch->playback_state = PLAYBACK_PLAYING;
529			BCM2835_AUDIO_UNLOCK(sc);
530			bcm2835_audio_start(ch);
531		}
532
533		if (ch->playback_state == PLAYBACK_IDLE)
534			continue;
535
536		if (sndbuf_getready(ch->buffer) == 0)
537			continue;
538
539		count = sndbuf_getready(ch->buffer);
540		size = sndbuf_getsize(ch->buffer);
541		readyptr = sndbuf_getreadyptr(ch->buffer);
542
543		BCM2835_AUDIO_LOCK(sc);
544		if (readyptr + count > size)
545			count = size - readyptr;
546		count = min(count, ch->available_space);
547		count -= (count % VCHIQ_AUDIO_PACKET_SIZE);
548		BCM2835_AUDIO_UNLOCK(sc);
549
550		if (count < VCHIQ_AUDIO_PACKET_SIZE)
551			continue;
552
553		buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + readyptr;
554
555		bcm2835_audio_write_samples(ch, buf, count);
556		BCM2835_AUDIO_LOCK(sc);
557		ch->unsubmittedptr = (ch->unsubmittedptr + count) % sndbuf_getsize(ch->buffer);
558		ch->available_space -= count;
559		ch->submitted_samples += count;
560		KASSERT(ch->available_space >= 0, ("ch->available_space == %d\n", ch->available_space));
561		BCM2835_AUDIO_UNLOCK(sc);
562	}
563
564	BCM2835_AUDIO_LOCK(sc);
565	sc->worker_state = WORKER_STOPPED;
566	cv_signal(&sc->worker_cv);
567	BCM2835_AUDIO_UNLOCK(sc);
568
569	kproc_exit(0);
570}
571
572static void
573bcm2835_audio_create_worker(struct bcm2835_audio_info *sc)
574{
575	struct proc *newp;
576
577	sc->worker_state = WORKER_RUNNING;
578	if (kproc_create(bcm2835_audio_worker, (void*)sc, &newp, 0, 0,
579	    "bcm2835_audio_worker") != 0) {
580		printf("failed to create bcm2835_audio_worker\n");
581	}
582}
583
584/* -------------------------------------------------------------------- */
585/* channel interface for VCHI audio */
586static void *
587bcmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
588{
589	struct bcm2835_audio_info *sc = devinfo;
590	struct bcm2835_audio_chinfo *ch = &sc->pch;
591	void *buffer;
592
593	if (dir == PCMDIR_REC)
594		return NULL;
595
596	ch->parent = sc;
597	ch->channel = c;
598	ch->buffer = b;
599
600	/* default values */
601	ch->spd = 44100;
602	ch->fmt = SND_FORMAT(AFMT_S16_LE, 2, 0);
603	ch->blksz = VCHIQ_AUDIO_PACKET_SIZE;
604
605	buffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
606
607	if (sndbuf_setup(ch->buffer, buffer, sc->bufsz) != 0) {
608		device_printf(sc->dev, "sndbuf_setup failed\n");
609		free(buffer, M_DEVBUF);
610		return NULL;
611	}
612
613	BCM2835_AUDIO_LOCK(sc);
614	bcm2835_worker_update_params(sc);
615	BCM2835_AUDIO_UNLOCK(sc);
616
617	return ch;
618}
619
620static int
621bcmchan_free(kobj_t obj, void *data)
622{
623	struct bcm2835_audio_chinfo *ch = data;
624	void *buffer;
625
626	buffer = sndbuf_getbuf(ch->buffer);
627	if (buffer)
628		free(buffer, M_DEVBUF);
629
630	return (0);
631}
632
633static int
634bcmchan_setformat(kobj_t obj, void *data, uint32_t format)
635{
636	struct bcm2835_audio_chinfo *ch = data;
637	struct bcm2835_audio_info *sc = ch->parent;
638
639	BCM2835_AUDIO_LOCK(sc);
640	ch->fmt = format;
641	bcm2835_worker_update_params(sc);
642	BCM2835_AUDIO_UNLOCK(sc);
643
644	return 0;
645}
646
647static uint32_t
648bcmchan_setspeed(kobj_t obj, void *data, uint32_t speed)
649{
650	struct bcm2835_audio_chinfo *ch = data;
651	struct bcm2835_audio_info *sc = ch->parent;
652
653	BCM2835_AUDIO_LOCK(sc);
654	ch->spd = speed;
655	bcm2835_worker_update_params(sc);
656	BCM2835_AUDIO_UNLOCK(sc);
657
658	return ch->spd;
659}
660
661static uint32_t
662bcmchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
663{
664	struct bcm2835_audio_chinfo *ch = data;
665
666	return ch->blksz;
667}
668
669static int
670bcmchan_trigger(kobj_t obj, void *data, int go)
671{
672	struct bcm2835_audio_chinfo *ch = data;
673	struct bcm2835_audio_info *sc = ch->parent;
674
675	if (!PCMTRIG_COMMON(go))
676		return (0);
677
678	switch (go) {
679	case PCMTRIG_START:
680		/* kickstart data flow */
681		chn_intr(sc->pch.channel);
682		ch->submitted_samples = 0;
683		ch->retrieved_samples = 0;
684		bcm2835_worker_play_start(sc);
685		break;
686
687	case PCMTRIG_STOP:
688	case PCMTRIG_ABORT:
689		bcm2835_worker_play_stop(sc);
690		break;
691
692	default:
693		break;
694	}
695	return 0;
696}
697
698static uint32_t
699bcmchan_getptr(kobj_t obj, void *data)
700{
701	struct bcm2835_audio_chinfo *ch = data;
702	struct bcm2835_audio_info *sc = ch->parent;
703	uint32_t ret;
704
705	BCM2835_AUDIO_LOCK(sc);
706	ret = ch->unsubmittedptr;
707	BCM2835_AUDIO_UNLOCK(sc);
708
709	return ret;
710}
711
712static struct pcmchan_caps *
713bcmchan_getcaps(kobj_t obj, void *data)
714{
715
716	return &bcm2835_audio_playcaps;
717}
718
719static kobj_method_t bcmchan_methods[] = {
720    	KOBJMETHOD(channel_init,		bcmchan_init),
721    	KOBJMETHOD(channel_free,		bcmchan_free),
722    	KOBJMETHOD(channel_setformat,		bcmchan_setformat),
723    	KOBJMETHOD(channel_setspeed,		bcmchan_setspeed),
724    	KOBJMETHOD(channel_setblocksize,	bcmchan_setblocksize),
725    	KOBJMETHOD(channel_trigger,		bcmchan_trigger),
726    	KOBJMETHOD(channel_getptr,		bcmchan_getptr),
727    	KOBJMETHOD(channel_getcaps,		bcmchan_getcaps),
728	KOBJMETHOD_END
729};
730CHANNEL_DECLARE(bcmchan);
731
732/************************************************************/
733
734static int
735bcmmix_init(struct snd_mixer *m)
736{
737
738	mix_setdevs(m, SOUND_MASK_VOLUME);
739
740	return (0);
741}
742
743static int
744bcmmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
745{
746    	struct bcm2835_audio_info *sc = mix_getdevinfo(m);
747
748	switch (dev) {
749	case SOUND_MIXER_VOLUME:
750		BCM2835_AUDIO_LOCK(sc);
751		sc->volume = left;
752		bcm2835_worker_update_params(sc);
753		BCM2835_AUDIO_UNLOCK(sc);
754
755		break;
756
757	default:
758		break;
759	}
760
761    	return left | (left << 8);
762}
763
764static kobj_method_t bcmmixer_methods[] = {
765    	KOBJMETHOD(mixer_init,		bcmmix_init),
766    	KOBJMETHOD(mixer_set,		bcmmix_set),
767	KOBJMETHOD_END
768};
769
770MIXER_DECLARE(bcmmixer);
771
772static int
773sysctl_bcm2835_audio_dest(SYSCTL_HANDLER_ARGS)
774{
775	struct bcm2835_audio_info *sc = arg1;
776	int val;
777	int err;
778
779	val = sc->dest;
780	err = sysctl_handle_int(oidp, &val, 0, req);
781	if (err || !req->newptr) /* error || read request */
782		return (err);
783
784	if ((val < 0) || (val > 2))
785		return (EINVAL);
786
787	BCM2835_AUDIO_LOCK(sc);
788	sc->dest = val;
789	bcm2835_worker_update_params(sc);
790	BCM2835_AUDIO_UNLOCK(sc);
791
792	if (bootverbose)
793		device_printf(sc->dev, "destination set to %s\n", dest_description(val));
794
795	return (0);
796}
797
798static void
799vchi_audio_sysctl_init(struct bcm2835_audio_info *sc)
800{
801	struct sysctl_ctx_list *ctx;
802	struct sysctl_oid *tree_node;
803	struct sysctl_oid_list *tree;
804
805	/*
806	 * Add system sysctl tree/handlers.
807	 */
808	ctx = device_get_sysctl_ctx(sc->dev);
809	tree_node = device_get_sysctl_tree(sc->dev);
810	tree = SYSCTL_CHILDREN(tree_node);
811	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "dest",
812	    CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
813	    sysctl_bcm2835_audio_dest, "IU", "audio destination, "
814	    "0 - auto, 1 - headphones, 2 - HDMI");
815	SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "callbacks",
816			CTLFLAG_RD, &sc->pch.callbacks,
817			"callbacks total");
818	SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "submitted",
819			CTLFLAG_RD, &sc->pch.submitted_samples,
820			"last play submitted samples");
821	SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "retrieved",
822			CTLFLAG_RD, &sc->pch.retrieved_samples,
823			"last play retrieved samples");
824	SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "underruns",
825			CTLFLAG_RD, &sc->pch.underruns,
826			"callback underruns");
827	SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "freebuffer",
828			CTLFLAG_RD, &sc->pch.available_space,
829			sc->pch.available_space, "callbacks total");
830	SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "starved",
831			CTLFLAG_RD, &sc->pch.starved,
832			sc->pch.starved, "number of starved conditions");
833}
834
835static void
836bcm2835_audio_identify(driver_t *driver, device_t parent)
837{
838
839	BUS_ADD_CHILD(parent, 0, "pcm", 0);
840}
841
842static int
843bcm2835_audio_probe(device_t dev)
844{
845
846	device_set_desc(dev, "VCHIQ audio");
847	return (BUS_PROBE_DEFAULT);
848}
849
850static void
851bcm2835_audio_delayed_init(void *xsc)
852{
853    	struct bcm2835_audio_info *sc;
854    	char status[SND_STATUSLEN];
855
856	sc = xsc;
857
858	config_intrhook_disestablish(&sc->intr_hook);
859
860	bcm2835_audio_init(sc);
861	bcm2835_audio_open(sc);
862	sc->volume = 75;
863	sc->dest = DEST_AUTO;
864
865    	if (mixer_init(sc->dev, &bcmmixer_class, sc)) {
866		device_printf(sc->dev, "mixer_init failed\n");
867		goto no;
868	}
869
870    	if (pcm_register(sc->dev, sc, 1, 0)) {
871		device_printf(sc->dev, "pcm_register failed\n");
872		goto no;
873	}
874
875	pcm_addchan(sc->dev, PCMDIR_PLAY, &bcmchan_class, sc);
876    	snprintf(status, SND_STATUSLEN, "at VCHIQ");
877	pcm_setstatus(sc->dev, status);
878
879	bcm2835_audio_reset_channel(&sc->pch);
880	bcm2835_audio_create_worker(sc);
881
882	vchi_audio_sysctl_init(sc);
883
884no:
885	;
886}
887
888static int
889bcm2835_audio_attach(device_t dev)
890{
891    	struct bcm2835_audio_info *sc;
892
893	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
894
895	sc->dev = dev;
896	sc->bufsz = VCHIQ_AUDIO_BUFFER_SIZE;
897
898	mtx_init(&sc->lock, device_get_nameunit(dev),
899	    "bcm_audio_lock", MTX_DEF);
900	cv_init(&sc->worker_cv, "worker_cv");
901	sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
902
903	/*
904	 * We need interrupts enabled for VCHI to work properly,
905	 * so delay initialization until it happens.
906	 */
907	sc->intr_hook.ich_func = bcm2835_audio_delayed_init;
908	sc->intr_hook.ich_arg = sc;
909
910	if (config_intrhook_establish(&sc->intr_hook) != 0)
911		goto no;
912
913    	return 0;
914
915no:
916    	return ENXIO;
917}
918
919static int
920bcm2835_audio_detach(device_t dev)
921{
922	int r;
923	struct bcm2835_audio_info *sc;
924	sc = pcm_getdevinfo(dev);
925
926	/* Stop worker thread */
927	BCM2835_AUDIO_LOCK(sc);
928	sc->worker_state = WORKER_STOPPING;
929	cv_signal(&sc->worker_cv);
930	/* Wait for thread to exit */
931	while (sc->worker_state != WORKER_STOPPED)
932		cv_wait_sig(&sc->worker_cv, &sc->lock);
933	BCM2835_AUDIO_UNLOCK(sc);
934
935	r = pcm_unregister(dev);
936	if (r)
937		return r;
938
939	mtx_destroy(&sc->lock);
940	cv_destroy(&sc->worker_cv);
941
942	bcm2835_audio_release(sc);
943
944    	free(sc, M_DEVBUF);
945
946	return 0;
947}
948
949static device_method_t bcm2835_audio_methods[] = {
950	/* Device interface */
951	DEVMETHOD(device_identify,	bcm2835_audio_identify),
952	DEVMETHOD(device_probe,		bcm2835_audio_probe),
953	DEVMETHOD(device_attach,	bcm2835_audio_attach),
954	DEVMETHOD(device_detach,	bcm2835_audio_detach),
955	{ 0, 0 }
956};
957
958static driver_t bcm2835_audio_driver = {
959	"pcm",
960	bcm2835_audio_methods,
961	PCM_SOFTC_SIZE,
962};
963
964DRIVER_MODULE(bcm2835_audio, vchiq, bcm2835_audio_driver, 0, 0);
965MODULE_DEPEND(bcm2835_audio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
966MODULE_DEPEND(bcm2835_audio, vchiq, 1, 1, 1);
967MODULE_VERSION(bcm2835_audio, 1);
968