Commit f40a2867 authored by Anton Yakovlev's avatar Anton Yakovlev Committed by Takashi Iwai

ALSA: virtio: handling control and I/O messages for the PCM device

The driver implements a message-based transport for I/O substream
operations. Before the start of the substream, the hardware buffer is
sliced into I/O messages, the number of which is equal to the current
number of periods. The size of each message is equal to the current
size of one period.

I/O messages are organized in an ordered queue. The completion of the
I/O message indicates an elapsed period (the only exception is the end
of the stream for the capture substream). Upon completion, the message
is automatically re-added to the end of the queue.
Signed-off-by: default avatarAnton Yakovlev <anton.yakovlev@opensynergy.com>
Link: https://lore.kernel.org/r/20210302164709.3142702-6-anton.yakovlev@opensynergy.comSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 29b96bf5
...@@ -5,5 +5,6 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o ...@@ -5,5 +5,6 @@ obj-$(CONFIG_SND_VIRTIO) += virtio_snd.o
virtio_snd-objs := \ virtio_snd-objs := \
virtio_card.o \ virtio_card.o \
virtio_ctl_msg.o \ virtio_ctl_msg.o \
virtio_pcm.o virtio_pcm.o \
virtio_pcm_msg.o
...@@ -55,6 +55,12 @@ static void virtsnd_event_send(struct virtqueue *vqueue, ...@@ -55,6 +55,12 @@ static void virtsnd_event_send(struct virtqueue *vqueue,
static void virtsnd_event_dispatch(struct virtio_snd *snd, static void virtsnd_event_dispatch(struct virtio_snd *snd,
struct virtio_snd_event *event) struct virtio_snd_event *event)
{ {
switch (le32_to_cpu(event->hdr.code)) {
case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
case VIRTIO_SND_EVT_PCM_XRUN:
virtsnd_pcm_event(snd, event);
break;
}
} }
/** /**
...@@ -101,11 +107,15 @@ static int virtsnd_find_vqs(struct virtio_snd *snd) ...@@ -101,11 +107,15 @@ static int virtsnd_find_vqs(struct virtio_snd *snd)
struct virtio_device *vdev = snd->vdev; struct virtio_device *vdev = snd->vdev;
static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = { static vq_callback_t *callbacks[VIRTIO_SND_VQ_MAX] = {
[VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb, [VIRTIO_SND_VQ_CONTROL] = virtsnd_ctl_notify_cb,
[VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb [VIRTIO_SND_VQ_EVENT] = virtsnd_event_notify_cb,
[VIRTIO_SND_VQ_TX] = virtsnd_pcm_tx_notify_cb,
[VIRTIO_SND_VQ_RX] = virtsnd_pcm_rx_notify_cb
}; };
static const char *names[VIRTIO_SND_VQ_MAX] = { static const char *names[VIRTIO_SND_VQ_MAX] = {
[VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl", [VIRTIO_SND_VQ_CONTROL] = "virtsnd-ctl",
[VIRTIO_SND_VQ_EVENT] = "virtsnd-event" [VIRTIO_SND_VQ_EVENT] = "virtsnd-event",
[VIRTIO_SND_VQ_TX] = "virtsnd-tx",
[VIRTIO_SND_VQ_RX] = "virtsnd-rx"
}; };
struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 }; struct virtqueue *vqs[VIRTIO_SND_VQ_MAX] = { 0 };
unsigned int i; unsigned int i;
...@@ -318,8 +328,12 @@ static void virtsnd_remove(struct virtio_device *vdev) ...@@ -318,8 +328,12 @@ static void virtsnd_remove(struct virtio_device *vdev)
vdev->config->del_vqs(vdev); vdev->config->del_vqs(vdev);
vdev->config->reset(vdev); vdev->config->reset(vdev);
for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) for (i = 0; snd->substreams && i < snd->nsubstreams; ++i) {
cancel_work_sync(&snd->substreams[i].elapsed_period); struct virtio_pcm_substream *vss = &snd->substreams[i];
cancel_work_sync(&vss->elapsed_period);
virtsnd_pcm_msg_free(vss);
}
kfree(snd->event_msgs); kfree(snd->event_msgs);
} }
......
...@@ -79,4 +79,13 @@ virtsnd_rx_queue(struct virtio_snd *snd) ...@@ -79,4 +79,13 @@ virtsnd_rx_queue(struct virtio_snd *snd)
return &snd->queues[VIRTIO_SND_VQ_RX]; return &snd->queues[VIRTIO_SND_VQ_RX];
} }
static inline struct virtio_snd_queue *
virtsnd_pcm_queue(struct virtio_pcm_substream *vss)
{
if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK)
return virtsnd_tx_queue(vss->snd);
else
return virtsnd_rx_queue(vss->snd);
}
#endif /* VIRTIO_SND_CARD_H */ #endif /* VIRTIO_SND_CARD_H */
...@@ -353,6 +353,8 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd) ...@@ -353,6 +353,8 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd)
vss->snd = snd; vss->snd = snd;
vss->sid = i; vss->sid = i;
INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed); INIT_WORK(&vss->elapsed_period, virtsnd_pcm_period_elapsed);
init_waitqueue_head(&vss->msg_empty);
spin_lock_init(&vss->lock);
rc = virtsnd_pcm_build_hw(vss, &info[i]); rc = virtsnd_pcm_build_hw(vss, &info[i]);
if (rc) if (rc)
...@@ -477,3 +479,33 @@ int virtsnd_pcm_build_devs(struct virtio_snd *snd) ...@@ -477,3 +479,33 @@ int virtsnd_pcm_build_devs(struct virtio_snd *snd)
return 0; return 0;
} }
/**
* virtsnd_pcm_event() - Handle the PCM device event notification.
* @snd: VirtIO sound device.
* @event: VirtIO sound event.
*
* Context: Interrupt context.
*/
void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event)
{
struct virtio_pcm_substream *vss;
u32 sid = le32_to_cpu(event->data);
if (sid >= snd->nsubstreams)
return;
vss = &snd->substreams[sid];
switch (le32_to_cpu(event->hdr.code)) {
case VIRTIO_SND_EVT_PCM_PERIOD_ELAPSED:
/* TODO: deal with shmem elapsed period */
break;
case VIRTIO_SND_EVT_PCM_XRUN:
spin_lock(&vss->lock);
if (vss->xfer_enabled)
vss->xfer_xrun = true;
spin_unlock(&vss->lock);
break;
}
}
...@@ -23,6 +23,17 @@ struct virtio_pcm_msg; ...@@ -23,6 +23,17 @@ struct virtio_pcm_msg;
* @substream: Kernel ALSA substream. * @substream: Kernel ALSA substream.
* @hw: Kernel ALSA substream hardware descriptor. * @hw: Kernel ALSA substream hardware descriptor.
* @elapsed_period: Kernel work to handle the elapsed period state. * @elapsed_period: Kernel work to handle the elapsed period state.
* @lock: Spinlock that protects fields shared by interrupt handlers and
* substream operators.
* @buffer_bytes: Current buffer size in bytes.
* @hw_ptr: Substream hardware pointer value in bytes [0 ... buffer_bytes).
* @xfer_enabled: Data transfer state (0 - off, 1 - on).
* @xfer_xrun: Data underflow/overflow state (0 - no xrun, 1 - xrun).
* @msgs: Allocated I/O messages.
* @nmsgs: Number of allocated I/O messages.
* @msg_last_enqueued: Index of the last I/O message added to the virtqueue.
* @msg_count: Number of pending I/O messages in the virtqueue.
* @msg_empty: Notify when msg_count is zero.
*/ */
struct virtio_pcm_substream { struct virtio_pcm_substream {
struct virtio_snd *snd; struct virtio_snd *snd;
...@@ -33,6 +44,16 @@ struct virtio_pcm_substream { ...@@ -33,6 +44,16 @@ struct virtio_pcm_substream {
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
struct snd_pcm_hardware hw; struct snd_pcm_hardware hw;
struct work_struct elapsed_period; struct work_struct elapsed_period;
spinlock_t lock;
size_t buffer_bytes;
size_t hw_ptr;
bool xfer_enabled;
bool xfer_xrun;
struct virtio_pcm_msg **msgs;
unsigned int nmsgs;
int msg_last_enqueued;
unsigned int msg_count;
wait_queue_head_t msg_empty;
}; };
/** /**
...@@ -65,8 +86,27 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd); ...@@ -65,8 +86,27 @@ int virtsnd_pcm_parse_cfg(struct virtio_snd *snd);
int virtsnd_pcm_build_devs(struct virtio_snd *snd); int virtsnd_pcm_build_devs(struct virtio_snd *snd);
void virtsnd_pcm_event(struct virtio_snd *snd, struct virtio_snd_event *event);
void virtsnd_pcm_tx_notify_cb(struct virtqueue *vqueue);
void virtsnd_pcm_rx_notify_cb(struct virtqueue *vqueue);
struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid); struct virtio_pcm *virtsnd_pcm_find(struct virtio_snd *snd, u32 nid);
struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid); struct virtio_pcm *virtsnd_pcm_find_or_create(struct virtio_snd *snd, u32 nid);
struct virtio_snd_msg *
virtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *vss,
unsigned int command, gfp_t gfp);
int virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss,
unsigned int periods, unsigned int period_bytes);
void virtsnd_pcm_msg_free(struct virtio_pcm_substream *vss);
int virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss);
unsigned int virtsnd_pcm_msg_pending_num(struct virtio_pcm_substream *vss);
#endif /* VIRTIO_SND_PCM_H */ #endif /* VIRTIO_SND_PCM_H */
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment