aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/cx18/cx18-queue.c')
-rw-r--r--drivers/media/video/cx18/cx18-queue.c282
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
new file mode 100644
index 000000000000..65af1bb507ca
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -0,0 +1,282 @@
1/*
2 * cx18 buffer queues
3 *
4 * Derived from ivtv-queue.c
5 *
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
7 *
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.
12 *
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.
17 *
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
21 * 02111-1307 USA
22 */
23
24#include "cx18-driver.h"
25#include "cx18-streams.h"
26#include "cx18-queue.h"
27#include "cx18-scb.h"
28
29int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
30 const char __user *src, int copybytes)
31{
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))
35 return -EFAULT;
36 buf->bytesused += copybytes;
37 return copybytes;
38}
39
40void cx18_buf_swap(struct cx18_buffer *buf)
41{
42 int i;
43
44 for (i = 0; i < buf->bytesused; i += 4)
45 swab32s((u32 *)(buf->buf + i));
46}
47
48void cx18_queue_init(struct cx18_queue *q)
49{
50 INIT_LIST_HEAD(&q->list);
51 q->buffers = 0;
52 q->length = 0;
53 q->bytesused = 0;
54}
55
56void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
57 struct cx18_queue *q)
58{
59 unsigned long flags = 0;
60
61 /* clear the buffer if it is going to be enqueued to the free queue */
62 if (q == &s->q_free) {
63 buf->bytesused = 0;
64 buf->readpos = 0;
65 buf->b_flags = 0;
66 }
67 spin_lock_irqsave(&s->qlock, flags);
68 list_add_tail(&buf->list, &q->list);
69 q->buffers++;
70 q->length += s->buf_size;
71 q->bytesused += buf->bytesused - buf->readpos;
72 spin_unlock_irqrestore(&s->qlock, flags);
73}
74
75struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
76{
77 struct cx18_buffer *buf = NULL;
78 unsigned long flags = 0;
79
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);
84 q->buffers--;
85 q->length -= s->buf_size;
86 q->bytesused -= buf->bytesused - buf->readpos;
87 }
88 spin_unlock_irqrestore(&s->qlock, flags);
89 return buf;
90}
91
92struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
93 u32 bytesused)
94{
95 struct cx18 *cx = s->cx;
96 struct list_head *p;
97
98 list_for_each(p, &s->q_free.list) {
99 struct cx18_buffer *buf =
100 list_entry(p, struct cx18_buffer, list);
101
102 if (buf->id != id)
103 continue;
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)
108 return buf;
109 s->q_free.buffers--;
110 s->q_free.length -= s->buf_size;
111 s->q_full.buffers++;
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);
115 return buf;
116 }
117 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
118 return NULL;
119}
120
121static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
122 struct cx18_queue *to, int clear, int full)
123{
124 struct cx18_buffer *buf =
125 list_entry(from->list.next, struct cx18_buffer, list);
126
127 list_move_tail(from->list.next, &to->list);
128 from->buffers--;
129 from->length -= s->buf_size;
130 from->bytesused -= buf->bytesused - buf->readpos;
131 /* special handling for q_free */
132 if (clear)
133 buf->bytesused = buf->readpos = buf->b_flags = 0;
134 else if (full) {
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;
139 }
140 to->buffers++;
141 to->length += s->buf_size;
142 to->bytesused += buf->bytesused - buf->readpos;
143}
144
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
148 needed.
149
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.
153
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.
158
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. */
162int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
163 struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes)
164{
165 unsigned long flags;
166 int rc = 0;
167 int from_free = from == &s->q_free;
168 int to_free = to == &s->q_free;
169 int bytes_available;
170
171 spin_lock_irqsave(&s->qlock, flags);
172 if (needed_bytes == 0) {
173 from_free = 1;
174 needed_bytes = from->length;
175 }
176
177 bytes_available = from_free ? from->length : from->bytesused;
178 bytes_available += steal ? steal->length : 0;
179
180 if (bytes_available < needed_bytes) {
181 spin_unlock_irqrestore(&s->qlock, flags);
182 return -ENOMEM;
183 }
184 if (from_free) {
185 u32 old_length = to->length;
186
187 while (to->length - old_length < needed_bytes) {
188 if (list_empty(&from->list))
189 from = steal;
190 if (from == steal)
191 rc++; /* keep track of 'stolen' buffers */
192 cx18_queue_move_buf(s, from, to, 1, 0);
193 }
194 } else {
195 u32 old_bytesused = to->bytesused;
196
197 while (to->bytesused - old_bytesused < needed_bytes) {
198 if (list_empty(&from->list))
199 from = steal;
200 if (from == steal)
201 rc++; /* keep track of 'stolen' buffers */
202 cx18_queue_move_buf(s, from, to, to_free, rc);
203 }
204 }
205 spin_unlock_irqrestore(&s->qlock, flags);
206 return rc;
207}
208
209void cx18_flush_queues(struct cx18_stream *s)
210{
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);
213}
214
215int cx18_stream_alloc(struct cx18_stream *s)
216{
217 struct cx18 *cx = s->cx;
218 int i;
219
220 if (s->buffers == 0)
221 return 0;
222
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);
226
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));
231
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));
235 return -ENOMEM;
236 }
237
238 s->mdl_offset = cx->mdl_offset;
239
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);
244
245 if (buf == NULL)
246 break;
247 buf->buf = kmalloc(s->buf_size, GFP_KERNEL);
248 if (buf->buf == NULL) {
249 kfree(buf);
250 break;
251 }
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);
258 }
259 if (i == s->buffers) {
260 cx->mdl_offset += s->buffers;
261 return 0;
262 }
263 CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
264 cx18_stream_free(s);
265 return -ENOMEM;
266}
267
268void cx18_stream_free(struct cx18_stream *s)
269{
270 struct cx18_buffer *buf;
271
272 /* move all buffers to q_free */
273 cx18_flush_queues(s);
274
275 /* empty q_free */
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);
279 kfree(buf->buf);
280 kfree(buf);
281 }
282}