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_get_buf_irq(struct cx18_stream *s, u32 id,
84 struct cx18 *cx = s->cx;
88 list_for_each(p, &s->q_free.list) {
89 struct cx18_buffer *buf =
90 list_entry(p, struct cx18_buffer, list);
94 buf->bytesused = bytesused;
95 /* the transport buffers are handled differently,
96 they are not moved to the full queue */
97 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);
105 spin_unlock(&s->qlock);
108 spin_unlock(&s->qlock);
109 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
113 static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
114 struct cx18_queue *to, int clear, int full)
116 struct cx18_buffer *buf =
117 list_entry(from->list.next, struct cx18_buffer, list);
119 list_move_tail(from->list.next, &to->list);
121 from->length -= s->buf_size;
122 from->bytesused -= buf->bytesused - buf->readpos;
123 /* special handling for q_free */
125 buf->bytesused = buf->readpos = buf->b_flags = 0;
127 /* special handling for stolen buffers, assume
128 all bytes are used. */
129 buf->bytesused = s->buf_size;
130 buf->readpos = buf->b_flags = 0;
133 to->length += s->buf_size;
134 to->bytesused += buf->bytesused - buf->readpos;
137 /* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
138 If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
139 If 'steal' != NULL, then buffers may also taken from that queue if
142 The buffer is automatically cleared if it goes to the free queue. It is
143 also cleared if buffers need to be taken from the 'steal' queue and
144 the 'from' queue is the free queue.
146 When 'from' is q_free, then needed_bytes is compared to the total
147 available buffer length, otherwise needed_bytes is compared to the
148 bytesused value. For the 'steal' queue the total available buffer
149 length is always used.
151 -ENOMEM is returned if the buffers could not be obtained, 0 if all
152 buffers where obtained from the 'from' list and if non-zero then
153 the number of stolen buffers is returned. */
154 static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
155 struct cx18_queue *steal, struct cx18_queue *to,
160 int from_free = from == &s->q_free;
161 int to_free = to == &s->q_free;
164 spin_lock_irqsave(&s->qlock, flags);
165 if (needed_bytes == 0) {
167 needed_bytes = from->length;
170 bytes_available = from_free ? from->length : from->bytesused;
171 bytes_available += steal ? steal->length : 0;
173 if (bytes_available < needed_bytes) {
174 spin_unlock_irqrestore(&s->qlock, flags);
178 u32 old_length = to->length;
180 while (to->length - old_length < needed_bytes) {
181 if (list_empty(&from->list))
184 rc++; /* keep track of 'stolen' buffers */
185 cx18_queue_move_buf(s, from, to, 1, 0);
188 u32 old_bytesused = to->bytesused;
190 while (to->bytesused - old_bytesused < needed_bytes) {
191 if (list_empty(&from->list))
194 rc++; /* keep track of 'stolen' buffers */
195 cx18_queue_move_buf(s, from, to, to_free, rc);
198 spin_unlock_irqrestore(&s->qlock, flags);
202 void cx18_flush_queues(struct cx18_stream *s)
204 cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
205 cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
208 int cx18_stream_alloc(struct cx18_stream *s)
210 struct cx18 *cx = s->cx;
216 CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
217 s->name, s->buffers, s->buf_size,
218 s->buffers * s->buf_size / 1024);
220 if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
221 (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) {
222 unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE -
223 ((char __iomem *)cx->scb->cpu_mdl));
225 CX18_ERR("Too many buffers, cannot fit in SCB area\n");
226 CX18_ERR("Max buffers = %zd\n",
227 bufsz / sizeof(struct cx18_mdl));
231 s->mdl_offset = cx->mdl_offset;
233 /* allocate stream buffers. Initially all buffers are in q_free. */
234 for (i = 0; i < s->buffers; i++) {
235 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
236 GFP_KERNEL|__GFP_NOWARN);
240 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
241 if (buf->buf == NULL) {
245 buf->id = cx->buffer_id++;
246 INIT_LIST_HEAD(&buf->list);
247 buf->dma_handle = pci_map_single(s->cx->dev,
248 buf->buf, s->buf_size, s->dma);
249 cx18_buf_sync_for_cpu(s, buf);
250 cx18_enqueue(s, buf, &s->q_free);
252 if (i == s->buffers) {
253 cx->mdl_offset += s->buffers;
256 CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
261 void cx18_stream_free(struct cx18_stream *s)
263 struct cx18_buffer *buf;
265 /* move all buffers to q_free */
266 cx18_flush_queues(s);
269 while ((buf = cx18_dequeue(s, &s->q_free))) {
270 pci_unmap_single(s->cx->dev, buf->dma_handle,
271 s->buf_size, s->dma);