blob: a31da49f9123d780a59801364a98a2f896d90ea0 [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
Andy Walls00587172008-09-03 16:47:50 -030081struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id,
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030082 u32 bytesused)
83{
84 struct cx18 *cx = s->cx;
85 struct list_head *p;
86
Andy Walls00587172008-09-03 16:47:50 -030087 spin_lock(&s->qlock);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030088 list_for_each(p, &s->q_free.list) {
89 struct cx18_buffer *buf =
90 list_entry(p, struct cx18_buffer, list);
91
92 if (buf->id != id)
93 continue;
94 buf->bytesused = bytesused;
95 /* the transport buffers are handled differently,
Andy Walls00587172008-09-03 16:47:50 -030096 they are not moved to the full queue */
97 if (s->type != CX18_ENC_STREAM_TYPE_TS) {
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 }
105 spin_unlock(&s->qlock);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300106 return buf;
107 }
Andy Walls00587172008-09-03 16:47:50 -0300108 spin_unlock(&s->qlock);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300109 CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
110 return NULL;
111}
112
113static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
114 struct cx18_queue *to, int clear, int full)
115{
116 struct cx18_buffer *buf =
117 list_entry(from->list.next, struct cx18_buffer, list);
118
119 list_move_tail(from->list.next, &to->list);
120 from->buffers--;
121 from->length -= s->buf_size;
122 from->bytesused -= buf->bytesused - buf->readpos;
123 /* special handling for q_free */
124 if (clear)
125 buf->bytesused = buf->readpos = buf->b_flags = 0;
126 else if (full) {
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;
131 }
132 to->buffers++;
133 to->length += s->buf_size;
134 to->bytesused += buf->bytesused - buf->readpos;
135}
136
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
140 needed.
141
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.
145
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.
150
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. */
Adrian Bunk50510992008-05-05 18:25:22 -0300154static int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
155 struct cx18_queue *steal, struct cx18_queue *to,
156 int needed_bytes)
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300157{
158 unsigned long flags;
159 int rc = 0;
160 int from_free = from == &s->q_free;
161 int to_free = to == &s->q_free;
162 int bytes_available;
163
164 spin_lock_irqsave(&s->qlock, flags);
165 if (needed_bytes == 0) {
166 from_free = 1;
167 needed_bytes = from->length;
168 }
169
170 bytes_available = from_free ? from->length : from->bytesused;
171 bytes_available += steal ? steal->length : 0;
172
173 if (bytes_available < needed_bytes) {
174 spin_unlock_irqrestore(&s->qlock, flags);
175 return -ENOMEM;
176 }
177 if (from_free) {
178 u32 old_length = to->length;
179
180 while (to->length - old_length < needed_bytes) {
181 if (list_empty(&from->list))
182 from = steal;
183 if (from == steal)
184 rc++; /* keep track of 'stolen' buffers */
185 cx18_queue_move_buf(s, from, to, 1, 0);
186 }
187 } else {
188 u32 old_bytesused = to->bytesused;
189
190 while (to->bytesused - old_bytesused < needed_bytes) {
191 if (list_empty(&from->list))
192 from = steal;
193 if (from == steal)
194 rc++; /* keep track of 'stolen' buffers */
195 cx18_queue_move_buf(s, from, to, to_free, rc);
196 }
197 }
198 spin_unlock_irqrestore(&s->qlock, flags);
199 return rc;
200}
201
202void cx18_flush_queues(struct cx18_stream *s)
203{
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);
206}
207
208int cx18_stream_alloc(struct cx18_stream *s)
209{
210 struct cx18 *cx = s->cx;
211 int i;
212
213 if (s->buffers == 0)
214 return 0;
215
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);
219
Hans Verkuilc6eb8ea2008-09-03 17:11:54 -0300220 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));
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300224
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));
228 return -ENOMEM;
229 }
230
231 s->mdl_offset = cx->mdl_offset;
232
233 /* allocate stream buffers. Initially all buffers are in q_free. */
234 for (i = 0; i < s->buffers; i++) {
Hans Verkuil3f983872008-05-01 10:31:12 -0300235 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
236 GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300237
238 if (buf == NULL)
239 break;
Hans Verkuil3f983872008-05-01 10:31:12 -0300240 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300241 if (buf->buf == NULL) {
242 kfree(buf);
243 break;
244 }
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);
251 }
252 if (i == s->buffers) {
253 cx->mdl_offset += s->buffers;
254 return 0;
255 }
256 CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
257 cx18_stream_free(s);
258 return -ENOMEM;
259}
260
261void cx18_stream_free(struct cx18_stream *s)
262{
263 struct cx18_buffer *buf;
264
265 /* move all buffers to q_free */
266 cx18_flush_queues(s);
267
268 /* empty q_free */
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);
272 kfree(buf->buf);
273 kfree(buf);
274 }
275}