Example #1
0
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;
}
Example #2
0
/* close callback */
static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
{
	/* the hardware-specific codes will be here */

	struct bcm2835_chip *chip;
	struct snd_pcm_runtime *runtime;
	struct bcm2835_alsa_stream *alsa_stream;

	audio_info(" .. IN\n");

	chip = snd_pcm_substream_chip(substream);
	if (mutex_lock_interruptible(&chip->audio_mutex)) {
		audio_error("Interrupted whilst waiting for lock\n");
		return -EINTR;
	}
	runtime = substream->runtime;
	alsa_stream = runtime->private_data;

	audio_info("Alsa close\n");

	/*
	 * Call stop if it's still running. This happens when app
	 * is force killed and we don't get a stop trigger.
	 */
	if (alsa_stream->running) {
		int err;
		err = bcm2835_audio_stop(alsa_stream);
		alsa_stream->running = 0;
		if (err)
			audio_error(" Failed to STOP alsa device\n");
	}

	alsa_stream->period_size = 0;
	alsa_stream->buffer_size = 0;

	if (alsa_stream->open) {
		alsa_stream->open = 0;
		bcm2835_audio_close(alsa_stream);
	}
	if (alsa_stream->chip)
		alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL;
	/*
	 * Do not free up alsa_stream here, it will be freed up by
	 * runtime->private_free callback we registered in *_open above
	 */

	chip->opened &= ~(1 << substream->number);

	mutex_unlock(&chip->audio_mutex);
	audio_info(" .. OUT\n");

	return 0;
}
Example #3
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;
}
Example #4
0
/* close callback */
static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
{
	/* the hardware-specific codes will be here */

	struct snd_pcm_runtime *runtime = substream->runtime;
	bcm2835_alsa_stream_t *alsa_stream = runtime->private_data;

	audio_info(" .. IN\n");
	audio_warning("Alsa close\n");

	/*
	 * Call stop if it's still running. This happens when app
	 * is force killed and we don't get a stop trigger.
	 */
	if (alsa_stream->running) {
		int err;
		err = bcm2835_audio_stop(alsa_stream);
		alsa_stream->running = 0;
		if (err != 0)
			audio_error(" Failed to STOP alsa device\n");
	}

	alsa_stream->period_size = 0;
	alsa_stream->buffer_size = 0;

	if (alsa_stream->open) {
		alsa_stream->open = 0;
		bcm2835_audio_close(alsa_stream);
	}
	if (alsa_stream->chip)
		alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL;
	/*
	 * Do not free up alsa_stream here, it will be freed up by
	 * runtime->private_free callback we registered in *_open above
	 */

	audio_info(" .. OUT\n");

	return 0;
}
Example #5
0
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);
}