162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * virtio-snd: Virtio sound device
462306a36Sopenharmony_ci * Copyright (C) 2021 OpenSynergy GmbH
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <sound/pcm_params.h>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include "virtio_card.h"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci/**
1162306a36Sopenharmony_ci * struct virtio_pcm_msg - VirtIO I/O message.
1262306a36Sopenharmony_ci * @substream: VirtIO PCM substream.
1362306a36Sopenharmony_ci * @xfer: Request header payload.
1462306a36Sopenharmony_ci * @status: Response header payload.
1562306a36Sopenharmony_ci * @length: Data length in bytes.
1662306a36Sopenharmony_ci * @sgs: Payload scatter-gather table.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_cistruct virtio_pcm_msg {
1962306a36Sopenharmony_ci	struct virtio_pcm_substream *substream;
2062306a36Sopenharmony_ci	struct virtio_snd_pcm_xfer xfer;
2162306a36Sopenharmony_ci	struct virtio_snd_pcm_status status;
2262306a36Sopenharmony_ci	size_t length;
2362306a36Sopenharmony_ci	struct scatterlist sgs[];
2462306a36Sopenharmony_ci};
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * enum pcm_msg_sg_index - Index values for the virtio_pcm_msg->sgs field in
2862306a36Sopenharmony_ci *                         an I/O message.
2962306a36Sopenharmony_ci * @PCM_MSG_SG_XFER: Element containing a virtio_snd_pcm_xfer structure.
3062306a36Sopenharmony_ci * @PCM_MSG_SG_STATUS: Element containing a virtio_snd_pcm_status structure.
3162306a36Sopenharmony_ci * @PCM_MSG_SG_DATA: The first element containing a data buffer.
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_cienum pcm_msg_sg_index {
3462306a36Sopenharmony_ci	PCM_MSG_SG_XFER = 0,
3562306a36Sopenharmony_ci	PCM_MSG_SG_STATUS,
3662306a36Sopenharmony_ci	PCM_MSG_SG_DATA
3762306a36Sopenharmony_ci};
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci/**
4062306a36Sopenharmony_ci * virtsnd_pcm_sg_num() - Count the number of sg-elements required to represent
4162306a36Sopenharmony_ci *                        vmalloc'ed buffer.
4262306a36Sopenharmony_ci * @data: Pointer to vmalloc'ed buffer.
4362306a36Sopenharmony_ci * @length: Buffer size.
4462306a36Sopenharmony_ci *
4562306a36Sopenharmony_ci * Context: Any context.
4662306a36Sopenharmony_ci * Return: Number of physically contiguous parts in the @data.
4762306a36Sopenharmony_ci */
4862306a36Sopenharmony_cistatic int virtsnd_pcm_sg_num(u8 *data, unsigned int length)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	phys_addr_t sg_address;
5162306a36Sopenharmony_ci	unsigned int sg_length;
5262306a36Sopenharmony_ci	int num = 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	while (length) {
5562306a36Sopenharmony_ci		struct page *pg = vmalloc_to_page(data);
5662306a36Sopenharmony_ci		phys_addr_t pg_address = page_to_phys(pg);
5762306a36Sopenharmony_ci		size_t pg_length;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		pg_length = PAGE_SIZE - offset_in_page(data);
6062306a36Sopenharmony_ci		if (pg_length > length)
6162306a36Sopenharmony_ci			pg_length = length;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		if (!num || sg_address + sg_length != pg_address) {
6462306a36Sopenharmony_ci			sg_address = pg_address;
6562306a36Sopenharmony_ci			sg_length = pg_length;
6662306a36Sopenharmony_ci			num++;
6762306a36Sopenharmony_ci		} else {
6862306a36Sopenharmony_ci			sg_length += pg_length;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		data += pg_length;
7262306a36Sopenharmony_ci		length -= pg_length;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return num;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/**
7962306a36Sopenharmony_ci * virtsnd_pcm_sg_from() - Build sg-list from vmalloc'ed buffer.
8062306a36Sopenharmony_ci * @sgs: Preallocated sg-list to populate.
8162306a36Sopenharmony_ci * @nsgs: The maximum number of elements in the @sgs.
8262306a36Sopenharmony_ci * @data: Pointer to vmalloc'ed buffer.
8362306a36Sopenharmony_ci * @length: Buffer size.
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci * Splits the buffer into physically contiguous parts and makes an sg-list of
8662306a36Sopenharmony_ci * such parts.
8762306a36Sopenharmony_ci *
8862306a36Sopenharmony_ci * Context: Any context.
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_cistatic void virtsnd_pcm_sg_from(struct scatterlist *sgs, int nsgs, u8 *data,
9162306a36Sopenharmony_ci				unsigned int length)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	int idx = -1;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	while (length) {
9662306a36Sopenharmony_ci		struct page *pg = vmalloc_to_page(data);
9762306a36Sopenharmony_ci		size_t pg_length;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci		pg_length = PAGE_SIZE - offset_in_page(data);
10062306a36Sopenharmony_ci		if (pg_length > length)
10162306a36Sopenharmony_ci			pg_length = length;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		if (idx == -1 ||
10462306a36Sopenharmony_ci		    sg_phys(&sgs[idx]) + sgs[idx].length != page_to_phys(pg)) {
10562306a36Sopenharmony_ci			if (idx + 1 == nsgs)
10662306a36Sopenharmony_ci				break;
10762306a36Sopenharmony_ci			sg_set_page(&sgs[++idx], pg, pg_length,
10862306a36Sopenharmony_ci				    offset_in_page(data));
10962306a36Sopenharmony_ci		} else {
11062306a36Sopenharmony_ci			sgs[idx].length += pg_length;
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci		data += pg_length;
11462306a36Sopenharmony_ci		length -= pg_length;
11562306a36Sopenharmony_ci	}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	sg_mark_end(&sgs[idx]);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/**
12162306a36Sopenharmony_ci * virtsnd_pcm_msg_alloc() - Allocate I/O messages.
12262306a36Sopenharmony_ci * @vss: VirtIO PCM substream.
12362306a36Sopenharmony_ci * @periods: Current number of periods.
12462306a36Sopenharmony_ci * @period_bytes: Current period size in bytes.
12562306a36Sopenharmony_ci *
12662306a36Sopenharmony_ci * The function slices the buffer into @periods parts (each with the size of
12762306a36Sopenharmony_ci * @period_bytes), and creates @periods corresponding I/O messages.
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Context: Any context that permits to sleep.
13062306a36Sopenharmony_ci * Return: 0 on success, -ENOMEM on failure.
13162306a36Sopenharmony_ci */
13262306a36Sopenharmony_ciint virtsnd_pcm_msg_alloc(struct virtio_pcm_substream *vss,
13362306a36Sopenharmony_ci			  unsigned int periods, unsigned int period_bytes)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = vss->substream->runtime;
13662306a36Sopenharmony_ci	unsigned int i;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	vss->msgs = kcalloc(periods, sizeof(*vss->msgs), GFP_KERNEL);
13962306a36Sopenharmony_ci	if (!vss->msgs)
14062306a36Sopenharmony_ci		return -ENOMEM;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	vss->nmsgs = periods;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	for (i = 0; i < periods; ++i) {
14562306a36Sopenharmony_ci		u8 *data = runtime->dma_area + period_bytes * i;
14662306a36Sopenharmony_ci		int sg_num = virtsnd_pcm_sg_num(data, period_bytes);
14762306a36Sopenharmony_ci		struct virtio_pcm_msg *msg;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		msg = kzalloc(struct_size(msg, sgs, sg_num + 2), GFP_KERNEL);
15062306a36Sopenharmony_ci		if (!msg)
15162306a36Sopenharmony_ci			return -ENOMEM;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		msg->substream = vss;
15462306a36Sopenharmony_ci		sg_init_one(&msg->sgs[PCM_MSG_SG_XFER], &msg->xfer,
15562306a36Sopenharmony_ci			    sizeof(msg->xfer));
15662306a36Sopenharmony_ci		sg_init_one(&msg->sgs[PCM_MSG_SG_STATUS], &msg->status,
15762306a36Sopenharmony_ci			    sizeof(msg->status));
15862306a36Sopenharmony_ci		msg->length = period_bytes;
15962306a36Sopenharmony_ci		virtsnd_pcm_sg_from(&msg->sgs[PCM_MSG_SG_DATA], sg_num, data,
16062306a36Sopenharmony_ci				    period_bytes);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		vss->msgs[i] = msg;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	return 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/**
16962306a36Sopenharmony_ci * virtsnd_pcm_msg_free() - Free all allocated I/O messages.
17062306a36Sopenharmony_ci * @vss: VirtIO PCM substream.
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci * Context: Any context.
17362306a36Sopenharmony_ci */
17462306a36Sopenharmony_civoid virtsnd_pcm_msg_free(struct virtio_pcm_substream *vss)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	unsigned int i;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	for (i = 0; vss->msgs && i < vss->nmsgs; ++i)
17962306a36Sopenharmony_ci		kfree(vss->msgs[i]);
18062306a36Sopenharmony_ci	kfree(vss->msgs);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	vss->msgs = NULL;
18362306a36Sopenharmony_ci	vss->nmsgs = 0;
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * virtsnd_pcm_msg_send() - Send asynchronous I/O messages.
18862306a36Sopenharmony_ci * @vss: VirtIO PCM substream.
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * All messages are organized in an ordered circular list. Each time the
19162306a36Sopenharmony_ci * function is called, all currently non-enqueued messages are added to the
19262306a36Sopenharmony_ci * virtqueue. For this, the function keeps track of two values:
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci *   msg_last_enqueued = index of the last enqueued message,
19562306a36Sopenharmony_ci *   msg_count = # of pending messages in the virtqueue.
19662306a36Sopenharmony_ci *
19762306a36Sopenharmony_ci * Context: Any context. Expects the tx/rx queue and the VirtIO substream
19862306a36Sopenharmony_ci *          spinlocks to be held by caller.
19962306a36Sopenharmony_ci * Return: 0 on success, -errno on failure.
20062306a36Sopenharmony_ci */
20162306a36Sopenharmony_ciint virtsnd_pcm_msg_send(struct virtio_pcm_substream *vss)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct snd_pcm_runtime *runtime = vss->substream->runtime;
20462306a36Sopenharmony_ci	struct virtio_snd *snd = vss->snd;
20562306a36Sopenharmony_ci	struct virtio_device *vdev = snd->vdev;
20662306a36Sopenharmony_ci	struct virtqueue *vqueue = virtsnd_pcm_queue(vss)->vqueue;
20762306a36Sopenharmony_ci	int i;
20862306a36Sopenharmony_ci	int n;
20962306a36Sopenharmony_ci	bool notify = false;
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	i = (vss->msg_last_enqueued + 1) % runtime->periods;
21262306a36Sopenharmony_ci	n = runtime->periods - vss->msg_count;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	for (; n; --n, i = (i + 1) % runtime->periods) {
21562306a36Sopenharmony_ci		struct virtio_pcm_msg *msg = vss->msgs[i];
21662306a36Sopenharmony_ci		struct scatterlist *psgs[] = {
21762306a36Sopenharmony_ci			&msg->sgs[PCM_MSG_SG_XFER],
21862306a36Sopenharmony_ci			&msg->sgs[PCM_MSG_SG_DATA],
21962306a36Sopenharmony_ci			&msg->sgs[PCM_MSG_SG_STATUS]
22062306a36Sopenharmony_ci		};
22162306a36Sopenharmony_ci		int rc;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci		msg->xfer.stream_id = cpu_to_le32(vss->sid);
22462306a36Sopenharmony_ci		memset(&msg->status, 0, sizeof(msg->status));
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci		if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK)
22762306a36Sopenharmony_ci			rc = virtqueue_add_sgs(vqueue, psgs, 2, 1, msg,
22862306a36Sopenharmony_ci					       GFP_ATOMIC);
22962306a36Sopenharmony_ci		else
23062306a36Sopenharmony_ci			rc = virtqueue_add_sgs(vqueue, psgs, 1, 2, msg,
23162306a36Sopenharmony_ci					       GFP_ATOMIC);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci		if (rc) {
23462306a36Sopenharmony_ci			dev_err(&vdev->dev,
23562306a36Sopenharmony_ci				"SID %u: failed to send I/O message\n",
23662306a36Sopenharmony_ci				vss->sid);
23762306a36Sopenharmony_ci			return rc;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		vss->msg_last_enqueued = i;
24162306a36Sopenharmony_ci		vss->msg_count++;
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	if (!(vss->features & (1U << VIRTIO_SND_PCM_F_MSG_POLLING)))
24562306a36Sopenharmony_ci		notify = virtqueue_kick_prepare(vqueue);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	if (notify)
24862306a36Sopenharmony_ci		virtqueue_notify(vqueue);
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return 0;
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci/**
25462306a36Sopenharmony_ci * virtsnd_pcm_msg_pending_num() - Returns the number of pending I/O messages.
25562306a36Sopenharmony_ci * @vss: VirtIO substream.
25662306a36Sopenharmony_ci *
25762306a36Sopenharmony_ci * Context: Any context.
25862306a36Sopenharmony_ci * Return: Number of messages.
25962306a36Sopenharmony_ci */
26062306a36Sopenharmony_ciunsigned int virtsnd_pcm_msg_pending_num(struct virtio_pcm_substream *vss)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	unsigned int num;
26362306a36Sopenharmony_ci	unsigned long flags;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	spin_lock_irqsave(&vss->lock, flags);
26662306a36Sopenharmony_ci	num = vss->msg_count;
26762306a36Sopenharmony_ci	spin_unlock_irqrestore(&vss->lock, flags);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return num;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci/**
27362306a36Sopenharmony_ci * virtsnd_pcm_msg_complete() - Complete an I/O message.
27462306a36Sopenharmony_ci * @msg: I/O message.
27562306a36Sopenharmony_ci * @written_bytes: Number of bytes written to the message.
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci * Completion of the message means the elapsed period. If transmission is
27862306a36Sopenharmony_ci * allowed, then each completed message is immediately placed back at the end
27962306a36Sopenharmony_ci * of the queue.
28062306a36Sopenharmony_ci *
28162306a36Sopenharmony_ci * For the playback substream, @written_bytes is equal to sizeof(msg->status).
28262306a36Sopenharmony_ci *
28362306a36Sopenharmony_ci * For the capture substream, @written_bytes is equal to sizeof(msg->status)
28462306a36Sopenharmony_ci * plus the number of captured bytes.
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * Context: Interrupt context. Takes and releases the VirtIO substream spinlock.
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistatic void virtsnd_pcm_msg_complete(struct virtio_pcm_msg *msg,
28962306a36Sopenharmony_ci				     size_t written_bytes)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct virtio_pcm_substream *vss = msg->substream;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	/*
29462306a36Sopenharmony_ci	 * hw_ptr always indicates the buffer position of the first I/O message
29562306a36Sopenharmony_ci	 * in the virtqueue. Therefore, on each completion of an I/O message,
29662306a36Sopenharmony_ci	 * the hw_ptr value is unconditionally advanced.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	spin_lock(&vss->lock);
29962306a36Sopenharmony_ci	/*
30062306a36Sopenharmony_ci	 * If the capture substream returned an incorrect status, then just
30162306a36Sopenharmony_ci	 * increase the hw_ptr by the message size.
30262306a36Sopenharmony_ci	 */
30362306a36Sopenharmony_ci	if (vss->direction == SNDRV_PCM_STREAM_PLAYBACK ||
30462306a36Sopenharmony_ci	    written_bytes <= sizeof(msg->status))
30562306a36Sopenharmony_ci		vss->hw_ptr += msg->length;
30662306a36Sopenharmony_ci	else
30762306a36Sopenharmony_ci		vss->hw_ptr += written_bytes - sizeof(msg->status);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (vss->hw_ptr >= vss->buffer_bytes)
31062306a36Sopenharmony_ci		vss->hw_ptr -= vss->buffer_bytes;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	vss->xfer_xrun = false;
31362306a36Sopenharmony_ci	vss->msg_count--;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (vss->xfer_enabled) {
31662306a36Sopenharmony_ci		struct snd_pcm_runtime *runtime = vss->substream->runtime;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		runtime->delay =
31962306a36Sopenharmony_ci			bytes_to_frames(runtime,
32062306a36Sopenharmony_ci					le32_to_cpu(msg->status.latency_bytes));
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		schedule_work(&vss->elapsed_period);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		virtsnd_pcm_msg_send(vss);
32562306a36Sopenharmony_ci	} else if (!vss->msg_count) {
32662306a36Sopenharmony_ci		wake_up_all(&vss->msg_empty);
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci	spin_unlock(&vss->lock);
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci/**
33262306a36Sopenharmony_ci * virtsnd_pcm_notify_cb() - Process all completed I/O messages.
33362306a36Sopenharmony_ci * @queue: Underlying tx/rx virtqueue.
33462306a36Sopenharmony_ci *
33562306a36Sopenharmony_ci * Context: Interrupt context. Takes and releases the tx/rx queue spinlock.
33662306a36Sopenharmony_ci */
33762306a36Sopenharmony_cistatic inline void virtsnd_pcm_notify_cb(struct virtio_snd_queue *queue)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	struct virtio_pcm_msg *msg;
34062306a36Sopenharmony_ci	u32 written_bytes;
34162306a36Sopenharmony_ci	unsigned long flags;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	spin_lock_irqsave(&queue->lock, flags);
34462306a36Sopenharmony_ci	do {
34562306a36Sopenharmony_ci		virtqueue_disable_cb(queue->vqueue);
34662306a36Sopenharmony_ci		while ((msg = virtqueue_get_buf(queue->vqueue, &written_bytes)))
34762306a36Sopenharmony_ci			virtsnd_pcm_msg_complete(msg, written_bytes);
34862306a36Sopenharmony_ci		if (unlikely(virtqueue_is_broken(queue->vqueue)))
34962306a36Sopenharmony_ci			break;
35062306a36Sopenharmony_ci	} while (!virtqueue_enable_cb(queue->vqueue));
35162306a36Sopenharmony_ci	spin_unlock_irqrestore(&queue->lock, flags);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci/**
35562306a36Sopenharmony_ci * virtsnd_pcm_tx_notify_cb() - Process all completed TX messages.
35662306a36Sopenharmony_ci * @vqueue: Underlying tx virtqueue.
35762306a36Sopenharmony_ci *
35862306a36Sopenharmony_ci * Context: Interrupt context.
35962306a36Sopenharmony_ci */
36062306a36Sopenharmony_civoid virtsnd_pcm_tx_notify_cb(struct virtqueue *vqueue)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct virtio_snd *snd = vqueue->vdev->priv;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	virtsnd_pcm_notify_cb(virtsnd_tx_queue(snd));
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/**
36862306a36Sopenharmony_ci * virtsnd_pcm_rx_notify_cb() - Process all completed RX messages.
36962306a36Sopenharmony_ci * @vqueue: Underlying rx virtqueue.
37062306a36Sopenharmony_ci *
37162306a36Sopenharmony_ci * Context: Interrupt context.
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_civoid virtsnd_pcm_rx_notify_cb(struct virtqueue *vqueue)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct virtio_snd *snd = vqueue->vdev->priv;
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	virtsnd_pcm_notify_cb(virtsnd_rx_queue(snd));
37862306a36Sopenharmony_ci}
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci/**
38162306a36Sopenharmony_ci * virtsnd_pcm_ctl_msg_alloc() - Allocate and initialize the PCM device control
38262306a36Sopenharmony_ci *                               message for the specified substream.
38362306a36Sopenharmony_ci * @vss: VirtIO PCM substream.
38462306a36Sopenharmony_ci * @command: Control request code (VIRTIO_SND_R_PCM_XXX).
38562306a36Sopenharmony_ci * @gfp: Kernel flags for memory allocation.
38662306a36Sopenharmony_ci *
38762306a36Sopenharmony_ci * Context: Any context. May sleep if @gfp flags permit.
38862306a36Sopenharmony_ci * Return: Allocated message on success, NULL on failure.
38962306a36Sopenharmony_ci */
39062306a36Sopenharmony_cistruct virtio_snd_msg *
39162306a36Sopenharmony_civirtsnd_pcm_ctl_msg_alloc(struct virtio_pcm_substream *vss,
39262306a36Sopenharmony_ci			  unsigned int command, gfp_t gfp)
39362306a36Sopenharmony_ci{
39462306a36Sopenharmony_ci	size_t request_size = sizeof(struct virtio_snd_pcm_hdr);
39562306a36Sopenharmony_ci	size_t response_size = sizeof(struct virtio_snd_hdr);
39662306a36Sopenharmony_ci	struct virtio_snd_msg *msg;
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	switch (command) {
39962306a36Sopenharmony_ci	case VIRTIO_SND_R_PCM_SET_PARAMS:
40062306a36Sopenharmony_ci		request_size = sizeof(struct virtio_snd_pcm_set_params);
40162306a36Sopenharmony_ci		break;
40262306a36Sopenharmony_ci	}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	msg = virtsnd_ctl_msg_alloc(request_size, response_size, gfp);
40562306a36Sopenharmony_ci	if (msg) {
40662306a36Sopenharmony_ci		struct virtio_snd_pcm_hdr *hdr = virtsnd_ctl_msg_request(msg);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci		hdr->hdr.code = cpu_to_le32(command);
40962306a36Sopenharmony_ci		hdr->stream_id = cpu_to_le32(vss->sid);
41062306a36Sopenharmony_ci	}
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return msg;
41362306a36Sopenharmony_ci}
414