18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci    buffer queues.
48c2ecf20Sopenharmony_ci    Copyright (C) 2003-2004  Kevin Thayer <nufan_wfk at yahoo.com>
58c2ecf20Sopenharmony_ci    Copyright (C) 2004  Chris Kennedy <c@groovy.org>
68c2ecf20Sopenharmony_ci    Copyright (C) 2005-2007  Hans Verkuil <hverkuil@xs4all.nl>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "ivtv-driver.h"
118c2ecf20Sopenharmony_ci#include "ivtv-queue.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ciint ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
148c2ecf20Sopenharmony_ci{
158c2ecf20Sopenharmony_ci	if (s->buf_size - buf->bytesused < copybytes)
168c2ecf20Sopenharmony_ci		copybytes = s->buf_size - buf->bytesused;
178c2ecf20Sopenharmony_ci	if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
188c2ecf20Sopenharmony_ci		return -EFAULT;
198c2ecf20Sopenharmony_ci	}
208c2ecf20Sopenharmony_ci	buf->bytesused += copybytes;
218c2ecf20Sopenharmony_ci	return copybytes;
228c2ecf20Sopenharmony_ci}
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_civoid ivtv_buf_swap(struct ivtv_buffer *buf)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	int i;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	for (i = 0; i < buf->bytesused; i += 4)
298c2ecf20Sopenharmony_ci		swab32s((u32 *)(buf->buf + i));
308c2ecf20Sopenharmony_ci}
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_civoid ivtv_queue_init(struct ivtv_queue *q)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&q->list);
358c2ecf20Sopenharmony_ci	q->buffers = 0;
368c2ecf20Sopenharmony_ci	q->length = 0;
378c2ecf20Sopenharmony_ci	q->bytesused = 0;
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_civoid ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	unsigned long flags;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	/* clear the buffer if it is going to be enqueued to the free queue */
458c2ecf20Sopenharmony_ci	if (q == &s->q_free) {
468c2ecf20Sopenharmony_ci		buf->bytesused = 0;
478c2ecf20Sopenharmony_ci		buf->readpos = 0;
488c2ecf20Sopenharmony_ci		buf->b_flags = 0;
498c2ecf20Sopenharmony_ci		buf->dma_xfer_cnt = 0;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->qlock, flags);
528c2ecf20Sopenharmony_ci	list_add_tail(&buf->list, &q->list);
538c2ecf20Sopenharmony_ci	q->buffers++;
548c2ecf20Sopenharmony_ci	q->length += s->buf_size;
558c2ecf20Sopenharmony_ci	q->bytesused += buf->bytesused - buf->readpos;
568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->qlock, flags);
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistruct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct ivtv_buffer *buf = NULL;
628c2ecf20Sopenharmony_ci	unsigned long flags;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->qlock, flags);
658c2ecf20Sopenharmony_ci	if (!list_empty(&q->list)) {
668c2ecf20Sopenharmony_ci		buf = list_entry(q->list.next, struct ivtv_buffer, list);
678c2ecf20Sopenharmony_ci		list_del_init(q->list.next);
688c2ecf20Sopenharmony_ci		q->buffers--;
698c2ecf20Sopenharmony_ci		q->length -= s->buf_size;
708c2ecf20Sopenharmony_ci		q->bytesused -= buf->bytesused - buf->readpos;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->qlock, flags);
738c2ecf20Sopenharmony_ci	return buf;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
778c2ecf20Sopenharmony_ci		struct ivtv_queue *to, int clear)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	list_move_tail(from->list.next, &to->list);
828c2ecf20Sopenharmony_ci	from->buffers--;
838c2ecf20Sopenharmony_ci	from->length -= s->buf_size;
848c2ecf20Sopenharmony_ci	from->bytesused -= buf->bytesused - buf->readpos;
858c2ecf20Sopenharmony_ci	/* special handling for q_free */
868c2ecf20Sopenharmony_ci	if (clear)
878c2ecf20Sopenharmony_ci		buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
888c2ecf20Sopenharmony_ci	to->buffers++;
898c2ecf20Sopenharmony_ci	to->length += s->buf_size;
908c2ecf20Sopenharmony_ci	to->bytesused += buf->bytesused - buf->readpos;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
948c2ecf20Sopenharmony_ci   If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
958c2ecf20Sopenharmony_ci   If 'steal' != NULL, then buffers may also taken from that queue if
968c2ecf20Sopenharmony_ci   needed, but only if 'from' is the free queue.
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci   The buffer is automatically cleared if it goes to the free queue. It is
998c2ecf20Sopenharmony_ci   also cleared if buffers need to be taken from the 'steal' queue and
1008c2ecf20Sopenharmony_ci   the 'from' queue is the free queue.
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci   When 'from' is q_free, then needed_bytes is compared to the total
1038c2ecf20Sopenharmony_ci   available buffer length, otherwise needed_bytes is compared to the
1048c2ecf20Sopenharmony_ci   bytesused value. For the 'steal' queue the total available buffer
1058c2ecf20Sopenharmony_ci   length is always used.
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci   -ENOMEM is returned if the buffers could not be obtained, 0 if all
1088c2ecf20Sopenharmony_ci   buffers where obtained from the 'from' list and if non-zero then
1098c2ecf20Sopenharmony_ci   the number of stolen buffers is returned. */
1108c2ecf20Sopenharmony_ciint ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
1118c2ecf20Sopenharmony_ci		    struct ivtv_queue *to, int needed_bytes)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	unsigned long flags;
1148c2ecf20Sopenharmony_ci	int rc = 0;
1158c2ecf20Sopenharmony_ci	int from_free = from == &s->q_free;
1168c2ecf20Sopenharmony_ci	int to_free = to == &s->q_free;
1178c2ecf20Sopenharmony_ci	int bytes_available, bytes_steal;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	spin_lock_irqsave(&s->qlock, flags);
1208c2ecf20Sopenharmony_ci	if (needed_bytes == 0) {
1218c2ecf20Sopenharmony_ci		from_free = 1;
1228c2ecf20Sopenharmony_ci		needed_bytes = from->length;
1238c2ecf20Sopenharmony_ci	}
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	bytes_available = from_free ? from->length : from->bytesused;
1268c2ecf20Sopenharmony_ci	bytes_steal = (from_free && steal) ? steal->length : 0;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	if (bytes_available + bytes_steal < needed_bytes) {
1298c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&s->qlock, flags);
1308c2ecf20Sopenharmony_ci		return -ENOMEM;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	while (steal && bytes_available < needed_bytes) {
1338c2ecf20Sopenharmony_ci		struct ivtv_buffer *buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
1348c2ecf20Sopenharmony_ci		u16 dma_xfer_cnt = buf->dma_xfer_cnt;
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		/* move buffers from the tail of the 'steal' queue to the tail of the
1378c2ecf20Sopenharmony_ci		   'from' queue. Always copy all the buffers with the same dma_xfer_cnt
1388c2ecf20Sopenharmony_ci		   value, this ensures that you do not end up with partial frame data
1398c2ecf20Sopenharmony_ci		   if one frame is stored in multiple buffers. */
1408c2ecf20Sopenharmony_ci		while (dma_xfer_cnt == buf->dma_xfer_cnt) {
1418c2ecf20Sopenharmony_ci			list_move_tail(steal->list.prev, &from->list);
1428c2ecf20Sopenharmony_ci			rc++;
1438c2ecf20Sopenharmony_ci			steal->buffers--;
1448c2ecf20Sopenharmony_ci			steal->length -= s->buf_size;
1458c2ecf20Sopenharmony_ci			steal->bytesused -= buf->bytesused - buf->readpos;
1468c2ecf20Sopenharmony_ci			buf->bytesused = buf->readpos = buf->b_flags = buf->dma_xfer_cnt = 0;
1478c2ecf20Sopenharmony_ci			from->buffers++;
1488c2ecf20Sopenharmony_ci			from->length += s->buf_size;
1498c2ecf20Sopenharmony_ci			bytes_available += s->buf_size;
1508c2ecf20Sopenharmony_ci			if (list_empty(&steal->list))
1518c2ecf20Sopenharmony_ci				break;
1528c2ecf20Sopenharmony_ci			buf = list_entry(steal->list.prev, struct ivtv_buffer, list);
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci	if (from_free) {
1568c2ecf20Sopenharmony_ci		u32 old_length = to->length;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci		while (to->length - old_length < needed_bytes) {
1598c2ecf20Sopenharmony_ci			ivtv_queue_move_buf(s, from, to, 1);
1608c2ecf20Sopenharmony_ci		}
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci	else {
1638c2ecf20Sopenharmony_ci		u32 old_bytesused = to->bytesused;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		while (to->bytesused - old_bytesused < needed_bytes) {
1668c2ecf20Sopenharmony_ci			ivtv_queue_move_buf(s, from, to, to_free);
1678c2ecf20Sopenharmony_ci		}
1688c2ecf20Sopenharmony_ci	}
1698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&s->qlock, flags);
1708c2ecf20Sopenharmony_ci	return rc;
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_civoid ivtv_flush_queues(struct ivtv_stream *s)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
1768c2ecf20Sopenharmony_ci	ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
1778c2ecf20Sopenharmony_ci	ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
1788c2ecf20Sopenharmony_ci	ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
1798c2ecf20Sopenharmony_ci}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ciint ivtv_stream_alloc(struct ivtv_stream *s)
1828c2ecf20Sopenharmony_ci{
1838c2ecf20Sopenharmony_ci	struct ivtv *itv = s->itv;
1848c2ecf20Sopenharmony_ci	int SGsize = sizeof(struct ivtv_sg_host_element) * s->buffers;
1858c2ecf20Sopenharmony_ci	int i;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	if (s->buffers == 0)
1888c2ecf20Sopenharmony_ci		return 0;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
1918c2ecf20Sopenharmony_ci		s->dma != PCI_DMA_NONE ? "DMA " : "",
1928c2ecf20Sopenharmony_ci		s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	s->sg_pending = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
1958c2ecf20Sopenharmony_ci	if (s->sg_pending == NULL) {
1968c2ecf20Sopenharmony_ci		IVTV_ERR("Could not allocate sg_pending for %s stream\n", s->name);
1978c2ecf20Sopenharmony_ci		return -ENOMEM;
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci	s->sg_pending_size = 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	s->sg_processing = kzalloc(SGsize, GFP_KERNEL|__GFP_NOWARN);
2028c2ecf20Sopenharmony_ci	if (s->sg_processing == NULL) {
2038c2ecf20Sopenharmony_ci		IVTV_ERR("Could not allocate sg_processing for %s stream\n", s->name);
2048c2ecf20Sopenharmony_ci		kfree(s->sg_pending);
2058c2ecf20Sopenharmony_ci		s->sg_pending = NULL;
2068c2ecf20Sopenharmony_ci		return -ENOMEM;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci	s->sg_processing_size = 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	s->sg_dma = kzalloc(sizeof(struct ivtv_sg_element),
2118c2ecf20Sopenharmony_ci					GFP_KERNEL|__GFP_NOWARN);
2128c2ecf20Sopenharmony_ci	if (s->sg_dma == NULL) {
2138c2ecf20Sopenharmony_ci		IVTV_ERR("Could not allocate sg_dma for %s stream\n", s->name);
2148c2ecf20Sopenharmony_ci		kfree(s->sg_pending);
2158c2ecf20Sopenharmony_ci		s->sg_pending = NULL;
2168c2ecf20Sopenharmony_ci		kfree(s->sg_processing);
2178c2ecf20Sopenharmony_ci		s->sg_processing = NULL;
2188c2ecf20Sopenharmony_ci		return -ENOMEM;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	if (ivtv_might_use_dma(s)) {
2218c2ecf20Sopenharmony_ci		s->sg_handle = pci_map_single(itv->pdev, s->sg_dma,
2228c2ecf20Sopenharmony_ci				sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
2238c2ecf20Sopenharmony_ci		ivtv_stream_sync_for_cpu(s);
2248c2ecf20Sopenharmony_ci	}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* allocate stream buffers. Initially all buffers are in q_free. */
2278c2ecf20Sopenharmony_ci	for (i = 0; i < s->buffers; i++) {
2288c2ecf20Sopenharmony_ci		struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer),
2298c2ecf20Sopenharmony_ci						GFP_KERNEL|__GFP_NOWARN);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci		if (buf == NULL)
2328c2ecf20Sopenharmony_ci			break;
2338c2ecf20Sopenharmony_ci		buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL|__GFP_NOWARN);
2348c2ecf20Sopenharmony_ci		if (buf->buf == NULL) {
2358c2ecf20Sopenharmony_ci			kfree(buf);
2368c2ecf20Sopenharmony_ci			break;
2378c2ecf20Sopenharmony_ci		}
2388c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&buf->list);
2398c2ecf20Sopenharmony_ci		if (ivtv_might_use_dma(s)) {
2408c2ecf20Sopenharmony_ci			buf->dma_handle = pci_map_single(s->itv->pdev,
2418c2ecf20Sopenharmony_ci				buf->buf, s->buf_size + 256, s->dma);
2428c2ecf20Sopenharmony_ci			ivtv_buf_sync_for_cpu(s, buf);
2438c2ecf20Sopenharmony_ci		}
2448c2ecf20Sopenharmony_ci		ivtv_enqueue(s, buf, &s->q_free);
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	if (i == s->buffers)
2478c2ecf20Sopenharmony_ci		return 0;
2488c2ecf20Sopenharmony_ci	IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
2498c2ecf20Sopenharmony_ci	ivtv_stream_free(s);
2508c2ecf20Sopenharmony_ci	return -ENOMEM;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_civoid ivtv_stream_free(struct ivtv_stream *s)
2548c2ecf20Sopenharmony_ci{
2558c2ecf20Sopenharmony_ci	struct ivtv_buffer *buf;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	/* move all buffers to q_free */
2588c2ecf20Sopenharmony_ci	ivtv_flush_queues(s);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	/* empty q_free */
2618c2ecf20Sopenharmony_ci	while ((buf = ivtv_dequeue(s, &s->q_free))) {
2628c2ecf20Sopenharmony_ci		if (ivtv_might_use_dma(s))
2638c2ecf20Sopenharmony_ci			pci_unmap_single(s->itv->pdev, buf->dma_handle,
2648c2ecf20Sopenharmony_ci				s->buf_size + 256, s->dma);
2658c2ecf20Sopenharmony_ci		kfree(buf->buf);
2668c2ecf20Sopenharmony_ci		kfree(buf);
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	/* Free SG Array/Lists */
2708c2ecf20Sopenharmony_ci	if (s->sg_dma != NULL) {
2718c2ecf20Sopenharmony_ci		if (s->sg_handle != IVTV_DMA_UNMAPPED) {
2728c2ecf20Sopenharmony_ci			pci_unmap_single(s->itv->pdev, s->sg_handle,
2738c2ecf20Sopenharmony_ci				 sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
2748c2ecf20Sopenharmony_ci			s->sg_handle = IVTV_DMA_UNMAPPED;
2758c2ecf20Sopenharmony_ci		}
2768c2ecf20Sopenharmony_ci		kfree(s->sg_pending);
2778c2ecf20Sopenharmony_ci		kfree(s->sg_processing);
2788c2ecf20Sopenharmony_ci		kfree(s->sg_dma);
2798c2ecf20Sopenharmony_ci		s->sg_pending = NULL;
2808c2ecf20Sopenharmony_ci		s->sg_processing = NULL;
2818c2ecf20Sopenharmony_ci		s->sg_dma = NULL;
2828c2ecf20Sopenharmony_ci		s->sg_pending_size = 0;
2838c2ecf20Sopenharmony_ci		s->sg_processing_size = 0;
2848c2ecf20Sopenharmony_ci	}
2858c2ecf20Sopenharmony_ci}
286