Robert Jarzmik <robert.jarz...@free.fr> writes:

> Guennadi Liakhovetski <g.liakhovet...@gmx.de> writes:
>
>>>             /* init DMA for Y channel */
>>
>> How about taking the loop over the sg list out of pxa_init_dma_channel() 
>> to avoid having to iterate it from the beginning each time? Then you would 
>> be able to split it into channels inside that global loop? Would that 
>> work? Of course you might need to rearrange functions to avoid too deep 
>> code nesting.
>
> Ok, will try that.
> The more I think of it, the more it looks to me like a generic thing : take an
> sglist, and an array of sizes, and split the sglist into several sglists, each
> of the defined size in the array.
>
> Or more code-like speaking :
>   - sglist_split(struct scatterlist *sg_int, size_t *sizes, int nb_sizes,
>                  struct scatterlist **sg_out)
>   - and sg_out is an array of nb_sizes (struct scatterlist *sg)
>
> So I will try that out. Maybe if that works out for pxa_camera, Jens or 
> Russell
> would accept that into lib/scatterlist.c.
Ok, I made the code ... and I hate it.
It's in [1], which is an incremental patch over patch 4/4.

If that's what you had in mind, tell me.

Cheers.

--
Robert

[1] The despised patch
---<8---
commit 43bbb9a4e3ac
Author: Robert Jarzmik <robert.jarz...@free.fr>
Date:   Tue Jul 14 20:17:51 2015 +0200

    tmp: pxa_camera: working on sg_split
    
    Signed-off-by: Robert Jarzmik <robert.jarz...@free.fr>

diff --git a/drivers/media/platform/soc_camera/pxa_camera.c 
b/drivers/media/platform/soc_camera/pxa_camera.c
index 26a66b9ff570..83efd284e976 100644
--- a/drivers/media/platform/soc_camera/pxa_camera.c
+++ b/drivers/media/platform/soc_camera/pxa_camera.c
@@ -287,64 +287,110 @@ static void free_buffer(struct videobuf_queue *vq, 
struct pxa_buffer *buf)
                &buf->vb, buf->vb.baddr, buf->vb.bsize);
 }
 
