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 int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
30 const char __user *src, int copybytes)
32 if (s->buf_size - buf->bytesused < copybytes)
33 copybytes = s->buf_size - buf->bytesused;
34 if (copy_from_user(buf->buf + buf->bytesused, src, copybytes))
36 buf->bytesused += copybytes;
40 void cx18_buf_swap(struct cx18_buffer *buf)
44 for (i = 0; i < buf->bytesused; i += 4)
45 swab32s((u32 *)(buf->buf + i));
48 void cx18_queue_init(struct cx18_queue *q)
50 INIT_LIST_HEAD(&q->list);
56 void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
59 unsigned long flags = 0;
61 /* clear the buffer if it is going to be enqueued to the free queue */
62 if (q == &s->q_free) {
67 spin_lock_irqsave(&s->qlock, flags);
68 list_add_tail(&buf->list, &q->list);
70 q->length += s->buf_size;
71 q->bytesused += buf->bytesused - buf->readpos;
72 spin_unlock_irqrestore(&s->qlock, flags);
75 struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
77 struct cx18_buffer *buf = NULL;
78 unsigned long flags = 0;
80 spin_lock_irqsave(&s->qlock, flags);
81 if (!list_empty(&q->list)) {
82 buf = list_entry(q->list.next, struct cx18_buffer, list);
83 list_del_init(q->list.next);
85 q->length -= s->buf_size;
86 q->bytesused -= buf->bytesused - buf->readpos;
88 spin_unlock_irqrestore(&s->qlock, flags);
92 struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
95 struct cx18 *cx = s->cx;
98 list_for_each(p, &s->q_free.list) {
99 struct cx18_buffer *buf =
100 list_entry(p, struct cx18_buffer, list);
104 buf->bytesused = bytesused;
105 /* the transport buffers are handled differently,
106 so there is no need to move them to the full queue */
107 if (s->type == CX18_ENC_STREAM_TYPE_TS)
110 s->q_free.length -= s->buf_size;
112 s->q_full.length += s->buf_size;
113 s->q_full.bytesused += buf->bytesused;
114 list_move_tail(&buf->list, &s->q_full.list);
117 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
121 static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
122 struct cx18_queue *to, int clear, int full)
124 struct cx18_buffer *buf =
125 list_entry(from->list.next, struct cx18_buffer, list);
127 list_move_tail(from->list.next, &to->list);
129 from->length -= s->buf_size;
130 from->bytesused -= buf->bytesused - buf->readpos;
131 /* special handling for q_free */
133 buf->bytesused = buf->readpos = buf->b_flags = 0;
135 /* special handling for stolen buffers, assume
136 all bytes are used. */
137 buf->bytesused = s->buf_size;
138 buf->readpos = buf->b_flags = 0;
141 to->length += s->buf_size;
142 to->bytesused += buf->bytesused - buf->readpos;
145 /* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
146 If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
147 If 'steal' != NULL, then buffers may also taken from that queue if
150 The buffer is automatically cleared if it goes to the free queue. It is
151 also cleared if buffers need to be taken from the 'steal' queue and
152 the 'from' queue is the free queue.
154 When 'from' is q_free, then needed_bytes is compared to the total
155 available buffer length, otherwise needed_bytes is compared to the
156 bytesused value. For the 'steal' queue the total available buffer
157 length is always used.
159 -ENOMEM is returned if the buffers could not be obtained, 0 if all
160 buffers where obtained from the 'from' list and if non-zero then
161 the number of stolen buffers is returned. */
162 int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
163 struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes)
167 int from_free = from == &s->q_free;
168 int to_free = to == &s->q_free;
171 spin_lock_irqsave(&s->qlock, flags);
172 if (needed_bytes == 0) {
174 needed_bytes = from->length;
177 bytes_available = from_free ? from->length : from->bytesused;
178 bytes_available += steal ? steal->length : 0;
180 if (bytes_available < needed_bytes) {
181 spin_unlock_irqrestore(&s->qlock, flags);
185 u32 old_length = to->length;
187 while (to->length - old_length < needed_bytes) {
188 if (list_empty(&from->list))
191 rc++; /* keep track of 'stolen' buffers */
192 cx18_queue_move_buf(s, from, to, 1, 0);
195 u32 old_bytesused = to->bytesused;
197 while (to->bytesused - old_bytesused < needed_bytes) {
198 if (list_empty(&from->list))
201 rc++; /* keep track of 'stolen' buffers */
202 cx18_queue_move_buf(s, from, to, to_free, rc);
205 spin_unlock_irqrestore(&s->qlock, flags);
209 void cx18_flush_queues(struct cx18_stream *s)
211 cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
212 cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
215 int cx18_stream_alloc(struct cx18_stream *s)
217 struct cx18 *cx = s->cx;
223 CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
224 s->name, s->buffers, s->buf_size,
225 s->buffers * s->buf_size / 1024);
227 if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
228 (char *)cx->scb) > SCB_RESERVED_SIZE) {
229 unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE -
230 ((char *)cx->scb->cpu_mdl));
232 CX18_ERR("Too many buffers, cannot fit in SCB area\n");
233 CX18_ERR("Max buffers = %zd\n",
234 bufsz / sizeof(struct cx18_mdl));
238 s->mdl_offset = cx->mdl_offset;
240 /* allocate stream buffers. Initially all buffers are in q_free. */
241 for (i = 0; i < s->buffers; i++) {
242 struct cx18_buffer *buf =
243 kzalloc(sizeof(struct cx18_buffer), GFP_KERNEL);
247 buf->buf = kmalloc(s->buf_size, GFP_KERNEL);
248 if (buf->buf == NULL) {
252 buf->id = cx->buffer_id++;
253 INIT_LIST_HEAD(&buf->list);
254 buf->dma_handle = pci_map_single(s->cx->dev,
255 buf->buf, s->buf_size, s->dma);
256 cx18_buf_sync_for_cpu(s, buf);
257 cx18_enqueue(s, buf, &s->q_free);
259 if (i == s->buffers) {
260 cx->mdl_offset += s->buffers;
263 CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
268 void cx18_stream_free(struct cx18_stream *s)
270 struct cx18_buffer *buf;
272 /* move all buffers to q_free */
273 cx18_flush_queues(s);
276 while ((buf = cx18_dequeue(s, &s->q_free))) {
277 pci_unmap_single(s->cx->dev, buf->dma_handle,
278 s->buf_size, s->dma);