V4L/DVB (8757): v4l-dvb: fix a bunch of sparse warnings
[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_get_buf_irq(struct cx18_stream *s, u32 id,
82         u32 bytesused)
83 {
84         struct cx18 *cx = s->cx;
85         struct list_head *p;
86
87         spin_lock(&s->qlock);
88         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,
96                    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);
106                 return buf;
107         }
108         spin_unlock(&s->qlock);
109         CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
110         return NULL;
111 }
112
113 static 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. */
154 static 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)
157 {
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
202 void 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
208 int 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
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));
224
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++) {
235                 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
236                                                 GFP_KERNEL|__GFP_NOWARN);
237
238                 if (buf == NULL)
239                         break;
240                 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
241                 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
261 void 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 }