162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 962306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1062306a36Sopenharmony_ci * the Free Software Foundation. 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/mm.h> 1562306a36Sopenharmony_ci#include <linux/refcount.h> 1662306a36Sopenharmony_ci#include <linux/scatterlist.h> 1762306a36Sopenharmony_ci#include <linux/sched.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/vmalloc.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2262306a36Sopenharmony_ci#include <media/videobuf2-memops.h> 2362306a36Sopenharmony_ci#include <media/videobuf2-dma-sg.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic int debug; 2662306a36Sopenharmony_cimodule_param(debug, int, 0644); 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci#define dprintk(level, fmt, arg...) \ 2962306a36Sopenharmony_ci do { \ 3062306a36Sopenharmony_ci if (debug >= level) \ 3162306a36Sopenharmony_ci printk(KERN_DEBUG "vb2-dma-sg: " fmt, ## arg); \ 3262306a36Sopenharmony_ci } while (0) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistruct vb2_dma_sg_buf { 3562306a36Sopenharmony_ci struct device *dev; 3662306a36Sopenharmony_ci void *vaddr; 3762306a36Sopenharmony_ci struct page **pages; 3862306a36Sopenharmony_ci struct frame_vector *vec; 3962306a36Sopenharmony_ci int offset; 4062306a36Sopenharmony_ci enum dma_data_direction dma_dir; 4162306a36Sopenharmony_ci struct sg_table sg_table; 4262306a36Sopenharmony_ci /* 4362306a36Sopenharmony_ci * This will point to sg_table when used with the MMAP or USERPTR 4462306a36Sopenharmony_ci * memory model, and to the dma_buf sglist when used with the 4562306a36Sopenharmony_ci * DMABUF memory model. 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci struct sg_table *dma_sgt; 4862306a36Sopenharmony_ci size_t size; 4962306a36Sopenharmony_ci unsigned int num_pages; 5062306a36Sopenharmony_ci refcount_t refcount; 5162306a36Sopenharmony_ci struct vb2_vmarea_handler handler; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci struct dma_buf_attachment *db_attach; 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci struct vb2_buffer *vb; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_cistatic void vb2_dma_sg_put(void *buf_priv); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int vb2_dma_sg_alloc_compacted(struct vb2_dma_sg_buf *buf, 6162306a36Sopenharmony_ci gfp_t gfp_flags) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci unsigned int last_page = 0; 6462306a36Sopenharmony_ci unsigned long size = buf->size; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci while (size > 0) { 6762306a36Sopenharmony_ci struct page *pages; 6862306a36Sopenharmony_ci int order; 6962306a36Sopenharmony_ci int i; 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci order = get_order(size); 7262306a36Sopenharmony_ci /* Don't over allocate*/ 7362306a36Sopenharmony_ci if ((PAGE_SIZE << order) > size) 7462306a36Sopenharmony_ci order--; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci pages = NULL; 7762306a36Sopenharmony_ci while (!pages) { 7862306a36Sopenharmony_ci pages = alloc_pages(GFP_KERNEL | __GFP_ZERO | 7962306a36Sopenharmony_ci __GFP_NOWARN | gfp_flags, order); 8062306a36Sopenharmony_ci if (pages) 8162306a36Sopenharmony_ci break; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci if (order == 0) { 8462306a36Sopenharmony_ci while (last_page--) 8562306a36Sopenharmony_ci __free_page(buf->pages[last_page]); 8662306a36Sopenharmony_ci return -ENOMEM; 8762306a36Sopenharmony_ci } 8862306a36Sopenharmony_ci order--; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci split_page(pages, order); 9262306a36Sopenharmony_ci for (i = 0; i < (1 << order); i++) 9362306a36Sopenharmony_ci buf->pages[last_page++] = &pages[i]; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci size -= PAGE_SIZE << order; 9662306a36Sopenharmony_ci } 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic void *vb2_dma_sg_alloc(struct vb2_buffer *vb, struct device *dev, 10262306a36Sopenharmony_ci unsigned long size) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf; 10562306a36Sopenharmony_ci struct sg_table *sgt; 10662306a36Sopenharmony_ci int ret; 10762306a36Sopenharmony_ci int num_pages; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci if (WARN_ON(!dev) || WARN_ON(!size)) 11062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci buf = kzalloc(sizeof *buf, GFP_KERNEL); 11362306a36Sopenharmony_ci if (!buf) 11462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci buf->vaddr = NULL; 11762306a36Sopenharmony_ci buf->dma_dir = vb->vb2_queue->dma_dir; 11862306a36Sopenharmony_ci buf->offset = 0; 11962306a36Sopenharmony_ci buf->size = size; 12062306a36Sopenharmony_ci /* size is already page aligned */ 12162306a36Sopenharmony_ci buf->num_pages = size >> PAGE_SHIFT; 12262306a36Sopenharmony_ci buf->dma_sgt = &buf->sg_table; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * NOTE: dma-sg allocates memory using the page allocator directly, so 12662306a36Sopenharmony_ci * there is no memory consistency guarantee, hence dma-sg ignores DMA 12762306a36Sopenharmony_ci * attributes passed from the upper layer. 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_ci buf->pages = kvcalloc(buf->num_pages, sizeof(struct page *), GFP_KERNEL); 13062306a36Sopenharmony_ci if (!buf->pages) 13162306a36Sopenharmony_ci goto fail_pages_array_alloc; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci ret = vb2_dma_sg_alloc_compacted(buf, vb->vb2_queue->gfp_flags); 13462306a36Sopenharmony_ci if (ret) 13562306a36Sopenharmony_ci goto fail_pages_alloc; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci ret = sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, 13862306a36Sopenharmony_ci buf->num_pages, 0, size, GFP_KERNEL); 13962306a36Sopenharmony_ci if (ret) 14062306a36Sopenharmony_ci goto fail_table_alloc; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Prevent the device from being released while the buffer is used */ 14362306a36Sopenharmony_ci buf->dev = get_device(dev); 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci sgt = &buf->sg_table; 14662306a36Sopenharmony_ci /* 14762306a36Sopenharmony_ci * No need to sync to the device, this will happen later when the 14862306a36Sopenharmony_ci * prepare() memop is called. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_ci if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir, 15162306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC)) 15262306a36Sopenharmony_ci goto fail_map; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci buf->handler.refcount = &buf->refcount; 15562306a36Sopenharmony_ci buf->handler.put = vb2_dma_sg_put; 15662306a36Sopenharmony_ci buf->handler.arg = buf; 15762306a36Sopenharmony_ci buf->vb = vb; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci refcount_set(&buf->refcount, 1); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci dprintk(1, "%s: Allocated buffer of %d pages\n", 16262306a36Sopenharmony_ci __func__, buf->num_pages); 16362306a36Sopenharmony_ci return buf; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cifail_map: 16662306a36Sopenharmony_ci put_device(buf->dev); 16762306a36Sopenharmony_ci sg_free_table(buf->dma_sgt); 16862306a36Sopenharmony_cifail_table_alloc: 16962306a36Sopenharmony_ci num_pages = buf->num_pages; 17062306a36Sopenharmony_ci while (num_pages--) 17162306a36Sopenharmony_ci __free_page(buf->pages[num_pages]); 17262306a36Sopenharmony_cifail_pages_alloc: 17362306a36Sopenharmony_ci kvfree(buf->pages); 17462306a36Sopenharmony_cifail_pages_array_alloc: 17562306a36Sopenharmony_ci kfree(buf); 17662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void vb2_dma_sg_put(void *buf_priv) 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 18262306a36Sopenharmony_ci struct sg_table *sgt = &buf->sg_table; 18362306a36Sopenharmony_ci int i = buf->num_pages; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (refcount_dec_and_test(&buf->refcount)) { 18662306a36Sopenharmony_ci dprintk(1, "%s: Freeing buffer of %d pages\n", __func__, 18762306a36Sopenharmony_ci buf->num_pages); 18862306a36Sopenharmony_ci dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, 18962306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 19062306a36Sopenharmony_ci if (buf->vaddr) 19162306a36Sopenharmony_ci vm_unmap_ram(buf->vaddr, buf->num_pages); 19262306a36Sopenharmony_ci sg_free_table(buf->dma_sgt); 19362306a36Sopenharmony_ci while (--i >= 0) 19462306a36Sopenharmony_ci __free_page(buf->pages[i]); 19562306a36Sopenharmony_ci kvfree(buf->pages); 19662306a36Sopenharmony_ci put_device(buf->dev); 19762306a36Sopenharmony_ci kfree(buf); 19862306a36Sopenharmony_ci } 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic void vb2_dma_sg_prepare(void *buf_priv) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 20462306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (buf->vb->skip_cache_sync_on_prepare) 20762306a36Sopenharmony_ci return; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic void vb2_dma_sg_finish(void *buf_priv) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 21562306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (buf->vb->skip_cache_sync_on_finish) 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); 22162306a36Sopenharmony_ci} 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic void *vb2_dma_sg_get_userptr(struct vb2_buffer *vb, struct device *dev, 22462306a36Sopenharmony_ci unsigned long vaddr, unsigned long size) 22562306a36Sopenharmony_ci{ 22662306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf; 22762306a36Sopenharmony_ci struct sg_table *sgt; 22862306a36Sopenharmony_ci struct frame_vector *vec; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci if (WARN_ON(!dev)) 23162306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci buf = kzalloc(sizeof *buf, GFP_KERNEL); 23462306a36Sopenharmony_ci if (!buf) 23562306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci buf->vaddr = NULL; 23862306a36Sopenharmony_ci buf->dev = dev; 23962306a36Sopenharmony_ci buf->dma_dir = vb->vb2_queue->dma_dir; 24062306a36Sopenharmony_ci buf->offset = vaddr & ~PAGE_MASK; 24162306a36Sopenharmony_ci buf->size = size; 24262306a36Sopenharmony_ci buf->dma_sgt = &buf->sg_table; 24362306a36Sopenharmony_ci buf->vb = vb; 24462306a36Sopenharmony_ci vec = vb2_create_framevec(vaddr, size, 24562306a36Sopenharmony_ci buf->dma_dir == DMA_FROM_DEVICE || 24662306a36Sopenharmony_ci buf->dma_dir == DMA_BIDIRECTIONAL); 24762306a36Sopenharmony_ci if (IS_ERR(vec)) 24862306a36Sopenharmony_ci goto userptr_fail_pfnvec; 24962306a36Sopenharmony_ci buf->vec = vec; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci buf->pages = frame_vector_pages(vec); 25262306a36Sopenharmony_ci if (IS_ERR(buf->pages)) 25362306a36Sopenharmony_ci goto userptr_fail_sgtable; 25462306a36Sopenharmony_ci buf->num_pages = frame_vector_count(vec); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (sg_alloc_table_from_pages(buf->dma_sgt, buf->pages, 25762306a36Sopenharmony_ci buf->num_pages, buf->offset, size, 0)) 25862306a36Sopenharmony_ci goto userptr_fail_sgtable; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci sgt = &buf->sg_table; 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * No need to sync to the device, this will happen later when the 26362306a36Sopenharmony_ci * prepare() memop is called. 26462306a36Sopenharmony_ci */ 26562306a36Sopenharmony_ci if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir, 26662306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC)) 26762306a36Sopenharmony_ci goto userptr_fail_map; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci return buf; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ciuserptr_fail_map: 27262306a36Sopenharmony_ci sg_free_table(&buf->sg_table); 27362306a36Sopenharmony_ciuserptr_fail_sgtable: 27462306a36Sopenharmony_ci vb2_destroy_framevec(vec); 27562306a36Sopenharmony_ciuserptr_fail_pfnvec: 27662306a36Sopenharmony_ci kfree(buf); 27762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* 28162306a36Sopenharmony_ci * @put_userptr: inform the allocator that a USERPTR buffer will no longer 28262306a36Sopenharmony_ci * be used 28362306a36Sopenharmony_ci */ 28462306a36Sopenharmony_cistatic void vb2_dma_sg_put_userptr(void *buf_priv) 28562306a36Sopenharmony_ci{ 28662306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 28762306a36Sopenharmony_ci struct sg_table *sgt = &buf->sg_table; 28862306a36Sopenharmony_ci int i = buf->num_pages; 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci dprintk(1, "%s: Releasing userspace buffer of %d pages\n", 29162306a36Sopenharmony_ci __func__, buf->num_pages); 29262306a36Sopenharmony_ci dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); 29362306a36Sopenharmony_ci if (buf->vaddr) 29462306a36Sopenharmony_ci vm_unmap_ram(buf->vaddr, buf->num_pages); 29562306a36Sopenharmony_ci sg_free_table(buf->dma_sgt); 29662306a36Sopenharmony_ci if (buf->dma_dir == DMA_FROM_DEVICE || 29762306a36Sopenharmony_ci buf->dma_dir == DMA_BIDIRECTIONAL) 29862306a36Sopenharmony_ci while (--i >= 0) 29962306a36Sopenharmony_ci set_page_dirty_lock(buf->pages[i]); 30062306a36Sopenharmony_ci vb2_destroy_framevec(buf->vec); 30162306a36Sopenharmony_ci kfree(buf); 30262306a36Sopenharmony_ci} 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_cistatic void *vb2_dma_sg_vaddr(struct vb2_buffer *vb, void *buf_priv) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 30762306a36Sopenharmony_ci struct iosys_map map; 30862306a36Sopenharmony_ci int ret; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci BUG_ON(!buf); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (!buf->vaddr) { 31362306a36Sopenharmony_ci if (buf->db_attach) { 31462306a36Sopenharmony_ci ret = dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map); 31562306a36Sopenharmony_ci buf->vaddr = ret ? NULL : map.vaddr; 31662306a36Sopenharmony_ci } else { 31762306a36Sopenharmony_ci buf->vaddr = vm_map_ram(buf->pages, buf->num_pages, -1); 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci /* add offset in case userptr is not page-aligned */ 32262306a36Sopenharmony_ci return buf->vaddr ? buf->vaddr + buf->offset : NULL; 32362306a36Sopenharmony_ci} 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_cistatic unsigned int vb2_dma_sg_num_users(void *buf_priv) 32662306a36Sopenharmony_ci{ 32762306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return refcount_read(&buf->refcount); 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic int vb2_dma_sg_mmap(void *buf_priv, struct vm_area_struct *vma) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 33562306a36Sopenharmony_ci int err; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (!buf) { 33862306a36Sopenharmony_ci printk(KERN_ERR "No memory to map\n"); 33962306a36Sopenharmony_ci return -EINVAL; 34062306a36Sopenharmony_ci } 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci err = vm_map_pages(vma, buf->pages, buf->num_pages); 34362306a36Sopenharmony_ci if (err) { 34462306a36Sopenharmony_ci printk(KERN_ERR "Remapping memory, error: %d\n", err); 34562306a36Sopenharmony_ci return err; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* 34962306a36Sopenharmony_ci * Use common vm_area operations to track buffer refcount. 35062306a36Sopenharmony_ci */ 35162306a36Sopenharmony_ci vma->vm_private_data = &buf->handler; 35262306a36Sopenharmony_ci vma->vm_ops = &vb2_common_vm_ops; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci vma->vm_ops->open(vma); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/*********************************************/ 36062306a36Sopenharmony_ci/* DMABUF ops for exporters */ 36162306a36Sopenharmony_ci/*********************************************/ 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistruct vb2_dma_sg_attachment { 36462306a36Sopenharmony_ci struct sg_table sgt; 36562306a36Sopenharmony_ci enum dma_data_direction dma_dir; 36662306a36Sopenharmony_ci}; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_cistatic int vb2_dma_sg_dmabuf_ops_attach(struct dma_buf *dbuf, 36962306a36Sopenharmony_ci struct dma_buf_attachment *dbuf_attach) 37062306a36Sopenharmony_ci{ 37162306a36Sopenharmony_ci struct vb2_dma_sg_attachment *attach; 37262306a36Sopenharmony_ci unsigned int i; 37362306a36Sopenharmony_ci struct scatterlist *rd, *wr; 37462306a36Sopenharmony_ci struct sg_table *sgt; 37562306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = dbuf->priv; 37662306a36Sopenharmony_ci int ret; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci attach = kzalloc(sizeof(*attach), GFP_KERNEL); 37962306a36Sopenharmony_ci if (!attach) 38062306a36Sopenharmony_ci return -ENOMEM; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci sgt = &attach->sgt; 38362306a36Sopenharmony_ci /* Copy the buf->base_sgt scatter list to the attachment, as we can't 38462306a36Sopenharmony_ci * map the same scatter list to multiple attachments at the same time. 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci ret = sg_alloc_table(sgt, buf->dma_sgt->orig_nents, GFP_KERNEL); 38762306a36Sopenharmony_ci if (ret) { 38862306a36Sopenharmony_ci kfree(attach); 38962306a36Sopenharmony_ci return -ENOMEM; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci rd = buf->dma_sgt->sgl; 39362306a36Sopenharmony_ci wr = sgt->sgl; 39462306a36Sopenharmony_ci for (i = 0; i < sgt->orig_nents; ++i) { 39562306a36Sopenharmony_ci sg_set_page(wr, sg_page(rd), rd->length, rd->offset); 39662306a36Sopenharmony_ci rd = sg_next(rd); 39762306a36Sopenharmony_ci wr = sg_next(wr); 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci attach->dma_dir = DMA_NONE; 40162306a36Sopenharmony_ci dbuf_attach->priv = attach; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return 0; 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic void vb2_dma_sg_dmabuf_ops_detach(struct dma_buf *dbuf, 40762306a36Sopenharmony_ci struct dma_buf_attachment *db_attach) 40862306a36Sopenharmony_ci{ 40962306a36Sopenharmony_ci struct vb2_dma_sg_attachment *attach = db_attach->priv; 41062306a36Sopenharmony_ci struct sg_table *sgt; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci if (!attach) 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci sgt = &attach->sgt; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* release the scatterlist cache */ 41862306a36Sopenharmony_ci if (attach->dma_dir != DMA_NONE) 41962306a36Sopenharmony_ci dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0); 42062306a36Sopenharmony_ci sg_free_table(sgt); 42162306a36Sopenharmony_ci kfree(attach); 42262306a36Sopenharmony_ci db_attach->priv = NULL; 42362306a36Sopenharmony_ci} 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_cistatic struct sg_table *vb2_dma_sg_dmabuf_ops_map( 42662306a36Sopenharmony_ci struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci struct vb2_dma_sg_attachment *attach = db_attach->priv; 42962306a36Sopenharmony_ci struct sg_table *sgt; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci sgt = &attach->sgt; 43262306a36Sopenharmony_ci /* return previously mapped sg table */ 43362306a36Sopenharmony_ci if (attach->dma_dir == dma_dir) 43462306a36Sopenharmony_ci return sgt; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* release any previous cache */ 43762306a36Sopenharmony_ci if (attach->dma_dir != DMA_NONE) { 43862306a36Sopenharmony_ci dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 0); 43962306a36Sopenharmony_ci attach->dma_dir = DMA_NONE; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* mapping to the client with new direction */ 44362306a36Sopenharmony_ci if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 0)) { 44462306a36Sopenharmony_ci pr_err("failed to map scatterlist\n"); 44562306a36Sopenharmony_ci return ERR_PTR(-EIO); 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci attach->dma_dir = dma_dir; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci return sgt; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void vb2_dma_sg_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach, 45462306a36Sopenharmony_ci struct sg_table *sgt, enum dma_data_direction dma_dir) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci /* nothing to be done here */ 45762306a36Sopenharmony_ci} 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic void vb2_dma_sg_dmabuf_ops_release(struct dma_buf *dbuf) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci /* drop reference obtained in vb2_dma_sg_get_dmabuf */ 46262306a36Sopenharmony_ci vb2_dma_sg_put(dbuf->priv); 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int 46662306a36Sopenharmony_civb2_dma_sg_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, 46762306a36Sopenharmony_ci enum dma_data_direction direction) 46862306a36Sopenharmony_ci{ 46962306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = dbuf->priv; 47062306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci dma_sync_sg_for_cpu(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); 47362306a36Sopenharmony_ci return 0; 47462306a36Sopenharmony_ci} 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_cistatic int 47762306a36Sopenharmony_civb2_dma_sg_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, 47862306a36Sopenharmony_ci enum dma_data_direction direction) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = dbuf->priv; 48162306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci dma_sync_sg_for_device(buf->dev, sgt->sgl, sgt->nents, buf->dma_dir); 48462306a36Sopenharmony_ci return 0; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic int vb2_dma_sg_dmabuf_ops_vmap(struct dma_buf *dbuf, 48862306a36Sopenharmony_ci struct iosys_map *map) 48962306a36Sopenharmony_ci{ 49062306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf; 49162306a36Sopenharmony_ci void *vaddr; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci buf = dbuf->priv; 49462306a36Sopenharmony_ci vaddr = vb2_dma_sg_vaddr(buf->vb, buf); 49562306a36Sopenharmony_ci if (!vaddr) 49662306a36Sopenharmony_ci return -EINVAL; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci iosys_map_set_vaddr(map, vaddr); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_cistatic int vb2_dma_sg_dmabuf_ops_mmap(struct dma_buf *dbuf, 50462306a36Sopenharmony_ci struct vm_area_struct *vma) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci return vb2_dma_sg_mmap(dbuf->priv, vma); 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_cistatic const struct dma_buf_ops vb2_dma_sg_dmabuf_ops = { 51062306a36Sopenharmony_ci .attach = vb2_dma_sg_dmabuf_ops_attach, 51162306a36Sopenharmony_ci .detach = vb2_dma_sg_dmabuf_ops_detach, 51262306a36Sopenharmony_ci .map_dma_buf = vb2_dma_sg_dmabuf_ops_map, 51362306a36Sopenharmony_ci .unmap_dma_buf = vb2_dma_sg_dmabuf_ops_unmap, 51462306a36Sopenharmony_ci .begin_cpu_access = vb2_dma_sg_dmabuf_ops_begin_cpu_access, 51562306a36Sopenharmony_ci .end_cpu_access = vb2_dma_sg_dmabuf_ops_end_cpu_access, 51662306a36Sopenharmony_ci .vmap = vb2_dma_sg_dmabuf_ops_vmap, 51762306a36Sopenharmony_ci .mmap = vb2_dma_sg_dmabuf_ops_mmap, 51862306a36Sopenharmony_ci .release = vb2_dma_sg_dmabuf_ops_release, 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic struct dma_buf *vb2_dma_sg_get_dmabuf(struct vb2_buffer *vb, 52262306a36Sopenharmony_ci void *buf_priv, 52362306a36Sopenharmony_ci unsigned long flags) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 52662306a36Sopenharmony_ci struct dma_buf *dbuf; 52762306a36Sopenharmony_ci DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci exp_info.ops = &vb2_dma_sg_dmabuf_ops; 53062306a36Sopenharmony_ci exp_info.size = buf->size; 53162306a36Sopenharmony_ci exp_info.flags = flags; 53262306a36Sopenharmony_ci exp_info.priv = buf; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci if (WARN_ON(!buf->dma_sgt)) 53562306a36Sopenharmony_ci return NULL; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci dbuf = dma_buf_export(&exp_info); 53862306a36Sopenharmony_ci if (IS_ERR(dbuf)) 53962306a36Sopenharmony_ci return NULL; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* dmabuf keeps reference to vb2 buffer */ 54262306a36Sopenharmony_ci refcount_inc(&buf->refcount); 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci return dbuf; 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/*********************************************/ 54862306a36Sopenharmony_ci/* callbacks for DMABUF buffers */ 54962306a36Sopenharmony_ci/*********************************************/ 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_cistatic int vb2_dma_sg_map_dmabuf(void *mem_priv) 55262306a36Sopenharmony_ci{ 55362306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = mem_priv; 55462306a36Sopenharmony_ci struct sg_table *sgt; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci if (WARN_ON(!buf->db_attach)) { 55762306a36Sopenharmony_ci pr_err("trying to pin a non attached buffer\n"); 55862306a36Sopenharmony_ci return -EINVAL; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (WARN_ON(buf->dma_sgt)) { 56262306a36Sopenharmony_ci pr_err("dmabuf buffer is already pinned\n"); 56362306a36Sopenharmony_ci return 0; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* get the associated scatterlist for this buffer */ 56762306a36Sopenharmony_ci sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir); 56862306a36Sopenharmony_ci if (IS_ERR(sgt)) { 56962306a36Sopenharmony_ci pr_err("Error getting dmabuf scatterlist\n"); 57062306a36Sopenharmony_ci return -EINVAL; 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci buf->dma_sgt = sgt; 57462306a36Sopenharmony_ci buf->vaddr = NULL; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci return 0; 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void vb2_dma_sg_unmap_dmabuf(void *mem_priv) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = mem_priv; 58262306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 58362306a36Sopenharmony_ci struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (WARN_ON(!buf->db_attach)) { 58662306a36Sopenharmony_ci pr_err("trying to unpin a not attached buffer\n"); 58762306a36Sopenharmony_ci return; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (WARN_ON(!sgt)) { 59162306a36Sopenharmony_ci pr_err("dmabuf buffer is already unpinned\n"); 59262306a36Sopenharmony_ci return; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (buf->vaddr) { 59662306a36Sopenharmony_ci dma_buf_vunmap_unlocked(buf->db_attach->dmabuf, &map); 59762306a36Sopenharmony_ci buf->vaddr = NULL; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, buf->dma_dir); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci buf->dma_sgt = NULL; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void vb2_dma_sg_detach_dmabuf(void *mem_priv) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = mem_priv; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* if vb2 works correctly you should never detach mapped buffer */ 60962306a36Sopenharmony_ci if (WARN_ON(buf->dma_sgt)) 61062306a36Sopenharmony_ci vb2_dma_sg_unmap_dmabuf(buf); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci /* detach this attachment */ 61362306a36Sopenharmony_ci dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach); 61462306a36Sopenharmony_ci kfree(buf); 61562306a36Sopenharmony_ci} 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_cistatic void *vb2_dma_sg_attach_dmabuf(struct vb2_buffer *vb, struct device *dev, 61862306a36Sopenharmony_ci struct dma_buf *dbuf, unsigned long size) 61962306a36Sopenharmony_ci{ 62062306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf; 62162306a36Sopenharmony_ci struct dma_buf_attachment *dba; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (WARN_ON(!dev)) 62462306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (dbuf->size < size) 62762306a36Sopenharmony_ci return ERR_PTR(-EFAULT); 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 63062306a36Sopenharmony_ci if (!buf) 63162306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci buf->dev = dev; 63462306a36Sopenharmony_ci /* create attachment for the dmabuf with the user device */ 63562306a36Sopenharmony_ci dba = dma_buf_attach(dbuf, buf->dev); 63662306a36Sopenharmony_ci if (IS_ERR(dba)) { 63762306a36Sopenharmony_ci pr_err("failed to attach dmabuf\n"); 63862306a36Sopenharmony_ci kfree(buf); 63962306a36Sopenharmony_ci return dba; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci buf->dma_dir = vb->vb2_queue->dma_dir; 64362306a36Sopenharmony_ci buf->size = size; 64462306a36Sopenharmony_ci buf->db_attach = dba; 64562306a36Sopenharmony_ci buf->vb = vb; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci return buf; 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void *vb2_dma_sg_cookie(struct vb2_buffer *vb, void *buf_priv) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct vb2_dma_sg_buf *buf = buf_priv; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci return buf->dma_sgt; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ciconst struct vb2_mem_ops vb2_dma_sg_memops = { 65862306a36Sopenharmony_ci .alloc = vb2_dma_sg_alloc, 65962306a36Sopenharmony_ci .put = vb2_dma_sg_put, 66062306a36Sopenharmony_ci .get_userptr = vb2_dma_sg_get_userptr, 66162306a36Sopenharmony_ci .put_userptr = vb2_dma_sg_put_userptr, 66262306a36Sopenharmony_ci .prepare = vb2_dma_sg_prepare, 66362306a36Sopenharmony_ci .finish = vb2_dma_sg_finish, 66462306a36Sopenharmony_ci .vaddr = vb2_dma_sg_vaddr, 66562306a36Sopenharmony_ci .mmap = vb2_dma_sg_mmap, 66662306a36Sopenharmony_ci .num_users = vb2_dma_sg_num_users, 66762306a36Sopenharmony_ci .get_dmabuf = vb2_dma_sg_get_dmabuf, 66862306a36Sopenharmony_ci .map_dmabuf = vb2_dma_sg_map_dmabuf, 66962306a36Sopenharmony_ci .unmap_dmabuf = vb2_dma_sg_unmap_dmabuf, 67062306a36Sopenharmony_ci .attach_dmabuf = vb2_dma_sg_attach_dmabuf, 67162306a36Sopenharmony_ci .detach_dmabuf = vb2_dma_sg_detach_dmabuf, 67262306a36Sopenharmony_ci .cookie = vb2_dma_sg_cookie, 67362306a36Sopenharmony_ci}; 67462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vb2_dma_sg_memops); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ciMODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2"); 67762306a36Sopenharmony_ciMODULE_AUTHOR("Andrzej Pietrasiewicz"); 67862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 67962306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF); 680