* the ring buffer
* @total_bytes_transferred: cumulative bytes transferred by offload DSP
* @sleep: poll sleep
+ * @wait: drain wait queue
+ * @drain_wake: condition for drain wake
*/
struct snd_compr_runtime {
snd_pcm_state_t state;
u64 total_bytes_available;
u64 total_bytes_transferred;
wait_queue_head_t sleep;
+ wait_queue_head_t wait;
+ unsigned int drain_wake;
void *private_data;
};
wake_up(&stream->runtime->sleep);
}
+static inline void snd_compr_drain_notify(struct snd_compr_stream *stream)
+{
+ snd_BUG_ON(!stream);
+
+ stream->runtime->drain_wake = 1;
+ wake_up(&stream->runtime->wait);
+}
+
#endif
}
runtime->state = SNDRV_PCM_STATE_OPEN;
init_waitqueue_head(&runtime->sleep);
+ init_waitqueue_head(&runtime->wait);
data->stream.runtime = runtime;
f->private_data = (void *)data;
mutex_lock(&compr->lock);
if (!retval) {
stream->runtime->state = SNDRV_PCM_STATE_SETUP;
wake_up(&stream->runtime->sleep);
+ snd_compr_drain_notify(stream);
stream->runtime->total_bytes_available = 0;
stream->runtime->total_bytes_transferred = 0;
}
return retval;
}
+static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
+{
+ /*
+ * We are called with lock held. So drop the lock while we wait for
+ * drain complete notfication from the driver
+ *
+ * It is expected that driver will notify the drain completion and then
+ * stream will be moved to SETUP state, even if draining resulted in an
+ * error. We can trigger next track after this.
+ */
+ stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+ mutex_unlock(&stream->device->lock);
+
+ wait_event(stream->runtime->wait, stream->runtime->drain_wake);
+
+ wake_up(&stream->runtime->sleep);
+ mutex_lock(&stream->device->lock);
+
+ return 0;
+}
+
static int snd_compr_drain(struct snd_compr_stream *stream)
{
int retval;
if (stream->runtime->state == SNDRV_PCM_STATE_PREPARED ||
stream->runtime->state == SNDRV_PCM_STATE_SETUP)
return -EPERM;
+
+ stream->runtime->drain_wake = 0;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_DRAIN);
- if (!retval) {
- stream->runtime->state = SNDRV_PCM_STATE_DRAINING;
+ if (retval) {
+ pr_err("SND_COMPR_TRIGGER_DRAIN failed %d\n", retval);
wake_up(&stream->runtime->sleep);
+ return retval;
}
+
+ retval = snd_compress_wait_for_drain(stream);
+ stream->runtime->state = SNDRV_PCM_STATE_SETUP;
return retval;
}
if (stream->next_track == false)
return -EPERM;
+ stream->runtime->drain_wake = 0;
retval = stream->ops->trigger(stream, SND_COMPR_TRIGGER_PARTIAL_DRAIN);
+ if (retval) {
+ pr_err("Partial drain returned failure\n");
+ wake_up(&stream->runtime->sleep);
+ return retval;
+ }
stream->next_track = false;
- return retval;
+ return snd_compress_wait_for_drain(stream);
}
static long snd_compr_ioctl(struct file *f, unsigned int cmd, unsigned long arg)