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, ¶ms, 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