diff options
author | Robert Jarzmik <robert.jarzmik@free.fr> | 2009-03-31 02:44:21 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-04-06 20:43:45 -0400 |
commit | 37f5aefd537d5f98b3fa9726362effeccf1d2161 (patch) | |
tree | c9a27e6f7f5ada15c3774b79b394030552bd4798 /drivers/media/video/pxa_camera.c | |
parent | 92a8337b380f0978ac81f096d6324d3ad689f83e (diff) |
V4L/DVB (11320): pxa_camera: Remove YUV planar formats hole
All planes were PAGE aligned (ie. 4096 bytes aligned). This
is not consistent with YUV422 format, which requires Y, U
and V planes glued together. The new implementation forces
the alignement on 8 bytes (DMA requirement), which is almost
always the case (granted by width x height being a multiple
of 8).
The test cases include tests in both YUV422 and RGB565 :
- a picture of size 111 x 111 (cross RAM pages example)
- a picture of size 1023 x 4 in (under 1 RAM page)
- a picture of size 1024 x 4 in (exactly 1 RAM page)
- a picture of size 1025 x 4 in (over 1 RAM page)
- a picture of size 1280 x 1024 (many RAM pages)
Signed-off-by: Robert Jarzmik <robert.jarzmik@free.fr>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/pxa_camera.c')
-rw-r--r-- | drivers/media/video/pxa_camera.c | 143 |
1 files changed, 105 insertions, 38 deletions
diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 5f57e4018849..07f792b659d9 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c | |||
@@ -287,19 +287,63 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) | |||
287 | buf->vb.state = VIDEOBUF_NEEDS_INIT; | 287 | buf->vb.state = VIDEOBUF_NEEDS_INIT; |
288 | } | 288 | } |
289 | 289 | ||
290 | static int calculate_dma_sglen(struct scatterlist *sglist, int sglen, | ||
291 | int sg_first_ofs, int size) | ||
292 | { | ||
293 | int i, offset, dma_len, xfer_len; | ||
294 | struct scatterlist *sg; | ||
295 | |||
296 | offset = sg_first_ofs; | ||
297 | for_each_sg(sglist, sg, sglen, i) { | ||
298 | dma_len = sg_dma_len(sg); | ||
299 | |||
300 | /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ | ||
301 | xfer_len = roundup(min(dma_len - offset, size), 8); | ||
302 | |||
303 | size = max(0, size - xfer_len); | ||
304 | offset = 0; | ||
305 | if (size == 0) | ||
306 | break; | ||
307 | } | ||
308 | |||
309 | BUG_ON(size != 0); | ||
310 | return i + 1; | ||
311 | } | ||
312 | |||
313 | /** | ||
314 | * pxa_init_dma_channel - init dma descriptors | ||
315 | * @pcdev: pxa camera device | ||
316 | * @buf: pxa buffer to find pxa dma channel | ||
317 | * @dma: dma video buffer | ||
318 | * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V') | ||
319 | * @cibr: camera Receive Buffer Register | ||
320 | * @size: bytes to transfer | ||
321 | * @sg_first: first element of sg_list | ||
322 | * @sg_first_ofs: offset in first element of sg_list | ||
323 | * | ||
324 | * Prepares the pxa dma descriptors to transfer one camera channel. | ||
325 | * Beware sg_first and sg_first_ofs are both input and output parameters. | ||
326 | * | ||
327 | * Returns 0 or -ENOMEM if no coherent memory is available | ||
328 | */ | ||
290 | static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, | 329 | static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, |
291 | struct pxa_buffer *buf, | 330 | struct pxa_buffer *buf, |
292 | struct videobuf_dmabuf *dma, int channel, | 331 | struct videobuf_dmabuf *dma, int channel, |
293 | int sglen, int sg_start, int cibr, | 332 | int cibr, int size, |
294 | unsigned int size) | 333 | struct scatterlist **sg_first, int *sg_first_ofs) |
295 | { | 334 | { |
296 | struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; | 335 | struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; |
297 | int i; | 336 | struct scatterlist *sg; |
337 | int i, offset, sglen; | ||
338 | int dma_len = 0, xfer_len = 0; | ||
298 | 339 | ||
299 | if (pxa_dma->sg_cpu) | 340 | if (pxa_dma->sg_cpu) |
300 | dma_free_coherent(pcdev->dev, pxa_dma->sg_size, | 341 | dma_free_coherent(pcdev->dev, pxa_dma->sg_size, |
301 | pxa_dma->sg_cpu, pxa_dma->sg_dma); | 342 | pxa_dma->sg_cpu, pxa_dma->sg_dma); |
302 | 343 | ||
344 | sglen = calculate_dma_sglen(*sg_first, dma->sglen, | ||
345 | *sg_first_ofs, size); | ||
346 | |||
303 | pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); | 347 | pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); |
304 | pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, | 348 | pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, |
305 | &pxa_dma->sg_dma, GFP_KERNEL); | 349 | &pxa_dma->sg_dma, GFP_KERNEL); |
@@ -307,28 +351,54 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, | |||
307 | return -ENOMEM; | 351 | return -ENOMEM; |
308 | 352 | ||
309 | pxa_dma->sglen = sglen; | 353 | pxa_dma->sglen = sglen; |
354 | offset = *sg_first_ofs; | ||
310 | 355 | ||
311 | for (i = 0; i < sglen; i++) { | 356 | dev_dbg(pcdev->dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", |
312 | int sg_i = sg_start + i; | 357 | *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma); |
313 | struct scatterlist *sg = dma->sglist; | ||
314 | unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; | ||
315 | 358 | ||
316 | pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; | 359 | |
317 | pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); | 360 | for_each_sg(*sg_first, sg, sglen, i) { |
361 | dma_len = sg_dma_len(sg); | ||
318 | 362 | ||
319 | /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ | 363 | /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ |
320 | xfer_len = (min(dma_len, size) + 7) & ~7; | 364 | xfer_len = roundup(min(dma_len - offset, size), 8); |
321 | 365 | ||
366 | size = max(0, size - xfer_len); | ||
367 | |||
368 | pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; | ||
369 | pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset; | ||
322 | pxa_dma->sg_cpu[i].dcmd = | 370 | pxa_dma->sg_cpu[i].dcmd = |
323 | DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; | 371 | DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; |
324 | size -= dma_len; | ||
325 | pxa_dma->sg_cpu[i].ddadr = | 372 | pxa_dma->sg_cpu[i].ddadr = |
326 | pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); | 373 | pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); |
374 | |||
375 | dev_vdbg(pcdev->dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", | ||
376 | pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc), | ||
377 | sg_dma_address(sg) + offset, xfer_len); | ||
378 | offset = 0; | ||
379 | |||
380 | if (size == 0) | ||
381 | break; | ||
327 | } | 382 | } |
328 | 383 | ||
329 | pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; | 384 | pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; |
330 | pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; | 385 | pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; |
331 | 386 | ||
387 | /* | ||
388 | * Handle 1 special case : | ||
389 | * - in 3 planes (YUV422P format), we might finish with xfer_len equal | ||
390 | * to dma_len (end on PAGE boundary). In this case, the sg element | ||
391 | * for next plane should be the next after the last used to store the | ||
392 | * last scatter gather RAM page | ||
393 | */ | ||
394 | if (xfer_len >= dma_len) { | ||
395 | *sg_first_ofs = xfer_len - dma_len; | ||
396 | *sg_first = sg_next(sg); | ||
397 | } else { | ||
398 | *sg_first_ofs = xfer_len; | ||
399 | *sg_first = sg; | ||
400 | } | ||
401 | |||
332 | return 0; | 402 | return 0; |
333 | } | 403 | } |
334 | 404 | ||
@@ -340,7 +410,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, | |||
340 | struct pxa_camera_dev *pcdev = ici->priv; | 410 | struct pxa_camera_dev *pcdev = ici->priv; |
341 | struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); | 411 | struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); |
342 | int ret; | 412 | int ret; |
343 | int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; | ||
344 | int size_y, size_u = 0, size_v = 0; | 413 | int size_y, size_u = 0, size_v = 0; |
345 | 414 | ||
346 | dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, | 415 | dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, |
@@ -379,53 +448,51 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, | |||
379 | } | 448 | } |
380 | 449 | ||
381 | if (vb->state == VIDEOBUF_NEEDS_INIT) { | 450 | if (vb->state == VIDEOBUF_NEEDS_INIT) { |
382 | unsigned int size = vb->size; | 451 | int size = vb->size; |
452 | int next_ofs = 0; | ||
383 | struct videobuf_dmabuf *dma = videobuf_to_dma(vb); | 453 | struct videobuf_dmabuf *dma = videobuf_to_dma(vb); |
454 | struct scatterlist *sg; | ||
384 | 455 | ||
385 | ret = videobuf_iolock(vq, vb, NULL); | 456 | ret = videobuf_iolock(vq, vb, NULL); |
386 | if (ret) | 457 | if (ret) |
387 | goto fail; | 458 | goto fail; |
388 | 459 | ||
389 | if (pcdev->channels == 3) { | 460 | if (pcdev->channels == 3) { |
390 | /* FIXME the calculations should be more precise */ | ||
391 | sglen_y = dma->sglen / 2; | ||
392 | sglen_u = sglen_v = dma->sglen / 4 + 1; | ||
393 | sglen_yu = sglen_y + sglen_u; | ||
394 | size_y = size / 2; | 461 | size_y = size / 2; |
395 | size_u = size_v = size / 4; | 462 | size_u = size_v = size / 4; |
396 | } else { | 463 | } else { |
397 | sglen_y = dma->sglen; | ||
398 | size_y = size; | 464 | size_y = size; |
399 | } | 465 | } |
400 | 466 | ||
401 | /* init DMA for Y channel */ | 467 | sg = dma->sglist; |
402 | ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, | ||
403 | 0, 0x28, size_y); | ||
404 | 468 | ||
469 | /* init DMA for Y channel */ | ||
470 | ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y, | ||
471 | &sg, &next_ofs); | ||
405 | if (ret) { | 472 | if (ret) { |
406 | dev_err(pcdev->dev, | 473 | dev_err(pcdev->dev, |
407 | "DMA initialization for Y/RGB failed\n"); | 474 | "DMA initialization for Y/RGB failed\n"); |
408 | goto fail; | 475 | goto fail; |
409 | } | 476 | } |
410 | 477 | ||
411 | if (pcdev->channels == 3) { | 478 | /* init DMA for U channel */ |
412 | /* init DMA for U channel */ | 479 | if (size_u) |
413 | ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u, | 480 | ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, |
414 | sglen_y, 0x30, size_u); | 481 | size_u, &sg, &next_ofs); |
415 | if (ret) { | 482 | if (ret) { |
416 | dev_err(pcdev->dev, | 483 | dev_err(pcdev->dev, |
417 | "DMA initialization for U failed\n"); | 484 | "DMA initialization for U failed\n"); |
418 | goto fail_u; | 485 | goto fail_u; |
419 | } | 486 | } |
420 | 487 | ||
421 | /* init DMA for V channel */ | 488 | /* init DMA for V channel */ |
422 | ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, | 489 | if (size_v) |
423 | sglen_yu, 0x38, size_v); | 490 | ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, |
424 | if (ret) { | 491 | size_v, &sg, &next_ofs); |
425 | dev_err(pcdev->dev, | 492 | if (ret) { |
426 | "DMA initialization for V failed\n"); | 493 | dev_err(pcdev->dev, |
427 | goto fail_v; | 494 | "DMA initialization for V failed\n"); |
428 | } | 495 | goto fail_v; |
429 | } | 496 | } |
430 | 497 | ||
431 | vb->state = VIDEOBUF_PREPARED; | 498 | vb->state = VIDEOBUF_PREPARED; |