static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id) { bcm2835_alsa_stream_t *alsa_stream = (bcm2835_alsa_stream_t *) dev_id; uint32_t consumed = 0; int new_period = 0; audio_info(" .. IN\n"); audio_info("alsa_stream=%p substream=%p\n", alsa_stream, alsa_stream ? alsa_stream->substream : 0); if (alsa_stream->open) consumed = bcm2835_audio_retrieve_buffers(alsa_stream); /* We get called only if playback was triggered, So, the number of buffers we retrieve in * each iteration are the buffers that have been played out already */ if (alsa_stream->period_size) { if ((alsa_stream->pos / alsa_stream->period_size) != ((alsa_stream->pos + consumed) / alsa_stream->period_size)) new_period = 1; } audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n", alsa_stream->pos, consumed, alsa_stream->buffer_size, (int)(alsa_stream->period_size*alsa_stream->substream->runtime->periods), frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr), new_period); if (alsa_stream->buffer_size) { alsa_stream->pos += consumed &~ (1<<30); alsa_stream->pos %= alsa_stream->buffer_size; } if (alsa_stream->substream) { if (new_period) snd_pcm_period_elapsed(alsa_stream->substream); } else { audio_warning(" unexpected NULL substream\n"); } audio_info(" .. OUT\n"); return IRQ_HANDLED; }
/* 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; }
/* open callback */ static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) { bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); struct snd_pcm_runtime *runtime = substream->runtime; bcm2835_alsa_stream_t *alsa_stream; int idx; int err; audio_info(" .. IN (%d)\n", substream->number); audio_warning("Alsa open (%d)\n", substream->number); idx = substream->number; if (idx > MAX_SUBSTREAMS) { audio_error ("substream(%d) device doesn't exist max(%d) substreams allowed\n", idx, MAX_SUBSTREAMS); err = -ENODEV; goto out; } /* Check if we are ready */ if (!(chip->avail_substreams & (1 << idx))) { /* We are not ready yet */ audio_error("substream(%d) device is not ready yet\n", idx); err = -EAGAIN; goto out; } alsa_stream = kzalloc(sizeof(bcm2835_alsa_stream_t), GFP_KERNEL); if (alsa_stream == NULL) { return -ENOMEM; } /* Initialise alsa_stream */ alsa_stream->chip = chip; alsa_stream->substream = substream; alsa_stream->idx = idx; chip->alsa_stream[idx] = alsa_stream; sema_init(&alsa_stream->buffers_update_sem, 0); sema_init(&alsa_stream->control_sem, 0); spin_lock_init(&alsa_stream->lock); /* Enabled in start trigger, called on each "fifo irq" after that */ alsa_stream->enable_fifo_irq = 0; alsa_stream->fifo_irq_handler = bcm2835_playback_fifo_irq; runtime->private_data = alsa_stream; runtime->private_free = snd_bcm2835_playback_free; runtime->hw = snd_bcm2835_playback_hw; /* minimum 16 bytes alignment (for vchiq bulk transfers) */ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16); err = bcm2835_audio_open(alsa_stream); if (err != 0) { kfree(alsa_stream); return err; } alsa_stream->open = 1; alsa_stream->draining = 1; out: audio_info(" .. OUT =%d\n", err); return err; }