static int bcmchan_trigger(kobj_t obj, void *data, int go) { struct bcm2835_audio_chinfo *ch = data; struct bcm2835_audio_info *sc = ch->parent; if (!PCMTRIG_COMMON(go)) return (0); bcm2835_audio_lock(sc); switch (go) { case PCMTRIG_START: bcm2835_audio_start(ch); ch->playback_state = PLAYBACK_STARTING; /* wakeup worker thread */ cv_signal(&sc->data_cv); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: ch->playback_state = 1; bcm2835_audio_stop(ch); break; default: break; } bcm2835_audio_unlock(sc); return 0; }
/* trigger callback */ static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_pcm_runtime *runtime = substream->runtime; struct bcm2835_alsa_stream *alsa_stream = runtime->private_data; int err = 0; audio_info(" .. IN\n"); switch (cmd) { case SNDRV_PCM_TRIGGER_START: audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", alsa_stream->running); if (!alsa_stream->running) { err = bcm2835_audio_start(alsa_stream); if (!err) { alsa_stream->pcm_indirect.hw_io = alsa_stream->pcm_indirect.hw_data = bytes_to_frames(runtime, alsa_stream->pos); substream->ops->ack(substream); alsa_stream->running = 1; alsa_stream->draining = 1; } else { audio_error(" Failed to START alsa device (%d)\n", err); } } break; case SNDRV_PCM_TRIGGER_STOP: audio_debug ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING); if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { audio_info("DRAINING\n"); alsa_stream->draining = 1; } else { audio_info("DROPPING\n"); alsa_stream->draining = 0; } if (alsa_stream->running) { err = bcm2835_audio_stop(alsa_stream); if (err != 0) audio_error(" Failed to STOP alsa device (%d)\n", err); alsa_stream->running = 0; } break; default: err = -EINVAL; } audio_info(" .. OUT\n"); return err; }
static void bcm2835_audio_worker(void *data) { struct bcm2835_audio_info *sc = (struct bcm2835_audio_info *)data; struct bcm2835_audio_chinfo *ch = &sc->pch; uint32_t speed, format; uint32_t volume, dest; uint32_t flags; uint32_t count, size, readyptr; uint8_t *buf; ch->playback_state = PLAYBACK_IDLE; while (1) { if (sc->worker_state != WORKER_RUNNING) break; BCM2835_AUDIO_LOCK(sc); /* * wait until there are flags set or buffer is ready * to consume more samples */ while ((sc->flags_pending == 0) && bcm2835_audio_buffer_should_sleep(ch)) { cv_wait_sig(&sc->worker_cv, &sc->lock); } flags = sc->flags_pending; /* Clear pending flags */ sc->flags_pending = 0; BCM2835_AUDIO_UNLOCK(sc); /* Requested to change parameters */ if (flags & AUDIO_PARAMS) { BCM2835_AUDIO_LOCK(sc); speed = ch->spd; format = ch->fmt; volume = sc->volume; dest = sc->dest; BCM2835_AUDIO_UNLOCK(sc); if (ch->playback_state == PLAYBACK_IDLE) bcm2835_audio_update_params(sc, format, speed); bcm2835_audio_update_controls(sc, volume, dest); } /* Requested to stop playback */ if ((flags & AUDIO_STOP) && (ch->playback_state == PLAYBACK_PLAYING)) { bcm2835_audio_stop(ch); BCM2835_AUDIO_LOCK(sc); bcm2835_audio_reset_channel(&sc->pch); ch->playback_state = PLAYBACK_IDLE; BCM2835_AUDIO_UNLOCK(sc); continue; } /* Requested to start playback */ if ((flags & AUDIO_PLAY) && (ch->playback_state == PLAYBACK_IDLE)) { BCM2835_AUDIO_LOCK(sc); ch->playback_state = PLAYBACK_PLAYING; BCM2835_AUDIO_UNLOCK(sc); bcm2835_audio_start(ch); } if (ch->playback_state == PLAYBACK_IDLE) continue; if (sndbuf_getready(ch->buffer) == 0) continue; count = sndbuf_getready(ch->buffer); size = sndbuf_getsize(ch->buffer); readyptr = sndbuf_getreadyptr(ch->buffer); BCM2835_AUDIO_LOCK(sc); if (readyptr + count > size) count = size - readyptr; count = min(count, ch->available_space); count -= (count % VCHIQ_AUDIO_PACKET_SIZE); BCM2835_AUDIO_UNLOCK(sc); if (count < VCHIQ_AUDIO_PACKET_SIZE) continue; buf = (uint8_t*)sndbuf_getbuf(ch->buffer) + readyptr; bcm2835_audio_write_samples(ch, buf, count); BCM2835_AUDIO_LOCK(sc); ch->unsubmittedptr = (ch->unsubmittedptr + count) % sndbuf_getsize(ch->buffer); ch->available_space -= count; ch->submitted_samples += count; KASSERT(ch->available_space >= 0, ("ch->available_space == %d\n", ch->available_space)); BCM2835_AUDIO_UNLOCK(sc); } BCM2835_AUDIO_LOCK(sc); sc->worker_state = WORKER_STOPPED; cv_signal(&sc->worker_cv); BCM2835_AUDIO_UNLOCK(sc); kproc_exit(0); }