aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/ivtv/ivtv-queue.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/video/ivtv/ivtv-queue.c')
-rw-r--r--drivers/media/video/ivtv/ivtv-queue.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
new file mode 100644
index 000000000000..ccfcef1ad91a
--- /dev/null
+++ b/drivers/media/video/ivtv/ivtv-queue.c
@@ -0,0 +1,262 @@
1/*
2 buffer queues.
3 Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com>
4 Copyright (C) 2004 Chris Kennedy <c@groovy.org>
5 Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include "ivtv-driver.h"
23#include "ivtv-streams.h"
24#include "ivtv-queue.h"
25#include "ivtv-mailbox.h"
26
27int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes)
28{
29 if (s->buf_size - buf->bytesused < copybytes)
30 copybytes = s->buf_size - buf->bytesused;
31 if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) {
32 return -EFAULT;
33 }
34 buf->bytesused += copybytes;
35 return copybytes;
36}
37
38void ivtv_buf_swap(struct ivtv_buffer *buf)
39{
40 int i;
41
42 for (i = 0; i < buf->bytesused; i += 4)
43 swab32s((u32 *)(buf->buf + i));
44}
45
46void ivtv_queue_init(struct ivtv_queue *q)
47{
48 INIT_LIST_HEAD(&q->list);
49 q->buffers = 0;
50 q->length = 0;
51 q->bytesused = 0;
52}
53
54void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q)
55{
56 unsigned long flags = 0;
57
58 /* clear the buffer if it is going to be enqueued to the free queue */
59 if (q == &s->q_free) {
60 buf->bytesused = 0;
61 buf->readpos = 0;
62 buf->b_flags = 0;
63 }
64 spin_lock_irqsave(&s->qlock, flags);
65 list_add_tail(&buf->list, &q->list);
66 q->buffers++;
67 q->length += s->buf_size;
68 q->bytesused += buf->bytesused - buf->readpos;
69 spin_unlock_irqrestore(&s->qlock, flags);
70}
71
72struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q)
73{
74 struct ivtv_buffer *buf = NULL;
75 unsigned long flags = 0;
76
77 spin_lock_irqsave(&s->qlock, flags);
78 if (!list_empty(&q->list)) {
79 buf = list_entry(q->list.next, struct ivtv_buffer, list);
80 list_del_init(q->list.next);
81 q->buffers--;
82 q->length -= s->buf_size;
83 q->bytesused -= buf->bytesused - buf->readpos;
84 }
85 spin_unlock_irqrestore(&s->qlock, flags);
86 return buf;
87}
88
89static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from,
90 struct ivtv_queue *to, int clear, int full)
91{
92 struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list);
93
94 list_move_tail(from->list.next, &to->list);
95 from->buffers--;
96 from->length -= s->buf_size;
97 from->bytesused -= buf->bytesused - buf->readpos;
98 /* special handling for q_free */
99 if (clear)
100 buf->bytesused = buf->readpos = buf->b_flags = 0;
101 else if (full) {
102 /* special handling for stolen buffers, assume
103 all bytes are used. */
104 buf->bytesused = s->buf_size;
105 buf->readpos = buf->b_flags = 0;
106 }
107 to->buffers++;
108 to->length += s->buf_size;
109 to->bytesused += buf->bytesused - buf->readpos;
110}
111
112/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
113 If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
114 If 'steal' != NULL, then buffers may also taken from that queue if
115 needed.
116
117 The buffer is automatically cleared if it goes to the free queue. It is
118 also cleared if buffers need to be taken from the 'steal' queue and
119 the 'from' queue is the free queue.
120
121 When 'from' is q_free, then needed_bytes is compared to the total
122 available buffer length, otherwise needed_bytes is compared to the
123 bytesused value. For the 'steal' queue the total available buffer
124 length is always used.
125
126 -ENOMEM is returned if the buffers could not be obtained, 0 if all
127 buffers where obtained from the 'from' list and if non-zero then
128 the number of stolen buffers is returned. */
129int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal,
130 struct ivtv_queue *to, int needed_bytes)
131{
132 unsigned long flags;
133 int rc = 0;
134 int from_free = from == &s->q_free;
135 int to_free = to == &s->q_free;
136 int bytes_available;
137
138 spin_lock_irqsave(&s->qlock, flags);
139 if (needed_bytes == 0) {
140 from_free = 1;
141 needed_bytes = from->length;
142 }
143
144 bytes_available = from_free ? from->length : from->bytesused;
145 bytes_available += steal ? steal->length : 0;
146
147 if (bytes_available < needed_bytes) {
148 spin_unlock_irqrestore(&s->qlock, flags);
149 return -ENOMEM;
150 }
151 if (from_free) {
152 u32 old_length = to->length;
153
154 while (to->length - old_length < needed_bytes) {
155 if (list_empty(&from->list))
156 from = steal;
157 if (from == steal)
158 rc++; /* keep track of 'stolen' buffers */
159 ivtv_queue_move_buf(s, from, to, 1, 0);
160 }
161 }
162 else {
163 u32 old_bytesused = to->bytesused;
164
165 while (to->bytesused - old_bytesused < needed_bytes) {
166 if (list_empty(&from->list))
167 from = steal;
168 if (from == steal)
169 rc++; /* keep track of 'stolen' buffers */
170 ivtv_queue_move_buf(s, from, to, to_free, rc);
171 }
172 }
173 spin_unlock_irqrestore(&s->qlock, flags);
174 return rc;
175}
176
177void ivtv_flush_queues(struct ivtv_stream *s)
178{
179 ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
180 ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
181 ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0);
182 ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0);
183}
184
185int ivtv_stream_alloc(struct ivtv_stream *s)
186{
187 struct ivtv *itv = s->itv;
188 int SGsize = sizeof(struct ivtv_SG_element) * s->buffers;
189 int i;
190
191 if (s->buffers == 0)
192 return 0;
193
194 IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n",
195 s->dma != PCI_DMA_NONE ? "DMA " : "",
196 s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024);
197
198 /* Allocate DMA SG Arrays */
199 if (s->dma != PCI_DMA_NONE) {
200 s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL);
201 if (s->SGarray == NULL) {
202 IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name);
203 return -ENOMEM;
204 }
205 s->SG_length = 0;
206 s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma);
207 ivtv_stream_sync_for_cpu(s);
208 }
209
210 /* allocate stream buffers. Initially all buffers are in q_free. */
211 for (i = 0; i < s->buffers; i++) {
212 struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL);
213
214 if (buf == NULL)
215 break;
216 buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL);
217 if (buf->buf == NULL) {
218 kfree(buf);
219 break;
220 }
221 INIT_LIST_HEAD(&buf->list);
222 if (s->dma != PCI_DMA_NONE) {
223 buf->dma_handle = pci_map_single(s->itv->dev,
224 buf->buf, s->buf_size + 256, s->dma);
225 ivtv_buf_sync_for_cpu(s, buf);
226 }
227 ivtv_enqueue(s, buf, &s->q_free);
228 }
229 if (i == s->buffers)
230 return 0;
231 IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name);
232 ivtv_stream_free(s);
233 return -ENOMEM;
234}
235
236void ivtv_stream_free(struct ivtv_stream *s)
237{
238 struct ivtv_buffer *buf;
239
240 /* move all buffers to q_free */
241 ivtv_flush_queues(s);
242
243 /* empty q_free */
244 while ((buf = ivtv_dequeue(s, &s->q_free))) {
245 if (s->dma != PCI_DMA_NONE)
246 pci_unmap_single(s->itv->dev, buf->dma_handle,
247 s->buf_size + 256, s->dma);
248 kfree(buf->buf);
249 kfree(buf);
250 }
251
252 /* Free SG Array/Lists */
253 if (s->SGarray != NULL) {
254 if (s->SG_handle != IVTV_DMA_UNMAPPED) {
255 pci_unmap_single(s->itv->dev, s->SG_handle,
256 sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE);
257 s->SG_handle = IVTV_DMA_UNMAPPED;
258 }
259 s->SGarray = NULL;
260 s->SG_length = 0;
261 }
262}