blob: 6990b77c6200ab57ab29bb1a2962a67930ff1392 [file] [log] [blame]
Hans Verkuil1c1e45d2008-04-28 20:24:33 -03001/*
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
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030029void cx18_buf_swap(struct cx18_buffer *buf)
30{
31 int i;
32
33 for (i = 0; i < buf->bytesused; i += 4)
34 swab32s((u32 *)(buf->buf + i));
35}
36
37void cx18_queue_init(struct cx18_queue *q)
38{
39 INIT_LIST_HEAD(&q->list);
40 q->buffers = 0;
41 q->length = 0;
42 q->bytesused = 0;
43}
44
45void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
46 struct cx18_queue *q)
47{
48 unsigned long flags = 0;
49
50 /* clear the buffer if it is going to be enqueued to the free queue */
51 if (q == &s->q_free) {
52 buf->bytesused = 0;
53 buf->readpos = 0;
54 buf->b_flags = 0;
55 }
56 spin_lock_irqsave(&s->qlock, flags);
57 list_add_tail(&buf->list, &q->list);
58 q->buffers++;
59 q->length += s->buf_size;
60 q->bytesused += buf->bytesused - buf->readpos;
61 spin_unlock_irqrestore(&s->qlock, flags);
62}
63
64struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
65{
66 struct cx18_buffer *buf = NULL;
67 unsigned long flags = 0;
68
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);
73 q->buffers--;
74 q->length -= s->buf_size;
75 q->bytesused -= buf->bytesused - buf->readpos;
76 }
77 spin_unlock_irqrestore(&s->qlock, flags);
78 return buf;
79}
80
81struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
82 u32 bytesused)
83{
84 struct cx18 *cx = s->cx;
85 struct list_head *p;
86
87 list_for_each(p, &s->q_free.list) {
88 struct cx18_buffer *buf =
89 list_entry(p, struct cx18_buffer, list);
90
91 if (buf->id != id)
92 continue;
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)
97 return buf;
98 s->q_free.buffers--;
99 s->q_free.length -= s->buf_size;
100 s->q_full.buffers++;
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);
104 return buf;
105 }
106 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
107 return NULL;
108}
109
110static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
111 struct cx18_queue *to, int clear, int full)
112{
113 struct cx18_buffer *buf =
114 list_entry(from->list.next, struct cx18_buffer, list);
115
116 list_move_tail(from->list.next, &to->list);
117 from->buffers--;
118 from->length -= s->buf_size;
119 from->bytesused -= buf->bytesused - buf->readpos;
120 /* special handling for q_free */
121 if (clear)
122 buf->bytesused = buf->readpos = buf->b_flags = 0;
123 else if (full) {
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;
128 }
129 to->buffers++;
130 to->length += s->buf_size;
131 to->bytesused += buf->bytesused - buf->readpos;
132}
133
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
137 needed.
138
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.
142
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.
147
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. */
Adrian Bunk50510992008-05-05 18:25:22 -0300151static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
152 struct cx18_queue *steal, struct cx18_queue *to,
153 int needed_bytes)
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300154{
155 unsigned long flags;
156 int rc = 0;
157 int from_free = from == &s->q_free;
158 int to_free = to == &s->q_free;
159 int bytes_available;
160
161 spin_lock_irqsave(&s->qlock, flags);
162 if (needed_bytes == 0) {
163 from_free = 1;
164 needed_bytes = from->length;
165 }
166
167 bytes_available = from_free ? from->length : from->bytesused;
168 bytes_available += steal ? steal->length : 0;
169
170 if (bytes_available < needed_bytes) {
171 spin_unlock_irqrestore(&s->qlock, flags);
172 return -ENOMEM;
173 }
174 if (from_free) {
175 u32 old_length = to->length;
176
177 while (to->length - old_length < needed_bytes) {
178 if (list_empty(&from->list))
179 from = steal;
180 if (from == steal)
181 rc++; /* keep track of 'stolen' buffers */
182 cx18_queue_move_buf(s, from, to, 1, 0);
183 }
184 } else {
185 u32 old_bytesused = to->bytesused;
186
187 while (to->bytesused - old_bytesused < 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, to_free, rc);
193 }
194 }
195 spin_unlock_irqrestore(&s->qlock, flags);
196 return rc;
197}
198
199void cx18_flush_queues(struct cx18_stream *s)
200{
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);
203}
204
205int cx18_stream_alloc(struct cx18_stream *s)
206{
207 struct cx18 *cx = s->cx;
208 int i;
209
210 if (s->buffers == 0)
211 return 0;
212
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);
216
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));
221
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));
225 return -ENOMEM;
226 }
227
228 s->mdl_offset = cx->mdl_offset;
229
230 /* allocate stream buffers. Initially all buffers are in q_free. */
231 for (i = 0; i < s->buffers; i++) {
Hans Verkuil3f983872008-05-01 10:31:12 -0300232 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
233 GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300234
235 if (buf == NULL)
236 break;
Hans Verkuil3f983872008-05-01 10:31:12 -0300237 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300238 if (buf->buf == NULL) {
239 kfree(buf);
240 break;
241 }
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);
248 }
249 if (i == s->buffers) {
250 cx->mdl_offset += s->buffers;
251 return 0;
252 }
253 CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
254 cx18_stream_free(s);
255 return -ENOMEM;
256}
257
258void cx18_stream_free(struct cx18_stream *s)
259{
260 struct cx18_buffer *buf;
261
262 /* move all buffers to q_free */
263 cx18_flush_queues(s);
264
265 /* empty q_free */
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);
269 kfree(buf->buf);
270 kfree(buf);
271 }
272}