-static struct scatterlist *videobuf_sg_cut(struct scatterlist *sglist,
-                                          int sglen, int offset, int size,
-                                          int *new_sg_len)
+
+struct sg_splitter {
+       struct scatterlist *in_sg0;
+       int nents;
+       off_t skip_sg0;
+       size_t len_last_sg;
+       struct scatterlist *out_sg;
+};
+
+static struct sg_splitter *
+sg_calculate_split(struct scatterlist *in, off_t skip,
+                  const size_t *sizes, int nb_splits, gfp_t gfp_mask)
 {
-       struct scatterlist *sg0, *sg, *sg_first = NULL;
-       int i, dma_len, dropped_xfer_len, dropped_remain, remain;
-       int nfirst = -1, nfirst_offset = 0, xfer_len;
-
-       *new_sg_len = 0;
-       dropped_remain = offset;
-       remain = size;
-       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 */
-               dropped_xfer_len = roundup(min(dma_len, dropped_remain), 8);
-               if (dropped_remain)
-                       dropped_remain -= dropped_xfer_len;
-               xfer_len = dma_len - dropped_xfer_len;
-
-               if (nfirst < 0 && xfer_len > 0) {
-                       sg_first = sg;
-                       nfirst = i;
-                       nfirst_offset = dropped_xfer_len;
+       int i, nents;
+       size_t size, len;
+       struct sg_splitter *splitters, *curr;
+       struct scatterlist *sg;
+
+       splitters = kcalloc(nb_splits, sizeof(*splitters), gfp_mask);
+       if (!splitters)
+               return NULL;
+
+       nents = 0;
+       size = *sizes;
+       curr = splitters;
+       for_each_sg(in, sg, sg_nents(in), i) {
+               if (skip > sg_dma_len(sg)) {
+                       skip -= sg_dma_len(sg);
+                       continue;
+               }
+               len = min_t(size_t, size, sg_dma_len(sg) - skip);
+               if (!curr->in_sg0) {
+                       curr->in_sg0 = sg;
+                       curr->skip_sg0 = sg_dma_len(sg) - len;
                }
-               if (xfer_len > 0) {
-                       (*new_sg_len)++;
-                       remain -= xfer_len;
+               size -= len;
+               nents++;
+               if (!size) {
+                       curr->nents = nents;
+                       curr->len_last_sg = len;
+                       nents = 0;
+                       size = *(++sizes);
+
+                       if (!--nb_splits)
+                               break;
+
+                       if (len < curr->len_last_sg) {
+                               (splitters + 1)->in_sg0 = sg;
+                               (splitters + 1)->skip_sg0 = 0;
+                       }
+                       curr++;
                }
-               if (remain <= 0)
-                       break;
        }
-       WARN_ON(nfirst >= sglen);
 
-       sg0 = kmalloc_array(*new_sg_len, sizeof(struct scatterlist),
-                           GFP_KERNEL);
-       if (!sg0)
-               return NULL;
+       return splitters;
+}
 
-       remain = size;
-       for_each_sg(sg_first, sg, *new_sg_len, i) {
-               dma_len = sg_dma_len(sg);
-               sg0[i] = *sg;
+static int sg_split(struct scatterlist *in, const int nb_splits,
+                   const size_t *split_sizes, struct scatterlist **out,
+                   gfp_t gfp_mask)
+{
+       int i, j;
+       struct scatterlist *in_sg, *out_sg;
+       struct sg_splitter *splitters, *split;
 
-               sg0[i].offset = nfirst_offset;
-               nfirst_offset = 0;
+       splitters = sg_calculate_split(in, 0, split_sizes, nb_splits, gfp_mask);
+       if (!splitters)
+               return -ENOMEM;
 
-               xfer_len = min_t(int, remain, dma_len - sg0[i].offset);
-               xfer_len = roundup(xfer_len, 8);
-               sg_dma_len(&sg0[i]) = xfer_len;
+       for (i = 0; i < nb_splits; i++) {
+               (splitters + i)->out_sg =
+                       kmalloc_array((splitters + i)->nents,
+                                     sizeof(struct scatterlist), gfp_mask);
+               if (!(splitters + i)->out_sg)
+                       goto err;
+       }
 
-               remain -= xfer_len;
-               if (remain <= 0) {
-                       sg_mark_end(&sg0[i]);
-                       break;
+       for (i = 0; i < nb_splits; i++) {
+               split = splitters + i;
+               in_sg = split->in_sg0;
+               out_sg = split->out_sg;
+               out[i] = out_sg;
+               for (j = 0; j < split->nents; j++) {
+                       out_sg[j] = *in_sg;
+                       if (!j) {
+                               out_sg[j].offset = split->skip_sg0;
+                               sg_dma_len(&out_sg[j]) -= split->skip_sg0;
+                       } else {
+                               out_sg[j].offset = 0;
+                       }
+                       in_sg = sg_next(in_sg);
                }
+               sg_dma_len(out_sg + split->nents - 1) = split->len_last_sg;
+               sg_mark_end(out_sg + split->nents - 1);
        }
 
-       return sg0;
+       kfree(splitters);
+       return 0;
+
+err:
+       for (i = 0; i < nb_splits; i++)
+               kfree((splitters + i)->out_sg);
+       kfree(splitters);
+       return -ENOMEM;
 }
 
 static void pxa_camera_dma_irq(struct pxa_camera_dev *pcdev,
@@ -391,14 +437,11 @@ static int pxa_init_dma_channel(struct pxa_camera_dev 
*pcdev,
                                int cibr, int size, int offset)
 {
        struct dma_chan *dma_chan = pcdev->dma_chans[channel];
-       struct scatterlist *sg;
+       struct scatterlist *sg = buf->sg[channel];
        int sglen;
        struct dma_async_tx_descriptor *tx;
 
-       sg = videobuf_sg_cut(dma->sglist, dma->sglen, offset, size, &sglen);
-       if (!sg)
-               goto fail;
-
+       sglen = sg_nents(sg);
        tx = dmaengine_prep_slave_sg(dma_chan, sg, sglen, DMA_DEV_TO_MEM,
                                     DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
        if (!tx) {
@@ -421,7 +464,6 @@ static int pxa_init_dma_channel(struct pxa_camera_dev 
*pcdev,
        }
 
        buf->descs[channel] = tx;
-       buf->sg[channel] = sg;
        buf->sg_len[channel] = sglen;
        return 0;
 fail:
@@ -458,6 +500,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
        struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb);
        int ret;
        int size_y, size_u = 0, size_v = 0;
+       size_t sizes[3];
 
        dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
                vb, vb->baddr, vb->bsize);
@@ -513,6 +556,16 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq,
                        size_y = size;
                }
 
+               sizes[0] = size_y;
+               sizes[1] = size_u;
+               sizes[2] = size_v;
+               ret = sg_split(dma->sglist, pcdev->channels, sizes, buf->sg,
+                              GFP_KERNEL);
+               if (ret) {
+                       dev_err(dev, "sg_split failed: %d\n", ret);
+                       goto fail;
+               }
+
                /* init DMA for Y channel */
                ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0,
                                           size_y, 0);
--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to