V4L/DVB (9726): cx18: Restore buffers that have fallen out of the transfer rotation
[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         atomic_set(&q->buffers, 0);
41         q->bytesused = 0;
42 }
43
44 void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
45                 struct cx18_queue *q)
46 {
47         /* clear the buffer if it is going to be enqueued to the free queue */
48         if (q == &s->q_free) {
49                 buf->bytesused = 0;
50                 buf->readpos = 0;
51                 buf->b_flags = 0;
52                 buf->skipped = 0;
53         }
54         mutex_lock(&s->qlock);
55         list_add_tail(&buf->list, &q->list);
56         atomic_inc(&q->buffers);
57         q->bytesused += buf->bytesused - buf->readpos;
58         mutex_unlock(&s->qlock);
59 }
60
61 struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
62 {
63         struct cx18_buffer *buf = NULL;
64
65         mutex_lock(&s->qlock);
66         if (!list_empty(&q->list)) {
67                 buf = list_entry(q->list.next, struct cx18_buffer, list);
68                 list_del_init(q->list.next);
69                 atomic_dec(&q->buffers);
70                 q->bytesused -= buf->bytesused - buf->readpos;
71                 buf->skipped = 0;
72         }
73         mutex_unlock(&s->qlock);
74         return buf;
75 }
76
77 struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
78         u32 bytesused)
79 {
80         struct cx18 *cx = s->cx;
81         struct cx18_buffer *buf;
82         struct cx18_buffer *ret = NULL;
83         struct list_head *p, *t;
84         LIST_HEAD(r);
85
86         mutex_lock(&s->qlock);
87         list_for_each_safe(p, t, &s->q_free.list) {
88                 buf = list_entry(p, struct cx18_buffer, list);
89
90                 if (buf->id != id) {
91                         buf->skipped++;
92                         if (buf->skipped >= atomic_read(&s->q_free.buffers)-1) {
93                                 /* buffer must have fallen out of rotation */
94                                 atomic_dec(&s->q_free.buffers);
95                                 list_move_tail(&buf->list, &r);
96                                 CX18_WARN("Skipped %s, buffer %d, %d "
97                                           "times - it must have dropped out of "
98                                           "rotation\n", s->name, buf->id,
99                                           buf->skipped);
100                         }
101                         continue;
102                 }
103
104                 buf->bytesused = bytesused;
105                 atomic_dec(&s->q_free.buffers);
106                 if (s->type == CX18_ENC_STREAM_TYPE_TS) {
107                         /*
108                          * TS doesn't use q_full, but for sweeping up lost
109                          * buffers, we want the TS to requeue the buffer just
110                          * before sending the MDL back to the firmware, so we
111                          * pull it off the list here.
112                          */
113                         list_del_init(&buf->list);
114                 } else {
115                         atomic_inc(&s->q_full.buffers);
116                         s->q_full.bytesused += buf->bytesused;
117                         list_move_tail(&buf->list, &s->q_full.list);
118                 }
119
120                 ret = buf;
121                 break;
122         }
123         mutex_unlock(&s->qlock);
124
125         /* Put lost buffers back into firmware transfer rotation */
126         while (!list_empty(&r)) {
127                 buf = list_entry(r.next, struct cx18_buffer, list);
128                 list_del_init(r.next);
129                 cx18_enqueue(s, buf, &s->q_free);
130                 cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
131                        (void __iomem *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
132                        1, buf->id, s->buf_size);
133                 CX18_INFO("Returning %s, buffer %d back to transfer rotation\n",
134                           s->name, buf->id);
135                 /* and there was much rejoicing... */
136         }
137         return ret;
138 }
139
140 /* Move all buffers of a queue to q_free, while flushing the buffers */
141 static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
142 {
143         struct cx18_buffer *buf;
144
145         if (q == &s->q_free)
146                 return;
147
148         mutex_lock(&s->qlock);
149         while (!list_empty(&q->list)) {
150                 buf = list_entry(q->list.next, struct cx18_buffer, list);
151                 list_move_tail(q->list.next, &s->q_free.list);
152                 buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0;
153                 atomic_inc(&s->q_free.buffers);
154         }
155         cx18_queue_init(q);
156         mutex_unlock(&s->qlock);
157 }
158
159 void cx18_flush_queues(struct cx18_stream *s)
160 {
161         cx18_queue_flush(s, &s->q_io);
162         cx18_queue_flush(s, &s->q_full);
163 }
164
165 int cx18_stream_alloc(struct cx18_stream *s)
166 {
167         struct cx18 *cx = s->cx;
168         int i;
169
170         if (s->buffers == 0)
171                 return 0;
172
173         CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
174                 s->name, s->buffers, s->buf_size,
175                 s->buffers * s->buf_size / 1024);
176
177         if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
178                                 (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) {
179                 unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE -
180                                         ((char __iomem *)cx->scb->cpu_mdl));
181
182                 CX18_ERR("Too many buffers, cannot fit in SCB area\n");
183                 CX18_ERR("Max buffers = %zd\n",
184                         bufsz / sizeof(struct cx18_mdl));
185                 return -ENOMEM;
186         }
187
188         s->mdl_offset = cx->mdl_offset;
189
190         /* allocate stream buffers. Initially all buffers are in q_free. */
191         for (i = 0; i < s->buffers; i++) {
192                 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer),
193                                                 GFP_KERNEL|__GFP_NOWARN);
194
195                 if (buf == NULL)
196                         break;
197                 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
198                 if (buf->buf == NULL) {
199                         kfree(buf);
200                         break;
201                 }
202                 buf->id = cx->buffer_id++;
203                 INIT_LIST_HEAD(&buf->list);
204                 buf->dma_handle = pci_map_single(s->cx->dev,
205                                 buf->buf, s->buf_size, s->dma);
206                 cx18_buf_sync_for_cpu(s, buf);
207                 cx18_enqueue(s, buf, &s->q_free);
208         }
209         if (i == s->buffers) {
210                 cx->mdl_offset += s->buffers;
211                 return 0;
212         }
213         CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
214         cx18_stream_free(s);
215         return -ENOMEM;
216 }
217
218 void cx18_stream_free(struct cx18_stream *s)
219 {
220         struct cx18_buffer *buf;
221
222         /* move all buffers to q_free */
223         cx18_flush_queues(s);
224
225         /* empty q_free */
226         while ((buf = cx18_dequeue(s, &s->q_free))) {
227                 pci_unmap_single(s->cx->dev, buf->dma_handle,
228                                 s->buf_size, s->dma);
229                 kfree(buf->buf);
230                 kfree(buf);
231         }
232 }