From patchwork Thu Mar 5 19:45:48 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Jarzmik X-Patchwork-Id: 904 Return-path: X-OfflineIMAP-40665319-426f6d626164696c-494e424f582e6c696e75782d6d65646961: 1241215050-0532382095979-v6.0.3 X-OfflineIMAP-1100790746-4c6f63616c-496e667261646561642e6c696e75782d6d65646961: 1241139067-0493588952328-v6.0.3 Envelope-to: mchehab@infradead.org Delivery-date: Thu, 05 Mar 2009 21:25:23 +0000 Received: from vger.kernel.org ([209.132.176.167]) by bombadil.infradead.org with esmtp (Exim 4.69 #1 (Red Hat Linux)) id 1LfK6X-0007Pa-LX for mchehab@infradead.org; Thu, 05 Mar 2009 20:23:42 +0000 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754715AbZCETqJ (ORCPT ); Thu, 5 Mar 2009 14:46:09 -0500 Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1754787AbZCETqJ (ORCPT ); Thu, 5 Mar 2009 14:46:09 -0500 Received: from smtp5-g21.free.fr ([212.27.42.5]:43931 "EHLO smtp5-g21.free.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754715AbZCETqG (ORCPT ); Thu, 5 Mar 2009 14:46:06 -0500 Received: from smtp5-g21.free.fr (localhost [127.0.0.1]) by smtp5-g21.free.fr (Postfix) with ESMTP id 7228AD4807F; Thu, 5 Mar 2009 20:45:55 +0100 (CET) Received: from localhost.localdomain (mur31-2-82-243-122-54.fbx.proxad.net [82.243.122.54]) by smtp5-g21.free.fr (Postfix) with ESMTP id 39F83D48125; Thu, 5 Mar 2009 20:45:53 +0100 (CET) From: Robert Jarzmik To: g.liakhovetski@gmx.de, mike@compulab.co.il Cc: linux-media@vger.kernel.org, Robert Jarzmik Subject: [PATCH 1/4] pxa_camera: Remove YUV planar formats hole Date: Thu, 5 Mar 2009 20:45:48 +0100 Message-Id: <1236282351-28471-2-git-send-email-robert.jarzmik@free.fr> X-Mailer: git-send-email 1.5.6.5 In-Reply-To: <1236282351-28471-1-git-send-email-robert.jarzmik@free.fr> References: <1236282351-28471-1-git-send-email-robert.jarzmik@free.fr> Sender: linux-media-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org 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 --- drivers/media/video/pxa_camera.c | 165 ++++++++++++++++++++++++++------------ 1 files changed, 114 insertions(+), 51 deletions(-) diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index e3e6b29..54df071 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -242,14 +242,13 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); /* planar capture requires Y, U and V buffers to be page aligned */ - if (pcdev->channels == 3) { - *size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */ - *size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */ - *size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */ - } else { - *size = icd->width * icd->height * - ((icd->current_fmt->depth + 7) >> 3); - } + if (pcdev->channels == 3) + *size = roundup(icd->width * icd->height, 8) /* Y pages */ + + roundup(icd->width * icd->height / 2, 8) /* U pages */ + + roundup(icd->width * icd->height / 2, 8); /* V pages */ + else + *size = roundup(icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3), 8); if (0 == *count) *count = 32; @@ -289,19 +288,63 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) buf->vb.state = VIDEOBUF_NEEDS_INIT; } +static int calculate_dma_sglen(struct scatterlist *sglist, int sglen, + int sg_first_ofs, int size) +{ + int i, offset, dma_len, xfer_len; + struct scatterlist *sg; + + offset = sg_first_ofs; + for_each_sg(sglist, sg, sglen, i) { + dma_len = sg_dma_len(sg); + + /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ + xfer_len = roundup(min(dma_len - offset, size), 8); + + size = max(0, size - xfer_len); + offset = 0; + if (size == 0) + break; + } + + BUG_ON(size != 0); + return i + 1; +} + +/** + * pxa_init_dma_channel - init dma descriptors + * @pcdev: pxa camera device + * @buf: pxa buffer to find pxa dma channel + * @dma: dma video buffer + * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V') + * @cibr: camera read fifo + * @size: bytes to transfer + * @sg_first: index of first element of sg_list + * @sg_first_ofs: offset in first element of sg_list + * + * Prepares the pxa dma descriptors to transfer one camera channel. + * Beware sg_first and sg_first_ofs are both input and output parameters. + * + * Returns 0 + */ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf, struct videobuf_dmabuf *dma, int channel, - int sglen, int sg_start, int cibr, - unsigned int size) + int cibr, int size, + struct scatterlist **sg_first, int *sg_first_ofs) { struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; - int i; + struct scatterlist *sg; + int i, offset, sglen; + int dma_len = 0, xfer_len = 0; if (pxa_dma->sg_cpu) dma_free_coherent(pcdev->dev, pxa_dma->sg_size, pxa_dma->sg_cpu, pxa_dma->sg_dma); + sglen = calculate_dma_sglen(*sg_first, dma->sglen, + *sg_first_ofs, size); + pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, &pxa_dma->sg_dma, GFP_KERNEL); @@ -309,27 +352,51 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, return -ENOMEM; pxa_dma->sglen = sglen; + offset = *sg_first_ofs; - for (i = 0; i < sglen; i++) { - int sg_i = sg_start + i; - struct scatterlist *sg = dma->sglist; - unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; + dev_dbg(pcdev->dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", + *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma); - pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; - pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); + + for_each_sg(*sg_first, sg, sglen, i) { + dma_len = sg_dma_len(sg); /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ - xfer_len = (min(dma_len, size) + 7) & ~7; + xfer_len = roundup(min(dma_len - offset, size), 8); + size = max(0, size - xfer_len); + + pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; + pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset; pxa_dma->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; - size -= dma_len; pxa_dma->sg_cpu[i].ddadr = pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); + + dev_vdbg(pcdev->dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", + pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc), + sg_dma_address(sg) + offset, xfer_len); + offset = 0; + + if (size == 0) + break; } - pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; - pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; + pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP; + pxa_dma->sg_cpu[sglen].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN; + + *sg_first_ofs = xfer_len; + /* + * Handle 1 special case : + * - if we finish the DMA transfer in the last 7 bytes of a RAM page + * then we return the sg element pointing on the next page + */ + if (*sg_first_ofs >= dma_len) { + *sg_first_ofs -= dma_len; + *sg_first = sg_next(sg); + } else { + *sg_first = sg; + } return 0; } @@ -342,9 +409,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; - int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; - int size_y, size_u = 0, size_v = 0; - + int size_y = 0, size_u = 0, size_v = 0; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -381,53 +446,51 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, } if (vb->state == VIDEOBUF_NEEDS_INIT) { - unsigned int size = vb->size; + int size = vb->size; + int next_ofs = 0; struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + struct scatterlist *sg; ret = videobuf_iolock(vq, vb, NULL); if (ret) goto fail; if (pcdev->channels == 3) { - /* FIXME the calculations should be more precise */ - sglen_y = dma->sglen / 2; - sglen_u = sglen_v = dma->sglen / 4 + 1; - sglen_yu = sglen_y + sglen_u; size_y = size / 2; size_u = size_v = size / 4; } else { - sglen_y = dma->sglen; size_y = size; } - /* init DMA for Y channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, - 0, 0x28, size_y); + sg = dma->sglist; + /* init DMA for Y channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y, + &sg, &next_ofs); if (ret) { dev_err(pcdev->dev, "DMA initialization for Y/RGB failed\n"); goto fail; } - if (pcdev->channels == 3) { - /* init DMA for U channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u, - sglen_y, 0x30, size_u); - if (ret) { - dev_err(pcdev->dev, - "DMA initialization for U failed\n"); - goto fail_u; - } + /* init DMA for U channel */ + if (size_u) + ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, + size_u, &sg, &next_ofs); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for U failed\n"); + goto fail_u; + } - /* init DMA for V channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, - sglen_yu, 0x38, size_v); - if (ret) { - dev_err(pcdev->dev, - "DMA initialization for V failed\n"); - goto fail_v; - } + /* init DMA for V channel */ + if (size_v) + ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, + size_u, &sg, &next_ofs); + if (ret) { + dev_err(pcdev->dev, + "DMA initialization for V failed\n"); + goto fail_v; } vb->state = VIDEOBUF_PREPARED;