4 * Derived from ivtv-queue.c
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 #include "cx18-driver.h"
25 #include "cx18-streams.h"
26 #include "cx18-queue.h"
29 void cx18_buf_swap(struct cx18_buffer *buf)
33 for (i = 0; i < buf->bytesused; i += 4)
34 swab32s((u32 *)(buf->buf + i));
37 void cx18_queue_init(struct cx18_queue *q)
39 INIT_LIST_HEAD(&q->list);
45 void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
48 unsigned long flags = 0;
50 /* clear the buffer if it is going to be enqueued to the free queue */
51 if (q == &s->q_free) {
56 spin_lock_irqsave(&s->qlock, flags);
57 list_add_tail(&buf->list, &q->list);
59 q->length += s->buf_size;
60 q->bytesused += buf->bytesused - buf->readpos;
61 spin_unlock_irqrestore(&s->qlock, flags);
64 struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
66 struct cx18_buffer *buf = NULL;
67 unsigned long flags = 0;
69 spin_lock_irqsave(&s->qlock, flags);
70 if (!list_empty(&q->list)) {
71 buf = list_entry(q->list.next, struct cx18_buffer, list);
72 list_del_init(q->list.next);
74 q->length -= s->buf_size;
75 q->bytesused -= buf->bytesused - buf->readpos;
77 spin_unlock_irqrestore(&s->qlock, flags);
81 struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
84 struct cx18 *cx = s->cx;
87 list_for_each(p, &s->q_free.list) {
88 struct cx18_buffer *buf =
89 list_entry(p, struct cx18_buffer, list);
93 buf->bytesused = bytesused;
94 /* the transport buffers are handled differently,
95 so there is no need to move them to the full queue */
96 if (s->type == CX18_ENC_STREAM_TYPE_TS)
99 s->q_free.length -= s->buf_size;
101 s->q_full.length += s->buf_size;
102 s->q_full.bytesused += buf->bytesused;
103 list_move_tail(&buf->list, &s->q_full.list);
106 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
110 static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
111 struct cx18_queue *to, int clear, int full)
113 struct cx18_buffer *buf =
114 list_entry(from->list.next, struct cx18_buffer, list);
116 list_move_tail(from->list.next, &to->list);
118 from->length -= s->buf_size;
119 from->bytesused -= buf->bytesused - buf->readpos;
120 /* special handling for q_free */
122 buf->bytesused = buf->readpos = buf->b_flags = 0;
124 /* special handling for stolen buffers, assume
125 all bytes are used. */
126 buf->bytesused = s->buf_size;
127 buf->readpos = buf->b_flags = 0;
130 to->length += s->buf_size;
131 to->bytesused += buf->bytesused - buf->readpos;
134 /* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
135 If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
136 If 'steal' != NULL, then buffers may also taken from that queue if
139 The buffer is automatically cleared if it goes to the free queue. It is
140 also cleared if buffers need to be taken from the 'steal' queue and
141 the 'from' queue is the free queue.
143 When 'from' is q_free, then needed_bytes is compared to the total
144 available buffer length, otherwise needed_bytes is compared to the
145 bytesused value. For the 'steal' queue the total available buffer
146 length is always used.
148 -ENOMEM is returned if the buffers could not be obtained, 0 if all
149 buffers where obtained from the 'from' list and if non-zero then
150 the number of stolen buffers is returned. */
151 static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
152 struct cx18_queue *steal, struct cx18_queue *to,
157 int from_free = from == &s->q_free;
158 int to_free = to == &s->q_free;
161 spin_lock_irqsave(&s->qlock, flags);
162 if (needed_bytes == 0) {
164 needed_bytes = from->length;
167 bytes_available = from_free ? from->length : from->bytesused;
168 bytes_available += steal ? steal->length : 0;
170 if (bytes_available < needed_bytes) {
171 spin_unlock_irqrestore(&s->qlock, flags);
175 u32 old_length = to->length;
177 while (to->length - old_length < needed_bytes) {
178 if (list_empty(&from->list))
181 rc++; /* keep track of 'stolen' buffers */
182 cx18_queue_move_buf(s, from, to, 1, 0);
185 u32 old_bytesused = to->bytesused;
187 while (to->bytesused - old_bytesused < needed_bytes) {
188 if (list_empty(&from->list))
191 rc++; /* keep track of 'stolen' buffers */
192 cx18_queue_move_buf(s, from, to, to_free, rc);
195 spin_unlock_irqrestore(&s->qlock, flags);
199 void cx18_flush_queues(struct cx18_stream *s)
201 cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
202 cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
205 int cx18_stream_alloc(struct cx18_stream *s)
207 struct cx18 *cx = s->cx;
213 CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
214 s->name, s->buffers, s->buf_size,
215 s->buffers * s->buf_size / 1024);
217 if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
218 (char *)cx->scb) > SCB_RESERVED_SIZE) {
219 unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE -
220 ((char *)cx->scb->cpu_mdl));
222 CX18_ERR("Too many buffers, cannot fit in SCB area\n");
223 CX18_ERR("Max buffers = %zd\n",
224 bufsz / sizeof(struct cx18_mdl));
228 s->mdl_offset = cx->mdl_offset;
230 /* allocate stream buffers. Initially all buffers are in q_free. */
231 for (i = 0; i < s->buffers; i++) {
232 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
233 GFP_KERNEL|__GFP_NOWARN);
237 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
238 if (buf->buf == NULL) {
242 buf->id = cx->buffer_id++;
243 INIT_LIST_HEAD(&buf->list);
244 buf->dma_handle = pci_map_single(s->cx->dev,
245 buf->buf, s->buf_size, s->dma);
246 cx18_buf_sync_for_cpu(s, buf);
247 cx18_enqueue(s, buf, &s->q_free);
249 if (i == s->buffers) {
250 cx->mdl_offset += s->buffers;
253 CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
258 void cx18_stream_free(struct cx18_stream *s)
260 struct cx18_buffer *buf;
262 /* move all buffers to q_free */
263 cx18_flush_queues(s);
266 while ((buf = cx18_dequeue(s, &s->q_free))) {
267 pci_unmap_single(s->cx->dev, buf->dma_handle,
268 s->buf_size, s->dma);