The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/arm/broadcom/bcm2835/bcm2835_audio.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

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

Cache object: 686fc756964576f28d5caed6d7e3f822


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.