Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 142385
b: refs/heads/master
c: 37f5aef
h: refs/heads/master
i:
  142383: 9a9a6f1
v: v3
  • Loading branch information
Robert Jarzmik authored and Mauro Carvalho Chehab committed Apr 7, 2009
1 parent 92d2eba commit bdfb482
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 39 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 92a8337b380f0978ac81f096d6324d3ad689f83e
refs/heads/master: 37f5aefd537d5f98b3fa9726362effeccf1d2161
143 changes: 105 additions & 38 deletions trunk/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 bdfb482

Please sign in to comment.