162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci    buffer queues.
462306a36Sopenharmony_ci    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
562306a36Sopenharmony_ci    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
662306a36Sopenharmony_ci    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "ivtv-driver.h"
1162306a36Sopenharmony_ci#include "ivtv-queue.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ciint ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	if (s->buf_size - buf->bytesused < copybytes)
1662306a36Sopenharmony_ci		copybytes = s->buf_size - buf->bytesused;
1762306a36Sopenharmony_ci	if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
1862306a36Sopenharmony_ci		return -EFAULT;
1962306a36Sopenharmony_ci	}
2062306a36Sopenharmony_ci	buf->bytesused += copybytes;
2162306a36Sopenharmony_ci	return copybytes;
2262306a36Sopenharmony_ci}
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_civoid ivtv_buf_swap(struct ivtv_buffer *buf)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	int i;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	for (i = 0; i < buf->bytesused; i += 4)
2962306a36Sopenharmony_ci		swab32s((u32 *)(buf->buf + i));
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_civoid ivtv_queue_init(struct ivtv_queue *q)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	INIT_LIST_HEAD(&q->list);
3562306a36Sopenharmony_ci	q->buffers = 0;
3662306a36Sopenharmony_ci	q->length = 0;
3762306a36Sopenharmony_ci	q->bytesused = 0;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	unsigned long flags;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	/* clear the buffer if it is going to be enqueued to the free queue */
4562306a36Sopenharmony_ci	if (q == &s->q_free) {
4662306a36Sopenharmony_ci		buf->bytesused = 0;
4762306a36Sopenharmony_ci		buf->readpos = 0;
4862306a36Sopenharmony_ci		buf->b_flags = 0;
4962306a36Sopenharmony_ci		buf->dma_xfer_cnt = 0;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci	spin_lock_irqsave(&s->qlock, flags);
5262306a36Sopenharmony_ci	list_add_tail(&buf->list, &q->list);
5362306a36Sopenharmony_ci	q->buffers++;
5462306a36Sopenharmony_ci	q->length += s->buf_size;
5562306a36Sopenharmony_ci	q->bytesused += buf->bytesused - buf->readpos;
5662306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->qlock, flags);
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistruct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct ivtv_buffer *buf = NULL;
6262306a36Sopenharmony_ci	unsigned long flags;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	spin_lock_irqsave(&s->qlock, flags);
6562306a36Sopenharmony_ci	if (!list_empty(&q->list)) {
6662306a36Sopenharmony_ci		buf = list_entry(q->list.next, struct ivtv_buffer, list);
6762306a36Sopenharmony_ci		list_del_init(q->list.next);
6862306a36Sopenharmony_ci		q->buffers--;
6962306a36Sopenharmony_ci		q->length -= s->buf_size;
7062306a36Sopenharmony_ci		q->bytesused -= buf->bytesused - buf->readpos;
7162306a36Sopenharmony_ci	}
7262306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->qlock, flags);
7362306a36Sopenharmony_ci	return buf;
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
7762306a36Sopenharmony_ci		struct ivtv_queue *to, int clear)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	list_move_tail(from->list.next, &to->list);
8262306a36Sopenharmony_ci	from->buffers--;
8362306a36Sopenharmony_ci	from->length -= s->buf_size;
8462306a36Sopenharmony_ci	from->bytesused -= buf->bytesused - buf->readpos;
8562306a36Sopenharmony_ci	/* special handling for q_free */
8662306a36Sopenharmony_ci	if (clear)
8762306a36Sopenharmony_ci		buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
8862306a36Sopenharmony_ci	to->buffers++;
8962306a36Sopenharmony_ci	to->length += s->buf_size;
9062306a36Sopenharmony_ci	to->bytesused += buf->bytesused - buf->readpos;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
9462306a36Sopenharmony_ci   If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
9562306a36Sopenharmony_ci   If 'steal' != NULL, then buffers may also taken from that queue if
9662306a36Sopenharmony_ci   needed, but only if 'from' is the free queue.
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci   The buffer is automatically cleared if it goes to the free queue. It is
9962306a36Sopenharmony_ci   also cleared if buffers need to be taken from the 'steal' queue and
10062306a36Sopenharmony_ci   the 'from' queue is the free queue.
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci   When 'from' is q_free, then needed_bytes is compared to the total
10362306a36Sopenharmony_ci   available buffer length, otherwise needed_bytes is compared to the
10462306a36Sopenharmony_ci   bytesused value. For the 'steal' queue the total available buffer
10562306a36Sopenharmony_ci   length is always used.
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci   -ENOMEM is returned if the buffers could not be obtained, 0 if all
10862306a36Sopenharmony_ci   buffers where obtained from the 'from' list and if non-zero then
10962306a36Sopenharmony_ci   the number of stolen buffers is returned. */
11062306a36Sopenharmony_ciint ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
11162306a36Sopenharmony_ci		    struct ivtv_queue *to, int needed_bytes)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	unsigned long flags;
11462306a36Sopenharmony_ci	int rc = 0;
11562306a36Sopenharmony_ci	int from_free = from == &s->q_free;
11662306a36Sopenharmony_ci	int to_free = to == &s->q_free;
11762306a36Sopenharmony_ci	int bytes_available, bytes_steal;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	spin_lock_irqsave(&s->qlock, flags);
12062306a36Sopenharmony_ci	if (needed_bytes == 0) {
12162306a36Sopenharmony_ci		from_free = 1;
12262306a36Sopenharmony_ci		needed_bytes = from->length;
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	bytes_available = from_free ? from->length : from->bytesused;
12662306a36Sopenharmony_ci	bytes_steal = (from_free && steal) ? steal->length : 0;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	if (bytes_available + bytes_steal < needed_bytes) {
12962306a36Sopenharmony_ci		spin_unlock_irqrestore(&s->qlock, flags);
13062306a36Sopenharmony_ci		return -ENOMEM;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci	while (steal && bytes_available < needed_bytes) {
13362306a36Sopenharmony_ci		struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
13462306a36Sopenharmony_ci		u16 dma_xfer_cnt = buf->dma_xfer_cnt;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci		/* move buffers from the tail of the 'steal' queue to the tail of the
13762306a36Sopenharmony_ci		   'from' queue. Always copy all the buffers with the same dma_xfer_cnt
13862306a36Sopenharmony_ci		   value, this ensures that you do not end up with partial frame data
13962306a36Sopenharmony_ci		   if one frame is stored in multiple buffers. */
14062306a36Sopenharmony_ci		while (dma_xfer_cnt == buf->dma_xfer_cnt) {
14162306a36Sopenharmony_ci			list_move_tail(steal->list.prev, &from->list);
14262306a36Sopenharmony_ci			rc++;
14362306a36Sopenharmony_ci			steal->buffers--;
14462306a36Sopenharmony_ci			steal->length -= s->buf_size;
14562306a36Sopenharmony_ci			steal->bytesused -= buf->bytesused - buf->readpos;
14662306a36Sopenharmony_ci			buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
14762306a36Sopenharmony_ci			from->buffers++;
14862306a36Sopenharmony_ci			from->length += s->buf_size;
14962306a36Sopenharmony_ci			bytes_available += s->buf_size;
15062306a36Sopenharmony_ci			if (list_empty(&steal->list))
15162306a36Sopenharmony_ci				break;
15262306a36Sopenharmony_ci			buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
15362306a36Sopenharmony_ci		}
15462306a36Sopenharmony_ci	}
15562306a36Sopenharmony_ci	if (from_free) {
15662306a36Sopenharmony_ci		u32 old_length = to->length;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		while (to->length - old_length < needed_bytes) {
15962306a36Sopenharmony_ci			ivtv_queue_move_buf(s, from, to, 1);
16062306a36Sopenharmony_ci		}
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci	else {
16362306a36Sopenharmony_ci		u32 old_bytesused = to->bytesused;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci		while (to->bytesused - old_bytesused < needed_bytes) {
16662306a36Sopenharmony_ci			ivtv_queue_move_buf(s, from, to, to_free);
16762306a36Sopenharmony_ci		}
16862306a36Sopenharmony_ci	}
16962306a36Sopenharmony_ci	spin_unlock_irqrestore(&s->qlock, flags);
17062306a36Sopenharmony_ci	return rc;
17162306a36Sopenharmony_ci}
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_civoid ivtv_flush_queues(struct ivtv_stream *s)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
17662306a36Sopenharmony_ci	ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
17762306a36Sopenharmony_ci	ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
17862306a36Sopenharmony_ci	ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ciint ivtv_stream_alloc(struct ivtv_stream *s)
18262306a36Sopenharmony_ci{
18362306a36Sopenharmony_ci	struct ivtv *itv = s->itv;
18462306a36Sopenharmony_ci	int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers;
18562306a36Sopenharmony_ci	int i;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (s->buffers == 0)
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
19162306a36Sopenharmony_ci		s->dma != DMA_NONE ? "DMA " : "",
19262306a36Sopenharmony_ci		s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
19562306a36Sopenharmony_ci	if (s->sg_pending == NULL) {
19662306a36Sopenharmony_ci		IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name);
19762306a36Sopenharmony_ci		return -ENOMEM;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci	s->sg_pending_size = 0;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
20262306a36Sopenharmony_ci	if (s->sg_processing == NULL) {
20362306a36Sopenharmony_ci		IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name);
20462306a36Sopenharmony_ci		kfree(s->sg_pending);
20562306a36Sopenharmony_ci		s->sg_pending = NULL;
20662306a36Sopenharmony_ci		return -ENOMEM;
20762306a36Sopenharmony_ci	}
20862306a36Sopenharmony_ci	s->sg_processing_size = 0;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element),
21162306a36Sopenharmony_ci					GFP_KERNEL|__GFP_NOWARN);
21262306a36Sopenharmony_ci	if (s->sg_dma == NULL) {
21362306a36Sopenharmony_ci		IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name);
21462306a36Sopenharmony_ci		kfree(s->sg_pending);
21562306a36Sopenharmony_ci		s->sg_pending = NULL;
21662306a36Sopenharmony_ci		kfree(s->sg_processing);
21762306a36Sopenharmony_ci		s->sg_processing = NULL;
21862306a36Sopenharmony_ci		return -ENOMEM;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	if (ivtv_might_use_dma(s)) {
22162306a36Sopenharmony_ci		s->sg_handle = dma_map_single(&itv->pdev->dev, s->sg_dma,
22262306a36Sopenharmony_ci					      sizeof(struct ivtv_sg_element),
22362306a36Sopenharmony_ci					      DMA_TO_DEVICE);
22462306a36Sopenharmony_ci		ivtv_stream_sync_for_cpu(s);
22562306a36Sopenharmony_ci	}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* allocate stream buffers. Initially all buffers are in q_free. */
22862306a36Sopenharmony_ci	for (i = 0; i < s->buffers; i++) {
22962306a36Sopenharmony_ci		struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer),
23062306a36Sopenharmony_ci						GFP_KERNEL|__GFP_NOWARN);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci		if (buf == NULL)
23362306a36Sopenharmony_ci			break;
23462306a36Sopenharmony_ci		buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN);
23562306a36Sopenharmony_ci		if (buf->buf == NULL) {
23662306a36Sopenharmony_ci			kfree(buf);
23762306a36Sopenharmony_ci			break;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci		INIT_LIST_HEAD(&buf->list);
24062306a36Sopenharmony_ci		if (ivtv_might_use_dma(s)) {
24162306a36Sopenharmony_ci			buf->dma_handle = dma_map_single(&s->itv->pdev->dev,
24262306a36Sopenharmony_ci				buf->buf, s->buf_size + 256, s->dma);
24362306a36Sopenharmony_ci			ivtv_buf_sync_for_cpu(s, buf);
24462306a36Sopenharmony_ci		}
24562306a36Sopenharmony_ci		ivtv_enqueue(s, buf, &s->q_free);
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	if (i == s->buffers)
24862306a36Sopenharmony_ci		return 0;
24962306a36Sopenharmony_ci	IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
25062306a36Sopenharmony_ci	ivtv_stream_free(s);
25162306a36Sopenharmony_ci	return -ENOMEM;
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_civoid ivtv_stream_free(struct ivtv_stream *s)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	struct ivtv_buffer *buf;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* move all buffers to q_free */
25962306a36Sopenharmony_ci	ivtv_flush_queues(s);
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/* empty q_free */
26262306a36Sopenharmony_ci	while ((buf = ivtv_dequeue(s, &s->q_free))) {
26362306a36Sopenharmony_ci		if (ivtv_might_use_dma(s))
26462306a36Sopenharmony_ci			dma_unmap_single(&s->itv->pdev->dev, buf->dma_handle,
26562306a36Sopenharmony_ci					 s->buf_size + 256, s->dma);
26662306a36Sopenharmony_ci		kfree(buf->buf);
26762306a36Sopenharmony_ci		kfree(buf);
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	/* Free SG Array/Lists */
27162306a36Sopenharmony_ci	if (s->sg_dma != NULL) {
27262306a36Sopenharmony_ci		if (s->sg_handle != IVTV_DMA_UNMAPPED) {
27362306a36Sopenharmony_ci			dma_unmap_single(&s->itv->pdev->dev, s->sg_handle,
27462306a36Sopenharmony_ci					 sizeof(struct ivtv_sg_element),
27562306a36Sopenharmony_ci					 DMA_TO_DEVICE);
27662306a36Sopenharmony_ci			s->sg_handle = IVTV_DMA_UNMAPPED;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci		kfree(s->sg_pending);
27962306a36Sopenharmony_ci		kfree(s->sg_processing);
28062306a36Sopenharmony_ci		kfree(s->sg_dma);
28162306a36Sopenharmony_ci		s->sg_pending = NULL;
28262306a36Sopenharmony_ci		s->sg_processing = NULL;
28362306a36Sopenharmony_ci		s->sg_dma = NULL;
28462306a36Sopenharmony_ci		s->sg_pending_size = 0;
28562306a36Sopenharmony_ci		s->sg_processing_size = 0;
28662306a36Sopenharmony_ci	}
28762306a36Sopenharmony_ci}
288