aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media
diff options
context:
space:
mode:
authorAndy Walls <awalls@radix.net>2009-11-08 21:45:24 -0500
committerMauro Carvalho Chehab <mchehab@redhat.com>2009-12-05 15:41:51 -0500
commit52fcb3ecc6707f52dfe4297f96b7609d4ba517fb (patch)
treefe8ecd66c20b10e8b2ba63c667a6afe78c23a2e1 /drivers/media
parentfa655dda5ce6e5ac4a9b94fd451358edca2ddab8 (diff)
V4L/DVB (13429): cx18: Add Memory Descriptor List (MDL) layer to buffer handling
Add a Memory Descriptor List (MDL) layer to buffer handling to implement scatter-gather I/O. Currently there is still only 1 buffer per MDL. Signed-off-by: Andy Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r--drivers/media/video/cx18/cx18-driver.c6
-rw-r--r--drivers/media/video/cx18/cx18-driver.h45
-rw-r--r--drivers/media/video/cx18/cx18-fileops.c128
-rw-r--r--drivers/media/video/cx18/cx18-ioctl.c3
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.c58
-rw-r--r--drivers/media/video/cx18/cx18-mailbox.h6
-rw-r--r--drivers/media/video/cx18/cx18-queue.c316
-rw-r--r--drivers/media/video/cx18/cx18-queue.h54
-rw-r--r--drivers/media/video/cx18/cx18-streams.c50
-rw-r--r--drivers/media/video/cx18/cx18-streams.h10
-rw-r--r--drivers/media/video/cx18/cx18-vbi.c35
-rw-r--r--drivers/media/video/cx18/cx18-vbi.h2
12 files changed, 540 insertions, 173 deletions
diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
index e12082b8a08d..ba4c3ceffbb3 100644
--- a/drivers/media/video/cx18/cx18-driver.c
+++ b/drivers/media/video/cx18/cx18-driver.c
@@ -669,6 +669,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
669 cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; 669 cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
670 cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; 670 cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
671 671
672 /* IVTV style VBI insertion into MPEG streams */
673 INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list);
674 INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list);
675 INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list);
676 list_add(&cx->vbi.sliced_mpeg_buf.list,
677 &cx->vbi.sliced_mpeg_mdl.buf_list);
672 return 0; 678 return 0;
673} 679}
674 680
diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
index 1a899e0773d7..bed8bcc65411 100644
--- a/drivers/media/video/cx18/cx18-driver.h
+++ b/drivers/media/video/cx18/cx18-driver.h
@@ -246,8 +246,8 @@ struct cx18_options {
246 int radio; /* enable/disable radio */ 246 int radio; /* enable/disable radio */
247}; 247};
248 248
249/* per-buffer bit flags */ 249/* per-mdl bit flags */
250#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ 250#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */
251 251
252/* per-stream, s_flags */ 252/* per-stream, s_flags */
253#define CX18_F_S_CLAIMED 3 /* this stream is claimed */ 253#define CX18_F_S_CLAIMED 3 /* this stream is claimed */
@@ -274,15 +274,26 @@ struct cx18_options {
274struct cx18_buffer { 274struct cx18_buffer {
275 struct list_head list; 275 struct list_head list;
276 dma_addr_t dma_handle; 276 dma_addr_t dma_handle;
277 u32 id;
278 unsigned long b_flags;
279 unsigned skipped;
280 char *buf; 277 char *buf;
281 278
282 u32 bytesused; 279 u32 bytesused;
283 u32 readpos; 280 u32 readpos;
284}; 281};
285 282
283struct cx18_mdl {
284 struct list_head list;
285 u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */
286
287 unsigned int skipped;
288 unsigned long m_flags;
289
290 struct list_head buf_list;
291 struct cx18_buffer *curr_buf; /* current buffer in list for reading */
292
293 u32 bytesused;
294 u32 readpos;
295};
296
286struct cx18_queue { 297struct cx18_queue {
287 struct list_head list; 298 struct list_head list;
288 atomic_t depth; 299 atomic_t depth;
@@ -346,14 +357,20 @@ struct cx18_stream {
346 PCI_DMA_NONE */ 357 PCI_DMA_NONE */
347 wait_queue_head_t waitq; 358 wait_queue_head_t waitq;
348 359
349 /* Buffer Stats */ 360 /* Buffers */
350 u32 buffers; 361 struct list_head buf_pool; /* buffers not attached to an MDL */
351 u32 buf_size; 362 u32 buffers; /* total buffers owned by this stream */
363 u32 buf_size; /* size in bytes of a single buffer */
364
365 /* MDL sizes - all stream MDLs are the same size */
366 u32 bufs_per_mdl;
367 u32 mdl_size; /* total bytes in all buffers in a mdl */
352 368
353 /* Buffer Queues */ 369 /* MDL Queues */
354 struct cx18_queue q_free; /* free buffers */ 370 struct cx18_queue q_free; /* free - in rotation, not committed */
355 struct cx18_queue q_busy; /* busy buffers - in use by firmware */ 371 struct cx18_queue q_busy; /* busy - in use by firmware */
356 struct cx18_queue q_full; /* full buffers - data for user apps */ 372 struct cx18_queue q_full; /* full - data for user apps */
373 struct cx18_queue q_idle; /* idle - not in rotation */
357 374
358 struct work_struct out_work_order; 375 struct work_struct out_work_order;
359 376
@@ -481,10 +498,11 @@ struct vbi_info {
481 u32 inserted_frame; 498 u32 inserted_frame;
482 499
483 /* 500 /*
484 * A dummy driver stream transfer buffer with a copy of the next 501 * A dummy driver stream transfer mdl & buffer with a copy of the next
485 * sliced_mpeg_data[] buffer for output to userland apps. 502 * sliced_mpeg_data[] buffer for output to userland apps.
486 * Only used in cx18-fileops.c, but its state needs to persist at times. 503 * Only used in cx18-fileops.c, but its state needs to persist at times.
487 */ 504 */
505 struct cx18_mdl sliced_mpeg_mdl;
488 struct cx18_buffer sliced_mpeg_buf; 506 struct cx18_buffer sliced_mpeg_buf;
489}; 507};
490 508
@@ -511,7 +529,6 @@ struct cx18 {
511 u8 is_60hz; 529 u8 is_60hz;
512 u8 nof_inputs; /* number of video inputs */ 530 u8 nof_inputs; /* number of video inputs */
513 u8 nof_audio_inputs; /* number of audio inputs */ 531 u8 nof_audio_inputs; /* number of audio inputs */
514 u16 buffer_id; /* buffer ID counter */
515 u32 v4l2_cap; /* V4L2 capabilities of card */ 532 u32 v4l2_cap; /* V4L2 capabilities of card */
516 u32 hw_flags; /* Hardware description of the board */ 533 u32 hw_flags; /* Hardware description of the board */
517 unsigned int free_mdl_idx; 534 unsigned int free_mdl_idx;
diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
index e4a3faebff46..4e278db31cc9 100644
--- a/drivers/media/video/cx18/cx18-fileops.c
+++ b/drivers/media/video/cx18/cx18-fileops.c
@@ -166,11 +166,12 @@ static void cx18_dualwatch(struct cx18 *cx)
166} 166}
167 167
168 168
169static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err) 169static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block,
170 int *err)
170{ 171{
171 struct cx18 *cx = s->cx; 172 struct cx18 *cx = s->cx;
172 struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; 173 struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
173 struct cx18_buffer *buf; 174 struct cx18_mdl *mdl;
174 DEFINE_WAIT(wait); 175 DEFINE_WAIT(wait);
175 176
176 *err = 0; 177 *err = 0;
@@ -185,32 +186,33 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
185 } 186 }
186 if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && 187 if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
187 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { 188 !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
188 while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { 189 while ((mdl = cx18_dequeue(s_vbi,
190 &s_vbi->q_full))) {
189 /* byteswap and process VBI data */ 191 /* byteswap and process VBI data */
190 cx18_process_vbi_data(cx, buf, 192 cx18_process_vbi_data(cx, mdl,
191 s_vbi->type); 193 s_vbi->type);
192 cx18_stream_put_buf_fw(s_vbi, buf); 194 cx18_stream_put_mdl_fw(s_vbi, mdl);
193 } 195 }
194 } 196 }
195 buf = &cx->vbi.sliced_mpeg_buf; 197 mdl = &cx->vbi.sliced_mpeg_mdl;
196 if (buf->readpos != buf->bytesused) 198 if (mdl->readpos != mdl->bytesused)
197 return buf; 199 return mdl;
198 } 200 }
199 201
200 /* do we have new data? */ 202 /* do we have new data? */
201 buf = cx18_dequeue(s, &s->q_full); 203 mdl = cx18_dequeue(s, &s->q_full);
202 if (buf) { 204 if (mdl) {
203 if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP, 205 if (!test_and_clear_bit(CX18_F_M_NEED_SWAP,
204 &buf->b_flags)) 206 &mdl->m_flags))
205 return buf; 207 return mdl;
206 if (s->type == CX18_ENC_STREAM_TYPE_MPG) 208 if (s->type == CX18_ENC_STREAM_TYPE_MPG)
207 /* byteswap MPG data */ 209 /* byteswap MPG data */
208 cx18_buf_swap(buf); 210 cx18_mdl_swap(mdl);
209 else { 211 else {
210 /* byteswap and process VBI data */ 212 /* byteswap and process VBI data */
211 cx18_process_vbi_data(cx, buf, s->type); 213 cx18_process_vbi_data(cx, mdl, s->type);
212 } 214 }
213 return buf; 215 return mdl;
214 } 216 }
215 217
216 /* return if end of stream */ 218 /* return if end of stream */
@@ -241,21 +243,28 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
241 } 243 }
242} 244}
243 245
244static void cx18_setup_sliced_vbi_buf(struct cx18 *cx) 246static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx)
245{ 247{
248 struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl;
249 struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf;
246 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; 250 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
247 251
248 cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx]; 252 buf->buf = cx->vbi.sliced_mpeg_data[idx];
249 cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx]; 253 buf->bytesused = cx->vbi.sliced_mpeg_size[idx];
250 cx->vbi.sliced_mpeg_buf.readpos = 0; 254 buf->readpos = 0;
255
256 mdl->curr_buf = NULL;
257 mdl->bytesused = cx->vbi.sliced_mpeg_size[idx];
258 mdl->readpos = 0;
251} 259}
252 260
253static size_t cx18_copy_buf_to_user(struct cx18_stream *s, 261static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
254 struct cx18_buffer *buf, char __user *ubuf, size_t ucount) 262 struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop)
255{ 263{
256 struct cx18 *cx = s->cx; 264 struct cx18 *cx = s->cx;
257 size_t len = buf->bytesused - buf->readpos; 265 size_t len = buf->bytesused - buf->readpos;
258 266
267 *stop = false;
259 if (len > ucount) 268 if (len > ucount)
260 len = ucount; 269 len = ucount;
261 if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && 270 if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
@@ -335,7 +344,8 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
335 /* We declare we actually found a Program Pack*/ 344 /* We declare we actually found a Program Pack*/
336 cx->search_pack_header = 0; /* expect vid PES */ 345 cx->search_pack_header = 0; /* expect vid PES */
337 len = (char *)q - start; 346 len = (char *)q - start;
338 cx18_setup_sliced_vbi_buf(cx); 347 cx18_setup_sliced_vbi_mdl(cx);
348 *stop = true;
339 break; 349 break;
340 } 350 }
341 } 351 }
@@ -352,6 +362,60 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
352 return len; 362 return len;
353} 363}
354 364
365/**
366 * list_entry_is_past_end - check if a previous loop cursor is off list end
367 * @pos: the type * previously used as a loop cursor.
368 * @head: the head for your list.
369 * @member: the name of the list_struct within the struct.
370 *
371 * Check if the entry's list_head is the head of the list, thus it's not a
372 * real entry but was the loop cursor that walked past the end
373 */
374#define list_entry_is_past_end(pos, head, member) \
375 (&pos->member == (head))
376
377static size_t cx18_copy_mdl_to_user(struct cx18_stream *s,
378 struct cx18_mdl *mdl, char __user *ubuf, size_t ucount)
379{
380 size_t tot_written = 0;
381 int rc;
382 bool stop = false;
383
384 if (mdl->curr_buf == NULL)
385 mdl->curr_buf = list_first_entry(&mdl->buf_list,
386 struct cx18_buffer, list);
387
388 if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) {
389 /*
390 * For some reason we've exhausted the buffers, but the MDL
391 * object still said some data was unread.
392 * Fix that and bail out.
393 */
394 mdl->readpos = mdl->bytesused;
395 return 0;
396 }
397
398 list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) {
399
400 if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused)
401 continue;
402
403 rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written,
404 ucount - tot_written, &stop);
405 if (rc < 0)
406 return rc;
407 mdl->readpos += rc;
408 tot_written += rc;
409
410 if (stop || /* Forced stopping point for VBI insertion */
411 tot_written >= ucount || /* Reader request statisfied */
412 mdl->curr_buf->readpos < mdl->curr_buf->bytesused ||
413 mdl->readpos >= mdl->bytesused) /* MDL buffers drained */
414 break;
415 }
416 return tot_written;
417}
418
355static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, 419static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
356 size_t tot_count, int non_block) 420 size_t tot_count, int non_block)
357{ 421{
@@ -373,12 +437,12 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
373 single_frame = 1; 437 single_frame = 1;
374 438
375 for (;;) { 439 for (;;) {
376 struct cx18_buffer *buf; 440 struct cx18_mdl *mdl;
377 int rc; 441 int rc;
378 442
379 buf = cx18_get_buffer(s, non_block, &rc); 443 mdl = cx18_get_mdl(s, non_block, &rc);
380 /* if there is no data available... */ 444 /* if there is no data available... */
381 if (buf == NULL) { 445 if (mdl == NULL) {
382 /* if we got data, then return that regardless */ 446 /* if we got data, then return that regardless */
383 if (tot_written) 447 if (tot_written)
384 break; 448 break;
@@ -392,20 +456,20 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
392 return rc; 456 return rc;
393 } 457 }
394 458
395 rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, 459 rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written,
396 tot_count - tot_written); 460 tot_count - tot_written);
397 461
398 if (buf != &cx->vbi.sliced_mpeg_buf) { 462 if (mdl != &cx->vbi.sliced_mpeg_mdl) {
399 if (buf->readpos == buf->bytesused) 463 if (mdl->readpos == mdl->bytesused)
400 cx18_stream_put_buf_fw(s, buf); 464 cx18_stream_put_mdl_fw(s, mdl);
401 else 465 else
402 cx18_push(s, buf, &s->q_full); 466 cx18_push(s, mdl, &s->q_full);
403 } else if (buf->readpos == buf->bytesused) { 467 } else if (mdl->readpos == mdl->bytesused) {
404 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; 468 int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
405 469
406 cx->vbi.sliced_mpeg_size[idx] = 0; 470 cx->vbi.sliced_mpeg_size[idx] = 0;
407 cx->vbi.inserted_frame++; 471 cx->vbi.inserted_frame++;
408 cx->vbi_data_inserted += buf->bytesused; 472 cx->vbi_data_inserted += mdl->bytesused;
409 } 473 }
410 if (rc < 0) 474 if (rc < 0)
411 return rc; 475 return rc;
diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
index 6539b031b4eb..3e4fc192fdec 100644
--- a/drivers/media/video/cx18/cx18-ioctl.c
+++ b/drivers/media/video/cx18/cx18-ioctl.c
@@ -910,7 +910,8 @@ static int cx18_log_status(struct file *file, void *fh)
910 continue; 910 continue;
911 CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", 911 CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
912 s->name, s->s_flags, 912 s->name, s->s_flags,
913 atomic_read(&s->q_full.depth) * 100 / s->buffers, 913 atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100
914 / s->buffers,
914 (s->buffers * s->buf_size) / 1024, s->buffers); 915 (s->buffers * s->buf_size) / 1024, s->buffers);
915 } 916 }
916 CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", 917 CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
index 4a1249a7d46a..f231dd09c720 100644
--- a/drivers/media/video/cx18/cx18-mailbox.c
+++ b/drivers/media/video/cx18/cx18-mailbox.c
@@ -131,13 +131,39 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
131 * Functions that run in a work_queue work handling context 131 * Functions that run in a work_queue work handling context
132 */ 132 */
133 133
134static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl)
135{
136 struct cx18_buffer *buf;
137
138 if (!s->dvb.enabled || mdl->bytesused == 0)
139 return;
140
141 /* We ignore mdl and buf readpos accounting here - it doesn't matter */
142
143 /* The likely case */
144 if (list_is_singular(&mdl->buf_list)) {
145 buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
146 list);
147 if (buf->bytesused)
148 dvb_dmx_swfilter(&s->dvb.demux,
149 buf->buf, buf->bytesused);
150 return;
151 }
152
153 list_for_each_entry(buf, &mdl->buf_list, list) {
154 if (buf->bytesused == 0)
155 break;
156 dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused);
157 }
158}
159
134static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) 160static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
135{ 161{
136 u32 handle, mdl_ack_count, id; 162 u32 handle, mdl_ack_count, id;
137 struct cx18_mailbox *mb; 163 struct cx18_mailbox *mb;
138 struct cx18_mdl_ack *mdl_ack; 164 struct cx18_mdl_ack *mdl_ack;
139 struct cx18_stream *s; 165 struct cx18_stream *s;
140 struct cx18_buffer *buf; 166 struct cx18_mdl *mdl;
141 int i; 167 int i;
142 168
143 mb = &order->mb; 169 mb = &order->mb;
@@ -158,7 +184,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
158 id = mdl_ack->id; 184 id = mdl_ack->id;
159 /* 185 /*
160 * Simple integrity check for processing a stale (and possibly 186 * Simple integrity check for processing a stale (and possibly
161 * inconsistent mailbox): make sure the buffer id is in the 187 * inconsistent mailbox): make sure the MDL id is in the
162 * valid range for the stream. 188 * valid range for the stream.
163 * 189 *
164 * We go through the trouble of dealing with stale mailboxes 190 * We go through the trouble of dealing with stale mailboxes
@@ -169,44 +195,42 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order)
169 * There are occasions when we get a half changed mailbox, 195 * There are occasions when we get a half changed mailbox,
170 * which this check catches for a handle & id mismatch. If the 196 * which this check catches for a handle & id mismatch. If the
171 * handle and id do correspond, the worst case is that we 197 * handle and id do correspond, the worst case is that we
172 * completely lost the old buffer, but pick up the new buffer 198 * completely lost the old MDL, but pick up the new MDL
173 * early (but the new mdl_ack is guaranteed to be good in this 199 * early (but the new mdl_ack is guaranteed to be good in this
174 * case as the firmware wouldn't point us to a new mdl_ack until 200 * case as the firmware wouldn't point us to a new mdl_ack until
175 * it's filled in). 201 * it's filled in).
176 * 202 *
177 * cx18_queue_get buf() will detect the lost buffers 203 * cx18_queue_get_mdl() will detect the lost MDLs
178 * and send them back to q_free for fw rotation eventually. 204 * and send them back to q_free for fw rotation eventually.
179 */ 205 */
180 if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && 206 if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) &&
181 !(id >= s->mdl_base_idx && 207 !(id >= s->mdl_base_idx &&
182 id < (s->mdl_base_idx + s->buffers))) { 208 id < (s->mdl_base_idx + s->buffers))) {
183 CX18_WARN("Fell behind! Ignoring stale mailbox with " 209 CX18_WARN("Fell behind! Ignoring stale mailbox with "
184 " inconsistent data. Lost buffer for mailbox " 210 " inconsistent data. Lost MDL for mailbox "
185 "seq no %d\n", mb->request); 211 "seq no %d\n", mb->request);
186 break; 212 break;
187 } 213 }
188 buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); 214 mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used);
189 215
190 CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); 216 CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id);
191 if (buf == NULL) { 217 if (mdl == NULL) {
192 CX18_WARN("Could not find buf %d for stream %s\n", 218 CX18_WARN("Could not find MDL %d for stream %s\n",
193 id, s->name); 219 id, s->name);
194 continue; 220 continue;
195 } 221 }
196 222
197 CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", 223 CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n",
198 s->name, buf->bytesused); 224 s->name, mdl->bytesused);
199 225
200 if (s->type != CX18_ENC_STREAM_TYPE_TS) 226 if (s->type != CX18_ENC_STREAM_TYPE_TS)
201 cx18_enqueue(s, buf, &s->q_full); 227 cx18_enqueue(s, mdl, &s->q_full);
202 else { 228 else {
203 if (s->dvb.enabled) 229 cx18_mdl_send_to_dvb(s, mdl);
204 dvb_dmx_swfilter(&s->dvb.demux, buf->buf, 230 cx18_enqueue(s, mdl, &s->q_free);
205 buf->bytesused);
206 cx18_enqueue(s, buf, &s->q_free);
207 } 231 }
208 } 232 }
209 /* Put as many buffers as possible back into fw use */ 233 /* Put as many MDLs as possible back into fw use */
210 cx18_stream_load_fw_queue(s); 234 cx18_stream_load_fw_queue(s);
211 235
212 wake_up(&cx->dma_waitq); 236 wake_up(&cx->dma_waitq);
@@ -616,7 +640,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
616 640
617 /* 641 /*
618 * Wait for XPU to perform extra actions for the caller in some cases. 642 * Wait for XPU to perform extra actions for the caller in some cases.
619 * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers 643 * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs
620 * back in a burst shortly thereafter 644 * back in a burst shortly thereafter
621 */ 645 */
622 if (info->flags & API_SLOW) 646 if (info->flags & API_SLOW)
diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h
index e23aaac5b280..5ea07359904c 100644
--- a/drivers/media/video/cx18/cx18-mailbox.h
+++ b/drivers/media/video/cx18/cx18-mailbox.h
@@ -39,14 +39,14 @@
39struct cx18; 39struct cx18;
40 40
41/* 41/*
42 * This structure is used by CPU to provide completed buffers information 42 * This structure is used by CPU to provide completed MDL & buffers information.
43 * Its structure is dictrated by the layout of the SCB, required by the 43 * Its structure is dictated by the layout of the SCB, required by the
44 * firmware, but its defintion needs to be here, instead of in cx18-scb.h, 44 * firmware, but its defintion needs to be here, instead of in cx18-scb.h,
45 * for mailbox work order scheduling 45 * for mailbox work order scheduling
46 */ 46 */
47struct cx18_mdl_ack { 47struct cx18_mdl_ack {
48 u32 id; /* ID of a completed MDL */ 48 u32 id; /* ID of a completed MDL */
49 u32 data_used; /* Total data filled in the MDL for buffer 'id' */ 49 u32 data_used; /* Total data filled in the MDL with 'id' */
50}; 50};
51 51
52/* The cx18_mailbox struct is the mailbox structure which is used for passing 52/* The cx18_mailbox struct is the mailbox structure which is used for passing
diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
index b9bd4ff5535e..c1a49ecf9d98 100644
--- a/drivers/media/video/cx18/cx18-queue.c
+++ b/drivers/media/video/cx18/cx18-queue.c
@@ -26,6 +26,7 @@
26#include "cx18-queue.h" 26#include "cx18-queue.h"
27#include "cx18-streams.h" 27#include "cx18-streams.h"
28#include "cx18-scb.h" 28#include "cx18-scb.h"
29#include "cx18-io.h"
29 30
30void cx18_buf_swap(struct cx18_buffer *buf) 31void cx18_buf_swap(struct cx18_buffer *buf)
31{ 32{
@@ -35,6 +36,17 @@ void cx18_buf_swap(struct cx18_buffer *buf)
35 swab32s((u32 *)(buf->buf + i)); 36 swab32s((u32 *)(buf->buf + i));
36} 37}
37 38
39void _cx18_mdl_swap(struct cx18_mdl *mdl)
40{
41 struct cx18_buffer *buf;
42
43 list_for_each_entry(buf, &mdl->buf_list, list) {
44 if (buf->bytesused == 0)
45 break;
46 cx18_buf_swap(buf);
47 }
48}
49
38void cx18_queue_init(struct cx18_queue *q) 50void cx18_queue_init(struct cx18_queue *q)
39{ 51{
40 INIT_LIST_HEAD(&q->list); 52 INIT_LIST_HEAD(&q->list);
@@ -42,15 +54,16 @@ void cx18_queue_init(struct cx18_queue *q)
42 q->bytesused = 0; 54 q->bytesused = 0;
43} 55}
44 56
45struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 57struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
46 struct cx18_queue *q, int to_front) 58 struct cx18_queue *q, int to_front)
47{ 59{
48 /* clear the buffer if it is not to be enqueued to the full queue */ 60 /* clear the mdl if it is not to be enqueued to the full queue */
49 if (q != &s->q_full) { 61 if (q != &s->q_full) {
50 buf->bytesused = 0; 62 mdl->bytesused = 0;
51 buf->readpos = 0; 63 mdl->readpos = 0;
52 buf->b_flags = 0; 64 mdl->m_flags = 0;
53 buf->skipped = 0; 65 mdl->skipped = 0;
66 mdl->curr_buf = NULL;
54 } 67 }
55 68
56 /* q_busy is restricted to a max buffer count imposed by firmware */ 69 /* q_busy is restricted to a max buffer count imposed by firmware */
@@ -61,125 +74,270 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
61 spin_lock(&q->lock); 74 spin_lock(&q->lock);
62 75
63 if (to_front) 76 if (to_front)
64 list_add(&buf->list, &q->list); /* LIFO */ 77 list_add(&mdl->list, &q->list); /* LIFO */
65 else 78 else
66 list_add_tail(&buf->list, &q->list); /* FIFO */ 79 list_add_tail(&mdl->list, &q->list); /* FIFO */
67 q->bytesused += buf->bytesused - buf->readpos; 80 q->bytesused += mdl->bytesused - mdl->readpos;
68 atomic_inc(&q->depth); 81 atomic_inc(&q->depth);
69 82
70 spin_unlock(&q->lock); 83 spin_unlock(&q->lock);
71 return q; 84 return q;
72} 85}
73 86
74struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) 87struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
75{ 88{
76 struct cx18_buffer *buf = NULL; 89 struct cx18_mdl *mdl = NULL;
77 90
78 spin_lock(&q->lock); 91 spin_lock(&q->lock);
79 if (!list_empty(&q->list)) { 92 if (!list_empty(&q->list)) {
80 buf = list_first_entry(&q->list, struct cx18_buffer, list); 93 mdl = list_first_entry(&q->list, struct cx18_mdl, list);
81 list_del_init(&buf->list); 94 list_del_init(&mdl->list);
82 q->bytesused -= buf->bytesused - buf->readpos; 95 q->bytesused -= mdl->bytesused - mdl->readpos;
83 buf->skipped = 0; 96 mdl->skipped = 0;
84 atomic_dec(&q->depth); 97 atomic_dec(&q->depth);
85 } 98 }
86 spin_unlock(&q->lock); 99 spin_unlock(&q->lock);
87 return buf; 100 return mdl;
101}
102
103static void _cx18_mdl_set_buf_bytesused(struct cx18_stream *s,
104 struct cx18_mdl *mdl)
105{
106 struct cx18_buffer *buf;
107 u32 buf_size = s->buf_size;
108 u32 bytesused = mdl->bytesused;
109
110 list_for_each_entry(buf, &mdl->buf_list, list) {
111 buf->readpos = 0;
112 if (bytesused >= buf_size) {
113 buf->bytesused = buf_size;
114 bytesused -= buf_size;
115 } else {
116 buf->bytesused = bytesused;
117 bytesused = 0;
118 }
119 }
88} 120}
89 121
90struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, 122static inline void cx18_mdl_set_buf_bytesused(struct cx18_stream *s,
123 struct cx18_mdl *mdl)
124{
125 struct cx18_buffer *buf;
126
127 if (list_is_singular(&mdl->buf_list)) {
128 buf = list_first_entry(&mdl->buf_list, struct cx18_buffer,
129 list);
130 buf->bytesused = mdl->bytesused;
131 buf->readpos = 0;
132 } else {
133 _cx18_mdl_set_buf_bytesused(s, mdl);
134 }
135}
136
137struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
91 u32 bytesused) 138 u32 bytesused)
92{ 139{
93 struct cx18 *cx = s->cx; 140 struct cx18 *cx = s->cx;
94 struct cx18_buffer *buf; 141 struct cx18_mdl *mdl;
95 struct cx18_buffer *tmp; 142 struct cx18_mdl *tmp;
96 struct cx18_buffer *ret = NULL; 143 struct cx18_mdl *ret = NULL;
97 LIST_HEAD(sweep_up); 144 LIST_HEAD(sweep_up);
98 145
99 /* 146 /*
100 * We don't have to acquire multiple q locks here, because we are 147 * We don't have to acquire multiple q locks here, because we are
101 * serialized by the single threaded work handler. 148 * serialized by the single threaded work handler.
102 * Buffers from the firmware will thus remain in order as 149 * MDLs 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. 150 * they are moved from q_busy to q_full or to the dvb ring buffer.
104 */ 151 */
105 spin_lock(&s->q_busy.lock); 152 spin_lock(&s->q_busy.lock);
106 list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { 153 list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) {
107 /* 154 /*
108 * We should find what the firmware told us is done, 155 * 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 156 * right at the front of the queue. If we don't, we likely have
110 * missed a buffer done message from the firmware. 157 * missed an mdl done message from the firmware.
111 * Once we skip a buffer repeatedly, relative to the size of 158 * Once we skip an mdl repeatedly, relative to the size of
112 * q_busy, we have high confidence we've missed it. 159 * q_busy, we have high confidence we've missed it.
113 */ 160 */
114 if (buf->id != id) { 161 if (mdl->id != id) {
115 buf->skipped++; 162 mdl->skipped++;
116 if (buf->skipped >= atomic_read(&s->q_busy.depth)-1) { 163 if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) {
117 /* buffer must have fallen out of rotation */ 164 /* mdl must have fallen out of rotation */
118 CX18_WARN("Skipped %s, buffer %d, %d " 165 CX18_WARN("Skipped %s, MDL %d, %d "
119 "times - it must have dropped out of " 166 "times - it must have dropped out of "
120 "rotation\n", s->name, buf->id, 167 "rotation\n", s->name, mdl->id,
121 buf->skipped); 168 mdl->skipped);
122 /* Sweep it up to put it back into rotation */ 169 /* Sweep it up to put it back into rotation */
123 list_move_tail(&buf->list, &sweep_up); 170 list_move_tail(&mdl->list, &sweep_up);
124 atomic_dec(&s->q_busy.depth); 171 atomic_dec(&s->q_busy.depth);
125 } 172 }
126 continue; 173 continue;
127 } 174 }
128 /* 175 /*
129 * We pull the desired buffer off of the queue here. Something 176 * We pull the desired mdl off of the queue here. Something
130 * will have to put it back on a queue later. 177 * will have to put it back on a queue later.
131 */ 178 */
132 list_del_init(&buf->list); 179 list_del_init(&mdl->list);
133 atomic_dec(&s->q_busy.depth); 180 atomic_dec(&s->q_busy.depth);
134 ret = buf; 181 ret = mdl;
135 break; 182 break;
136 } 183 }
137 spin_unlock(&s->q_busy.lock); 184 spin_unlock(&s->q_busy.lock);
138 185
139 /* 186 /*
140 * We found the buffer for which we were looking. Get it ready for 187 * We found the mdl for which we were looking. Get it ready for
141 * the caller to put on q_full or in the dvb ring buffer. 188 * the caller to put on q_full or in the dvb ring buffer.
142 */ 189 */
143 if (ret != NULL) { 190 if (ret != NULL) {
144 ret->bytesused = bytesused; 191 ret->bytesused = bytesused;
145 ret->skipped = 0; 192 ret->skipped = 0;
146 /* readpos and b_flags were 0'ed when the buf went on q_busy */ 193 /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */
147 cx18_buf_sync_for_cpu(s, ret); 194 cx18_mdl_set_buf_bytesused(s, ret);
195 cx18_mdl_sync_for_cpu(s, ret);
148 if (s->type != CX18_ENC_STREAM_TYPE_TS) 196 if (s->type != CX18_ENC_STREAM_TYPE_TS)
149 set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); 197 set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags);
150 } 198 }
151 199
152 /* Put any buffers the firmware is ignoring back into normal rotation */ 200 /* Put any mdls the firmware is ignoring back into normal rotation */
153 list_for_each_entry_safe(buf, tmp, &sweep_up, list) { 201 list_for_each_entry_safe(mdl, tmp, &sweep_up, list) {
154 list_del_init(&buf->list); 202 list_del_init(&mdl->list);
155 cx18_enqueue(s, buf, &s->q_free); 203 cx18_enqueue(s, mdl, &s->q_free);
156 } 204 }
157 return ret; 205 return ret;
158} 206}
159 207
160/* Move all buffers of a queue to q_free, while flushing the buffers */ 208/* Move all mdls of a queue, while flushing the mdl */
161static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) 209static void cx18_queue_flush(struct cx18_stream *s,
210 struct cx18_queue *q_src, struct cx18_queue *q_dst)
162{ 211{
163 struct cx18_buffer *buf; 212 struct cx18_mdl *mdl;
164 213
165 if (q == &s->q_free) 214 /* It only makes sense to flush to q_free or q_idle */
215 if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy)
166 return; 216 return;
167 217
168 spin_lock(&q->lock); 218 spin_lock(&q_src->lock);
169 while (!list_empty(&q->list)) { 219 spin_lock(&q_dst->lock);
170 buf = list_first_entry(&q->list, struct cx18_buffer, list); 220 while (!list_empty(&q_src->list)) {
171 list_move_tail(&buf->list, &s->q_free.list); 221 mdl = list_first_entry(&q_src->list, struct cx18_mdl, list);
172 buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; 222 list_move_tail(&mdl->list, &q_dst->list);
173 atomic_inc(&s->q_free.depth); 223 mdl->bytesused = 0;
224 mdl->readpos = 0;
225 mdl->m_flags = 0;
226 mdl->skipped = 0;
227 mdl->curr_buf = NULL;
228 atomic_inc(&q_dst->depth);
174 } 229 }
175 cx18_queue_init(q); 230 cx18_queue_init(q_src);
176 spin_unlock(&q->lock); 231 spin_unlock(&q_src->lock);
232 spin_unlock(&q_dst->lock);
177} 233}
178 234
179void cx18_flush_queues(struct cx18_stream *s) 235void cx18_flush_queues(struct cx18_stream *s)
180{ 236{
181 cx18_queue_flush(s, &s->q_busy); 237 cx18_queue_flush(s, &s->q_busy, &s->q_free);
182 cx18_queue_flush(s, &s->q_full); 238 cx18_queue_flush(s, &s->q_full, &s->q_free);
239}
240
241/*
242 * Note, s->buf_pool is not protected by a lock,
243 * the stream better not have *anything* going on when calling this
244 */
245void cx18_unload_queues(struct cx18_stream *s)
246{
247 struct cx18_queue *q_idle = &s->q_idle;
248 struct cx18_mdl *mdl;
249 struct cx18_buffer *buf;
250
251 /* Move all MDLS to q_idle */
252 cx18_queue_flush(s, &s->q_busy, q_idle);
253 cx18_queue_flush(s, &s->q_full, q_idle);
254 cx18_queue_flush(s, &s->q_free, q_idle);
255
256 /* Reset MDL id's and move all buffers back to the stream's buf_pool */
257 spin_lock(&q_idle->lock);
258 list_for_each_entry(mdl, &q_idle->list, list) {
259 while (!list_empty(&mdl->buf_list)) {
260 buf = list_first_entry(&mdl->buf_list,
261 struct cx18_buffer, list);
262 list_move_tail(&buf->list, &s->buf_pool);
263 buf->bytesused = 0;
264 buf->readpos = 0;
265 }
266 mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */
267 /* all other mdl fields were cleared by cx18_queue_flush() */
268 }
269 spin_unlock(&q_idle->lock);
270}
271
272/*
273 * Note, s->buf_pool is not protected by a lock,
274 * the stream better not have *anything* going on when calling this
275 */
276void cx18_load_queues(struct cx18_stream *s)
277{
278 struct cx18 *cx = s->cx;
279 struct cx18_mdl *mdl;
280 struct cx18_buffer *buf;
281 int mdl_id;
282 int i;
283
284 /*
285 * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free
286 * Excess MDLs are left on q_idle
287 * Excess buffers are left in buf_pool and/or on an MDL in q_idle
288 */
289 mdl_id = s->mdl_base_idx;
290 for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl;
291 mdl != NULL && i == s->bufs_per_mdl;
292 mdl = cx18_dequeue(s, &s->q_idle)) {
293
294 mdl->id = mdl_id;
295
296 for (i = 0; i < s->bufs_per_mdl; i++) {
297 if (list_empty(&s->buf_pool))
298 break;
299
300 buf = list_first_entry(&s->buf_pool, struct cx18_buffer,
301 list);
302 list_move_tail(&buf->list, &mdl->buf_list);
303
304 /* update the firmware's MDL array with this buffer */
305 cx18_writel(cx, buf->dma_handle,
306 &cx->scb->cpu_mdl[mdl_id + i].paddr);
307 cx18_writel(cx, s->buf_size,
308 &cx->scb->cpu_mdl[mdl_id + i].length);
309 }
310
311 if (i == s->bufs_per_mdl)
312 cx18_enqueue(s, mdl, &s->q_free);
313 else
314 cx18_push(s, mdl, &s->q_idle); /* not enough buffers */
315 mdl_id += i;
316 }
317}
318
319void _cx18_mdl_sync_for_cpu(struct cx18_stream *s, struct cx18_mdl *mdl)
320{
321 int dma = s->dma;
322 u32 buf_size = s->buf_size;
323 struct pci_dev *pci_dev = s->cx->pci_dev;
324 struct cx18_buffer *buf;
325
326 list_for_each_entry(buf, &mdl->buf_list, list)
327 pci_dma_sync_single_for_cpu(pci_dev, buf->dma_handle,
328 buf_size, dma);
329}
330
331void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl)
332{
333 int dma = s->dma;
334 u32 buf_size = s->buf_size;
335 struct pci_dev *pci_dev = s->cx->pci_dev;
336 struct cx18_buffer *buf;
337
338 list_for_each_entry(buf, &mdl->buf_list, list)
339 pci_dma_sync_single_for_device(pci_dev, buf->dma_handle,
340 buf_size, dma);
183} 341}
184 342
185int cx18_stream_alloc(struct cx18_stream *s) 343int cx18_stream_alloc(struct cx18_stream *s)
@@ -207,24 +365,40 @@ int cx18_stream_alloc(struct cx18_stream *s)
207 365
208 s->mdl_base_idx = cx->free_mdl_idx; 366 s->mdl_base_idx = cx->free_mdl_idx;
209 367
210 /* allocate stream buffers. Initially all buffers are in q_free. */ 368 /* allocate stream buffers and MDLs */
211 for (i = 0; i < s->buffers; i++) { 369 for (i = 0; i < s->buffers; i++) {
212 struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), 370 struct cx18_mdl *mdl;
213 GFP_KERNEL|__GFP_NOWARN); 371 struct cx18_buffer *buf;
214 372
215 if (buf == NULL) 373 /* 1 MDL per buffer to handle the worst & also default case */
374 mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN);
375 if (mdl == NULL)
216 break; 376 break;
377
378 buf = kzalloc(sizeof(struct cx18_buffer),
379 GFP_KERNEL|__GFP_NOWARN);
380 if (buf == NULL) {
381 kfree(mdl);
382 break;
383 }
384
217 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); 385 buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN);
218 if (buf->buf == NULL) { 386 if (buf->buf == NULL) {
387 kfree(mdl);
219 kfree(buf); 388 kfree(buf);
220 break; 389 break;
221 } 390 }
222 buf->id = cx->buffer_id++; 391
392 INIT_LIST_HEAD(&mdl->list);
393 INIT_LIST_HEAD(&mdl->buf_list);
394 mdl->id = s->mdl_base_idx; /* a somewhat safe value */
395 cx18_enqueue(s, mdl, &s->q_idle);
396
223 INIT_LIST_HEAD(&buf->list); 397 INIT_LIST_HEAD(&buf->list);
224 buf->dma_handle = pci_map_single(s->cx->pci_dev, 398 buf->dma_handle = pci_map_single(s->cx->pci_dev,
225 buf->buf, s->buf_size, s->dma); 399 buf->buf, s->buf_size, s->dma);
226 cx18_buf_sync_for_cpu(s, buf); 400 cx18_buf_sync_for_cpu(s, buf);
227 cx18_enqueue(s, buf, &s->q_free); 401 list_add_tail(&buf->list, &s->buf_pool);
228 } 402 }
229 if (i == s->buffers) { 403 if (i == s->buffers) {
230 cx->free_mdl_idx += s->buffers; 404 cx->free_mdl_idx += s->buffers;
@@ -237,13 +411,21 @@ int cx18_stream_alloc(struct cx18_stream *s)
237 411
238void cx18_stream_free(struct cx18_stream *s) 412void cx18_stream_free(struct cx18_stream *s)
239{ 413{
414 struct cx18_mdl *mdl;
240 struct cx18_buffer *buf; 415 struct cx18_buffer *buf;
241 416
242 /* move all buffers to q_free */ 417 /* move all buffers to buf_pool and all MDLs to q_idle */
243 cx18_flush_queues(s); 418 cx18_unload_queues(s);
419
420 /* empty q_idle */
421 while ((mdl = cx18_dequeue(s, &s->q_idle)))
422 kfree(mdl);
423
424 /* empty buf_pool */
425 while (!list_empty(&s->buf_pool)) {
426 buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list);
427 list_del_init(&buf->list);
244 428
245 /* empty q_free */
246 while ((buf = cx18_dequeue(s, &s->q_free))) {
247 pci_unmap_single(s->cx->pci_dev, buf->dma_handle, 429 pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
248 s->buf_size, s->dma); 430 s->buf_size, s->dma);
249 kfree(buf->buf); 431 kfree(buf->buf);
diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
index 4de06269d88f..96747e5e7c3c 100644
--- a/drivers/media/video/cx18/cx18-queue.h
+++ b/drivers/media/video/cx18/cx18-queue.h
@@ -33,6 +33,19 @@ static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
33 s->buf_size, s->dma); 33 s->buf_size, s->dma);
34} 34}
35 35
36void _cx18_mdl_sync_for_cpu(struct cx18_stream *s, struct cx18_mdl *mdl);
37
38static inline void cx18_mdl_sync_for_cpu(struct cx18_stream *s,
39 struct cx18_mdl *mdl)
40{
41 if (list_is_singular(&mdl->buf_list))
42 cx18_buf_sync_for_cpu(s, list_first_entry(&mdl->buf_list,
43 struct cx18_buffer,
44 list));
45 else
46 _cx18_mdl_sync_for_cpu(s, mdl);
47}
48
36static inline void cx18_buf_sync_for_device(struct cx18_stream *s, 49static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
37 struct cx18_buffer *buf) 50 struct cx18_buffer *buf)
38{ 51{
@@ -40,32 +53,59 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
40 s->buf_size, s->dma); 53 s->buf_size, s->dma);
41} 54}
42 55
56void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl);
57
58static inline void cx18_mdl_sync_for_device(struct cx18_stream *s,
59 struct cx18_mdl *mdl)
60{
61 if (list_is_singular(&mdl->buf_list))
62 cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list,
63 struct cx18_buffer,
64 list));
65 else
66 _cx18_mdl_sync_for_device(s, mdl);
67}
68
43void cx18_buf_swap(struct cx18_buffer *buf); 69void cx18_buf_swap(struct cx18_buffer *buf);
70void _cx18_mdl_swap(struct cx18_mdl *mdl);
71
72static inline void cx18_mdl_swap(struct cx18_mdl *mdl)
73{
74 if (list_is_singular(&mdl->buf_list))
75 cx18_buf_swap(list_first_entry(&mdl->buf_list,
76 struct cx18_buffer, list));
77 else
78 _cx18_mdl_swap(mdl);
79}
44 80
45/* cx18_queue utility functions */ 81/* cx18_queue utility functions */
46struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 82struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
47 struct cx18_queue *q, int to_front); 83 struct cx18_queue *q, int to_front);
48 84
49static inline 85static inline
50struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, 86struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl,
51 struct cx18_queue *q) 87 struct cx18_queue *q)
52{ 88{
53 return _cx18_enqueue(s, buf, q, 0); /* FIFO */ 89 return _cx18_enqueue(s, mdl, q, 0); /* FIFO */
54} 90}
55 91
56static inline 92static inline
57struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, 93struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl,
58 struct cx18_queue *q) 94 struct cx18_queue *q)
59{ 95{
60 return _cx18_enqueue(s, buf, q, 1); /* LIFO */ 96 return _cx18_enqueue(s, mdl, q, 1); /* LIFO */
61} 97}
62 98
63void cx18_queue_init(struct cx18_queue *q); 99void cx18_queue_init(struct cx18_queue *q);
64struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); 100struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
65struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, 101struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id,
66 u32 bytesused); 102 u32 bytesused);
67void cx18_flush_queues(struct cx18_stream *s); 103void cx18_flush_queues(struct cx18_stream *s);
68 104
105/* queue MDL reconfiguration helpers */
106void cx18_unload_queues(struct cx18_stream *s);
107void cx18_load_queues(struct cx18_stream *s);
108
69/* cx18_stream utility functions */ 109/* cx18_stream utility functions */
70int cx18_stream_alloc(struct cx18_stream *s); 110int cx18_stream_alloc(struct cx18_stream *s);
71void cx18_stream_free(struct cx18_stream *s); 111void cx18_stream_free(struct cx18_stream *s);
diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
index 10228c5ca5a0..9f8adda6f261 100644
--- a/drivers/media/video/cx18/cx18-streams.c
+++ b/drivers/media/video/cx18/cx18-streams.c
@@ -115,6 +115,9 @@ static void cx18_stream_init(struct cx18 *cx, int type)
115 s->dma = cx18_stream_info[type].dma; 115 s->dma = cx18_stream_info[type].dma;
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 INIT_LIST_HEAD(&s->buf_pool);
119 s->bufs_per_mdl = 1;
120 s->mdl_size = s->buf_size * s->bufs_per_mdl;
118 121
119 init_waitqueue_head(&s->waitq); 122 init_waitqueue_head(&s->waitq);
120 s->id = -1; 123 s->id = -1;
@@ -124,6 +127,8 @@ static void cx18_stream_init(struct cx18 *cx, int type)
124 cx18_queue_init(&s->q_busy); 127 cx18_queue_init(&s->q_busy);
125 spin_lock_init(&s->q_full.lock); 128 spin_lock_init(&s->q_full.lock);
126 cx18_queue_init(&s->q_full); 129 cx18_queue_init(&s->q_full);
130 spin_lock_init(&s->q_idle.lock);
131 cx18_queue_init(&s->q_idle);
127 132
128 INIT_WORK(&s->out_work_order, cx18_out_work_handler); 133 INIT_WORK(&s->out_work_order, cx18_out_work_handler);
129} 134}
@@ -441,8 +446,8 @@ static void cx18_vbi_setup(struct cx18_stream *s)
441} 446}
442 447
443static 448static
444struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, 449struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s,
445 struct cx18_buffer *buf) 450 struct cx18_mdl *mdl)
446{ 451{
447 struct cx18 *cx = s->cx; 452 struct cx18 *cx = s->cx;
448 struct cx18_queue *q; 453 struct cx18_queue *q;
@@ -451,16 +456,16 @@ struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s,
451 if (s->handle == CX18_INVALID_TASK_HANDLE || 456 if (s->handle == CX18_INVALID_TASK_HANDLE ||
452 test_bit(CX18_F_S_STOPPING, &s->s_flags) || 457 test_bit(CX18_F_S_STOPPING, &s->s_flags) ||
453 !test_bit(CX18_F_S_STREAMING, &s->s_flags)) 458 !test_bit(CX18_F_S_STREAMING, &s->s_flags))
454 return cx18_enqueue(s, buf, &s->q_free); 459 return cx18_enqueue(s, mdl, &s->q_free);
455 460
456 q = cx18_enqueue(s, buf, &s->q_busy); 461 q = cx18_enqueue(s, mdl, &s->q_busy);
457 if (q != &s->q_busy) 462 if (q != &s->q_busy)
458 return q; /* The firmware has the max buffers it can handle */ 463 return q; /* The firmware has the max MDLs it can handle */
459 464
460 cx18_buf_sync_for_device(s, buf); 465 cx18_mdl_sync_for_device(s, mdl);
461 cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, 466 cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
462 (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 467 (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem,
463 1, buf->id, s->buf_size); 468 s->bufs_per_mdl, mdl->id, s->mdl_size);
464 return q; 469 return q;
465} 470}
466 471
@@ -468,7 +473,7 @@ static
468void _cx18_stream_load_fw_queue(struct cx18_stream *s) 473void _cx18_stream_load_fw_queue(struct cx18_stream *s)
469{ 474{
470 struct cx18_queue *q; 475 struct cx18_queue *q;
471 struct cx18_buffer *buf; 476 struct cx18_mdl *mdl;
472 477
473 if (atomic_read(&s->q_free.depth) == 0 || 478 if (atomic_read(&s->q_free.depth) == 0 ||
474 atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM) 479 atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM)
@@ -476,10 +481,10 @@ void _cx18_stream_load_fw_queue(struct cx18_stream *s)
476 481
477 /* Move from q_free to q_busy notifying the firmware, until the limit */ 482 /* Move from q_free to q_busy notifying the firmware, until the limit */
478 do { 483 do {
479 buf = cx18_dequeue(s, &s->q_free); 484 mdl = cx18_dequeue(s, &s->q_free);
480 if (buf == NULL) 485 if (mdl == NULL)
481 break; 486 break;
482 q = _cx18_stream_put_buf_fw(s, buf); 487 q = _cx18_stream_put_mdl_fw(s, mdl);
483 } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM 488 } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM
484 && q == &s->q_busy); 489 && q == &s->q_busy);
485} 490}
@@ -492,11 +497,21 @@ void cx18_out_work_handler(struct work_struct *work)
492 _cx18_stream_load_fw_queue(s); 497 _cx18_stream_load_fw_queue(s);
493} 498}
494 499
500static void cx18_stream_configure_mdls(struct cx18_stream *s)
501{
502 cx18_unload_queues(s);
503
504 /* For now */
505 s->bufs_per_mdl = 1;
506 s->mdl_size = s->buf_size * s->bufs_per_mdl;
507
508 cx18_load_queues(s);
509}
510
495int cx18_start_v4l2_encode_stream(struct cx18_stream *s) 511int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
496{ 512{
497 u32 data[MAX_MB_ARGUMENTS]; 513 u32 data[MAX_MB_ARGUMENTS];
498 struct cx18 *cx = s->cx; 514 struct cx18 *cx = s->cx;
499 struct cx18_buffer *buf;
500 int captype = 0; 515 int captype = 0;
501 struct cx18_api_func_private priv; 516 struct cx18_api_func_private priv;
502 517
@@ -619,14 +634,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
619 (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); 634 (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
620 635
621 /* Init all the cpu_mdls for this stream */ 636 /* Init all the cpu_mdls for this stream */
622 cx18_flush_queues(s); 637 cx18_stream_configure_mdls(s);
623 spin_lock(&s->q_free.lock);
624 list_for_each_entry(buf, &s->q_free.list, list) {
625 cx18_writel(cx, buf->dma_handle,
626 &cx->scb->cpu_mdl[buf->id].paddr);
627 cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
628 }
629 spin_unlock(&s->q_free.lock);
630 _cx18_stream_load_fw_queue(s); 638 _cx18_stream_load_fw_queue(s);
631 639
632 /* begin_capture */ 640 /* begin_capture */
diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h
index 1afc3fd9d822..4a01db5e5a35 100644
--- a/drivers/media/video/cx18/cx18-streams.h
+++ b/drivers/media/video/cx18/cx18-streams.h
@@ -28,18 +28,18 @@ int cx18_streams_setup(struct cx18 *cx);
28int cx18_streams_register(struct cx18 *cx); 28int cx18_streams_register(struct cx18 *cx);
29void cx18_streams_cleanup(struct cx18 *cx, int unregister); 29void cx18_streams_cleanup(struct cx18 *cx, int unregister);
30 30
31/* Related to submission of buffers to firmware */ 31/* Related to submission of mdls to firmware */
32static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) 32static inline void cx18_stream_load_fw_queue(struct cx18_stream *s)
33{ 33{
34 struct cx18 *cx = s->cx; 34 struct cx18 *cx = s->cx;
35 queue_work(cx->out_work_queue, &s->out_work_order); 35 queue_work(cx->out_work_queue, &s->out_work_order);
36} 36}
37 37
38static inline void cx18_stream_put_buf_fw(struct cx18_stream *s, 38static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s,
39 struct cx18_buffer *buf) 39 struct cx18_mdl *mdl)
40{ 40{
41 /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ 41 /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */
42 cx18_enqueue(s, buf, &s->q_free); 42 cx18_enqueue(s, mdl, &s->q_free);
43 cx18_stream_load_fw_queue(s); 43 cx18_stream_load_fw_queue(s);
44} 44}
45 45
diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
index c2aef4add31d..574c1c6974f8 100644
--- a/drivers/media/video/cx18/cx18-vbi.c
+++ b/drivers/media/video/cx18/cx18-vbi.c
@@ -105,6 +105,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
105 105
106/* Compress raw VBI format, removes leading SAV codes and surplus space 106/* Compress raw VBI format, removes leading SAV codes and surplus space
107 after the frame. Returns new compressed size. */ 107 after the frame. Returns new compressed size. */
108/* FIXME - this function ignores the input size. */
108static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) 109static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
109{ 110{
110 u32 line_size = vbi_active_samples; 111 u32 line_size = vbi_active_samples;
@@ -185,8 +186,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
185 return line; 186 return line;
186} 187}
187 188
188void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, 189static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf)
189 int streamtype)
190{ 190{
191 /* 191 /*
192 * The CX23418 provides a 12 byte header in its raw VBI buffers to us: 192 * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
@@ -203,9 +203,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
203 u32 pts; 203 u32 pts;
204 int lines; 204 int lines;
205 205
206 if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
207 return;
208
209 /* 206 /*
210 * The CX23418 sends us data that is 32 bit little-endian swapped, 207 * The CX23418 sends us data that is 32 bit little-endian swapped,
211 * but we want the raw VBI bytes in the order they were in the raster 208 * but we want the raw VBI bytes in the order they were in the raster
@@ -250,3 +247,31 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
250 copy_vbi_data(cx, lines, pts); 247 copy_vbi_data(cx, lines, pts);
251 cx->vbi.frame++; 248 cx->vbi.frame++;
252} 249}
250
251void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
252 int streamtype)
253{
254 struct cx18_buffer *buf;
255 u32 orig_used;
256
257 if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
258 return;
259
260 /*
261 * Big assumption here:
262 * Every buffer hooked to the MDL's buf_list is a complete VBI frame
263 * that ends at the end of the buffer.
264 *
265 * To assume anything else would make the code in this file
266 * more complex, or require extra memcpy()'s to make the
267 * buffers satisfy the above assumption. It's just simpler to set
268 * up the encoder buffer transfers to make the assumption true.
269 */
270 list_for_each_entry(buf, &mdl->buf_list, list) {
271 orig_used = buf->bytesused;
272 if (orig_used == 0)
273 break;
274 _cx18_process_vbi_data(cx, buf);
275 mdl->bytesused -= (orig_used - buf->bytesused);
276 }
277}
diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
index e7e1ae427f34..b365cf4b4668 100644
--- a/drivers/media/video/cx18/cx18-vbi.h
+++ b/drivers/media/video/cx18/cx18-vbi.h
@@ -21,6 +21,6 @@
21 * 02111-1307 USA 21 * 02111-1307 USA
22 */ 22 */
23 23
24void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, 24void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl,
25 int streamtype); 25 int streamtype);
26int cx18_used_line(struct cx18 *cx, int line, int field); 26int cx18_used_line(struct cx18 *cx, int line, int field);