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
322f99b597SOleksandr Tymoshenko #include "mixer_if.h"
332f99b597SOleksandr Tymoshenko
342f99b597SOleksandr Tymoshenko #include "interface/compat/vchi_bsd.h"
352f99b597SOleksandr Tymoshenko #include "interface/vchi/vchi.h"
362f99b597SOleksandr Tymoshenko #include "interface/vchiq_arm/vchiq.h"
372f99b597SOleksandr Tymoshenko
382f99b597SOleksandr Tymoshenko #include "vc_vchi_audioserv_defs.h"
392f99b597SOleksandr Tymoshenko
403a48aebfSOleksandr Tymoshenko /* Audio destination */
412f99b597SOleksandr Tymoshenko #define DEST_AUTO 0
422f99b597SOleksandr Tymoshenko #define DEST_HEADPHONES 1
432f99b597SOleksandr Tymoshenko #define DEST_HDMI 2
442f99b597SOleksandr Tymoshenko
453a48aebfSOleksandr Tymoshenko /* Playback state */
463a48aebfSOleksandr Tymoshenko #define PLAYBACK_IDLE 0
473a48aebfSOleksandr Tymoshenko #define PLAYBACK_PLAYING 1
483a48aebfSOleksandr Tymoshenko #define PLAYBACK_STOPPING 2
493a48aebfSOleksandr Tymoshenko
503a48aebfSOleksandr Tymoshenko /* Worker thread state */
513a48aebfSOleksandr Tymoshenko #define WORKER_RUNNING 0
523a48aebfSOleksandr Tymoshenko #define WORKER_STOPPING 1
533a48aebfSOleksandr Tymoshenko #define WORKER_STOPPED 2
543a48aebfSOleksandr Tymoshenko
553a48aebfSOleksandr Tymoshenko /*
563a48aebfSOleksandr Tymoshenko * Worker thread flags, set to 1 in flags_pending
573a48aebfSOleksandr Tymoshenko * when driver requests one or another operation
583a48aebfSOleksandr Tymoshenko * from worker. Cleared to 0 once worker performs
593a48aebfSOleksandr Tymoshenko * the operations.
603a48aebfSOleksandr Tymoshenko */
613a48aebfSOleksandr Tymoshenko #define AUDIO_PARAMS (1 << 0)
623a48aebfSOleksandr Tymoshenko #define AUDIO_PLAY (1 << 1)
633a48aebfSOleksandr Tymoshenko #define AUDIO_STOP (1 << 2)
643a48aebfSOleksandr Tymoshenko
652f99b597SOleksandr Tymoshenko #define VCHIQ_AUDIO_PACKET_SIZE 4000
663a48aebfSOleksandr Tymoshenko #define VCHIQ_AUDIO_BUFFER_SIZE 10*VCHIQ_AUDIO_PACKET_SIZE
672f99b597SOleksandr Tymoshenko
682f99b597SOleksandr Tymoshenko #define VCHIQ_AUDIO_MAX_VOLUME
692f99b597SOleksandr Tymoshenko /* volume in terms of 0.01dB */
702f99b597SOleksandr Tymoshenko #define VCHIQ_AUDIO_VOLUME_MIN -10239
712f99b597SOleksandr Tymoshenko #define VCHIQ_AUDIO_VOLUME(db100) (uint32_t)(-((db100) << 8)/100)
722f99b597SOleksandr Tymoshenko
732f99b597SOleksandr Tymoshenko /* dB levels with 5% volume step */
742f99b597SOleksandr Tymoshenko static int db_levels[] = {
752f99b597SOleksandr Tymoshenko VCHIQ_AUDIO_VOLUME_MIN, -4605, -3794, -3218, -2772,
762f99b597SOleksandr Tymoshenko -2407, -2099, -1832, -1597, -1386,
772f99b597SOleksandr Tymoshenko -1195, -1021, -861, -713, -575,
782f99b597SOleksandr Tymoshenko -446, -325, -210, -102, 0,
792f99b597SOleksandr Tymoshenko };
802f99b597SOleksandr Tymoshenko
812f99b597SOleksandr Tymoshenko static uint32_t bcm2835_audio_playfmt[] = {
822f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_U8, 1, 0),
832f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_U8, 2, 0),
842f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_S8, 1, 0),
852f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_S8, 2, 0),
862f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_S16_LE, 1, 0),
872f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_S16_LE, 2, 0),
882f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_U16_LE, 1, 0),
892f99b597SOleksandr Tymoshenko SND_FORMAT(AFMT_U16_LE, 2, 0),
902f99b597SOleksandr Tymoshenko 0
912f99b597SOleksandr Tymoshenko };
922f99b597SOleksandr Tymoshenko
932f99b597SOleksandr Tymoshenko static struct pcmchan_caps bcm2835_audio_playcaps = {8000, 48000, bcm2835_audio_playfmt, 0};
942f99b597SOleksandr Tymoshenko
952f99b597SOleksandr Tymoshenko struct bcm2835_audio_info;
962f99b597SOleksandr Tymoshenko
972f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo {
982f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *parent;
992f99b597SOleksandr Tymoshenko struct pcm_channel *channel;
1002f99b597SOleksandr Tymoshenko struct snd_dbuf *buffer;
1012f99b597SOleksandr Tymoshenko uint32_t fmt, spd, blksz;
1022f99b597SOleksandr Tymoshenko
1033a48aebfSOleksandr Tymoshenko /* Pointer to first unsubmitted sample */
1043a48aebfSOleksandr Tymoshenko uint32_t unsubmittedptr;
1053a48aebfSOleksandr Tymoshenko /*
1063a48aebfSOleksandr Tymoshenko * Number of bytes in "submitted but not played"
1073a48aebfSOleksandr Tymoshenko * pseudo-buffer
1083a48aebfSOleksandr Tymoshenko */
1093a48aebfSOleksandr Tymoshenko int available_space;
1102f99b597SOleksandr Tymoshenko int playback_state;
1113a48aebfSOleksandr Tymoshenko uint64_t callbacks;
1123a48aebfSOleksandr Tymoshenko uint64_t submitted_samples;
1133a48aebfSOleksandr Tymoshenko uint64_t retrieved_samples;
1143a48aebfSOleksandr Tymoshenko uint64_t underruns;
1153a48aebfSOleksandr Tymoshenko int starved;
1162f99b597SOleksandr Tymoshenko };
1172f99b597SOleksandr Tymoshenko
1182f99b597SOleksandr Tymoshenko struct bcm2835_audio_info {
1192f99b597SOleksandr Tymoshenko device_t dev;
1202f99b597SOleksandr Tymoshenko unsigned int bufsz;
1212f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo pch;
1222f99b597SOleksandr Tymoshenko uint32_t dest, volume;
1232f99b597SOleksandr Tymoshenko struct intr_config_hook intr_hook;
1242f99b597SOleksandr Tymoshenko
1252f99b597SOleksandr Tymoshenko /* VCHI data */
1262f99b597SOleksandr Tymoshenko VCHI_INSTANCE_T vchi_instance;
1272f99b597SOleksandr Tymoshenko VCHI_CONNECTION_T *vchi_connection;
1282f99b597SOleksandr Tymoshenko VCHI_SERVICE_HANDLE_T vchi_handle;
1292f99b597SOleksandr Tymoshenko
1303a48aebfSOleksandr Tymoshenko struct mtx lock;
13169cab2d1SOleksandr Tymoshenko struct cv worker_cv;
13269cab2d1SOleksandr Tymoshenko
1333a48aebfSOleksandr Tymoshenko uint32_t flags_pending;
1342f99b597SOleksandr Tymoshenko
1353a48aebfSOleksandr Tymoshenko /* Worker thread state */
1363a48aebfSOleksandr Tymoshenko int worker_state;
1372f99b597SOleksandr Tymoshenko };
1382f99b597SOleksandr Tymoshenko
1393a48aebfSOleksandr Tymoshenko #define BCM2835_AUDIO_LOCK(sc) mtx_lock(&(sc)->lock)
1403a48aebfSOleksandr Tymoshenko #define BCM2835_AUDIO_LOCKED(sc) mtx_assert(&(sc)->lock, MA_OWNED)
1413a48aebfSOleksandr Tymoshenko #define BCM2835_AUDIO_UNLOCK(sc) mtx_unlock(&(sc)->lock)
1422f99b597SOleksandr Tymoshenko
1432f99b597SOleksandr Tymoshenko static const char *
dest_description(uint32_t dest)1442f99b597SOleksandr Tymoshenko dest_description(uint32_t dest)
1452f99b597SOleksandr Tymoshenko {
1462f99b597SOleksandr Tymoshenko switch (dest) {
1472f99b597SOleksandr Tymoshenko case DEST_AUTO:
1482f99b597SOleksandr Tymoshenko return "AUTO";
1492f99b597SOleksandr Tymoshenko break;
1502f99b597SOleksandr Tymoshenko
1512f99b597SOleksandr Tymoshenko case DEST_HEADPHONES:
1522f99b597SOleksandr Tymoshenko return "HEADPHONES";
1532f99b597SOleksandr Tymoshenko break;
1542f99b597SOleksandr Tymoshenko
1552f99b597SOleksandr Tymoshenko case DEST_HDMI:
1562f99b597SOleksandr Tymoshenko return "HDMI";
1572f99b597SOleksandr Tymoshenko break;
1582f99b597SOleksandr Tymoshenko default:
1592f99b597SOleksandr Tymoshenko return "UNKNOWN";
1602f99b597SOleksandr Tymoshenko break;
1612f99b597SOleksandr Tymoshenko }
1622f99b597SOleksandr Tymoshenko }
1632f99b597SOleksandr Tymoshenko
1642f99b597SOleksandr Tymoshenko static void
bcm2835_worker_update_params(struct bcm2835_audio_info * sc)1653a48aebfSOleksandr Tymoshenko bcm2835_worker_update_params(struct bcm2835_audio_info *sc)
1663a48aebfSOleksandr Tymoshenko {
1673a48aebfSOleksandr Tymoshenko
1683a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCKED(sc);
1693a48aebfSOleksandr Tymoshenko
1703a48aebfSOleksandr Tymoshenko sc->flags_pending |= AUDIO_PARAMS;
1713a48aebfSOleksandr Tymoshenko cv_signal(&sc->worker_cv);
1723a48aebfSOleksandr Tymoshenko }
1733a48aebfSOleksandr Tymoshenko
1743a48aebfSOleksandr Tymoshenko static void
bcm2835_worker_play_start(struct bcm2835_audio_info * sc)1753a48aebfSOleksandr Tymoshenko bcm2835_worker_play_start(struct bcm2835_audio_info *sc)
1763a48aebfSOleksandr Tymoshenko {
1773a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
1783a48aebfSOleksandr Tymoshenko sc->flags_pending &= ~(AUDIO_STOP);
1793a48aebfSOleksandr Tymoshenko sc->flags_pending |= AUDIO_PLAY;
1803a48aebfSOleksandr Tymoshenko cv_signal(&sc->worker_cv);
1813a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
1823a48aebfSOleksandr Tymoshenko }
1833a48aebfSOleksandr Tymoshenko
1843a48aebfSOleksandr Tymoshenko static void
bcm2835_worker_play_stop(struct bcm2835_audio_info * sc)1853a48aebfSOleksandr Tymoshenko bcm2835_worker_play_stop(struct bcm2835_audio_info *sc)
1863a48aebfSOleksandr Tymoshenko {
1873a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
1883a48aebfSOleksandr Tymoshenko sc->flags_pending &= ~(AUDIO_PLAY);
1893a48aebfSOleksandr Tymoshenko sc->flags_pending |= AUDIO_STOP;
1903a48aebfSOleksandr Tymoshenko cv_signal(&sc->worker_cv);
1913a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
1923a48aebfSOleksandr Tymoshenko }
1933a48aebfSOleksandr Tymoshenko
1943a48aebfSOleksandr Tymoshenko static void
bcm2835_audio_callback(void * param,const VCHI_CALLBACK_REASON_T reason,void * msg_handle)1952f99b597SOleksandr Tymoshenko bcm2835_audio_callback(void *param, const VCHI_CALLBACK_REASON_T reason, void *msg_handle)
1962f99b597SOleksandr Tymoshenko {
1972f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)param;
1982f99b597SOleksandr Tymoshenko int32_t status;
1992f99b597SOleksandr Tymoshenko uint32_t msg_len;
2002f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
2012f99b597SOleksandr Tymoshenko
2022f99b597SOleksandr Tymoshenko if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
2032f99b597SOleksandr Tymoshenko return;
2042f99b597SOleksandr Tymoshenko
2052f99b597SOleksandr Tymoshenko status = vchi_msg_dequeue(sc->vchi_handle,
2062f99b597SOleksandr Tymoshenko &m, sizeof m, &msg_len, VCHI_FLAGS_NONE);
2074f6be8c7SJohn Baldwin if (status != 0)
2084f6be8c7SJohn Baldwin return;
2092f99b597SOleksandr Tymoshenko if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
210ebb62474SOleksandr Tymoshenko if (m.u.result.success) {
211ebb62474SOleksandr Tymoshenko device_printf(sc->dev,
212ebb62474SOleksandr Tymoshenko "msg type %08x failed\n",
213ebb62474SOleksandr Tymoshenko m.type);
214ebb62474SOleksandr Tymoshenko }
2152f99b597SOleksandr Tymoshenko } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
2162f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = m.u.complete.cookie;
2172f99b597SOleksandr Tymoshenko
2182f99b597SOleksandr Tymoshenko int count = m.u.complete.count & 0xffff;
2192f99b597SOleksandr Tymoshenko int perr = (m.u.complete.count & (1U << 30)) != 0;
2203a48aebfSOleksandr Tymoshenko ch->callbacks++;
2213a48aebfSOleksandr Tymoshenko if (perr)
2223a48aebfSOleksandr Tymoshenko ch->underruns++;
2232f99b597SOleksandr Tymoshenko
2243a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
2253a48aebfSOleksandr Tymoshenko if (ch->playback_state != PLAYBACK_IDLE) {
2263a48aebfSOleksandr Tymoshenko /* Prevent LOR */
2273a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
2282c0b1f4fSOleksandr Tymoshenko chn_intr(sc->pch.channel);
2293a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
2303a48aebfSOleksandr Tymoshenko }
2313a48aebfSOleksandr Tymoshenko /* We should check again, state might have changed */
2323a48aebfSOleksandr Tymoshenko if (ch->playback_state != PLAYBACK_IDLE) {
2333a48aebfSOleksandr Tymoshenko if (!perr) {
2343a48aebfSOleksandr Tymoshenko if ((ch->available_space + count)> VCHIQ_AUDIO_BUFFER_SIZE) {
2353a48aebfSOleksandr Tymoshenko device_printf(sc->dev, "inconsistent data in callback:\n");
2363a48aebfSOleksandr Tymoshenko device_printf(sc->dev, "available_space == %d, count = %d, perr=%d\n",
2373a48aebfSOleksandr Tymoshenko ch->available_space, count, perr);
2383a48aebfSOleksandr Tymoshenko device_printf(sc->dev,
2393a48aebfSOleksandr Tymoshenko "retrieved_samples = %lld, submitted_samples = %lld\n",
2403a48aebfSOleksandr Tymoshenko ch->retrieved_samples, ch->submitted_samples);
2413a48aebfSOleksandr Tymoshenko }
2423a48aebfSOleksandr Tymoshenko ch->available_space += count;
2433a48aebfSOleksandr Tymoshenko ch->retrieved_samples += count;
2443a48aebfSOleksandr Tymoshenko }
2453a48aebfSOleksandr Tymoshenko if (perr || (ch->available_space >= VCHIQ_AUDIO_PACKET_SIZE))
24669cab2d1SOleksandr Tymoshenko cv_signal(&sc->worker_cv);
2473a48aebfSOleksandr Tymoshenko }
2483a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
2492f99b597SOleksandr Tymoshenko } else
2502f99b597SOleksandr Tymoshenko printf("%s: unknown m.type: %d\n", __func__, m.type);
2512f99b597SOleksandr Tymoshenko }
2522f99b597SOleksandr Tymoshenko
2532f99b597SOleksandr Tymoshenko /* VCHIQ stuff */
2542f99b597SOleksandr Tymoshenko static void
bcm2835_audio_init(struct bcm2835_audio_info * sc)2552f99b597SOleksandr Tymoshenko bcm2835_audio_init(struct bcm2835_audio_info *sc)
2562f99b597SOleksandr Tymoshenko {
2572f99b597SOleksandr Tymoshenko int status;
2582f99b597SOleksandr Tymoshenko
2592f99b597SOleksandr Tymoshenko /* Initialize and create a VCHI connection */
2602f99b597SOleksandr Tymoshenko status = vchi_initialise(&sc->vchi_instance);
2612f99b597SOleksandr Tymoshenko if (status != 0) {
2622f99b597SOleksandr Tymoshenko printf("vchi_initialise failed: %d\n", status);
2632f99b597SOleksandr Tymoshenko return;
2642f99b597SOleksandr Tymoshenko }
2652f99b597SOleksandr Tymoshenko
2662f99b597SOleksandr Tymoshenko status = vchi_connect(NULL, 0, sc->vchi_instance);
2672f99b597SOleksandr Tymoshenko if (status != 0) {
2682f99b597SOleksandr Tymoshenko printf("vchi_connect failed: %d\n", status);
2692f99b597SOleksandr Tymoshenko return;
2702f99b597SOleksandr Tymoshenko }
2712f99b597SOleksandr Tymoshenko
2722f99b597SOleksandr Tymoshenko SERVICE_CREATION_T params = {
2732f99b597SOleksandr Tymoshenko VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
2742f99b597SOleksandr Tymoshenko VC_AUDIO_SERVER_NAME, /* 4cc service code */
2752f99b597SOleksandr Tymoshenko sc->vchi_connection, /* passed in fn pointers */
2762f99b597SOleksandr Tymoshenko 0, /* rx fifo size */
2772f99b597SOleksandr Tymoshenko 0, /* tx fifo size */
2782f99b597SOleksandr Tymoshenko bcm2835_audio_callback, /* service callback */
2792f99b597SOleksandr Tymoshenko sc, /* service callback parameter */
2802f99b597SOleksandr Tymoshenko 1,
2812f99b597SOleksandr Tymoshenko 1,
2822f99b597SOleksandr Tymoshenko 0 /* want crc check on bulk transfers */
2832f99b597SOleksandr Tymoshenko };
2842f99b597SOleksandr Tymoshenko
2852f99b597SOleksandr Tymoshenko status = vchi_service_open(sc->vchi_instance, ¶ms,
2862f99b597SOleksandr Tymoshenko &sc->vchi_handle);
2872f99b597SOleksandr Tymoshenko
2883a48aebfSOleksandr Tymoshenko if (status != 0)
2892f99b597SOleksandr Tymoshenko sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
2902f99b597SOleksandr Tymoshenko }
2912f99b597SOleksandr Tymoshenko
2922f99b597SOleksandr Tymoshenko static void
bcm2835_audio_release(struct bcm2835_audio_info * sc)2932f99b597SOleksandr Tymoshenko bcm2835_audio_release(struct bcm2835_audio_info *sc)
2942f99b597SOleksandr Tymoshenko {
2952f99b597SOleksandr Tymoshenko int success;
2962f99b597SOleksandr Tymoshenko
2972f99b597SOleksandr Tymoshenko if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
2982f99b597SOleksandr Tymoshenko success = vchi_service_close(sc->vchi_handle);
2992f99b597SOleksandr Tymoshenko if (success != 0)
3002f99b597SOleksandr Tymoshenko printf("vchi_service_close failed: %d\n", success);
3013a48aebfSOleksandr Tymoshenko vchi_service_release(sc->vchi_handle);
3022f99b597SOleksandr Tymoshenko sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
3032f99b597SOleksandr Tymoshenko }
3042f99b597SOleksandr Tymoshenko
3052f99b597SOleksandr Tymoshenko vchi_disconnect(sc->vchi_instance);
3062f99b597SOleksandr Tymoshenko }
3072f99b597SOleksandr Tymoshenko
3082f99b597SOleksandr Tymoshenko static void
bcm2835_audio_reset_channel(struct bcm2835_audio_chinfo * ch)3092f99b597SOleksandr Tymoshenko bcm2835_audio_reset_channel(struct bcm2835_audio_chinfo *ch)
3102f99b597SOleksandr Tymoshenko {
3112f99b597SOleksandr Tymoshenko
3123a48aebfSOleksandr Tymoshenko ch->available_space = VCHIQ_AUDIO_BUFFER_SIZE;
3133a48aebfSOleksandr Tymoshenko ch->unsubmittedptr = 0;
3142f99b597SOleksandr Tymoshenko sndbuf_reset(ch->buffer);
3152f99b597SOleksandr Tymoshenko }
3162f99b597SOleksandr Tymoshenko
3172f99b597SOleksandr Tymoshenko static void
bcm2835_audio_start(struct bcm2835_audio_chinfo * ch)3182f99b597SOleksandr Tymoshenko bcm2835_audio_start(struct bcm2835_audio_chinfo *ch)
3192f99b597SOleksandr Tymoshenko {
3202f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
3212f99b597SOleksandr Tymoshenko int ret;
3222f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
3232f99b597SOleksandr Tymoshenko
3242f99b597SOleksandr Tymoshenko if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3252f99b597SOleksandr Tymoshenko m.type = VC_AUDIO_MSG_TYPE_START;
3262f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
3272f99b597SOleksandr Tymoshenko &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3282f99b597SOleksandr Tymoshenko
3292f99b597SOleksandr Tymoshenko if (ret != 0)
3302f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3312f99b597SOleksandr Tymoshenko }
3322f99b597SOleksandr Tymoshenko }
3332f99b597SOleksandr Tymoshenko
3342f99b597SOleksandr Tymoshenko static void
bcm2835_audio_stop(struct bcm2835_audio_chinfo * ch)3352f99b597SOleksandr Tymoshenko bcm2835_audio_stop(struct bcm2835_audio_chinfo *ch)
3362f99b597SOleksandr Tymoshenko {
3372f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
3382f99b597SOleksandr Tymoshenko int ret;
3392f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
3402f99b597SOleksandr Tymoshenko
3412f99b597SOleksandr Tymoshenko if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3422f99b597SOleksandr Tymoshenko m.type = VC_AUDIO_MSG_TYPE_STOP;
3432f99b597SOleksandr Tymoshenko m.u.stop.draining = 0;
3442f99b597SOleksandr Tymoshenko
3452f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
3462f99b597SOleksandr Tymoshenko &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3472f99b597SOleksandr Tymoshenko
3482f99b597SOleksandr Tymoshenko if (ret != 0)
3492f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3502f99b597SOleksandr Tymoshenko }
3512f99b597SOleksandr Tymoshenko }
3522f99b597SOleksandr Tymoshenko
3532f99b597SOleksandr Tymoshenko static void
bcm2835_audio_open(struct bcm2835_audio_info * sc)3542f99b597SOleksandr Tymoshenko bcm2835_audio_open(struct bcm2835_audio_info *sc)
3552f99b597SOleksandr Tymoshenko {
3562f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
3572f99b597SOleksandr Tymoshenko int ret;
3582f99b597SOleksandr Tymoshenko
3592f99b597SOleksandr Tymoshenko if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3602f99b597SOleksandr Tymoshenko m.type = VC_AUDIO_MSG_TYPE_OPEN;
3612f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
3622f99b597SOleksandr Tymoshenko &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3632f99b597SOleksandr Tymoshenko
3642f99b597SOleksandr Tymoshenko if (ret != 0)
3652f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3662f99b597SOleksandr Tymoshenko }
3672f99b597SOleksandr Tymoshenko }
3682f99b597SOleksandr Tymoshenko
3692f99b597SOleksandr Tymoshenko static void
bcm2835_audio_update_controls(struct bcm2835_audio_info * sc,uint32_t volume,uint32_t dest)37069cab2d1SOleksandr Tymoshenko bcm2835_audio_update_controls(struct bcm2835_audio_info *sc, uint32_t volume, uint32_t dest)
3712f99b597SOleksandr Tymoshenko {
3722f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
3732f99b597SOleksandr Tymoshenko int ret, db;
3742f99b597SOleksandr Tymoshenko
3752f99b597SOleksandr Tymoshenko if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3762f99b597SOleksandr Tymoshenko m.type = VC_AUDIO_MSG_TYPE_CONTROL;
37769cab2d1SOleksandr Tymoshenko m.u.control.dest = dest;
37869cab2d1SOleksandr Tymoshenko if (volume > 99)
37969cab2d1SOleksandr Tymoshenko volume = 99;
38069cab2d1SOleksandr Tymoshenko db = db_levels[volume/5];
3812f99b597SOleksandr Tymoshenko m.u.control.volume = VCHIQ_AUDIO_VOLUME(db);
3822f99b597SOleksandr Tymoshenko
3832f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
3842f99b597SOleksandr Tymoshenko &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
3852f99b597SOleksandr Tymoshenko
3862f99b597SOleksandr Tymoshenko if (ret != 0)
3872f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
3882f99b597SOleksandr Tymoshenko }
3892f99b597SOleksandr Tymoshenko }
3902f99b597SOleksandr Tymoshenko
3912f99b597SOleksandr Tymoshenko static void
bcm2835_audio_update_params(struct bcm2835_audio_info * sc,uint32_t fmt,uint32_t speed)39269cab2d1SOleksandr Tymoshenko bcm2835_audio_update_params(struct bcm2835_audio_info *sc, uint32_t fmt, uint32_t speed)
3932f99b597SOleksandr Tymoshenko {
3942f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
3952f99b597SOleksandr Tymoshenko int ret;
3962f99b597SOleksandr Tymoshenko
3972f99b597SOleksandr Tymoshenko if (sc->vchi_handle != VCHIQ_SERVICE_HANDLE_INVALID) {
3982f99b597SOleksandr Tymoshenko m.type = VC_AUDIO_MSG_TYPE_CONFIG;
39969cab2d1SOleksandr Tymoshenko m.u.config.channels = AFMT_CHANNEL(fmt);
40069cab2d1SOleksandr Tymoshenko m.u.config.samplerate = speed;
40169cab2d1SOleksandr Tymoshenko m.u.config.bps = AFMT_BIT(fmt);
4022f99b597SOleksandr Tymoshenko
4032f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
4042f99b597SOleksandr Tymoshenko &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
4052f99b597SOleksandr Tymoshenko
4062f99b597SOleksandr Tymoshenko if (ret != 0)
4072f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
4082f99b597SOleksandr Tymoshenko }
4092f99b597SOleksandr Tymoshenko }
4102f99b597SOleksandr Tymoshenko
4113a48aebfSOleksandr Tymoshenko static bool
bcm2835_audio_buffer_should_sleep(struct bcm2835_audio_chinfo * ch)4123a48aebfSOleksandr Tymoshenko bcm2835_audio_buffer_should_sleep(struct bcm2835_audio_chinfo *ch)
4132f99b597SOleksandr Tymoshenko {
4142f99b597SOleksandr Tymoshenko
4153a48aebfSOleksandr Tymoshenko if (ch->playback_state != PLAYBACK_PLAYING)
4163a48aebfSOleksandr Tymoshenko return (true);
4172f99b597SOleksandr Tymoshenko
4183a48aebfSOleksandr Tymoshenko /* Not enough data */
4193a48aebfSOleksandr Tymoshenko if (sndbuf_getready(ch->buffer) < VCHIQ_AUDIO_PACKET_SIZE) {
4203a48aebfSOleksandr Tymoshenko printf("starve\n");
4213a48aebfSOleksandr Tymoshenko ch->starved++;
4223a48aebfSOleksandr Tymoshenko return (true);
4232f99b597SOleksandr Tymoshenko }
4243a48aebfSOleksandr Tymoshenko
4253a48aebfSOleksandr Tymoshenko /* Not enough free space */
4263a48aebfSOleksandr Tymoshenko if (ch->available_space < VCHIQ_AUDIO_PACKET_SIZE) {
4273a48aebfSOleksandr Tymoshenko return (true);
4282f99b597SOleksandr Tymoshenko }
4293a48aebfSOleksandr Tymoshenko
4303a48aebfSOleksandr Tymoshenko return (false);
4312f99b597SOleksandr Tymoshenko }
4322f99b597SOleksandr Tymoshenko
4332f99b597SOleksandr Tymoshenko static void
bcm2835_audio_write_samples(struct bcm2835_audio_chinfo * ch,void * buf,uint32_t count)4343a48aebfSOleksandr Tymoshenko bcm2835_audio_write_samples(struct bcm2835_audio_chinfo *ch, void *buf, uint32_t count)
4352f99b597SOleksandr Tymoshenko {
4362f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
4372f99b597SOleksandr Tymoshenko VC_AUDIO_MSG_T m;
4382f99b597SOleksandr Tymoshenko int ret;
4392f99b597SOleksandr Tymoshenko
4402f99b597SOleksandr Tymoshenko if (sc->vchi_handle == VCHIQ_SERVICE_HANDLE_INVALID) {
4412f99b597SOleksandr Tymoshenko return;
4422f99b597SOleksandr Tymoshenko }
4432f99b597SOleksandr Tymoshenko
4442f99b597SOleksandr Tymoshenko m.type = VC_AUDIO_MSG_TYPE_WRITE;
4452f99b597SOleksandr Tymoshenko m.u.write.count = count;
4462f99b597SOleksandr Tymoshenko m.u.write.max_packet = VCHIQ_AUDIO_PACKET_SIZE;
4472f99b597SOleksandr Tymoshenko m.u.write.callback = NULL;
4482f99b597SOleksandr Tymoshenko m.u.write.cookie = ch;
4492f99b597SOleksandr Tymoshenko m.u.write.silence = 0;
4502f99b597SOleksandr Tymoshenko
4512f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
4522f99b597SOleksandr Tymoshenko &m, sizeof m, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
4532f99b597SOleksandr Tymoshenko
4542f99b597SOleksandr Tymoshenko if (ret != 0)
4552f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed (err %d)\n", __func__, ret);
4562f99b597SOleksandr Tymoshenko
4572f99b597SOleksandr Tymoshenko while (count > 0) {
4582f99b597SOleksandr Tymoshenko int bytes = MIN((int)m.u.write.max_packet, (int)count);
4592f99b597SOleksandr Tymoshenko ret = vchi_msg_queue(sc->vchi_handle,
4602f99b597SOleksandr Tymoshenko buf, bytes, VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL);
4612f99b597SOleksandr Tymoshenko if (ret != 0)
4622f99b597SOleksandr Tymoshenko printf("%s: vchi_msg_queue failed: %d\n",
4632f99b597SOleksandr Tymoshenko __func__, ret);
4642f99b597SOleksandr Tymoshenko buf = (char *)buf + bytes;
4652f99b597SOleksandr Tymoshenko count -= bytes;
4662f99b597SOleksandr Tymoshenko }
4672f99b597SOleksandr Tymoshenko }
4682f99b597SOleksandr Tymoshenko
4692f99b597SOleksandr Tymoshenko static void
bcm2835_audio_worker(void * data)4702f99b597SOleksandr Tymoshenko bcm2835_audio_worker(void *data)
4712f99b597SOleksandr Tymoshenko {
4722f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data;
4732f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = &sc->pch;
47469cab2d1SOleksandr Tymoshenko uint32_t speed, format;
47569cab2d1SOleksandr Tymoshenko uint32_t volume, dest;
4763a48aebfSOleksandr Tymoshenko uint32_t flags;
4773a48aebfSOleksandr Tymoshenko uint32_t count, size, readyptr;
4783a48aebfSOleksandr Tymoshenko uint8_t *buf;
47969cab2d1SOleksandr Tymoshenko
4803a48aebfSOleksandr Tymoshenko ch->playback_state = PLAYBACK_IDLE;
4813a48aebfSOleksandr Tymoshenko
4822f99b597SOleksandr Tymoshenko while (1) {
4833a48aebfSOleksandr Tymoshenko if (sc->worker_state != WORKER_RUNNING)
4842f99b597SOleksandr Tymoshenko break;
4852f99b597SOleksandr Tymoshenko
4863a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
4873a48aebfSOleksandr Tymoshenko /*
4883a48aebfSOleksandr Tymoshenko * wait until there are flags set or buffer is ready
4893a48aebfSOleksandr Tymoshenko * to consume more samples
4903a48aebfSOleksandr Tymoshenko */
4913a48aebfSOleksandr Tymoshenko while ((sc->flags_pending == 0) &&
4923a48aebfSOleksandr Tymoshenko bcm2835_audio_buffer_should_sleep(ch)) {
4933a48aebfSOleksandr Tymoshenko cv_wait_sig(&sc->worker_cv, &sc->lock);
4943a48aebfSOleksandr Tymoshenko }
4953a48aebfSOleksandr Tymoshenko flags = sc->flags_pending;
4963a48aebfSOleksandr Tymoshenko /* Clear pending flags */
4973a48aebfSOleksandr Tymoshenko sc->flags_pending = 0;
4983a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
4993a48aebfSOleksandr Tymoshenko
5003a48aebfSOleksandr Tymoshenko /* Requested to change parameters */
5013a48aebfSOleksandr Tymoshenko if (flags & AUDIO_PARAMS) {
5023a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
50369cab2d1SOleksandr Tymoshenko speed = ch->spd;
50469cab2d1SOleksandr Tymoshenko format = ch->fmt;
50569cab2d1SOleksandr Tymoshenko volume = sc->volume;
50669cab2d1SOleksandr Tymoshenko dest = sc->dest;
5073a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
5083a48aebfSOleksandr Tymoshenko if (ch->playback_state == PLAYBACK_IDLE)
50969cab2d1SOleksandr Tymoshenko bcm2835_audio_update_params(sc, format, speed);
51069cab2d1SOleksandr Tymoshenko bcm2835_audio_update_controls(sc, volume, dest);
51169cab2d1SOleksandr Tymoshenko }
51269cab2d1SOleksandr Tymoshenko
5133a48aebfSOleksandr Tymoshenko /* Requested to stop playback */
5143a48aebfSOleksandr Tymoshenko if ((flags & AUDIO_STOP) &&
5153a48aebfSOleksandr Tymoshenko (ch->playback_state == PLAYBACK_PLAYING)) {
51669cab2d1SOleksandr Tymoshenko bcm2835_audio_stop(ch);
5173a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
5182f99b597SOleksandr Tymoshenko bcm2835_audio_reset_channel(&sc->pch);
5192f99b597SOleksandr Tymoshenko ch->playback_state = PLAYBACK_IDLE;
5203a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
5212c0b1f4fSOleksandr Tymoshenko continue;
5222f99b597SOleksandr Tymoshenko }
5232f99b597SOleksandr Tymoshenko
5243a48aebfSOleksandr Tymoshenko /* Requested to start playback */
5253a48aebfSOleksandr Tymoshenko if ((flags & AUDIO_PLAY) &&
5263a48aebfSOleksandr Tymoshenko (ch->playback_state == PLAYBACK_IDLE)) {
5273a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
5282f99b597SOleksandr Tymoshenko ch->playback_state = PLAYBACK_PLAYING;
5293a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
5303a48aebfSOleksandr Tymoshenko bcm2835_audio_start(ch);
5312f99b597SOleksandr Tymoshenko }
5323a48aebfSOleksandr Tymoshenko
5333a48aebfSOleksandr Tymoshenko if (ch->playback_state == PLAYBACK_IDLE)
5343a48aebfSOleksandr Tymoshenko continue;
5353a48aebfSOleksandr Tymoshenko
5363a48aebfSOleksandr Tymoshenko if (sndbuf_getready(ch->buffer) == 0)
5373a48aebfSOleksandr Tymoshenko continue;
5383a48aebfSOleksandr Tymoshenko
5393a48aebfSOleksandr Tymoshenko count = sndbuf_getready(ch->buffer);
5403a48aebfSOleksandr Tymoshenko size = sndbuf_getsize(ch->buffer);
5413a48aebfSOleksandr Tymoshenko readyptr = sndbuf_getreadyptr(ch->buffer);
5423a48aebfSOleksandr Tymoshenko
5433a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
5443a48aebfSOleksandr Tymoshenko if (readyptr + count > size)
5453a48aebfSOleksandr Tymoshenko count = size - readyptr;
5463a48aebfSOleksandr Tymoshenko count = min(count, ch->available_space);
5473a48aebfSOleksandr Tymoshenko count -= (count % VCHIQ_AUDIO_PACKET_SIZE);
5483a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
5493a48aebfSOleksandr Tymoshenko
5503a48aebfSOleksandr Tymoshenko if (count < VCHIQ_AUDIO_PACKET_SIZE)
5513a48aebfSOleksandr Tymoshenko continue;
5523a48aebfSOleksandr Tymoshenko
5533a48aebfSOleksandr Tymoshenko buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + readyptr;
5543a48aebfSOleksandr Tymoshenko
5553a48aebfSOleksandr Tymoshenko bcm2835_audio_write_samples(ch, buf, count);
5563a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
5573a48aebfSOleksandr Tymoshenko ch->unsubmittedptr = (ch->unsubmittedptr + count) % sndbuf_getsize(ch->buffer);
5583a48aebfSOleksandr Tymoshenko ch->available_space -= count;
5593a48aebfSOleksandr Tymoshenko ch->submitted_samples += count;
5603a48aebfSOleksandr Tymoshenko KASSERT(ch->available_space >= 0, ("ch->available_space == %d\n", ch->available_space));
5613a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
5622f99b597SOleksandr Tymoshenko }
5633a48aebfSOleksandr Tymoshenko
5643a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
5653a48aebfSOleksandr Tymoshenko sc->worker_state = WORKER_STOPPED;
5663a48aebfSOleksandr Tymoshenko cv_signal(&sc->worker_cv);
5673a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
5682f99b597SOleksandr Tymoshenko
5692f99b597SOleksandr Tymoshenko kproc_exit(0);
5702f99b597SOleksandr Tymoshenko }
5712f99b597SOleksandr Tymoshenko
5722f99b597SOleksandr Tymoshenko static void
bcm2835_audio_create_worker(struct bcm2835_audio_info * sc)5732f99b597SOleksandr Tymoshenko bcm2835_audio_create_worker(struct bcm2835_audio_info *sc)
5742f99b597SOleksandr Tymoshenko {
5752f99b597SOleksandr Tymoshenko struct proc *newp;
5762f99b597SOleksandr Tymoshenko
5773a48aebfSOleksandr Tymoshenko sc->worker_state = WORKER_RUNNING;
5782f99b597SOleksandr Tymoshenko if (kproc_create(bcm2835_audio_worker, (void*)sc, &newp, 0, 0,
5792f99b597SOleksandr Tymoshenko "bcm2835_audio_worker") != 0) {
5802f99b597SOleksandr Tymoshenko printf("failed to create bcm2835_audio_worker\n");
5812f99b597SOleksandr Tymoshenko }
5822f99b597SOleksandr Tymoshenko }
5832f99b597SOleksandr Tymoshenko
5842f99b597SOleksandr Tymoshenko /* -------------------------------------------------------------------- */
5852c0b1f4fSOleksandr Tymoshenko /* channel interface for VCHI audio */
5862f99b597SOleksandr Tymoshenko static void *
bcmchan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)5872f99b597SOleksandr Tymoshenko bcmchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
5882f99b597SOleksandr Tymoshenko {
5892f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = devinfo;
5902f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = &sc->pch;
5912f99b597SOleksandr Tymoshenko void *buffer;
5922f99b597SOleksandr Tymoshenko
5932f99b597SOleksandr Tymoshenko if (dir == PCMDIR_REC)
5942f99b597SOleksandr Tymoshenko return NULL;
5952f99b597SOleksandr Tymoshenko
5962f99b597SOleksandr Tymoshenko ch->parent = sc;
5972f99b597SOleksandr Tymoshenko ch->channel = c;
5982f99b597SOleksandr Tymoshenko ch->buffer = b;
5992f99b597SOleksandr Tymoshenko
6002f99b597SOleksandr Tymoshenko /* default values */
6012f99b597SOleksandr Tymoshenko ch->spd = 44100;
6022f99b597SOleksandr Tymoshenko ch->fmt = SND_FORMAT(AFMT_S16_LE, 2, 0);
6032f99b597SOleksandr Tymoshenko ch->blksz = VCHIQ_AUDIO_PACKET_SIZE;
6042f99b597SOleksandr Tymoshenko
6052f99b597SOleksandr Tymoshenko buffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
6062f99b597SOleksandr Tymoshenko
6072f99b597SOleksandr Tymoshenko if (sndbuf_setup(ch->buffer, buffer, sc->bufsz) != 0) {
60869cab2d1SOleksandr Tymoshenko device_printf(sc->dev, "sndbuf_setup failed\n");
6092f99b597SOleksandr Tymoshenko free(buffer, M_DEVBUF);
6102f99b597SOleksandr Tymoshenko return NULL;
6112f99b597SOleksandr Tymoshenko }
6122f99b597SOleksandr Tymoshenko
6133a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
6143a48aebfSOleksandr Tymoshenko bcm2835_worker_update_params(sc);
6153a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
6162f99b597SOleksandr Tymoshenko
6172f99b597SOleksandr Tymoshenko return ch;
6182f99b597SOleksandr Tymoshenko }
6192f99b597SOleksandr Tymoshenko
6202f99b597SOleksandr Tymoshenko static int
bcmchan_free(kobj_t obj,void * data)6212f99b597SOleksandr Tymoshenko bcmchan_free(kobj_t obj, void *data)
6222f99b597SOleksandr Tymoshenko {
6232f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = data;
6242f99b597SOleksandr Tymoshenko void *buffer;
6252f99b597SOleksandr Tymoshenko
6262f99b597SOleksandr Tymoshenko buffer = sndbuf_getbuf(ch->buffer);
6272f99b597SOleksandr Tymoshenko if (buffer)
6282f99b597SOleksandr Tymoshenko free(buffer, M_DEVBUF);
6292f99b597SOleksandr Tymoshenko
6302f99b597SOleksandr Tymoshenko return (0);
6312f99b597SOleksandr Tymoshenko }
6322f99b597SOleksandr Tymoshenko
6332f99b597SOleksandr Tymoshenko static int
bcmchan_setformat(kobj_t obj,void * data,uint32_t format)6342f99b597SOleksandr Tymoshenko bcmchan_setformat(kobj_t obj, void *data, uint32_t format)
6352f99b597SOleksandr Tymoshenko {
6362f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = data;
6372f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
6382f99b597SOleksandr Tymoshenko
6393a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
6402f99b597SOleksandr Tymoshenko ch->fmt = format;
6413a48aebfSOleksandr Tymoshenko bcm2835_worker_update_params(sc);
6423a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
64369cab2d1SOleksandr Tymoshenko
6442f99b597SOleksandr Tymoshenko return 0;
6452f99b597SOleksandr Tymoshenko }
6462f99b597SOleksandr Tymoshenko
6472f99b597SOleksandr Tymoshenko static uint32_t
bcmchan_setspeed(kobj_t obj,void * data,uint32_t speed)6482f99b597SOleksandr Tymoshenko bcmchan_setspeed(kobj_t obj, void *data, uint32_t speed)
6492f99b597SOleksandr Tymoshenko {
6502f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = data;
6512f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
6522f99b597SOleksandr Tymoshenko
6533a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
6542f99b597SOleksandr Tymoshenko ch->spd = speed;
6553a48aebfSOleksandr Tymoshenko bcm2835_worker_update_params(sc);
6563a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
65769cab2d1SOleksandr Tymoshenko
6582f99b597SOleksandr Tymoshenko return ch->spd;
6592f99b597SOleksandr Tymoshenko }
6602f99b597SOleksandr Tymoshenko
6612f99b597SOleksandr Tymoshenko static uint32_t
bcmchan_setblocksize(kobj_t obj,void * data,uint32_t blocksize)6622f99b597SOleksandr Tymoshenko bcmchan_setblocksize(kobj_t obj, void *data, uint32_t blocksize)
6632f99b597SOleksandr Tymoshenko {
6642f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = data;
6652f99b597SOleksandr Tymoshenko
6662f99b597SOleksandr Tymoshenko return ch->blksz;
6672f99b597SOleksandr Tymoshenko }
6682f99b597SOleksandr Tymoshenko
6692f99b597SOleksandr Tymoshenko static int
bcmchan_trigger(kobj_t obj,void * data,int go)6702f99b597SOleksandr Tymoshenko bcmchan_trigger(kobj_t obj, void *data, int go)
6712f99b597SOleksandr Tymoshenko {
6722f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = data;
6732f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
6742f99b597SOleksandr Tymoshenko
6752f99b597SOleksandr Tymoshenko if (!PCMTRIG_COMMON(go))
6762f99b597SOleksandr Tymoshenko return (0);
6772f99b597SOleksandr Tymoshenko
6782f99b597SOleksandr Tymoshenko switch (go) {
6792f99b597SOleksandr Tymoshenko case PCMTRIG_START:
68069cab2d1SOleksandr Tymoshenko /* kickstart data flow */
68169cab2d1SOleksandr Tymoshenko chn_intr(sc->pch.channel);
6823a48aebfSOleksandr Tymoshenko ch->submitted_samples = 0;
6833a48aebfSOleksandr Tymoshenko ch->retrieved_samples = 0;
6843a48aebfSOleksandr Tymoshenko bcm2835_worker_play_start(sc);
6852f99b597SOleksandr Tymoshenko break;
6862f99b597SOleksandr Tymoshenko
6872f99b597SOleksandr Tymoshenko case PCMTRIG_STOP:
6882f99b597SOleksandr Tymoshenko case PCMTRIG_ABORT:
6893a48aebfSOleksandr Tymoshenko bcm2835_worker_play_stop(sc);
6902f99b597SOleksandr Tymoshenko break;
6912f99b597SOleksandr Tymoshenko
6922f99b597SOleksandr Tymoshenko default:
6932f99b597SOleksandr Tymoshenko break;
6942f99b597SOleksandr Tymoshenko }
6952f99b597SOleksandr Tymoshenko return 0;
6962f99b597SOleksandr Tymoshenko }
6972f99b597SOleksandr Tymoshenko
6982f99b597SOleksandr Tymoshenko static uint32_t
bcmchan_getptr(kobj_t obj,void * data)6992f99b597SOleksandr Tymoshenko bcmchan_getptr(kobj_t obj, void *data)
7002f99b597SOleksandr Tymoshenko {
7012f99b597SOleksandr Tymoshenko struct bcm2835_audio_chinfo *ch = data;
7022f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = ch->parent;
7032f99b597SOleksandr Tymoshenko uint32_t ret;
7042f99b597SOleksandr Tymoshenko
7053a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
7063a48aebfSOleksandr Tymoshenko ret = ch->unsubmittedptr;
7073a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
7082f99b597SOleksandr Tymoshenko
7092f99b597SOleksandr Tymoshenko return ret;
7102f99b597SOleksandr Tymoshenko }
7112f99b597SOleksandr Tymoshenko
7122f99b597SOleksandr Tymoshenko static struct pcmchan_caps *
bcmchan_getcaps(kobj_t obj,void * data)7132f99b597SOleksandr Tymoshenko bcmchan_getcaps(kobj_t obj, void *data)
7142f99b597SOleksandr Tymoshenko {
7152f99b597SOleksandr Tymoshenko
7162f99b597SOleksandr Tymoshenko return &bcm2835_audio_playcaps;
7172f99b597SOleksandr Tymoshenko }
7182f99b597SOleksandr Tymoshenko
7192f99b597SOleksandr Tymoshenko static kobj_method_t bcmchan_methods[] = {
7202f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_init, bcmchan_init),
7212f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_free, bcmchan_free),
7222f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_setformat, bcmchan_setformat),
7232f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_setspeed, bcmchan_setspeed),
7242f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_setblocksize, bcmchan_setblocksize),
7252f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_trigger, bcmchan_trigger),
7262f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_getptr, bcmchan_getptr),
7272f99b597SOleksandr Tymoshenko KOBJMETHOD(channel_getcaps, bcmchan_getcaps),
7282f99b597SOleksandr Tymoshenko KOBJMETHOD_END
7292f99b597SOleksandr Tymoshenko };
7302f99b597SOleksandr Tymoshenko CHANNEL_DECLARE(bcmchan);
7312f99b597SOleksandr Tymoshenko
7322f99b597SOleksandr Tymoshenko /************************************************************/
7332f99b597SOleksandr Tymoshenko
7342f99b597SOleksandr Tymoshenko static int
bcmmix_init(struct snd_mixer * m)7352f99b597SOleksandr Tymoshenko bcmmix_init(struct snd_mixer *m)
7362f99b597SOleksandr Tymoshenko {
7372f99b597SOleksandr Tymoshenko
7382f99b597SOleksandr Tymoshenko mix_setdevs(m, SOUND_MASK_VOLUME);
7392f99b597SOleksandr Tymoshenko
7402f99b597SOleksandr Tymoshenko return (0);
7412f99b597SOleksandr Tymoshenko }
7422f99b597SOleksandr Tymoshenko
7432f99b597SOleksandr Tymoshenko static int
bcmmix_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)7442f99b597SOleksandr Tymoshenko bcmmix_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
7452f99b597SOleksandr Tymoshenko {
7462f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = mix_getdevinfo(m);
7472f99b597SOleksandr Tymoshenko
7482f99b597SOleksandr Tymoshenko switch (dev) {
7492f99b597SOleksandr Tymoshenko case SOUND_MIXER_VOLUME:
7503a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
7512f99b597SOleksandr Tymoshenko sc->volume = left;
7523a48aebfSOleksandr Tymoshenko bcm2835_worker_update_params(sc);
7533a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
7543a48aebfSOleksandr Tymoshenko
7552f99b597SOleksandr Tymoshenko break;
7562f99b597SOleksandr Tymoshenko
7572f99b597SOleksandr Tymoshenko default:
7582f99b597SOleksandr Tymoshenko break;
7592f99b597SOleksandr Tymoshenko }
7602f99b597SOleksandr Tymoshenko
7612f99b597SOleksandr Tymoshenko return left | (left << 8);
7622f99b597SOleksandr Tymoshenko }
7632f99b597SOleksandr Tymoshenko
7642f99b597SOleksandr Tymoshenko static kobj_method_t bcmmixer_methods[] = {
7652f99b597SOleksandr Tymoshenko KOBJMETHOD(mixer_init, bcmmix_init),
7662f99b597SOleksandr Tymoshenko KOBJMETHOD(mixer_set, bcmmix_set),
7672f99b597SOleksandr Tymoshenko KOBJMETHOD_END
7682f99b597SOleksandr Tymoshenko };
7692f99b597SOleksandr Tymoshenko
7702f99b597SOleksandr Tymoshenko MIXER_DECLARE(bcmmixer);
7712f99b597SOleksandr Tymoshenko
7722f99b597SOleksandr Tymoshenko static int
sysctl_bcm2835_audio_dest(SYSCTL_HANDLER_ARGS)7732f99b597SOleksandr Tymoshenko sysctl_bcm2835_audio_dest(SYSCTL_HANDLER_ARGS)
7742f99b597SOleksandr Tymoshenko {
7752f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc = arg1;
7762f99b597SOleksandr Tymoshenko int val;
7772f99b597SOleksandr Tymoshenko int err;
7782f99b597SOleksandr Tymoshenko
7792f99b597SOleksandr Tymoshenko val = sc->dest;
7802f99b597SOleksandr Tymoshenko err = sysctl_handle_int(oidp, &val, 0, req);
7812f99b597SOleksandr Tymoshenko if (err || !req->newptr) /* error || read request */
7822f99b597SOleksandr Tymoshenko return (err);
7832f99b597SOleksandr Tymoshenko
7842f99b597SOleksandr Tymoshenko if ((val < 0) || (val > 2))
7852f99b597SOleksandr Tymoshenko return (EINVAL);
7862f99b597SOleksandr Tymoshenko
7873a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
7882f99b597SOleksandr Tymoshenko sc->dest = val;
7893a48aebfSOleksandr Tymoshenko bcm2835_worker_update_params(sc);
7903a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
79169cab2d1SOleksandr Tymoshenko
7923a48aebfSOleksandr Tymoshenko if (bootverbose)
7932f99b597SOleksandr Tymoshenko device_printf(sc->dev, "destination set to %s\n", dest_description(val));
7942f99b597SOleksandr Tymoshenko
7952f99b597SOleksandr Tymoshenko return (0);
7962f99b597SOleksandr Tymoshenko }
7972f99b597SOleksandr Tymoshenko
7982f99b597SOleksandr Tymoshenko static void
vchi_audio_sysctl_init(struct bcm2835_audio_info * sc)7992f99b597SOleksandr Tymoshenko vchi_audio_sysctl_init(struct bcm2835_audio_info *sc)
8002f99b597SOleksandr Tymoshenko {
8012f99b597SOleksandr Tymoshenko struct sysctl_ctx_list *ctx;
8022f99b597SOleksandr Tymoshenko struct sysctl_oid *tree_node;
8032f99b597SOleksandr Tymoshenko struct sysctl_oid_list *tree;
8042f99b597SOleksandr Tymoshenko
8052f99b597SOleksandr Tymoshenko /*
8062f99b597SOleksandr Tymoshenko * Add system sysctl tree/handlers.
8072f99b597SOleksandr Tymoshenko */
8082f99b597SOleksandr Tymoshenko ctx = device_get_sysctl_ctx(sc->dev);
8092f99b597SOleksandr Tymoshenko tree_node = device_get_sysctl_tree(sc->dev);
8102f99b597SOleksandr Tymoshenko tree = SYSCTL_CHILDREN(tree_node);
8112f99b597SOleksandr Tymoshenko SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "dest",
8127029da5cSPawel Biernacki CTLFLAG_RW | CTLTYPE_UINT | CTLFLAG_NEEDGIANT, sc, sizeof(*sc),
8132f99b597SOleksandr Tymoshenko sysctl_bcm2835_audio_dest, "IU", "audio destination, "
8142f99b597SOleksandr Tymoshenko "0 - auto, 1 - headphones, 2 - HDMI");
8153a48aebfSOleksandr Tymoshenko SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "callbacks",
8163a48aebfSOleksandr Tymoshenko CTLFLAG_RD, &sc->pch.callbacks,
8173a48aebfSOleksandr Tymoshenko "callbacks total");
8183a48aebfSOleksandr Tymoshenko SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "submitted",
8193a48aebfSOleksandr Tymoshenko CTLFLAG_RD, &sc->pch.submitted_samples,
8203a48aebfSOleksandr Tymoshenko "last play submitted samples");
8213a48aebfSOleksandr Tymoshenko SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "retrieved",
8223a48aebfSOleksandr Tymoshenko CTLFLAG_RD, &sc->pch.retrieved_samples,
8233a48aebfSOleksandr Tymoshenko "last play retrieved samples");
8243a48aebfSOleksandr Tymoshenko SYSCTL_ADD_UQUAD(ctx, tree, OID_AUTO, "underruns",
8253a48aebfSOleksandr Tymoshenko CTLFLAG_RD, &sc->pch.underruns,
8263a48aebfSOleksandr Tymoshenko "callback underruns");
8273a48aebfSOleksandr Tymoshenko SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "freebuffer",
8283a48aebfSOleksandr Tymoshenko CTLFLAG_RD, &sc->pch.available_space,
8293a48aebfSOleksandr Tymoshenko sc->pch.available_space, "callbacks total");
8303a48aebfSOleksandr Tymoshenko SYSCTL_ADD_INT(ctx, tree, OID_AUTO, "starved",
8313a48aebfSOleksandr Tymoshenko CTLFLAG_RD, &sc->pch.starved,
8323a48aebfSOleksandr Tymoshenko sc->pch.starved, "number of starved conditions");
8332f99b597SOleksandr Tymoshenko }
8342f99b597SOleksandr Tymoshenko
8352f99b597SOleksandr Tymoshenko static void
bcm2835_audio_identify(driver_t * driver,device_t parent)8362f99b597SOleksandr Tymoshenko bcm2835_audio_identify(driver_t *driver, device_t parent)
8372f99b597SOleksandr Tymoshenko {
8382f99b597SOleksandr Tymoshenko
8392f99b597SOleksandr Tymoshenko BUS_ADD_CHILD(parent, 0, "pcm", 0);
8402f99b597SOleksandr Tymoshenko }
8412f99b597SOleksandr Tymoshenko
8422f99b597SOleksandr Tymoshenko static int
bcm2835_audio_probe(device_t dev)8432f99b597SOleksandr Tymoshenko bcm2835_audio_probe(device_t dev)
8442f99b597SOleksandr Tymoshenko {
8452f99b597SOleksandr Tymoshenko
846d28956b4SOleksandr Tymoshenko device_set_desc(dev, "VCHIQ audio");
8472f99b597SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT);
8482f99b597SOleksandr Tymoshenko }
8492f99b597SOleksandr Tymoshenko
8502f99b597SOleksandr Tymoshenko static void
bcm2835_audio_delayed_init(void * xsc)8512f99b597SOleksandr Tymoshenko bcm2835_audio_delayed_init(void *xsc)
8522f99b597SOleksandr Tymoshenko {
8532f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc;
8542f99b597SOleksandr Tymoshenko char status[SND_STATUSLEN];
8552f99b597SOleksandr Tymoshenko
8562f99b597SOleksandr Tymoshenko sc = xsc;
8572f99b597SOleksandr Tymoshenko
8582f99b597SOleksandr Tymoshenko config_intrhook_disestablish(&sc->intr_hook);
8592f99b597SOleksandr Tymoshenko
8602f99b597SOleksandr Tymoshenko bcm2835_audio_init(sc);
8612f99b597SOleksandr Tymoshenko bcm2835_audio_open(sc);
8622f99b597SOleksandr Tymoshenko sc->volume = 75;
8632f99b597SOleksandr Tymoshenko sc->dest = DEST_AUTO;
8642f99b597SOleksandr Tymoshenko
8652f99b597SOleksandr Tymoshenko if (mixer_init(sc->dev, &bcmmixer_class, sc)) {
8662f99b597SOleksandr Tymoshenko device_printf(sc->dev, "mixer_init failed\n");
8672f99b597SOleksandr Tymoshenko goto no;
8682f99b597SOleksandr Tymoshenko }
8692f99b597SOleksandr Tymoshenko
870*516a9c02SChristos Margiolis pcm_init(sc->dev, sc);
8712f99b597SOleksandr Tymoshenko
8722f99b597SOleksandr Tymoshenko pcm_addchan(sc->dev, PCMDIR_PLAY, &bcmchan_class, sc);
8732f99b597SOleksandr Tymoshenko snprintf(status, SND_STATUSLEN, "at VCHIQ");
874*516a9c02SChristos Margiolis if (pcm_register(sc->dev, status)) {
875*516a9c02SChristos Margiolis device_printf(sc->dev, "pcm_register failed\n");
876*516a9c02SChristos Margiolis goto no;
877*516a9c02SChristos Margiolis }
8782f99b597SOleksandr Tymoshenko
8792f99b597SOleksandr Tymoshenko bcm2835_audio_reset_channel(&sc->pch);
8802f99b597SOleksandr Tymoshenko bcm2835_audio_create_worker(sc);
8812f99b597SOleksandr Tymoshenko
8822f99b597SOleksandr Tymoshenko vchi_audio_sysctl_init(sc);
8832f99b597SOleksandr Tymoshenko
8842f99b597SOleksandr Tymoshenko no:
8852f99b597SOleksandr Tymoshenko ;
8862f99b597SOleksandr Tymoshenko }
8872f99b597SOleksandr Tymoshenko
8882f99b597SOleksandr Tymoshenko static int
bcm2835_audio_attach(device_t dev)8892f99b597SOleksandr Tymoshenko bcm2835_audio_attach(device_t dev)
8902f99b597SOleksandr Tymoshenko {
8912f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc;
8922f99b597SOleksandr Tymoshenko
8932f99b597SOleksandr Tymoshenko sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO);
8942f99b597SOleksandr Tymoshenko
8952f99b597SOleksandr Tymoshenko sc->dev = dev;
8962f99b597SOleksandr Tymoshenko sc->bufsz = VCHIQ_AUDIO_BUFFER_SIZE;
8972f99b597SOleksandr Tymoshenko
8983a48aebfSOleksandr Tymoshenko mtx_init(&sc->lock, device_get_nameunit(dev),
8993a48aebfSOleksandr Tymoshenko "bcm_audio_lock", MTX_DEF);
90069cab2d1SOleksandr Tymoshenko cv_init(&sc->worker_cv, "worker_cv");
9012f99b597SOleksandr Tymoshenko sc->vchi_handle = VCHIQ_SERVICE_HANDLE_INVALID;
9022f99b597SOleksandr Tymoshenko
9032f99b597SOleksandr Tymoshenko /*
9042f99b597SOleksandr Tymoshenko * We need interrupts enabled for VCHI to work properly,
905255eff3bSPedro F. Giffuni * so delay initialization until it happens.
9062f99b597SOleksandr Tymoshenko */
9072f99b597SOleksandr Tymoshenko sc->intr_hook.ich_func = bcm2835_audio_delayed_init;
9082f99b597SOleksandr Tymoshenko sc->intr_hook.ich_arg = sc;
9092f99b597SOleksandr Tymoshenko
9102f99b597SOleksandr Tymoshenko if (config_intrhook_establish(&sc->intr_hook) != 0)
9112f99b597SOleksandr Tymoshenko goto no;
9122f99b597SOleksandr Tymoshenko
9132f99b597SOleksandr Tymoshenko return 0;
9142f99b597SOleksandr Tymoshenko
9152f99b597SOleksandr Tymoshenko no:
9162f99b597SOleksandr Tymoshenko return ENXIO;
9172f99b597SOleksandr Tymoshenko }
9182f99b597SOleksandr Tymoshenko
9192f99b597SOleksandr Tymoshenko static int
bcm2835_audio_detach(device_t dev)9202f99b597SOleksandr Tymoshenko bcm2835_audio_detach(device_t dev)
9212f99b597SOleksandr Tymoshenko {
9222f99b597SOleksandr Tymoshenko int r;
9232f99b597SOleksandr Tymoshenko struct bcm2835_audio_info *sc;
9242f99b597SOleksandr Tymoshenko sc = pcm_getdevinfo(dev);
9252f99b597SOleksandr Tymoshenko
9262f99b597SOleksandr Tymoshenko /* Stop worker thread */
9273a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_LOCK(sc);
9283a48aebfSOleksandr Tymoshenko sc->worker_state = WORKER_STOPPING;
92969cab2d1SOleksandr Tymoshenko cv_signal(&sc->worker_cv);
9303a48aebfSOleksandr Tymoshenko /* Wait for thread to exit */
9313a48aebfSOleksandr Tymoshenko while (sc->worker_state != WORKER_STOPPED)
9323a48aebfSOleksandr Tymoshenko cv_wait_sig(&sc->worker_cv, &sc->lock);
9333a48aebfSOleksandr Tymoshenko BCM2835_AUDIO_UNLOCK(sc);
9342f99b597SOleksandr Tymoshenko
9352f99b597SOleksandr Tymoshenko r = pcm_unregister(dev);
9362f99b597SOleksandr Tymoshenko if (r)
9372f99b597SOleksandr Tymoshenko return r;
9382f99b597SOleksandr Tymoshenko
9393a48aebfSOleksandr Tymoshenko mtx_destroy(&sc->lock);
94069cab2d1SOleksandr Tymoshenko cv_destroy(&sc->worker_cv);
9412f99b597SOleksandr Tymoshenko
9422f99b597SOleksandr Tymoshenko bcm2835_audio_release(sc);
9432f99b597SOleksandr Tymoshenko
9442f99b597SOleksandr Tymoshenko free(sc, M_DEVBUF);
9452f99b597SOleksandr Tymoshenko
9462f99b597SOleksandr Tymoshenko return 0;
9472f99b597SOleksandr Tymoshenko }
9482f99b597SOleksandr Tymoshenko
9492f99b597SOleksandr Tymoshenko static device_method_t bcm2835_audio_methods[] = {
9502f99b597SOleksandr Tymoshenko /* Device interface */
9512f99b597SOleksandr Tymoshenko DEVMETHOD(device_identify, bcm2835_audio_identify),
9522f99b597SOleksandr Tymoshenko DEVMETHOD(device_probe, bcm2835_audio_probe),
9532f99b597SOleksandr Tymoshenko DEVMETHOD(device_attach, bcm2835_audio_attach),
9542f99b597SOleksandr Tymoshenko DEVMETHOD(device_detach, bcm2835_audio_detach),
9552f99b597SOleksandr Tymoshenko { 0, 0 }
9562f99b597SOleksandr Tymoshenko };
9572f99b597SOleksandr Tymoshenko
9582f99b597SOleksandr Tymoshenko static driver_t bcm2835_audio_driver = {
9592f99b597SOleksandr Tymoshenko "pcm",
9602f99b597SOleksandr Tymoshenko bcm2835_audio_methods,
9612f99b597SOleksandr Tymoshenko PCM_SOFTC_SIZE,
9622f99b597SOleksandr Tymoshenko };
9632f99b597SOleksandr Tymoshenko
9642287364eSJohn Baldwin DRIVER_MODULE(bcm2835_audio, vchiq, bcm2835_audio_driver, 0, 0);
9652f99b597SOleksandr Tymoshenko MODULE_DEPEND(bcm2835_audio, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
9662f99b597SOleksandr Tymoshenko MODULE_DEPEND(bcm2835_audio, vchiq, 1, 1, 1);
9672f99b597SOleksandr Tymoshenko MODULE_VERSION(bcm2835_audio, 1);
968