diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-queue.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-queue.c | 118 |
1 files changed, 77 insertions, 41 deletions
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 174682c2582f..8d9441e88c4e 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c | |||
@@ -4,6 +4,7 @@ | |||
4 | * Derived from ivtv-queue.c | 4 | * Derived from ivtv-queue.c |
5 | * | 5 | * |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | 6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> |
7 | * Copyright (C) 2008 Andy Walls <awalls@radix.net> | ||
7 | * | 8 | * |
8 | * This program is free software; you can redistribute it and/or modify | 9 | * 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 | * it under the terms of the GNU General Public License as published by |
@@ -41,91 +42,126 @@ void cx18_queue_init(struct cx18_queue *q) | |||
41 | q->bytesused = 0; | 42 | q->bytesused = 0; |
42 | } | 43 | } |
43 | 44 | ||
44 | void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, | 45 | struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, |
45 | struct cx18_queue *q) | 46 | struct cx18_queue *q, int to_front) |
46 | { | 47 | { |
47 | unsigned long flags = 0; | 48 | /* clear the buffer if it is not to be enqueued to the full queue */ |
48 | 49 | if (q != &s->q_full) { | |
49 | /* clear the buffer if it is going to be enqueued to the free queue */ | ||
50 | if (q == &s->q_free) { | ||
51 | buf->bytesused = 0; | 50 | buf->bytesused = 0; |
52 | buf->readpos = 0; | 51 | buf->readpos = 0; |
53 | buf->b_flags = 0; | 52 | buf->b_flags = 0; |
53 | buf->skipped = 0; | ||
54 | } | 54 | } |
55 | spin_lock_irqsave(&s->qlock, flags); | 55 | |
56 | list_add_tail(&buf->list, &q->list); | 56 | mutex_lock(&s->qlock); |
57 | atomic_inc(&q->buffers); | 57 | |
58 | /* q_busy is restricted to a max buffer count imposed by firmware */ | ||
59 | if (q == &s->q_busy && | ||
60 | atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) | ||
61 | q = &s->q_free; | ||
62 | |||
63 | if (to_front) | ||
64 | list_add(&buf->list, &q->list); /* LIFO */ | ||
65 | else | ||
66 | list_add_tail(&buf->list, &q->list); /* FIFO */ | ||
58 | q->bytesused += buf->bytesused - buf->readpos; | 67 | q->bytesused += buf->bytesused - buf->readpos; |
59 | spin_unlock_irqrestore(&s->qlock, flags); | 68 | atomic_inc(&q->buffers); |
69 | |||
70 | mutex_unlock(&s->qlock); | ||
71 | return q; | ||
60 | } | 72 | } |
61 | 73 | ||
62 | struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) | 74 | struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) |
63 | { | 75 | { |
64 | struct cx18_buffer *buf = NULL; | 76 | struct cx18_buffer *buf = NULL; |
65 | unsigned long flags = 0; | ||
66 | 77 | ||
67 | spin_lock_irqsave(&s->qlock, flags); | 78 | mutex_lock(&s->qlock); |
68 | if (!list_empty(&q->list)) { | 79 | if (!list_empty(&q->list)) { |
69 | buf = list_entry(q->list.next, struct cx18_buffer, list); | 80 | buf = list_first_entry(&q->list, struct cx18_buffer, list); |
70 | list_del_init(q->list.next); | 81 | list_del_init(&buf->list); |
71 | atomic_dec(&q->buffers); | ||
72 | q->bytesused -= buf->bytesused - buf->readpos; | 82 | q->bytesused -= buf->bytesused - buf->readpos; |
83 | buf->skipped = 0; | ||
84 | atomic_dec(&q->buffers); | ||
73 | } | 85 | } |
74 | spin_unlock_irqrestore(&s->qlock, flags); | 86 | mutex_unlock(&s->qlock); |
75 | return buf; | 87 | return buf; |
76 | } | 88 | } |
77 | 89 | ||
78 | struct cx18_buffer *cx18_queue_get_buf_irq(struct cx18_stream *s, u32 id, | 90 | struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, |
79 | u32 bytesused) | 91 | u32 bytesused) |
80 | { | 92 | { |
81 | struct cx18 *cx = s->cx; | 93 | struct cx18 *cx = s->cx; |
82 | struct list_head *p; | 94 | struct cx18_buffer *buf; |
83 | 95 | struct cx18_buffer *tmp; | |
84 | spin_lock(&s->qlock); | 96 | struct cx18_buffer *ret = NULL; |
85 | list_for_each(p, &s->q_free.list) { | 97 | |
86 | struct cx18_buffer *buf = | 98 | mutex_lock(&s->qlock); |
87 | list_entry(p, struct cx18_buffer, list); | 99 | list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { |
88 | 100 | if (buf->id != id) { | |
89 | if (buf->id != id) | 101 | buf->skipped++; |
102 | if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { | ||
103 | /* buffer must have fallen out of rotation */ | ||
104 | CX18_WARN("Skipped %s, buffer %d, %d " | ||
105 | "times - it must have dropped out of " | ||
106 | "rotation\n", s->name, buf->id, | ||
107 | buf->skipped); | ||
108 | /* move it to q_free */ | ||
109 | list_move_tail(&buf->list, &s->q_free.list); | ||
110 | buf->bytesused = buf->readpos = buf->b_flags = | ||
111 | buf->skipped = 0; | ||
112 | atomic_dec(&s->q_busy.buffers); | ||
113 | atomic_inc(&s->q_free.buffers); | ||
114 | } | ||
90 | continue; | 115 | continue; |
116 | } | ||
91 | 117 | ||
92 | buf->bytesused = bytesused; | 118 | buf->bytesused = bytesused; |
93 | atomic_dec(&s->q_free.buffers); | 119 | /* Sync the buffer before we release the qlock */ |
94 | atomic_inc(&s->q_full.buffers); | 120 | cx18_buf_sync_for_cpu(s, buf); |
95 | s->q_full.bytesused += buf->bytesused; | 121 | if (s->type == CX18_ENC_STREAM_TYPE_TS) { |
96 | list_move_tail(&buf->list, &s->q_full.list); | 122 | /* |
123 | * TS doesn't use q_full. As we pull the buffer off of | ||
124 | * the queue here, the caller will have to put it back. | ||
125 | */ | ||
126 | list_del_init(&buf->list); | ||
127 | } else { | ||
128 | /* Move buffer from q_busy to q_full */ | ||
129 | list_move_tail(&buf->list, &s->q_full.list); | ||
130 | set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); | ||
131 | s->q_full.bytesused += buf->bytesused; | ||
132 | atomic_inc(&s->q_full.buffers); | ||
133 | } | ||
134 | atomic_dec(&s->q_busy.buffers); | ||
97 | 135 | ||
98 | spin_unlock(&s->qlock); | 136 | ret = buf; |
99 | return buf; | 137 | break; |
100 | } | 138 | } |
101 | spin_unlock(&s->qlock); | 139 | mutex_unlock(&s->qlock); |
102 | CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name); | 140 | return ret; |
103 | return NULL; | ||
104 | } | 141 | } |
105 | 142 | ||
106 | /* Move all buffers of a queue to q_free, while flushing the buffers */ | 143 | /* Move all buffers of a queue to q_free, while flushing the buffers */ |
107 | static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) | 144 | static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) |
108 | { | 145 | { |
109 | unsigned long flags; | ||
110 | struct cx18_buffer *buf; | 146 | struct cx18_buffer *buf; |
111 | 147 | ||
112 | if (q == &s->q_free) | 148 | if (q == &s->q_free) |
113 | return; | 149 | return; |
114 | 150 | ||
115 | spin_lock_irqsave(&s->qlock, flags); | 151 | mutex_lock(&s->qlock); |
116 | while (!list_empty(&q->list)) { | 152 | while (!list_empty(&q->list)) { |
117 | buf = list_entry(q->list.next, struct cx18_buffer, list); | 153 | buf = list_first_entry(&q->list, struct cx18_buffer, list); |
118 | list_move_tail(q->list.next, &s->q_free.list); | 154 | list_move_tail(&buf->list, &s->q_free.list); |
119 | buf->bytesused = buf->readpos = buf->b_flags = 0; | 155 | buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; |
120 | atomic_inc(&s->q_free.buffers); | 156 | atomic_inc(&s->q_free.buffers); |
121 | } | 157 | } |
122 | cx18_queue_init(q); | 158 | cx18_queue_init(q); |
123 | spin_unlock_irqrestore(&s->qlock, flags); | 159 | mutex_unlock(&s->qlock); |
124 | } | 160 | } |
125 | 161 | ||
126 | void cx18_flush_queues(struct cx18_stream *s) | 162 | void cx18_flush_queues(struct cx18_stream *s) |
127 | { | 163 | { |
128 | cx18_queue_flush(s, &s->q_io); | 164 | cx18_queue_flush(s, &s->q_busy); |
129 | cx18_queue_flush(s, &s->q_full); | 165 | cx18_queue_flush(s, &s->q_full); |
130 | } | 166 | } |
131 | 167 | ||