Skip to content

Commit

Permalink
V4L/DVB (11320): pxa_camera: Remove YUV planar formats hole
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Robert Jarzmik authored and Mauro Carvalho Chehab committed Apr 7, 2009
1 parent 92a8337 commit 37f5aef
Showing 1 changed file with 105 additions and 38 deletions.
143 changes: 105 additions & 38 deletions drivers/media/video/pxa_camera.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,48 +287,118 @@ 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 Receive Buffer Register
* @size: bytes to transfer
* @sg_first: 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 or -ENOMEM if no coherent memory is available
*/
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);
if (!pxa_dma->sg_cpu)
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;

/*
* Handle 1 special case :
* - in 3 planes (YUV422P format), we might finish with xfer_len equal
* to dma_len (end on PAGE boundary). In this case, the sg element
* for next plane should be the next after the last used to store the
* last scatter gather RAM page
*/
if (xfer_len >= dma_len) {
*sg_first_ofs = xfer_len - dma_len;
*sg_first = sg_next(sg);
} else {
*sg_first_ofs = xfer_len;
*sg_first = sg;
}

return 0;
}

Expand All @@ -340,7 +410,6 @@ 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;

dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
Expand Down Expand Up @@ -379,53 +448,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_v, &sg, &next_ofs);
if (ret) {
dev_err(pcdev->dev,
"DMA initialization for V failed\n");
goto fail_v;
}

vb->state = VIDEOBUF_PREPARED;
Expand Down

0 comments on commit 37f5aef

Please sign in to comment.