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