V4L/DVB (7856): cx18/: possible cleanups
[safe/jmp/linux-2.6] / drivers / media / video / cx18 / cx18-queue.c
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
29 void 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
37 void 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
45 void 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
64 struct 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
81 struct 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
110 static 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. */
151 static 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)
154 {
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
199 void 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
205 int 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++) {
232                 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
233                                                 GFP_KERNEL|__GFP_NOWARN);
234
235                 if (buf == NULL)
236                         break;
237                 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
238                 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
258 void 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 }