aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2009-04-13 22:08:00 -0400
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-06-16 17:20:44 -0400
commit40c5520f55924ba87090d0d93222baad74202559 (patch)
tree731576eb5bb3c37df60c8cc78adbfc4d18bc0dd4
parent5f0a3cfcfd315d87de8f80af49b114daf7137823 (diff)
V4L/DVB (11618): cx18: Convert per stream mutex locks to per queue spin locks
To avoid sleeps in providing buffers to user space and in handling incoming buffers from the capture unit, converted the per stream mutex for locking queues to 3 spin locks. There is now a spin lock per queue to increase concurrency when moving buffers around. Also simplified queue manipulations and buffer handling of incoming buffers of data from the capture unit. Signed-off-by: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r--drivers/media/video/cx18/cx18-driver.h2
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c25
-rw-r--r--drivers/media/video/cx18/cx18-queue.c83
-rw-r--r--drivers/media/video/cx18/cx18-streams.c8
4 files changed, 69 insertions, 49 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 62dca432fdbb..35a6758a7aac 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -286,6 +286,7 @@ struct cx18_queue {
286 struct list_head list; 286 struct list_head list;
287 atomic_t buffers; 287 atomic_t buffers;
288 u32 bytesused; 288 u32 bytesused;
289 spinlock_t lock;
289}; 290};
290 291
291struct cx18_dvb { 292struct cx18_dvb {
@@ -365,7 +366,6 @@ struct cx18_stream {
365 unsigned mdl_offset; 366 unsigned mdl_offset;
366 367
367 u32 id; 368 u32 id;
368 struct mutex qlock; /* locks access to the queues */
369 unsigned long s_flags; /* status flags, see above */ 369 unsigned long s_flags; /* status flags, see above */
370 int dma; /* can be PCI_DMA_TODEVICE, 370 int dma; /* can be PCI_DMA_TODEVICE,
371 PCI_DMA_FROMDEVICE or 371 PCI_DMA_FROMDEVICE or
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index df7d61d0a13e..afe46c3d4057 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -191,23 +191,24 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
191 if (buf == NULL) { 191 if (buf == NULL) {
192 CX18_WARN("Could not find buf %d for stream %s\n", 192 CX18_WARN("Could not find buf %d for stream %s\n",
193 id, s->name); 193 id, s->name);
194 /* Put as many buffers as possible back into fw use */
195 cx18_stream_load_fw_queue(s);
196 continue; 194 continue;
197 } 195 }
198 196
199 if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { 197 CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
200 CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", 198 s->name, buf->bytesused);
201 buf->bytesused); 199
202 dvb_dmx_swfilter(&s->dvb.demux, buf->buf, 200 if (s->type != CX18_ENC_STREAM_TYPE_TS)
203 buf->bytesused); 201 cx18_enqueue(s, buf, &s->q_full);
202 else {
203 if (s->dvb.enabled)
204 dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
205 buf->bytesused);
206 cx18_enqueue(s, buf, &s->q_free);
204 } 207 }
205 /* Put as many buffers as possible back into fw use */
206 cx18_stream_load_fw_queue(s);
207 /* Put back TS buffer, since it was removed from all queues */
208 if (s->type == CX18_ENC_STREAM_TYPE_TS)
209 cx18_stream_put_buf_fw(s, buf);
210 } 208 }
209 /* Put as many buffers as possible back into fw use */
210 cx18_stream_load_fw_queue(s);
211
211 wake_up(&cx->dma_waitq); 212 wake_up(&cx->dma_waitq);
212 if (s->id != -1) 213 if (s->id != -1)
213 wake_up(&s->waitq); 214 wake_up(&s->waitq);
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index 3046b8e74345..693a745b0858 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -53,13 +53,13 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
53 buf->skipped = 0; 53 buf->skipped = 0;
54 } 54 }
55 55
56 mutex_lock(&s->qlock);
57
58 /* q_busy is restricted to a max buffer count imposed by firmware */ 56 /* q_busy is restricted to a max buffer count imposed by firmware */
59 if (q == &s->q_busy && 57 if (q == &s->q_busy &&
60 atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) 58 atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM)
61 q = &s->q_free; 59 q = &s->q_free;
62 60
61 spin_lock(&q->lock);
62
63 if (to_front) 63 if (to_front)
64 list_add(&buf->list, &q->list); /* LIFO */ 64 list_add(&buf->list, &q->list); /* LIFO */
65 else 65 else
@@ -67,7 +67,7 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
67 q->bytesused += buf->bytesused - buf->readpos; 67 q->bytesused += buf->bytesused - buf->readpos;
68 atomic_inc(&q->buffers); 68 atomic_inc(&q->buffers);
69 69
70 mutex_unlock(&s->qlock); 70 spin_unlock(&q->lock);
71 return q; 71 return q;
72} 72}
73 73
@@ -75,7 +75,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
75{ 75{
76 struct cx18_buffer *buf = NULL; 76 struct cx18_buffer *buf = NULL;
77 77
78 mutex_lock(&s->qlock); 78 spin_lock(&q->lock);
79 if (!list_empty(&q->list)) { 79 if (!list_empty(&q->list)) {
80 buf = list_first_entry(&q->list, struct cx18_buffer, list); 80 buf = list_first_entry(&q->list, struct cx18_buffer, list);
81 list_del_init(&buf->list); 81 list_del_init(&buf->list);
@@ -83,7 +83,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
83 buf->skipped = 0; 83 buf->skipped = 0;
84 atomic_dec(&q->buffers); 84 atomic_dec(&q->buffers);
85 } 85 }
86 mutex_unlock(&s->qlock); 86 spin_unlock(&q->lock);
87 return buf; 87 return buf;
88} 88}
89 89
@@ -94,9 +94,23 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
94 struct cx18_buffer *buf; 94 struct cx18_buffer *buf;
95 struct cx18_buffer *tmp; 95 struct cx18_buffer *tmp;
96 struct cx18_buffer *ret = NULL; 96 struct cx18_buffer *ret = NULL;
97 97 LIST_HEAD(sweep_up);
98 mutex_lock(&s->qlock); 98
99 /*
100 * We don't have to acquire multiple q locks here, because we are
101 * serialized by the single threaded work handler.
102 * Buffers from the firmware will thus remain in order as
103 * they are moved from q_busy to q_full or to the dvb ring buffer.
104 */
105 spin_lock(&s->q_busy.lock);
99 list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { 106 list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) {
107 /*
108 * We should find what the firmware told us is done,
109 * right at the front of the queue. If we don't, we likely have
110 * missed a buffer done message from the firmware.
111 * Once we skip a buffer repeatedly, relative to the size of
112 * q_busy, we have high confidence we've missed it.
113 */
100 if (buf->id != id) { 114 if (buf->id != id) {
101 buf->skipped++; 115 buf->skipped++;
102 if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { 116 if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) {
@@ -105,38 +119,41 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id,
105 "times - it must have dropped out of " 119 "times - it must have dropped out of "
106 "rotation\n", s->name, buf->id, 120 "rotation\n", s->name, buf->id,
107 buf->skipped); 121 buf->skipped);
108 /* move it to q_free */ 122 /* Sweep it up to put it back into rotation */
109 list_move_tail(&buf->list, &s->q_free.list); 123 list_move_tail(&buf->list, &sweep_up);
110 buf->bytesused = buf->readpos = buf->b_flags =
111 buf->skipped = 0;
112 atomic_dec(&s->q_busy.buffers); 124 atomic_dec(&s->q_busy.buffers);
113 atomic_inc(&s->q_free.buffers);
114 } 125 }
115 continue; 126 continue;
116 } 127 }
117 128 /*
118 buf->bytesused = bytesused; 129 * We pull the desired buffer off of the queue here. Something
119 /* Sync the buffer before we release the qlock */ 130 * will have to put it back on a queue later.
120 cx18_buf_sync_for_cpu(s, buf); 131 */
121 if (s->type == CX18_ENC_STREAM_TYPE_TS) { 132 list_del_init(&buf->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); 133 atomic_dec(&s->q_busy.buffers);
135
136 ret = buf; 134 ret = buf;
137 break; 135 break;
138 } 136 }
139 mutex_unlock(&s->qlock); 137 spin_unlock(&s->q_busy.lock);
138
139 /*
140 * We found the buffer for which we were looking. Get it ready for
141 * the caller to put on q_full or in the dvb ring buffer.
142 */
143 if (ret != NULL) {
144 ret->bytesused = bytesused;
145 ret->skipped = 0;
146 /* readpos and b_flags were 0'ed when the buf went on q_busy */
147 cx18_buf_sync_for_cpu(s, ret);
148 if (s->type != CX18_ENC_STREAM_TYPE_TS)
149 set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags);
150 }
151
152 /* Put any buffers the firmware is ignoring back into normal rotation */
153 list_for_each_entry_safe(buf, tmp, &sweep_up, list) {
154 list_del_init(&buf->list);
155 cx18_enqueue(s, buf, &s->q_free);
156 }
140 return ret; 157 return ret;
141} 158}
142 159
@@ -148,7 +165,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
148 if (q == &s->q_free) 165 if (q == &s->q_free)
149 return; 166 return;
150 167
151 mutex_lock(&s->qlock); 168 spin_lock(&q->lock);
152 while (!list_empty(&q->list)) { 169 while (!list_empty(&q->list)) {
153 buf = list_first_entry(&q->list, struct cx18_buffer, list); 170 buf = list_first_entry(&q->list, struct cx18_buffer, list);
154 list_move_tail(&buf->list, &s->q_free.list); 171 list_move_tail(&buf->list, &s->q_free.list);
@@ -156,7 +173,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q)
156 atomic_inc(&s->q_free.buffers); 173 atomic_inc(&s->q_free.buffers);
157 } 174 }
158 cx18_queue_init(q); 175 cx18_queue_init(q);
159 mutex_unlock(&s->qlock); 176 spin_unlock(&q->lock);
160} 177}
161 178
162void cx18_flush_queues(struct cx18_stream *s) 179void cx18_flush_queues(struct cx18_stream *s)
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index bbeb01c5cf32..e1934e9cfdc8 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -116,11 +116,13 @@ static void cx18_stream_init(struct cx18 *cx, int type)
116 s->buffers = cx->stream_buffers[type]; 116 s->buffers = cx->stream_buffers[type];
117 s->buf_size = cx->stream_buf_size[type]; 117 s->buf_size = cx->stream_buf_size[type];
118 118
119 mutex_init(&s->qlock);
120 init_waitqueue_head(&s->waitq); 119 init_waitqueue_head(&s->waitq);
121 s->id = -1; 120 s->id = -1;
121 spin_lock_init(&s->q_free.lock);
122 cx18_queue_init(&s->q_free); 122 cx18_queue_init(&s->q_free);
123 spin_lock_init(&s->q_busy.lock);
123 cx18_queue_init(&s->q_busy); 124 cx18_queue_init(&s->q_busy);
125 spin_lock_init(&s->q_full.lock);
124 cx18_queue_init(&s->q_full); 126 cx18_queue_init(&s->q_full);
125} 127}
126 128
@@ -685,13 +687,13 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
685 687
686 /* Init all the cpu_mdls for this stream */ 688 /* Init all the cpu_mdls for this stream */
687 cx18_flush_queues(s); 689 cx18_flush_queues(s);
688 mutex_lock(&s->qlock); 690 spin_lock(&s->q_free.lock);
689 list_for_each_entry(buf, &s->q_free.list, list) { 691 list_for_each_entry(buf, &s->q_free.list, list) {
690 cx18_writel(cx, buf->dma_handle, 692 cx18_writel(cx, buf->dma_handle,
691 &cx->scb->cpu_mdl[buf->id].paddr); 693 &cx->scb->cpu_mdl[buf->id].paddr);
692 cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); 694 cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
693 } 695 }
694 mutex_unlock(&s->qlock); 696 spin_unlock(&s->q_free.lock);
695 _cx18_stream_load_fw_queue(s); 697 _cx18_stream_load_fw_queue(s);
696 698
697 /* begin_capture */ 699 /* begin_capture */