diff options
Diffstat (limited to 'drivers/gpu/msm/kgsl_sharedmem.c')
-rw-r--r-- | drivers/gpu/msm/kgsl_sharedmem.c | 57 |
1 files changed, 29 insertions, 28 deletions
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index fa95b4dfe718..7f4a5a3b251f 100644 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -623,6 +623,9 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, uint64_t size, unsigned int op) { void *addr = NULL; + struct sg_table *sgt = NULL; + struct scatterlist *sg; + unsigned int i, pos = 0; int ret = 0; if (size == 0 || size > UINT_MAX) @@ -650,40 +653,38 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset, * If the buffer is not to mapped to kernel, perform cache * operations after mapping to kernel. */ - if (memdesc->sgt != NULL) { - struct scatterlist *sg; - unsigned int i, pos = 0; + if (memdesc->sgt != NULL) + sgt = memdesc->sgt; + else { + if (memdesc->pages == NULL) + return ret; + + sgt = kgsl_alloc_sgt_from_pages(memdesc); + if (IS_ERR(sgt)) + return PTR_ERR(sgt); + } - for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) { - uint64_t sg_offset, sg_left; + for_each_sg(sgt->sgl, sg, sgt->nents, i) { + uint64_t sg_offset, sg_left; - if (offset >= (pos + sg->length)) { - pos += sg->length; - continue; - } - sg_offset = offset > pos ? offset - pos : 0; - sg_left = (sg->length - sg_offset > size) ? size : - sg->length - sg_offset; - ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset, - sg_left, op); - size -= sg_left; - if (size == 0) - break; + if (offset >= (pos + sg->length)) { pos += sg->length; + continue; } - } else if (memdesc->pages != NULL) { - addr = vmap(memdesc->pages, memdesc->page_count, - VM_IOREMAP, pgprot_writecombine(PAGE_KERNEL)); - if (addr == NULL) - return -ENOMEM; + sg_offset = offset > pos ? offset - pos : 0; + sg_left = (sg->length - sg_offset > size) ? size : + sg->length - sg_offset; + ret = kgsl_do_cache_op(sg_page(sg), NULL, sg_offset, + sg_left, op); + size -= sg_left; + if (size == 0) + break; + pos += sg->length; + } - /* Make sure the offset + size do not overflow the address */ - if (addr + ((size_t) offset + (size_t) size) < addr) - return -ERANGE; + if (memdesc->sgt == NULL) + kgsl_free_sgt(sgt); - ret = kgsl_do_cache_op(NULL, addr, offset, size, op); - vunmap(addr); - } return ret; } EXPORT_SYMBOL(kgsl_cache_range_op); |