xref: /freebsd/sys/arm/broadcom/bcm2835/bcm2835_audio.c (revision ebb62474abaef01b2bf1cee797666a3681f31d65)
12f99b597SOleksandr Tymoshenko /*-
22f99b597SOleksandr Tymoshenko  * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
32f99b597SOleksandr Tymoshenko  *
42f99b597SOleksandr Tymoshenko  * Redistribution and use in source and binary forms, with or without
52f99b597SOleksandr Tymoshenko  * modification, are permitted provided that the following conditions
62f99b597SOleksandr Tymoshenko  * are met:
72f99b597SOleksandr Tymoshenko  * 1. Redistributions of source code must retain the above copyright
82f99b597SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer.
92f99b597SOleksandr Tymoshenko  * 2. Redistributions in binary form must reproduce the above copyright
102f99b597SOleksandr Tymoshenko  *    notice, this list of conditions and the following disclaimer in the
112f99b597SOleksandr Tymoshenko  *    documentation and/or other materials provided with the distribution.
122f99b597SOleksandr Tymoshenko  *
132f99b597SOleksandr Tymoshenko  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
142f99b597SOleksandr Tymoshenko  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
152f99b597SOleksandr Tymoshenko  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
162f99b597SOleksandr Tymoshenko  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
172f99b597SOleksandr Tymoshenko  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
182f99b597SOleksandr Tymoshenko  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
192f99b597SOleksandr Tymoshenko  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
202f99b597SOleksandr Tymoshenko  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
212f99b597SOleksandr Tymoshenko  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
222f99b597SOleksandr Tymoshenko  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
232f99b597SOleksandr Tymoshenko  * SUCH DAMAGE.
242f99b597SOleksandr Tymoshenko  */
252f99b597SOleksandr Tymoshenko 
262f99b597SOleksandr Tymoshenko #ifdef HAVE_KERNEL_OPTION_HEADERS
272f99b597SOleksandr Tymoshenko #include "opt_snd.h"
282f99b597SOleksandr Tymoshenko #endif
292f99b597SOleksandr Tymoshenko 
302f99b597SOleksandr Tymoshenko #include <dev/sound/pcm/sound.h>
312f99b597SOleksandr Tymoshenko #include <dev/sound/chip.h>
322f99b597SOleksandr Tymoshenko 
332f99b597SOleksandr Tymoshenko #include "mixer_if.h"
342f99b597SOleksandr Tymoshenko 
352f99b597SOleksandr Tymoshenko #include "interface/compat/vchi_bsd.h"
362f99b597SOleksandr Tymoshenko #include "interface/vchi/vchi.h"
372f99b597SOleksandr Tymoshenko #include "interface/vchiq_arm/vchiq.h"
382f99b597SOleksandr Tymoshenko 
392f99b597SOleksandr Tymoshenko #include "vc_vchi_audioserv_defs.h"
402f99b597SOleksandr Tymoshenko 
412f99b597SOleksandr Tymoshenko SND_DECLARE_FILE("$FreeBSD$");
422f99b597SOleksandr Tymoshenko 
432f99b597SOleksandr Tymoshenko #define	DEST_AUTO		0
442f99b597SOleksandr Tymoshenko #define	DEST_HEADPHONES		1
452f99b597SOleksandr Tymoshenko #define	DEST_HDMI		2
462f99b597SOleksandr Tymoshenko 
472f99b597SOleksandr Tymoshenko #define	VCHIQ_AUDIO_PACKET_SIZE	4000
482f99b597SOleksandr Tymoshenko #define	VCHIQ_AUDIO_BUFFER_SIZE	128000
492f99b597SOleksandr Tymoshenko 
502f99b597SOleksandr Tymoshenko #define	VCHIQ_AUDIO_MAX_VOLUME
512f99b597SOleksandr Tymoshenko /* volume in terms of 0.01dB */
522f99b597SOleksandr Tymoshenko #define VCHIQ_AUDIO_VOLUME_MIN -10239
532f99b597SOleksandr Tymoshenko #define VCHIQ_AUDIO_VOLUME(db100) (uint32_t)(-((db100) << 8)/100)
542f99b597SOleksandr Tymoshenko 
552f99b597SOleksandr Tymoshenko /* dB levels with 5% volume step */
562f99b597SOleksandr Tymoshenko static int db_levels[] = {
572f99b597SOleksandr Tymoshenko 	VCHIQ_AUDIO_VOLUME_MIN, -4605, -3794, -3218, -2772,
582f99b597SOleksandr Tymoshenko 	-2407, -2099, -1832, -1597, -1386,
592f99b597SOleksandr Tymoshenko 	-1195, -1021, -861, -713, -575,
602f99b597SOleksandr Tymoshenko 	-446, -325, -210, -102, 0,
612f99b597SOleksandr Tymoshenko };
622f99b597SOleksandr Tymoshenko 
632f99b597SOleksandr Tymoshenko static uint32_t bcm2835_audio_playfmt[] = {
642f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_U8, 1, 0),
652f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_U8, 2, 0),
662f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_S8, 1, 0),
672f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_S8, 2, 0),
682f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_S16_LE, 1, 0),
692f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_S16_LE, 2, 0),
702f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_U16_LE, 1, 0),
712f99b597SOleksandr Tymoshenko 	SND_FORMAT(AFMT_U16_LE, 2, 0),
722f99b597SOleksandr Tymoshenko 	0
732f99b597SOleksandr Tymoshenko };
742f99b597SOleksandr Tymoshenko 
752f99b597SOleksandr Tymoshenko static struct pcmchan_caps bcm2835_audio_playcaps = {8000, 48000, bcm2835_audio_playfmt, 0};
762f99b597SOleksandr Tymoshenko 
772f99b597SOleksandr Tymoshenko struct bcm2835_audio_info;
782f99b597SOleksandr Tymoshenko 
792f99b597SOleksandr Tymoshenko #define	PLAYBACK_IDLE		0
802f99b597SOleksandr Tymoshenko #define	PLAYBACK_STARTING	1
812f99b597SOleksandr Tymoshenko #define	PLAYBACK_PLAYING	2
822f99b597SOleksandr Tymoshenko #define	PLAYBACK_STOPPING	3
832f99b597SOleksandr Tymoshenko 
842f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo {
852f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *parent;
862f99b597SOleksandr Tymoshenko 	struct pcm_channel *channel;
872f99b597SOleksandr Tymoshenko 	struct snd_dbuf *buffer;
882f99b597SOleksandr Tymoshenko 	uint32_t fmt, spd, blksz;
892f99b597SOleksandr Tymoshenko 
902f99b597SOleksandr Tymoshenko 	uint32_t complete_pos;
912f99b597SOleksandr Tymoshenko 	uint32_t free_buffer;
922f99b597SOleksandr Tymoshenko 	uint32_t buffered_ptr;
932f99b597SOleksandr Tymoshenko 	int playback_state;
942f99b597SOleksandr Tymoshenko };
952f99b597SOleksandr Tymoshenko 
962f99b597SOleksandr Tymoshenko struct bcm2835_audio_info {
972f99b597SOleksandr Tymoshenko 	device_t dev;
982f99b597SOleksandr Tymoshenko 	unsigned int bufsz;
992f99b597SOleksandr Tymoshenko     	struct bcm2835_audio_chinfo pch;
1002f99b597SOleksandr Tymoshenko 	uint32_t dest, volume;
1012f99b597SOleksandr Tymoshenko 	struct mtx *lock;
1022f99b597SOleksandr Tymoshenko 	struct intr_config_hook intr_hook;
1032f99b597SOleksandr Tymoshenko 
1042f99b597SOleksandr Tymoshenko 	/* VCHI data */
1052f99b597SOleksandr Tymoshenko 	struct mtx vchi_lock;
1062f99b597SOleksandr Tymoshenko 
1072f99b597SOleksandr Tymoshenko 	VCHI_INSTANCE_T vchi_instance;
1082f99b597SOleksandr Tymoshenko 	VCHI_CONNECTION_T *vchi_connection;
1092f99b597SOleksandr Tymoshenko 	VCHI_SERVICE_HANDLE_T vchi_handle;
1102f99b597SOleksandr Tymoshenko 
1112f99b597SOleksandr Tymoshenko 	struct mtx data_lock;
1122f99b597SOleksandr Tymoshenko 	struct cv data_cv;
1132f99b597SOleksandr Tymoshenko 
1142f99b597SOleksandr Tymoshenko 	/* Unloadign module */
1152f99b597SOleksandr Tymoshenko 	int unloading;
1162f99b597SOleksandr Tymoshenko };
1172f99b597SOleksandr Tymoshenko 
1182f99b597SOleksandr Tymoshenko #define bcm2835_audio_lock(_ess) snd_mtxlock((_ess)->lock)
1192f99b597SOleksandr Tymoshenko #define bcm2835_audio_unlock(_ess) snd_mtxunlock((_ess)->lock)
1202f99b597SOleksandr Tymoshenko #define bcm2835_audio_lock_assert(_ess) snd_mtxassert((_ess)->lock)
1212f99b597SOleksandr Tymoshenko 
1222f99b597SOleksandr Tymoshenko #define VCHIQ_VCHI_LOCK(sc)		mtx_lock(&(sc)->vchi_lock)
1232f99b597SOleksandr Tymoshenko #define VCHIQ_VCHI_UNLOCK(sc)		mtx_unlock(&(sc)->vchi_lock)
1242f99b597SOleksandr Tymoshenko 
1252f99b597SOleksandr Tymoshenko static const char *
1262f99b597SOleksandr Tymoshenko dest_description(uint32_t dest)
1272f99b597SOleksandr Tymoshenko {
1282f99b597SOleksandr Tymoshenko 	switch (dest) {
1292f99b597SOleksandr Tymoshenko 		case DEST_AUTO:
1302f99b597SOleksandr Tymoshenko 			return "AUTO";
1312f99b597SOleksandr Tymoshenko 			break;
1322f99b597SOleksandr Tymoshenko 
1332f99b597SOleksandr Tymoshenko 		case DEST_HEADPHONES:
1342f99b597SOleksandr Tymoshenko 			return "HEADPHONES";
1352f99b597SOleksandr Tymoshenko 			break;
1362f99b597SOleksandr Tymoshenko 
1372f99b597SOleksandr Tymoshenko 		case DEST_HDMI:
1382f99b597SOleksandr Tymoshenko 			return "HDMI";
1392f99b597SOleksandr Tymoshenko 			break;
1402f99b597SOleksandr Tymoshenko 		default:
1412f99b597SOleksandr Tymoshenko 			return "UNKNOWN";
1422f99b597SOleksandr Tymoshenko 			break;
1432f99b597SOleksandr Tymoshenko 	}
1442f99b597SOleksandr Tymoshenko }
1452f99b597SOleksandr Tymoshenko 
1462f99b597SOleksandr Tymoshenko static void
1472f99b597SOleksandr Tymoshenko bcm2835_audio_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle)
1482f99b597SOleksandr Tymoshenko {
1492f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)param;
1502f99b597SOleksandr Tymoshenko 	int32_t status;
1512f99b597SOleksandr Tymoshenko 	uint32_t msg_len;
1522f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
1532f99b597SOleksandr Tymoshenko 
1542f99b597SOleksandr Tymoshenko 	if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
1552f99b597SOleksandr Tymoshenko 		return;
1562f99b597SOleksandr Tymoshenko 
1572f99b597SOleksandr Tymoshenko 	status = vchi_msg_dequeue(sc->vchi_handle,
1582f99b597SOleksandr Tymoshenko 	    &m, sizeof m, &msg_len, VCHI_FLAGS_NONE);
1592f99b597SOleksandr Tymoshenko 	if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
160*ebb62474SOleksandr Tymoshenko 		if (m.u.result.success) {
161*ebb62474SOleksandr Tymoshenko 			device_printf(sc->dev,
162*ebb62474SOleksandr Tymoshenko 			    "msg type %08x failed\n",
163*ebb62474SOleksandr Tymoshenko 			    m.type);
164*ebb62474SOleksandr Tymoshenko 		}
1652f99b597SOleksandr Tymoshenko 	} else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
1662f99b597SOleksandr Tymoshenko 		struct bcm2835_audio_chinfo *ch = m.u.complete.cookie;
1672f99b597SOleksandr Tymoshenko 
1682f99b597SOleksandr Tymoshenko 		int count = m.u.complete.count & 0xffff;
1692f99b597SOleksandr Tymoshenko 		int perr = (m.u.complete.count & (1U << 30)) != 0;
1702f99b597SOleksandr Tymoshenko 
1712f99b597SOleksandr Tymoshenko 		ch->complete_pos = (ch->complete_pos + count) % sndbuf_getsize(ch->buffer);
1722f99b597SOleksandr Tymoshenko 		ch->free_buffer += count;
1732f99b597SOleksandr Tymoshenko 
1742f99b597SOleksandr Tymoshenko 		if (perr || ch->free_buffer >= VCHIQ_AUDIO_PACKET_SIZE) {
1752f99b597SOleksandr Tymoshenko 			chn_intr(ch->channel);
1762f99b597SOleksandr Tymoshenko 			cv_signal(&sc->data_cv);
1772f99b597SOleksandr Tymoshenko 		}
1782f99b597SOleksandr Tymoshenko 	} else
1792f99b597SOleksandr Tymoshenko 		printf("%s: unknown m.type: %d\n", __func__, m.type);
1802f99b597SOleksandr Tymoshenko }
1812f99b597SOleksandr Tymoshenko 
1822f99b597SOleksandr Tymoshenko /* VCHIQ stuff */
1832f99b597SOleksandr Tymoshenko static void
1842f99b597SOleksandr Tymoshenko bcm2835_audio_init(struct bcm2835_audio_info *sc)
1852f99b597SOleksandr Tymoshenko {
1862f99b597SOleksandr Tymoshenko 	int status;
1872f99b597SOleksandr Tymoshenko 
1882f99b597SOleksandr Tymoshenko 	/* Initialize and create a VCHI connection */
1892f99b597SOleksandr Tymoshenko 	status = vchi_initialise(&sc->vchi_instance);
1902f99b597SOleksandr Tymoshenko 	if (status != 0) {
1912f99b597SOleksandr Tymoshenko 		printf("vchi_initialise failed: %d\n", status);
1922f99b597SOleksandr Tymoshenko 		return;
1932f99b597SOleksandr Tymoshenko 	}
1942f99b597SOleksandr Tymoshenko 
1952f99b597SOleksandr Tymoshenko 	status = vchi_connect(NULL, 0, sc->vchi_instance);
1962f99b597SOleksandr Tymoshenko 	if (status != 0) {
1972f99b597SOleksandr Tymoshenko 		printf("vchi_connect failed: %d\n", status);
1982f99b597SOleksandr Tymoshenko 		return;
1992f99b597SOleksandr Tymoshenko 	}
2002f99b597SOleksandr Tymoshenko 
2012f99b597SOleksandr Tymoshenko 	SERVICE_CREATION_T params = {
2022f99b597SOleksandr Tymoshenko 	    VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
2032f99b597SOleksandr Tymoshenko 	    VC_AUDIO_SERVER_NAME,   /* 4cc service code */
2042f99b597SOleksandr Tymoshenko 	    sc->vchi_connection,    /* passed in fn pointers */
2052f99b597SOleksandr Tymoshenko 	    0,  /* rx fifo size */
2062f99b597SOleksandr Tymoshenko 	    0,  /* tx fifo size */
2072f99b597SOleksandr Tymoshenko 	    bcm2835_audio_callback,    /* service callback */
2082f99b597SOleksandr Tymoshenko 	    sc,   /* service callback parameter */
2092f99b597SOleksandr Tymoshenko 	    1,
2102f99b597SOleksandr Tymoshenko 	    1,
2112f99b597SOleksandr Tymoshenko 	    0   /* want crc check on bulk transfers */
2122f99b597SOleksandr Tymoshenko 	};
2132f99b597SOleksandr Tymoshenko 
2142f99b597SOleksandr Tymoshenko 	status = vchi_service_open(sc->vchi_instance, &params,
2152f99b597SOleksandr Tymoshenko 	    &sc->vchi_handle);
2162f99b597SOleksandr Tymoshenko 
2172f99b597SOleksandr Tymoshenko 	if (status == 0)
2182f99b597SOleksandr Tymoshenko 		/* Finished with the service for now */
2192f99b597SOleksandr Tymoshenko 		vchi_service_release(sc->vchi_handle);
2202f99b597SOleksandr Tymoshenko 	else
2212f99b597SOleksandr Tymoshenko 		sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
2222f99b597SOleksandr Tymoshenko }
2232f99b597SOleksandr Tymoshenko 
2242f99b597SOleksandr Tymoshenko static void
2252f99b597SOleksandr Tymoshenko bcm2835_audio_release(struct bcm2835_audio_info *sc)
2262f99b597SOleksandr Tymoshenko {
2272f99b597SOleksandr Tymoshenko 	int success;
2282f99b597SOleksandr Tymoshenko 
2292f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
2302f99b597SOleksandr Tymoshenko 		vchi_service_use(sc->vchi_handle);
2312f99b597SOleksandr Tymoshenko 		success = vchi_service_close(sc->vchi_handle);
2322f99b597SOleksandr Tymoshenko 		if (success != 0)
2332f99b597SOleksandr Tymoshenko 			printf("vchi_service_close failed: %d\n", success);
2342f99b597SOleksandr Tymoshenko 		sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
2352f99b597SOleksandr Tymoshenko 	}
2362f99b597SOleksandr Tymoshenko 
2372f99b597SOleksandr Tymoshenko 	vchi_disconnect(sc->vchi_instance);
2382f99b597SOleksandr Tymoshenko }
2392f99b597SOleksandr Tymoshenko 
2402f99b597SOleksandr Tymoshenko static void
2412f99b597SOleksandr Tymoshenko bcm2835_audio_reset_channel(struct bcm2835_audio_chinfo *ch)
2422f99b597SOleksandr Tymoshenko {
2432f99b597SOleksandr Tymoshenko 	ch->free_buffer = VCHIQ_AUDIO_BUFFER_SIZE;
2442f99b597SOleksandr Tymoshenko 	ch->playback_state = 0;
2452f99b597SOleksandr Tymoshenko 	ch->buffered_ptr = 0;
2462f99b597SOleksandr Tymoshenko 	ch->complete_pos = 0;
2472f99b597SOleksandr Tymoshenko 
2482f99b597SOleksandr Tymoshenko 	sndbuf_reset(ch->buffer);
2492f99b597SOleksandr Tymoshenko }
2502f99b597SOleksandr Tymoshenko 
2512f99b597SOleksandr Tymoshenko static void
2522f99b597SOleksandr Tymoshenko bcm2835_audio_start(struct bcm2835_audio_chinfo *ch)
2532f99b597SOleksandr Tymoshenko {
2542f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
2552f99b597SOleksandr Tymoshenko 	int ret;
2562f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
2572f99b597SOleksandr Tymoshenko 
2582f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_LOCK(sc);
2592f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
2602f99b597SOleksandr Tymoshenko 		vchi_service_use(sc->vchi_handle);
2612f99b597SOleksandr Tymoshenko 
2622f99b597SOleksandr Tymoshenko 		bcm2835_audio_reset_channel(ch);
2632f99b597SOleksandr Tymoshenko 
2642f99b597SOleksandr Tymoshenko 		m.type = VC_AUDIO_MSG_TYPE_START;
2652f99b597SOleksandr Tymoshenko 		ret = vchi_msg_queue(sc->vchi_handle,
2662f99b597SOleksandr Tymoshenko 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
2672f99b597SOleksandr Tymoshenko 
2682f99b597SOleksandr Tymoshenko 		if (ret != 0)
2692f99b597SOleksandr Tymoshenko 			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
2702f99b597SOleksandr Tymoshenko 
2712f99b597SOleksandr Tymoshenko 		vchi_service_release(sc->vchi_handle);
2722f99b597SOleksandr Tymoshenko 	}
2732f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_UNLOCK(sc);
2742f99b597SOleksandr Tymoshenko 
2752f99b597SOleksandr Tymoshenko }
2762f99b597SOleksandr Tymoshenko 
2772f99b597SOleksandr Tymoshenko static void
2782f99b597SOleksandr Tymoshenko bcm2835_audio_stop(struct bcm2835_audio_chinfo *ch)
2792f99b597SOleksandr Tymoshenko {
2802f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
2812f99b597SOleksandr Tymoshenko 	int ret;
2822f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
2832f99b597SOleksandr Tymoshenko 
2842f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_LOCK(sc);
2852f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
2862f99b597SOleksandr Tymoshenko 		vchi_service_use(sc->vchi_handle);
2872f99b597SOleksandr Tymoshenko 
2882f99b597SOleksandr Tymoshenko 		m.type = VC_AUDIO_MSG_TYPE_STOP;
2892f99b597SOleksandr Tymoshenko 		m.u.stop.draining = 0;
2902f99b597SOleksandr Tymoshenko 
2912f99b597SOleksandr Tymoshenko 		ret = vchi_msg_queue(sc->vchi_handle,
2922f99b597SOleksandr Tymoshenko 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
2932f99b597SOleksandr Tymoshenko 
2942f99b597SOleksandr Tymoshenko 		if (ret != 0)
2952f99b597SOleksandr Tymoshenko 			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
2962f99b597SOleksandr Tymoshenko 
2972f99b597SOleksandr Tymoshenko 		vchi_service_release(sc->vchi_handle);
2982f99b597SOleksandr Tymoshenko 	}
2992f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_UNLOCK(sc);
3002f99b597SOleksandr Tymoshenko }
3012f99b597SOleksandr Tymoshenko 
3022f99b597SOleksandr Tymoshenko static void
3032f99b597SOleksandr Tymoshenko bcm2835_audio_open(struct bcm2835_audio_info *sc)
3042f99b597SOleksandr Tymoshenko {
3052f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
3062f99b597SOleksandr Tymoshenko 	int ret;
3072f99b597SOleksandr Tymoshenko 
3082f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_LOCK(sc);
3092f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3102f99b597SOleksandr Tymoshenko 		vchi_service_use(sc->vchi_handle);
3112f99b597SOleksandr Tymoshenko 
3122f99b597SOleksandr Tymoshenko 		m.type = VC_AUDIO_MSG_TYPE_OPEN;
3132f99b597SOleksandr Tymoshenko 		ret = vchi_msg_queue(sc->vchi_handle,
3142f99b597SOleksandr Tymoshenko 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3152f99b597SOleksandr Tymoshenko 
3162f99b597SOleksandr Tymoshenko 		if (ret != 0)
3172f99b597SOleksandr Tymoshenko 			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3182f99b597SOleksandr Tymoshenko 
3192f99b597SOleksandr Tymoshenko 		vchi_service_release(sc->vchi_handle);
3202f99b597SOleksandr Tymoshenko 	}
3212f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_UNLOCK(sc);
3222f99b597SOleksandr Tymoshenko }
3232f99b597SOleksandr Tymoshenko 
3242f99b597SOleksandr Tymoshenko static void
3252f99b597SOleksandr Tymoshenko bcm2835_audio_update_controls(struct bcm2835_audio_info *sc)
3262f99b597SOleksandr Tymoshenko {
3272f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
3282f99b597SOleksandr Tymoshenko 	int ret, db;
3292f99b597SOleksandr Tymoshenko 
3302f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_LOCK(sc);
3312f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3322f99b597SOleksandr Tymoshenko 		vchi_service_use(sc->vchi_handle);
3332f99b597SOleksandr Tymoshenko 
3342f99b597SOleksandr Tymoshenko 		m.type = VC_AUDIO_MSG_TYPE_CONTROL;
3352f99b597SOleksandr Tymoshenko 		m.u.control.dest = sc->dest;
3362f99b597SOleksandr Tymoshenko 		if (sc->volume > 99)
3372f99b597SOleksandr Tymoshenko 			sc->volume = 99;
3382f99b597SOleksandr Tymoshenko 		db = db_levels[sc->volume/5];
3392f99b597SOleksandr Tymoshenko 		m.u.control.volume = VCHIQ_AUDIO_VOLUME(db);
3402f99b597SOleksandr Tymoshenko 
3412f99b597SOleksandr Tymoshenko 		ret = vchi_msg_queue(sc->vchi_handle,
3422f99b597SOleksandr Tymoshenko 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3432f99b597SOleksandr Tymoshenko 
3442f99b597SOleksandr Tymoshenko 		if (ret != 0)
3452f99b597SOleksandr Tymoshenko 			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3462f99b597SOleksandr Tymoshenko 
3472f99b597SOleksandr Tymoshenko 		vchi_service_release(sc->vchi_handle);
3482f99b597SOleksandr Tymoshenko 	}
3492f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_UNLOCK(sc);
3502f99b597SOleksandr Tymoshenko }
3512f99b597SOleksandr Tymoshenko 
3522f99b597SOleksandr Tymoshenko static void
3532f99b597SOleksandr Tymoshenko bcm2835_audio_update_params(struct bcm2835_audio_info *sc, struct bcm2835_audio_chinfo *ch)
3542f99b597SOleksandr Tymoshenko {
3552f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
3562f99b597SOleksandr Tymoshenko 	int ret;
3572f99b597SOleksandr Tymoshenko 
3582f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_LOCK(sc);
3592f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3602f99b597SOleksandr Tymoshenko 		vchi_service_use(sc->vchi_handle);
3612f99b597SOleksandr Tymoshenko 
3622f99b597SOleksandr Tymoshenko 		m.type = VC_AUDIO_MSG_TYPE_CONFIG;
3632f99b597SOleksandr Tymoshenko 		m.u.config.channels = AFMT_CHANNEL(ch->fmt);
3642f99b597SOleksandr Tymoshenko 		m.u.config.samplerate = ch->spd;
3652f99b597SOleksandr Tymoshenko 		m.u.config.bps = AFMT_BIT(ch->fmt);
3662f99b597SOleksandr Tymoshenko 
3672f99b597SOleksandr Tymoshenko 		ret = vchi_msg_queue(sc->vchi_handle,
3682f99b597SOleksandr Tymoshenko 		    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3692f99b597SOleksandr Tymoshenko 
3702f99b597SOleksandr Tymoshenko 		if (ret != 0)
3712f99b597SOleksandr Tymoshenko 			printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3722f99b597SOleksandr Tymoshenko 
3732f99b597SOleksandr Tymoshenko 		vchi_service_release(sc->vchi_handle);
3742f99b597SOleksandr Tymoshenko 	}
3752f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_UNLOCK(sc);
3762f99b597SOleksandr Tymoshenko }
3772f99b597SOleksandr Tymoshenko 
3782f99b597SOleksandr Tymoshenko static __inline uint32_t
3792f99b597SOleksandr Tymoshenko vchiq_unbuffered_bytes(struct bcm2835_audio_chinfo *ch)
3802f99b597SOleksandr Tymoshenko {
3812f99b597SOleksandr Tymoshenko 	uint32_t size, ready, readyptr, readyend;
3822f99b597SOleksandr Tymoshenko 
3832f99b597SOleksandr Tymoshenko 	size = sndbuf_getsize(ch->buffer);
3842f99b597SOleksandr Tymoshenko 	readyptr = sndbuf_getreadyptr(ch->buffer);
3852f99b597SOleksandr Tymoshenko 	ready = sndbuf_getready(ch->buffer);
3862f99b597SOleksandr Tymoshenko 
3872f99b597SOleksandr Tymoshenko 	readyend = readyptr + ready;
3882f99b597SOleksandr Tymoshenko 	/* Normal case */
3892f99b597SOleksandr Tymoshenko 	if (ch->buffered_ptr >= readyptr) {
3902f99b597SOleksandr Tymoshenko 		if (readyend > ch->buffered_ptr)
3912f99b597SOleksandr Tymoshenko 			return readyend - ch->buffered_ptr;
3922f99b597SOleksandr Tymoshenko 		else
3932f99b597SOleksandr Tymoshenko 			return 0;
3942f99b597SOleksandr Tymoshenko 	}
3952f99b597SOleksandr Tymoshenko 	else { /* buffered_ptr overflow */
3962f99b597SOleksandr Tymoshenko 		if (readyend > ch->buffered_ptr + size)
3972f99b597SOleksandr Tymoshenko 			return readyend - ch->buffered_ptr - size;
3982f99b597SOleksandr Tymoshenko 		else
3992f99b597SOleksandr Tymoshenko 			return 0;
4002f99b597SOleksandr Tymoshenko 	}
4012f99b597SOleksandr Tymoshenko }
4022f99b597SOleksandr Tymoshenko 
4032f99b597SOleksandr Tymoshenko static void
4042f99b597SOleksandr Tymoshenko bcm2835_audio_write_samples(struct bcm2835_audio_chinfo *ch)
4052f99b597SOleksandr Tymoshenko {
4062f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
4072f99b597SOleksandr Tymoshenko 	VC_AUDIO_MSG_T m;
4082f99b597SOleksandr Tymoshenko 	void *buf;
4092f99b597SOleksandr Tymoshenko 	uint32_t count, size;
4102f99b597SOleksandr Tymoshenko 	int ret;
4112f99b597SOleksandr Tymoshenko 
4122f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_LOCK(sc);
4132f99b597SOleksandr Tymoshenko 	if (sc->vchi_handle == VCHIQ_SERVICE_HANDLE_INVALID) {
4142f99b597SOleksandr Tymoshenko 		VCHIQ_VCHI_UNLOCK(sc);
4152f99b597SOleksandr Tymoshenko 		return;
4162f99b597SOleksandr Tymoshenko 	}
4172f99b597SOleksandr Tymoshenko 
4182f99b597SOleksandr Tymoshenko 	vchi_service_use(sc->vchi_handle);
4192f99b597SOleksandr Tymoshenko 
4202f99b597SOleksandr Tymoshenko 	size = sndbuf_getsize(ch->buffer);
4212f99b597SOleksandr Tymoshenko 	count = vchiq_unbuffered_bytes(ch);
4222f99b597SOleksandr Tymoshenko 	buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + ch->buffered_ptr;
4232f99b597SOleksandr Tymoshenko 
4242f99b597SOleksandr Tymoshenko 	if (ch->buffered_ptr + count > size)
4252f99b597SOleksandr Tymoshenko 		count = size - ch->buffered_ptr;
4262f99b597SOleksandr Tymoshenko 
4272f99b597SOleksandr Tymoshenko 	if (count < VCHIQ_AUDIO_PACKET_SIZE)
4282f99b597SOleksandr Tymoshenko 		goto done;
4292f99b597SOleksandr Tymoshenko 
4302f99b597SOleksandr Tymoshenko 	count = min(count, ch->free_buffer);
4312f99b597SOleksandr Tymoshenko 	count -= count % VCHIQ_AUDIO_PACKET_SIZE;
4322f99b597SOleksandr Tymoshenko 
4332f99b597SOleksandr Tymoshenko 	m.type = VC_AUDIO_MSG_TYPE_WRITE;
4342f99b597SOleksandr Tymoshenko 	m.u.write.count = count;
4352f99b597SOleksandr Tymoshenko 	m.u.write.max_packet = VCHIQ_AUDIO_PACKET_SIZE;
4362f99b597SOleksandr Tymoshenko 	m.u.write.callback = NULL;
4372f99b597SOleksandr Tymoshenko 	m.u.write.cookie = ch;
4382f99b597SOleksandr Tymoshenko 	if (buf)
4392f99b597SOleksandr Tymoshenko 		m.u.write.silence = 0;
4402f99b597SOleksandr Tymoshenko 	else
4412f99b597SOleksandr Tymoshenko 		m.u.write.silence = 1;
4422f99b597SOleksandr Tymoshenko 
4432f99b597SOleksandr Tymoshenko 	ret = vchi_msg_queue(sc->vchi_handle,
4442f99b597SOleksandr Tymoshenko 	    &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
4452f99b597SOleksandr Tymoshenko 
4462f99b597SOleksandr Tymoshenko 	if (ret != 0)
4472f99b597SOleksandr Tymoshenko 		printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
4482f99b597SOleksandr Tymoshenko 
4492f99b597SOleksandr Tymoshenko 	if (buf) {
4502f99b597SOleksandr Tymoshenko 		while (count > 0) {
4512f99b597SOleksandr Tymoshenko 			int bytes = MIN((int)m.u.write.max_packet, (int)count);
4522f99b597SOleksandr Tymoshenko 			ch->free_buffer -= bytes;
4532f99b597SOleksandr Tymoshenko 			ch->buffered_ptr += bytes;
4542f99b597SOleksandr Tymoshenko 			ch->buffered_ptr = ch->buffered_ptr % size;
4552f99b597SOleksandr Tymoshenko 			ret = vchi_msg_queue(sc->vchi_handle,
4562f99b597SOleksandr Tymoshenko 			    buf, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
4572f99b597SOleksandr Tymoshenko 			if (ret != 0)
4582f99b597SOleksandr Tymoshenko 				printf("%s: vchi_msg_queue failed: %d\n",
4592f99b597SOleksandr Tymoshenko 				    __func__, ret);
4602f99b597SOleksandr Tymoshenko 			buf = (char *)buf + bytes;
4612f99b597SOleksandr Tymoshenko 			count -= bytes;
4622f99b597SOleksandr Tymoshenko 		}
4632f99b597SOleksandr Tymoshenko 	}
4642f99b597SOleksandr Tymoshenko done:
4652f99b597SOleksandr Tymoshenko 
4662f99b597SOleksandr Tymoshenko 	vchi_service_release(sc->vchi_handle);
4672f99b597SOleksandr Tymoshenko 	VCHIQ_VCHI_UNLOCK(sc);
4682f99b597SOleksandr Tymoshenko }
4692f99b597SOleksandr Tymoshenko 
4702f99b597SOleksandr Tymoshenko static void
4712f99b597SOleksandr Tymoshenko bcm2835_audio_worker(void *data)
4722f99b597SOleksandr Tymoshenko {
4732f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data;
4742f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = &sc->pch;
4752f99b597SOleksandr Tymoshenko 	mtx_lock(&sc->data_lock);
4762f99b597SOleksandr Tymoshenko 	while(1) {
4772f99b597SOleksandr Tymoshenko 
4782f99b597SOleksandr Tymoshenko 		if (sc->unloading)
4792f99b597SOleksandr Tymoshenko 			break;
4802f99b597SOleksandr Tymoshenko 
4812f99b597SOleksandr Tymoshenko 		if ((ch->playback_state == PLAYBACK_PLAYING) &&
4822f99b597SOleksandr Tymoshenko 		    (vchiq_unbuffered_bytes(ch) >= VCHIQ_AUDIO_PACKET_SIZE)
4832f99b597SOleksandr Tymoshenko 		    && (ch->free_buffer >= VCHIQ_AUDIO_PACKET_SIZE)) {
4842f99b597SOleksandr Tymoshenko 			bcm2835_audio_write_samples(ch);
4852f99b597SOleksandr Tymoshenko 		} else {
4862f99b597SOleksandr Tymoshenko 			if (ch->playback_state == PLAYBACK_STOPPING) {
4872f99b597SOleksandr Tymoshenko 				bcm2835_audio_reset_channel(&sc->pch);
4882f99b597SOleksandr Tymoshenko 				ch->playback_state = PLAYBACK_IDLE;
4892f99b597SOleksandr Tymoshenko 			}
4902f99b597SOleksandr Tymoshenko 
4912f99b597SOleksandr Tymoshenko 			cv_wait_sig(&sc->data_cv, &sc->data_lock);
4922f99b597SOleksandr Tymoshenko 
4932f99b597SOleksandr Tymoshenko 			if (ch->playback_state == PLAYBACK_STARTING) {
4942f99b597SOleksandr Tymoshenko 				/* Give it initial kick */
4952f99b597SOleksandr Tymoshenko 				chn_intr(sc->pch.channel);
4962f99b597SOleksandr Tymoshenko 				ch->playback_state = PLAYBACK_PLAYING;
4972f99b597SOleksandr Tymoshenko 			}
4982f99b597SOleksandr Tymoshenko 		}
4992f99b597SOleksandr Tymoshenko 	}
5002f99b597SOleksandr Tymoshenko 	mtx_unlock(&sc->data_lock);
5012f99b597SOleksandr Tymoshenko 
5022f99b597SOleksandr Tymoshenko 	kproc_exit(0);
5032f99b597SOleksandr Tymoshenko }
5042f99b597SOleksandr Tymoshenko 
5052f99b597SOleksandr Tymoshenko static void
5062f99b597SOleksandr Tymoshenko bcm2835_audio_create_worker(struct bcm2835_audio_info *sc)
5072f99b597SOleksandr Tymoshenko {
5082f99b597SOleksandr Tymoshenko 	struct proc *newp;
5092f99b597SOleksandr Tymoshenko 
5102f99b597SOleksandr Tymoshenko 	if (kproc_create(bcm2835_audio_worker, (void*)sc, &newp, 0, 0,
5112f99b597SOleksandr Tymoshenko 	    "bcm2835_audio_worker") != 0) {
5122f99b597SOleksandr Tymoshenko 		printf("failed to create bcm2835_audio_worker\n");
5132f99b597SOleksandr Tymoshenko 	}
5142f99b597SOleksandr Tymoshenko }
5152f99b597SOleksandr Tymoshenko 
5162f99b597SOleksandr Tymoshenko /* -------------------------------------------------------------------- */
5172f99b597SOleksandr Tymoshenko /* channel interface for ESS18xx */
5182f99b597SOleksandr Tymoshenko static void *
5192f99b597SOleksandr Tymoshenko bcmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
5202f99b597SOleksandr Tymoshenko {
5212f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = devinfo;
5222f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = &sc->pch;
5232f99b597SOleksandr Tymoshenko 	void *buffer;
5242f99b597SOleksandr Tymoshenko 
5252f99b597SOleksandr Tymoshenko 	if (dir == PCMDIR_REC)
5262f99b597SOleksandr Tymoshenko 		return NULL;
5272f99b597SOleksandr Tymoshenko 
5282f99b597SOleksandr Tymoshenko 	ch->parent = sc;
5292f99b597SOleksandr Tymoshenko 	ch->channel = c;
5302f99b597SOleksandr Tymoshenko 	ch->buffer = b;
5312f99b597SOleksandr Tymoshenko 
5322f99b597SOleksandr Tymoshenko 	/* default values */
5332f99b597SOleksandr Tymoshenko 	ch->spd = 44100;
5342f99b597SOleksandr Tymoshenko 	ch->fmt = SND_FORMAT(AFMT_S16_LE, 2, 0);
5352f99b597SOleksandr Tymoshenko 	ch->blksz = VCHIQ_AUDIO_PACKET_SIZE;
5362f99b597SOleksandr Tymoshenko 
5372f99b597SOleksandr Tymoshenko 	buffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
5382f99b597SOleksandr Tymoshenko 
5392f99b597SOleksandr Tymoshenko 	if (sndbuf_setup(ch->buffer, buffer, sc->bufsz) != 0) {
5402f99b597SOleksandr Tymoshenko 		free(buffer, M_DEVBUF);
5412f99b597SOleksandr Tymoshenko 		return NULL;
5422f99b597SOleksandr Tymoshenko 	}
5432f99b597SOleksandr Tymoshenko 
5442f99b597SOleksandr Tymoshenko 	bcm2835_audio_update_params(sc, ch);
5452f99b597SOleksandr Tymoshenko 
5462f99b597SOleksandr Tymoshenko 	return ch;
5472f99b597SOleksandr Tymoshenko }
5482f99b597SOleksandr Tymoshenko 
5492f99b597SOleksandr Tymoshenko static int
5502f99b597SOleksandr Tymoshenko bcmchan_free(kobj_t obj, void *data)
5512f99b597SOleksandr Tymoshenko {
5522f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = data;
5532f99b597SOleksandr Tymoshenko 	void *buffer;
5542f99b597SOleksandr Tymoshenko 
5552f99b597SOleksandr Tymoshenko 	buffer = sndbuf_getbuf(ch->buffer);
5562f99b597SOleksandr Tymoshenko 	if (buffer)
5572f99b597SOleksandr Tymoshenko 		free(buffer, M_DEVBUF);
5582f99b597SOleksandr Tymoshenko 
5592f99b597SOleksandr Tymoshenko 	return (0);
5602f99b597SOleksandr Tymoshenko }
5612f99b597SOleksandr Tymoshenko 
5622f99b597SOleksandr Tymoshenko static int
5632f99b597SOleksandr Tymoshenko bcmchan_setformat(kobj_t obj, void *data, uint32_t format)
5642f99b597SOleksandr Tymoshenko {
5652f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = data;
5662f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
5672f99b597SOleksandr Tymoshenko 
5682f99b597SOleksandr Tymoshenko 	bcm2835_audio_lock(sc);
5692f99b597SOleksandr Tymoshenko 
5702f99b597SOleksandr Tymoshenko 	ch->fmt = format;
5712f99b597SOleksandr Tymoshenko 	bcm2835_audio_update_params(sc, ch);
5722f99b597SOleksandr Tymoshenko 
5732f99b597SOleksandr Tymoshenko 	bcm2835_audio_unlock(sc);
5742f99b597SOleksandr Tymoshenko 
5752f99b597SOleksandr Tymoshenko 	return 0;
5762f99b597SOleksandr Tymoshenko }
5772f99b597SOleksandr Tymoshenko 
5782f99b597SOleksandr Tymoshenko static uint32_t
5792f99b597SOleksandr Tymoshenko bcmchan_setspeed(kobj_t obj, void *data, uint32_t speed)
5802f99b597SOleksandr Tymoshenko {
5812f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = data;
5822f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
5832f99b597SOleksandr Tymoshenko 
5842f99b597SOleksandr Tymoshenko 	bcm2835_audio_lock(sc);
5852f99b597SOleksandr Tymoshenko 
5862f99b597SOleksandr Tymoshenko 	ch->spd = speed;
5872f99b597SOleksandr Tymoshenko 	bcm2835_audio_update_params(sc, ch);
5882f99b597SOleksandr Tymoshenko 
5892f99b597SOleksandr Tymoshenko 	bcm2835_audio_unlock(sc);
5902f99b597SOleksandr Tymoshenko 
5912f99b597SOleksandr Tymoshenko 	return ch->spd;
5922f99b597SOleksandr Tymoshenko }
5932f99b597SOleksandr Tymoshenko 
5942f99b597SOleksandr Tymoshenko static uint32_t
5952f99b597SOleksandr Tymoshenko bcmchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
5962f99b597SOleksandr Tymoshenko {
5972f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = data;
5982f99b597SOleksandr Tymoshenko 
5992f99b597SOleksandr Tymoshenko 	return ch->blksz;
6002f99b597SOleksandr Tymoshenko }
6012f99b597SOleksandr Tymoshenko 
6022f99b597SOleksandr Tymoshenko static int
6032f99b597SOleksandr Tymoshenko bcmchan_trigger(kobj_t obj, void *data, int go)
6042f99b597SOleksandr Tymoshenko {
6052f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = data;
6062f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
6072f99b597SOleksandr Tymoshenko 
6082f99b597SOleksandr Tymoshenko 	if (!PCMTRIG_COMMON(go))
6092f99b597SOleksandr Tymoshenko 		return (0);
6102f99b597SOleksandr Tymoshenko 
6112f99b597SOleksandr Tymoshenko 	bcm2835_audio_lock(sc);
6122f99b597SOleksandr Tymoshenko 
6132f99b597SOleksandr Tymoshenko 	switch (go) {
6142f99b597SOleksandr Tymoshenko 	case PCMTRIG_START:
6152f99b597SOleksandr Tymoshenko 		bcm2835_audio_start(ch);
6162f99b597SOleksandr Tymoshenko 		ch->playback_state = PLAYBACK_STARTING;
6172f99b597SOleksandr Tymoshenko 		/* wakeup worker thread */
6182f99b597SOleksandr Tymoshenko 		cv_signal(&sc->data_cv);
6192f99b597SOleksandr Tymoshenko 		break;
6202f99b597SOleksandr Tymoshenko 
6212f99b597SOleksandr Tymoshenko 	case PCMTRIG_STOP:
6222f99b597SOleksandr Tymoshenko 	case PCMTRIG_ABORT:
6232f99b597SOleksandr Tymoshenko 		ch->playback_state = 1;
6242f99b597SOleksandr Tymoshenko 		bcm2835_audio_stop(ch);
6252f99b597SOleksandr Tymoshenko 		break;
6262f99b597SOleksandr Tymoshenko 
6272f99b597SOleksandr Tymoshenko 	default:
6282f99b597SOleksandr Tymoshenko 		break;
6292f99b597SOleksandr Tymoshenko 	}
6302f99b597SOleksandr Tymoshenko 
6312f99b597SOleksandr Tymoshenko 	bcm2835_audio_unlock(sc);
6322f99b597SOleksandr Tymoshenko 	return 0;
6332f99b597SOleksandr Tymoshenko }
6342f99b597SOleksandr Tymoshenko 
6352f99b597SOleksandr Tymoshenko static uint32_t
6362f99b597SOleksandr Tymoshenko bcmchan_getptr(kobj_t obj, void *data)
6372f99b597SOleksandr Tymoshenko {
6382f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_chinfo *ch = data;
6392f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = ch->parent;
6402f99b597SOleksandr Tymoshenko 	uint32_t ret;
6412f99b597SOleksandr Tymoshenko 
6422f99b597SOleksandr Tymoshenko 	bcm2835_audio_lock(sc);
6432f99b597SOleksandr Tymoshenko 
6442f99b597SOleksandr Tymoshenko 	ret = ch->complete_pos - (ch->complete_pos % VCHIQ_AUDIO_PACKET_SIZE);
6452f99b597SOleksandr Tymoshenko 
6462f99b597SOleksandr Tymoshenko 	bcm2835_audio_unlock(sc);
6472f99b597SOleksandr Tymoshenko 
6482f99b597SOleksandr Tymoshenko 	return ret;
6492f99b597SOleksandr Tymoshenko }
6502f99b597SOleksandr Tymoshenko 
6512f99b597SOleksandr Tymoshenko static struct pcmchan_caps *
6522f99b597SOleksandr Tymoshenko bcmchan_getcaps(kobj_t obj, void *data)
6532f99b597SOleksandr Tymoshenko {
6542f99b597SOleksandr Tymoshenko 
6552f99b597SOleksandr Tymoshenko 	return &bcm2835_audio_playcaps;
6562f99b597SOleksandr Tymoshenko }
6572f99b597SOleksandr Tymoshenko 
6582f99b597SOleksandr Tymoshenko static kobj_method_t bcmchan_methods[] = {
6592f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_init,		bcmchan_init),
6602f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_free,		bcmchan_free),
6612f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_setformat,		bcmchan_setformat),
6622f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_setspeed,		bcmchan_setspeed),
6632f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_setblocksize,	bcmchan_setblocksize),
6642f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_trigger,		bcmchan_trigger),
6652f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_getptr,		bcmchan_getptr),
6662f99b597SOleksandr Tymoshenko     	KOBJMETHOD(channel_getcaps,		bcmchan_getcaps),
6672f99b597SOleksandr Tymoshenko 	KOBJMETHOD_END
6682f99b597SOleksandr Tymoshenko };
6692f99b597SOleksandr Tymoshenko CHANNEL_DECLARE(bcmchan);
6702f99b597SOleksandr Tymoshenko 
6712f99b597SOleksandr Tymoshenko /************************************************************/
6722f99b597SOleksandr Tymoshenko 
6732f99b597SOleksandr Tymoshenko static int
6742f99b597SOleksandr Tymoshenko bcmmix_init(struct snd_mixer *m)
6752f99b597SOleksandr Tymoshenko {
6762f99b597SOleksandr Tymoshenko 
6772f99b597SOleksandr Tymoshenko 	mix_setdevs(m, SOUND_MASK_VOLUME);
6782f99b597SOleksandr Tymoshenko 
6792f99b597SOleksandr Tymoshenko 	return (0);
6802f99b597SOleksandr Tymoshenko }
6812f99b597SOleksandr Tymoshenko 
6822f99b597SOleksandr Tymoshenko static int
6832f99b597SOleksandr Tymoshenko bcmmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
6842f99b597SOleksandr Tymoshenko {
6852f99b597SOleksandr Tymoshenko     	struct bcm2835_audio_info *sc = mix_getdevinfo(m);
6862f99b597SOleksandr Tymoshenko 
6872f99b597SOleksandr Tymoshenko 	switch (dev) {
6882f99b597SOleksandr Tymoshenko 	case SOUND_MIXER_VOLUME:
6892f99b597SOleksandr Tymoshenko 		sc->volume = left;
6902f99b597SOleksandr Tymoshenko 		bcm2835_audio_update_controls(sc);
6912f99b597SOleksandr Tymoshenko 		break;
6922f99b597SOleksandr Tymoshenko 
6932f99b597SOleksandr Tymoshenko 	default:
6942f99b597SOleksandr Tymoshenko 		break;
6952f99b597SOleksandr Tymoshenko 	}
6962f99b597SOleksandr Tymoshenko 
6972f99b597SOleksandr Tymoshenko     	return left | (left << 8);
6982f99b597SOleksandr Tymoshenko }
6992f99b597SOleksandr Tymoshenko 
7002f99b597SOleksandr Tymoshenko static kobj_method_t bcmmixer_methods[] = {
7012f99b597SOleksandr Tymoshenko     	KOBJMETHOD(mixer_init,		bcmmix_init),
7022f99b597SOleksandr Tymoshenko     	KOBJMETHOD(mixer_set,		bcmmix_set),
7032f99b597SOleksandr Tymoshenko 	KOBJMETHOD_END
7042f99b597SOleksandr Tymoshenko };
7052f99b597SOleksandr Tymoshenko 
7062f99b597SOleksandr Tymoshenko MIXER_DECLARE(bcmmixer);
7072f99b597SOleksandr Tymoshenko 
7082f99b597SOleksandr Tymoshenko static int
7092f99b597SOleksandr Tymoshenko sysctl_bcm2835_audio_dest(SYSCTL_HANDLER_ARGS)
7102f99b597SOleksandr Tymoshenko {
7112f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc = arg1;
7122f99b597SOleksandr Tymoshenko 	int val;
7132f99b597SOleksandr Tymoshenko 	int err;
7142f99b597SOleksandr Tymoshenko 
7152f99b597SOleksandr Tymoshenko 	val = sc->dest;
7162f99b597SOleksandr Tymoshenko 	err = sysctl_handle_int(oidp, &val, 0, req);
7172f99b597SOleksandr Tymoshenko 	if (err || !req->newptr) /* error || read request */
7182f99b597SOleksandr Tymoshenko 		return (err);
7192f99b597SOleksandr Tymoshenko 
7202f99b597SOleksandr Tymoshenko 	if ((val < 0) || (val > 2))
7212f99b597SOleksandr Tymoshenko 		return (EINVAL);
7222f99b597SOleksandr Tymoshenko 
7232f99b597SOleksandr Tymoshenko 	sc->dest = val;
7242f99b597SOleksandr Tymoshenko 	device_printf(sc->dev, "destination set to %s\n", dest_description(val));
7252f99b597SOleksandr Tymoshenko 	bcm2835_audio_update_controls(sc);
7262f99b597SOleksandr Tymoshenko 
7272f99b597SOleksandr Tymoshenko 	return (0);
7282f99b597SOleksandr Tymoshenko }
7292f99b597SOleksandr Tymoshenko 
7302f99b597SOleksandr Tymoshenko static void
7312f99b597SOleksandr Tymoshenko vchi_audio_sysctl_init(struct bcm2835_audio_info *sc)
7322f99b597SOleksandr Tymoshenko {
7332f99b597SOleksandr Tymoshenko 	struct sysctl_ctx_list *ctx;
7342f99b597SOleksandr Tymoshenko 	struct sysctl_oid *tree_node;
7352f99b597SOleksandr Tymoshenko 	struct sysctl_oid_list *tree;
7362f99b597SOleksandr Tymoshenko 
7372f99b597SOleksandr Tymoshenko 	/*
7382f99b597SOleksandr Tymoshenko 	 * Add system sysctl tree/handlers.
7392f99b597SOleksandr Tymoshenko 	 */
7402f99b597SOleksandr Tymoshenko 	ctx = device_get_sysctl_ctx(sc->dev);
7412f99b597SOleksandr Tymoshenko 	tree_node = device_get_sysctl_tree(sc->dev);
7422f99b597SOleksandr Tymoshenko 	tree = SYSCTL_CHILDREN(tree_node);
7432f99b597SOleksandr Tymoshenko 	SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "dest",
7442f99b597SOleksandr Tymoshenko 	    CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc),
7452f99b597SOleksandr Tymoshenko 	    sysctl_bcm2835_audio_dest, "IU", "audio destination, "
7462f99b597SOleksandr Tymoshenko 	    "0 - auto, 1 - headphones, 2 - HDMI");
7472f99b597SOleksandr Tymoshenko }
7482f99b597SOleksandr Tymoshenko 
7492f99b597SOleksandr Tymoshenko static void
7502f99b597SOleksandr Tymoshenko bcm2835_audio_identify(driver_t *driver, device_t parent)
7512f99b597SOleksandr Tymoshenko {
7522f99b597SOleksandr Tymoshenko 
7532f99b597SOleksandr Tymoshenko 	BUS_ADD_CHILD(parent, 0, "pcm", 0);
7542f99b597SOleksandr Tymoshenko }
7552f99b597SOleksandr Tymoshenko 
7562f99b597SOleksandr Tymoshenko static int
7572f99b597SOleksandr Tymoshenko bcm2835_audio_probe(device_t dev)
7582f99b597SOleksandr Tymoshenko {
7592f99b597SOleksandr Tymoshenko 
7602f99b597SOleksandr Tymoshenko 	device_set_desc(dev, "VCHQI audio");
7612f99b597SOleksandr Tymoshenko 	return (BUS_PROBE_DEFAULT);
7622f99b597SOleksandr Tymoshenko }
7632f99b597SOleksandr Tymoshenko 
7642f99b597SOleksandr Tymoshenko 
7652f99b597SOleksandr Tymoshenko static void
7662f99b597SOleksandr Tymoshenko bcm2835_audio_delayed_init(void *xsc)
7672f99b597SOleksandr Tymoshenko {
7682f99b597SOleksandr Tymoshenko     	struct bcm2835_audio_info *sc;
7692f99b597SOleksandr Tymoshenko     	char status[SND_STATUSLEN];
7702f99b597SOleksandr Tymoshenko 
7712f99b597SOleksandr Tymoshenko 	sc = xsc;
7722f99b597SOleksandr Tymoshenko 
7732f99b597SOleksandr Tymoshenko 	config_intrhook_disestablish(&sc->intr_hook);
7742f99b597SOleksandr Tymoshenko 
7752f99b597SOleksandr Tymoshenko 	bcm2835_audio_init(sc);
7762f99b597SOleksandr Tymoshenko 	bcm2835_audio_open(sc);
7772f99b597SOleksandr Tymoshenko 	sc->volume = 75;
7782f99b597SOleksandr Tymoshenko 	sc->dest = DEST_AUTO;
7792f99b597SOleksandr Tymoshenko 
7802f99b597SOleksandr Tymoshenko     	if (mixer_init(sc->dev, &bcmmixer_class, sc)) {
7812f99b597SOleksandr Tymoshenko 		device_printf(sc->dev, "mixer_init failed\n");
7822f99b597SOleksandr Tymoshenko 		goto no;
7832f99b597SOleksandr Tymoshenko 	}
7842f99b597SOleksandr Tymoshenko 
7852f99b597SOleksandr Tymoshenko     	if (pcm_register(sc->dev, sc, 1, 1)) {
7862f99b597SOleksandr Tymoshenko 		device_printf(sc->dev, "pcm_register failed\n");
7872f99b597SOleksandr Tymoshenko 		goto no;
7882f99b597SOleksandr Tymoshenko 	}
7892f99b597SOleksandr Tymoshenko 
7902f99b597SOleksandr Tymoshenko 	pcm_addchan(sc->dev, PCMDIR_PLAY, &bcmchan_class, sc);
7912f99b597SOleksandr Tymoshenko     	snprintf(status, SND_STATUSLEN, "at VCHIQ");
7922f99b597SOleksandr Tymoshenko 	pcm_setstatus(sc->dev, status);
7932f99b597SOleksandr Tymoshenko 
7942f99b597SOleksandr Tymoshenko 	bcm2835_audio_reset_channel(&sc->pch);
7952f99b597SOleksandr Tymoshenko 	bcm2835_audio_create_worker(sc);
7962f99b597SOleksandr Tymoshenko 
7972f99b597SOleksandr Tymoshenko 	vchi_audio_sysctl_init(sc);
7982f99b597SOleksandr Tymoshenko 
7992f99b597SOleksandr Tymoshenko no:
8002f99b597SOleksandr Tymoshenko 	;
8012f99b597SOleksandr Tymoshenko }
8022f99b597SOleksandr Tymoshenko 
8032f99b597SOleksandr Tymoshenko static int
8042f99b597SOleksandr Tymoshenko bcm2835_audio_attach(device_t dev)
8052f99b597SOleksandr Tymoshenko {
8062f99b597SOleksandr Tymoshenko     	struct bcm2835_audio_info *sc;
8072f99b597SOleksandr Tymoshenko 
8082f99b597SOleksandr Tymoshenko 	sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
8092f99b597SOleksandr Tymoshenko 
8102f99b597SOleksandr Tymoshenko 	sc->dev = dev;
8112f99b597SOleksandr Tymoshenko 	sc->bufsz = VCHIQ_AUDIO_BUFFER_SIZE;
8122f99b597SOleksandr Tymoshenko 
8132f99b597SOleksandr Tymoshenko 	sc->lock = snd_mtxcreate(device_get_nameunit(dev), "bcm2835_audio softc");
8142f99b597SOleksandr Tymoshenko 
8152f99b597SOleksandr Tymoshenko 	mtx_init(&sc->vchi_lock, "bcm2835_audio", "vchi_lock", MTX_DEF);
8162f99b597SOleksandr Tymoshenko 	mtx_init(&sc->data_lock, "data_mtx", "data_mtx", MTX_DEF);
8172f99b597SOleksandr Tymoshenko 	cv_init(&sc->data_cv, "data_cv");
8182f99b597SOleksandr Tymoshenko 	sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
8192f99b597SOleksandr Tymoshenko 
8202f99b597SOleksandr Tymoshenko 	/*
8212f99b597SOleksandr Tymoshenko 	 * We need interrupts enabled for VCHI to work properly,
8222f99b597SOleksandr Tymoshenko 	 * so delay intialization until it happens
8232f99b597SOleksandr Tymoshenko 	 */
8242f99b597SOleksandr Tymoshenko 	sc->intr_hook.ich_func = bcm2835_audio_delayed_init;
8252f99b597SOleksandr Tymoshenko 	sc->intr_hook.ich_arg = sc;
8262f99b597SOleksandr Tymoshenko 
8272f99b597SOleksandr Tymoshenko 	if (config_intrhook_establish(&sc->intr_hook) != 0)
8282f99b597SOleksandr Tymoshenko 		goto no;
8292f99b597SOleksandr Tymoshenko 
8302f99b597SOleksandr Tymoshenko     	return 0;
8312f99b597SOleksandr Tymoshenko 
8322f99b597SOleksandr Tymoshenko no:
8332f99b597SOleksandr Tymoshenko     	return ENXIO;
8342f99b597SOleksandr Tymoshenko }
8352f99b597SOleksandr Tymoshenko 
8362f99b597SOleksandr Tymoshenko static int
8372f99b597SOleksandr Tymoshenko bcm2835_audio_detach(device_t dev)
8382f99b597SOleksandr Tymoshenko {
8392f99b597SOleksandr Tymoshenko 	int r;
8402f99b597SOleksandr Tymoshenko 	struct bcm2835_audio_info *sc;
8412f99b597SOleksandr Tymoshenko 	sc = pcm_getdevinfo(dev);
8422f99b597SOleksandr Tymoshenko 
8432f99b597SOleksandr Tymoshenko 	/* Stop worker thread */
8442f99b597SOleksandr Tymoshenko 	sc->unloading = 1;
8452f99b597SOleksandr Tymoshenko 	cv_signal(&sc->data_cv);
8462f99b597SOleksandr Tymoshenko 
8472f99b597SOleksandr Tymoshenko 	r = pcm_unregister(dev);
8482f99b597SOleksandr Tymoshenko 	if (r)
8492f99b597SOleksandr Tymoshenko 		return r;
8502f99b597SOleksandr Tymoshenko 
8512f99b597SOleksandr Tymoshenko 	mtx_destroy(&sc->vchi_lock);
8522f99b597SOleksandr Tymoshenko 	mtx_destroy(&sc->data_lock);
8532f99b597SOleksandr Tymoshenko 	cv_destroy(&sc->data_cv);
8542f99b597SOleksandr Tymoshenko 
8552f99b597SOleksandr Tymoshenko 	bcm2835_audio_release(sc);
8562f99b597SOleksandr Tymoshenko 
8572f99b597SOleksandr Tymoshenko 	if (sc->lock) {
8582f99b597SOleksandr Tymoshenko 		snd_mtxfree(sc->lock);
8592f99b597SOleksandr Tymoshenko 		sc->lock = NULL;
8602f99b597SOleksandr Tymoshenko 	}
8612f99b597SOleksandr Tymoshenko 
8622f99b597SOleksandr Tymoshenko     	free(sc, M_DEVBUF);
8632f99b597SOleksandr Tymoshenko 
8642f99b597SOleksandr Tymoshenko 	return 0;
8652f99b597SOleksandr Tymoshenko }
8662f99b597SOleksandr Tymoshenko 
8672f99b597SOleksandr Tymoshenko static device_method_t bcm2835_audio_methods[] = {
8682f99b597SOleksandr Tymoshenko 	/* Device interface */
8692f99b597SOleksandr Tymoshenko 	DEVMETHOD(device_identify,	bcm2835_audio_identify),
8702f99b597SOleksandr Tymoshenko 	DEVMETHOD(device_probe,		bcm2835_audio_probe),
8712f99b597SOleksandr Tymoshenko 	DEVMETHOD(device_attach,	bcm2835_audio_attach),
8722f99b597SOleksandr Tymoshenko 	DEVMETHOD(device_detach,	bcm2835_audio_detach),
8732f99b597SOleksandr Tymoshenko 
8742f99b597SOleksandr Tymoshenko 	{ 0, 0 }
8752f99b597SOleksandr Tymoshenko };
8762f99b597SOleksandr Tymoshenko 
8772f99b597SOleksandr Tymoshenko static driver_t bcm2835_audio_driver = {
8782f99b597SOleksandr Tymoshenko 	"pcm",
8792f99b597SOleksandr Tymoshenko 	bcm2835_audio_methods,
8802f99b597SOleksandr Tymoshenko 	PCM_SOFTC_SIZE,
8812f99b597SOleksandr Tymoshenko };
8822f99b597SOleksandr Tymoshenko 
8832f99b597SOleksandr Tymoshenko DRIVER_MODULE(bcm2835_audio, vchiq, bcm2835_audio_driver, pcm_devclass, 0, 0);
8842f99b597SOleksandr Tymoshenko MODULE_DEPEND(bcm2835_audio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
8852f99b597SOleksandr Tymoshenko MODULE_DEPEND(bcm2835_audio, vchiq, 1, 1, 1);
8862f99b597SOleksandr Tymoshenko MODULE_VERSION(bcm2835_audio, 1);
887