162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author: Pawel Osciak <pawel@osciak.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/dma-buf.h> 1462306a36Sopenharmony_ci#include <linux/module.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/dma-mapping.h> 2062306a36Sopenharmony_ci#include <linux/highmem.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <media/videobuf2-v4l2.h> 2362306a36Sopenharmony_ci#include <media/videobuf2-dma-contig.h> 2462306a36Sopenharmony_ci#include <media/videobuf2-memops.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_cistruct vb2_dc_buf { 2762306a36Sopenharmony_ci struct device *dev; 2862306a36Sopenharmony_ci void *vaddr; 2962306a36Sopenharmony_ci unsigned long size; 3062306a36Sopenharmony_ci void *cookie; 3162306a36Sopenharmony_ci dma_addr_t dma_addr; 3262306a36Sopenharmony_ci unsigned long attrs; 3362306a36Sopenharmony_ci enum dma_data_direction dma_dir; 3462306a36Sopenharmony_ci struct sg_table *dma_sgt; 3562306a36Sopenharmony_ci struct frame_vector *vec; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* MMAP related */ 3862306a36Sopenharmony_ci struct vb2_vmarea_handler handler; 3962306a36Sopenharmony_ci refcount_t refcount; 4062306a36Sopenharmony_ci struct sg_table *sgt_base; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* DMABUF related */ 4362306a36Sopenharmony_ci struct dma_buf_attachment *db_attach; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci struct vb2_buffer *vb; 4662306a36Sopenharmony_ci bool non_coherent_mem; 4762306a36Sopenharmony_ci}; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/*********************************************/ 5062306a36Sopenharmony_ci/* scatterlist table functions */ 5162306a36Sopenharmony_ci/*********************************************/ 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic unsigned long vb2_dc_get_contiguous_size(struct sg_table *sgt) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci struct scatterlist *s; 5662306a36Sopenharmony_ci dma_addr_t expected = sg_dma_address(sgt->sgl); 5762306a36Sopenharmony_ci unsigned int i; 5862306a36Sopenharmony_ci unsigned long size = 0; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci for_each_sgtable_dma_sg(sgt, s, i) { 6162306a36Sopenharmony_ci if (sg_dma_address(s) != expected) 6262306a36Sopenharmony_ci break; 6362306a36Sopenharmony_ci expected += sg_dma_len(s); 6462306a36Sopenharmony_ci size += sg_dma_len(s); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci return size; 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci/*********************************************/ 7062306a36Sopenharmony_ci/* callbacks for all buffers */ 7162306a36Sopenharmony_ci/*********************************************/ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic void *vb2_dc_cookie(struct vb2_buffer *vb, void *buf_priv) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci return &buf->dma_addr; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * This function may fail if: 8262306a36Sopenharmony_ci * 8362306a36Sopenharmony_ci * - dma_buf_vmap() fails 8462306a36Sopenharmony_ci * E.g. due to lack of virtual mapping address space, or due to 8562306a36Sopenharmony_ci * dmabuf->ops misconfiguration. 8662306a36Sopenharmony_ci * 8762306a36Sopenharmony_ci * - dma_vmap_noncontiguous() fails 8862306a36Sopenharmony_ci * For instance, when requested buffer size is larger than totalram_pages(). 8962306a36Sopenharmony_ci * Relevant for buffers that use non-coherent memory. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * - Queue DMA attrs have DMA_ATTR_NO_KERNEL_MAPPING set 9262306a36Sopenharmony_ci * Relevant for buffers that use coherent memory. 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic void *vb2_dc_vaddr(struct vb2_buffer *vb, void *buf_priv) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (buf->vaddr) 9962306a36Sopenharmony_ci return buf->vaddr; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (buf->db_attach) { 10262306a36Sopenharmony_ci struct iosys_map map; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (!dma_buf_vmap_unlocked(buf->db_attach->dmabuf, &map)) 10562306a36Sopenharmony_ci buf->vaddr = map.vaddr; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return buf->vaddr; 10862306a36Sopenharmony_ci } 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (buf->non_coherent_mem) 11162306a36Sopenharmony_ci buf->vaddr = dma_vmap_noncontiguous(buf->dev, buf->size, 11262306a36Sopenharmony_ci buf->dma_sgt); 11362306a36Sopenharmony_ci return buf->vaddr; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic unsigned int vb2_dc_num_users(void *buf_priv) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci return refcount_read(&buf->refcount); 12162306a36Sopenharmony_ci} 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_cistatic void vb2_dc_prepare(void *buf_priv) 12462306a36Sopenharmony_ci{ 12562306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 12662306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* This takes care of DMABUF and user-enforced cache sync hint */ 12962306a36Sopenharmony_ci if (buf->vb->skip_cache_sync_on_prepare) 13062306a36Sopenharmony_ci return; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci if (!buf->non_coherent_mem) 13362306a36Sopenharmony_ci return; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci /* Non-coherent MMAP only */ 13662306a36Sopenharmony_ci if (buf->vaddr) 13762306a36Sopenharmony_ci flush_kernel_vmap_range(buf->vaddr, buf->size); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci /* For both USERPTR and non-coherent MMAP */ 14062306a36Sopenharmony_ci dma_sync_sgtable_for_device(buf->dev, sgt, buf->dma_dir); 14162306a36Sopenharmony_ci} 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic void vb2_dc_finish(void *buf_priv) 14462306a36Sopenharmony_ci{ 14562306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 14662306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* This takes care of DMABUF and user-enforced cache sync hint */ 14962306a36Sopenharmony_ci if (buf->vb->skip_cache_sync_on_finish) 15062306a36Sopenharmony_ci return; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci if (!buf->non_coherent_mem) 15362306a36Sopenharmony_ci return; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci /* Non-coherent MMAP only */ 15662306a36Sopenharmony_ci if (buf->vaddr) 15762306a36Sopenharmony_ci invalidate_kernel_vmap_range(buf->vaddr, buf->size); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* For both USERPTR and non-coherent MMAP */ 16062306a36Sopenharmony_ci dma_sync_sgtable_for_cpu(buf->dev, sgt, buf->dma_dir); 16162306a36Sopenharmony_ci} 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/*********************************************/ 16462306a36Sopenharmony_ci/* callbacks for MMAP buffers */ 16562306a36Sopenharmony_ci/*********************************************/ 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic void vb2_dc_put(void *buf_priv) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (!refcount_dec_and_test(&buf->refcount)) 17262306a36Sopenharmony_ci return; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (buf->non_coherent_mem) { 17562306a36Sopenharmony_ci if (buf->vaddr) 17662306a36Sopenharmony_ci dma_vunmap_noncontiguous(buf->dev, buf->vaddr); 17762306a36Sopenharmony_ci dma_free_noncontiguous(buf->dev, buf->size, 17862306a36Sopenharmony_ci buf->dma_sgt, buf->dma_dir); 17962306a36Sopenharmony_ci } else { 18062306a36Sopenharmony_ci if (buf->sgt_base) { 18162306a36Sopenharmony_ci sg_free_table(buf->sgt_base); 18262306a36Sopenharmony_ci kfree(buf->sgt_base); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci dma_free_attrs(buf->dev, buf->size, buf->cookie, 18562306a36Sopenharmony_ci buf->dma_addr, buf->attrs); 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci put_device(buf->dev); 18862306a36Sopenharmony_ci kfree(buf); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic int vb2_dc_alloc_coherent(struct vb2_dc_buf *buf) 19262306a36Sopenharmony_ci{ 19362306a36Sopenharmony_ci struct vb2_queue *q = buf->vb->vb2_queue; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci buf->cookie = dma_alloc_attrs(buf->dev, 19662306a36Sopenharmony_ci buf->size, 19762306a36Sopenharmony_ci &buf->dma_addr, 19862306a36Sopenharmony_ci GFP_KERNEL | q->gfp_flags, 19962306a36Sopenharmony_ci buf->attrs); 20062306a36Sopenharmony_ci if (!buf->cookie) 20162306a36Sopenharmony_ci return -ENOMEM; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (q->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING) 20462306a36Sopenharmony_ci return 0; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci buf->vaddr = buf->cookie; 20762306a36Sopenharmony_ci return 0; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_cistatic int vb2_dc_alloc_non_coherent(struct vb2_dc_buf *buf) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci struct vb2_queue *q = buf->vb->vb2_queue; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci buf->dma_sgt = dma_alloc_noncontiguous(buf->dev, 21562306a36Sopenharmony_ci buf->size, 21662306a36Sopenharmony_ci buf->dma_dir, 21762306a36Sopenharmony_ci GFP_KERNEL | q->gfp_flags, 21862306a36Sopenharmony_ci buf->attrs); 21962306a36Sopenharmony_ci if (!buf->dma_sgt) 22062306a36Sopenharmony_ci return -ENOMEM; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci buf->dma_addr = sg_dma_address(buf->dma_sgt->sgl); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* 22562306a36Sopenharmony_ci * For non-coherent buffers the kernel mapping is created on demand 22662306a36Sopenharmony_ci * in vb2_dc_vaddr(). 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci return 0; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic void *vb2_dc_alloc(struct vb2_buffer *vb, 23262306a36Sopenharmony_ci struct device *dev, 23362306a36Sopenharmony_ci unsigned long size) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci struct vb2_dc_buf *buf; 23662306a36Sopenharmony_ci int ret; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (WARN_ON(!dev)) 23962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci buf = kzalloc(sizeof *buf, GFP_KERNEL); 24262306a36Sopenharmony_ci if (!buf) 24362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci buf->attrs = vb->vb2_queue->dma_attrs; 24662306a36Sopenharmony_ci buf->dma_dir = vb->vb2_queue->dma_dir; 24762306a36Sopenharmony_ci buf->vb = vb; 24862306a36Sopenharmony_ci buf->non_coherent_mem = vb->vb2_queue->non_coherent_mem; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci buf->size = size; 25162306a36Sopenharmony_ci /* Prevent the device from being released while the buffer is used */ 25262306a36Sopenharmony_ci buf->dev = get_device(dev); 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci if (buf->non_coherent_mem) 25562306a36Sopenharmony_ci ret = vb2_dc_alloc_non_coherent(buf); 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci ret = vb2_dc_alloc_coherent(buf); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (ret) { 26062306a36Sopenharmony_ci dev_err(dev, "dma alloc of size %lu failed\n", size); 26162306a36Sopenharmony_ci kfree(buf); 26262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci buf->handler.refcount = &buf->refcount; 26662306a36Sopenharmony_ci buf->handler.put = vb2_dc_put; 26762306a36Sopenharmony_ci buf->handler.arg = buf; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci refcount_set(&buf->refcount, 1); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return buf; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int vb2_dc_mmap(void *buf_priv, struct vm_area_struct *vma) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 27762306a36Sopenharmony_ci int ret; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (!buf) { 28062306a36Sopenharmony_ci printk(KERN_ERR "No buffer to map\n"); 28162306a36Sopenharmony_ci return -EINVAL; 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (buf->non_coherent_mem) 28562306a36Sopenharmony_ci ret = dma_mmap_noncontiguous(buf->dev, vma, buf->size, 28662306a36Sopenharmony_ci buf->dma_sgt); 28762306a36Sopenharmony_ci else 28862306a36Sopenharmony_ci ret = dma_mmap_attrs(buf->dev, vma, buf->cookie, buf->dma_addr, 28962306a36Sopenharmony_ci buf->size, buf->attrs); 29062306a36Sopenharmony_ci if (ret) { 29162306a36Sopenharmony_ci pr_err("Remapping memory failed, error: %d\n", ret); 29262306a36Sopenharmony_ci return ret; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP); 29662306a36Sopenharmony_ci vma->vm_private_data = &buf->handler; 29762306a36Sopenharmony_ci vma->vm_ops = &vb2_common_vm_ops; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci vma->vm_ops->open(vma); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci pr_debug("%s: mapped dma addr 0x%08lx at 0x%08lx, size %lu\n", 30262306a36Sopenharmony_ci __func__, (unsigned long)buf->dma_addr, vma->vm_start, 30362306a36Sopenharmony_ci buf->size); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci/*********************************************/ 30962306a36Sopenharmony_ci/* DMABUF ops for exporters */ 31062306a36Sopenharmony_ci/*********************************************/ 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistruct vb2_dc_attachment { 31362306a36Sopenharmony_ci struct sg_table sgt; 31462306a36Sopenharmony_ci enum dma_data_direction dma_dir; 31562306a36Sopenharmony_ci}; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_cistatic int vb2_dc_dmabuf_ops_attach(struct dma_buf *dbuf, 31862306a36Sopenharmony_ci struct dma_buf_attachment *dbuf_attach) 31962306a36Sopenharmony_ci{ 32062306a36Sopenharmony_ci struct vb2_dc_attachment *attach; 32162306a36Sopenharmony_ci unsigned int i; 32262306a36Sopenharmony_ci struct scatterlist *rd, *wr; 32362306a36Sopenharmony_ci struct sg_table *sgt; 32462306a36Sopenharmony_ci struct vb2_dc_buf *buf = dbuf->priv; 32562306a36Sopenharmony_ci int ret; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci attach = kzalloc(sizeof(*attach), GFP_KERNEL); 32862306a36Sopenharmony_ci if (!attach) 32962306a36Sopenharmony_ci return -ENOMEM; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci sgt = &attach->sgt; 33262306a36Sopenharmony_ci /* Copy the buf->base_sgt scatter list to the attachment, as we can't 33362306a36Sopenharmony_ci * map the same scatter list to multiple attachments at the same time. 33462306a36Sopenharmony_ci */ 33562306a36Sopenharmony_ci ret = sg_alloc_table(sgt, buf->sgt_base->orig_nents, GFP_KERNEL); 33662306a36Sopenharmony_ci if (ret) { 33762306a36Sopenharmony_ci kfree(attach); 33862306a36Sopenharmony_ci return -ENOMEM; 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci rd = buf->sgt_base->sgl; 34262306a36Sopenharmony_ci wr = sgt->sgl; 34362306a36Sopenharmony_ci for (i = 0; i < sgt->orig_nents; ++i) { 34462306a36Sopenharmony_ci sg_set_page(wr, sg_page(rd), rd->length, rd->offset); 34562306a36Sopenharmony_ci rd = sg_next(rd); 34662306a36Sopenharmony_ci wr = sg_next(wr); 34762306a36Sopenharmony_ci } 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci attach->dma_dir = DMA_NONE; 35062306a36Sopenharmony_ci dbuf_attach->priv = attach; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci return 0; 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic void vb2_dc_dmabuf_ops_detach(struct dma_buf *dbuf, 35662306a36Sopenharmony_ci struct dma_buf_attachment *db_attach) 35762306a36Sopenharmony_ci{ 35862306a36Sopenharmony_ci struct vb2_dc_attachment *attach = db_attach->priv; 35962306a36Sopenharmony_ci struct sg_table *sgt; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci if (!attach) 36262306a36Sopenharmony_ci return; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci sgt = &attach->sgt; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* release the scatterlist cache */ 36762306a36Sopenharmony_ci if (attach->dma_dir != DMA_NONE) 36862306a36Sopenharmony_ci /* 36962306a36Sopenharmony_ci * Cache sync can be skipped here, as the vb2_dc memory is 37062306a36Sopenharmony_ci * allocated from device coherent memory, which means the 37162306a36Sopenharmony_ci * memory locations do not require any explicit cache 37262306a36Sopenharmony_ci * maintenance prior or after being used by the device. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 37562306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 37662306a36Sopenharmony_ci sg_free_table(sgt); 37762306a36Sopenharmony_ci kfree(attach); 37862306a36Sopenharmony_ci db_attach->priv = NULL; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic struct sg_table *vb2_dc_dmabuf_ops_map( 38262306a36Sopenharmony_ci struct dma_buf_attachment *db_attach, enum dma_data_direction dma_dir) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct vb2_dc_attachment *attach = db_attach->priv; 38562306a36Sopenharmony_ci struct sg_table *sgt; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci sgt = &attach->sgt; 38862306a36Sopenharmony_ci /* return previously mapped sg table */ 38962306a36Sopenharmony_ci if (attach->dma_dir == dma_dir) 39062306a36Sopenharmony_ci return sgt; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* release any previous cache */ 39362306a36Sopenharmony_ci if (attach->dma_dir != DMA_NONE) { 39462306a36Sopenharmony_ci dma_unmap_sgtable(db_attach->dev, sgt, attach->dma_dir, 39562306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 39662306a36Sopenharmony_ci attach->dma_dir = DMA_NONE; 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci /* 40062306a36Sopenharmony_ci * mapping to the client with new direction, no cache sync 40162306a36Sopenharmony_ci * required see comment in vb2_dc_dmabuf_ops_detach() 40262306a36Sopenharmony_ci */ 40362306a36Sopenharmony_ci if (dma_map_sgtable(db_attach->dev, sgt, dma_dir, 40462306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC)) { 40562306a36Sopenharmony_ci pr_err("failed to map scatterlist\n"); 40662306a36Sopenharmony_ci return ERR_PTR(-EIO); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci attach->dma_dir = dma_dir; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return sgt; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_cistatic void vb2_dc_dmabuf_ops_unmap(struct dma_buf_attachment *db_attach, 41562306a36Sopenharmony_ci struct sg_table *sgt, enum dma_data_direction dma_dir) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci /* nothing to be done here */ 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic void vb2_dc_dmabuf_ops_release(struct dma_buf *dbuf) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci /* drop reference obtained in vb2_dc_get_dmabuf */ 42362306a36Sopenharmony_ci vb2_dc_put(dbuf->priv); 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic int 42762306a36Sopenharmony_civb2_dc_dmabuf_ops_begin_cpu_access(struct dma_buf *dbuf, 42862306a36Sopenharmony_ci enum dma_data_direction direction) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci return 0; 43162306a36Sopenharmony_ci} 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_cistatic int 43462306a36Sopenharmony_civb2_dc_dmabuf_ops_end_cpu_access(struct dma_buf *dbuf, 43562306a36Sopenharmony_ci enum dma_data_direction direction) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci return 0; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic int vb2_dc_dmabuf_ops_vmap(struct dma_buf *dbuf, struct iosys_map *map) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci struct vb2_dc_buf *buf; 44362306a36Sopenharmony_ci void *vaddr; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci buf = dbuf->priv; 44662306a36Sopenharmony_ci vaddr = vb2_dc_vaddr(buf->vb, buf); 44762306a36Sopenharmony_ci if (!vaddr) 44862306a36Sopenharmony_ci return -EINVAL; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci iosys_map_set_vaddr(map, vaddr); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int vb2_dc_dmabuf_ops_mmap(struct dma_buf *dbuf, 45662306a36Sopenharmony_ci struct vm_area_struct *vma) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci return vb2_dc_mmap(dbuf->priv, vma); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_cistatic const struct dma_buf_ops vb2_dc_dmabuf_ops = { 46262306a36Sopenharmony_ci .attach = vb2_dc_dmabuf_ops_attach, 46362306a36Sopenharmony_ci .detach = vb2_dc_dmabuf_ops_detach, 46462306a36Sopenharmony_ci .map_dma_buf = vb2_dc_dmabuf_ops_map, 46562306a36Sopenharmony_ci .unmap_dma_buf = vb2_dc_dmabuf_ops_unmap, 46662306a36Sopenharmony_ci .begin_cpu_access = vb2_dc_dmabuf_ops_begin_cpu_access, 46762306a36Sopenharmony_ci .end_cpu_access = vb2_dc_dmabuf_ops_end_cpu_access, 46862306a36Sopenharmony_ci .vmap = vb2_dc_dmabuf_ops_vmap, 46962306a36Sopenharmony_ci .mmap = vb2_dc_dmabuf_ops_mmap, 47062306a36Sopenharmony_ci .release = vb2_dc_dmabuf_ops_release, 47162306a36Sopenharmony_ci}; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic struct sg_table *vb2_dc_get_base_sgt(struct vb2_dc_buf *buf) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci int ret; 47662306a36Sopenharmony_ci struct sg_table *sgt; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (buf->non_coherent_mem) 47962306a36Sopenharmony_ci return buf->dma_sgt; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci sgt = kmalloc(sizeof(*sgt), GFP_KERNEL); 48262306a36Sopenharmony_ci if (!sgt) { 48362306a36Sopenharmony_ci dev_err(buf->dev, "failed to alloc sg table\n"); 48462306a36Sopenharmony_ci return NULL; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci ret = dma_get_sgtable_attrs(buf->dev, sgt, buf->cookie, buf->dma_addr, 48862306a36Sopenharmony_ci buf->size, buf->attrs); 48962306a36Sopenharmony_ci if (ret < 0) { 49062306a36Sopenharmony_ci dev_err(buf->dev, "failed to get scatterlist from DMA API\n"); 49162306a36Sopenharmony_ci kfree(sgt); 49262306a36Sopenharmony_ci return NULL; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci return sgt; 49662306a36Sopenharmony_ci} 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cistatic struct dma_buf *vb2_dc_get_dmabuf(struct vb2_buffer *vb, 49962306a36Sopenharmony_ci void *buf_priv, 50062306a36Sopenharmony_ci unsigned long flags) 50162306a36Sopenharmony_ci{ 50262306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 50362306a36Sopenharmony_ci struct dma_buf *dbuf; 50462306a36Sopenharmony_ci DEFINE_DMA_BUF_EXPORT_INFO(exp_info); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci exp_info.ops = &vb2_dc_dmabuf_ops; 50762306a36Sopenharmony_ci exp_info.size = buf->size; 50862306a36Sopenharmony_ci exp_info.flags = flags; 50962306a36Sopenharmony_ci exp_info.priv = buf; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (!buf->sgt_base) 51262306a36Sopenharmony_ci buf->sgt_base = vb2_dc_get_base_sgt(buf); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci if (WARN_ON(!buf->sgt_base)) 51562306a36Sopenharmony_ci return NULL; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci dbuf = dma_buf_export(&exp_info); 51862306a36Sopenharmony_ci if (IS_ERR(dbuf)) 51962306a36Sopenharmony_ci return NULL; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* dmabuf keeps reference to vb2 buffer */ 52262306a36Sopenharmony_ci refcount_inc(&buf->refcount); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return dbuf; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci/*********************************************/ 52862306a36Sopenharmony_ci/* callbacks for USERPTR buffers */ 52962306a36Sopenharmony_ci/*********************************************/ 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic void vb2_dc_put_userptr(void *buf_priv) 53262306a36Sopenharmony_ci{ 53362306a36Sopenharmony_ci struct vb2_dc_buf *buf = buf_priv; 53462306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 53562306a36Sopenharmony_ci int i; 53662306a36Sopenharmony_ci struct page **pages; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (sgt) { 53962306a36Sopenharmony_ci /* 54062306a36Sopenharmony_ci * No need to sync to CPU, it's already synced to the CPU 54162306a36Sopenharmony_ci * since the finish() memop will have been called before this. 54262306a36Sopenharmony_ci */ 54362306a36Sopenharmony_ci dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, 54462306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC); 54562306a36Sopenharmony_ci pages = frame_vector_pages(buf->vec); 54662306a36Sopenharmony_ci /* sgt should exist only if vector contains pages... */ 54762306a36Sopenharmony_ci BUG_ON(IS_ERR(pages)); 54862306a36Sopenharmony_ci if (buf->dma_dir == DMA_FROM_DEVICE || 54962306a36Sopenharmony_ci buf->dma_dir == DMA_BIDIRECTIONAL) 55062306a36Sopenharmony_ci for (i = 0; i < frame_vector_count(buf->vec); i++) 55162306a36Sopenharmony_ci set_page_dirty_lock(pages[i]); 55262306a36Sopenharmony_ci sg_free_table(sgt); 55362306a36Sopenharmony_ci kfree(sgt); 55462306a36Sopenharmony_ci } else { 55562306a36Sopenharmony_ci dma_unmap_resource(buf->dev, buf->dma_addr, buf->size, 55662306a36Sopenharmony_ci buf->dma_dir, 0); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci vb2_destroy_framevec(buf->vec); 55962306a36Sopenharmony_ci kfree(buf); 56062306a36Sopenharmony_ci} 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic void *vb2_dc_get_userptr(struct vb2_buffer *vb, struct device *dev, 56362306a36Sopenharmony_ci unsigned long vaddr, unsigned long size) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct vb2_dc_buf *buf; 56662306a36Sopenharmony_ci struct frame_vector *vec; 56762306a36Sopenharmony_ci unsigned int offset; 56862306a36Sopenharmony_ci int n_pages, i; 56962306a36Sopenharmony_ci int ret = 0; 57062306a36Sopenharmony_ci struct sg_table *sgt; 57162306a36Sopenharmony_ci unsigned long contig_size; 57262306a36Sopenharmony_ci unsigned long dma_align = dma_get_cache_alignment(); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Only cache aligned DMA transfers are reliable */ 57562306a36Sopenharmony_ci if (!IS_ALIGNED(vaddr | size, dma_align)) { 57662306a36Sopenharmony_ci pr_debug("user data must be aligned to %lu bytes\n", dma_align); 57762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (!size) { 58162306a36Sopenharmony_ci pr_debug("size is zero\n"); 58262306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (WARN_ON(!dev)) 58662306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci buf = kzalloc(sizeof *buf, GFP_KERNEL); 58962306a36Sopenharmony_ci if (!buf) 59062306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci buf->dev = dev; 59362306a36Sopenharmony_ci buf->dma_dir = vb->vb2_queue->dma_dir; 59462306a36Sopenharmony_ci buf->vb = vb; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci offset = lower_32_bits(offset_in_page(vaddr)); 59762306a36Sopenharmony_ci vec = vb2_create_framevec(vaddr, size, buf->dma_dir == DMA_FROM_DEVICE || 59862306a36Sopenharmony_ci buf->dma_dir == DMA_BIDIRECTIONAL); 59962306a36Sopenharmony_ci if (IS_ERR(vec)) { 60062306a36Sopenharmony_ci ret = PTR_ERR(vec); 60162306a36Sopenharmony_ci goto fail_buf; 60262306a36Sopenharmony_ci } 60362306a36Sopenharmony_ci buf->vec = vec; 60462306a36Sopenharmony_ci n_pages = frame_vector_count(vec); 60562306a36Sopenharmony_ci ret = frame_vector_to_pages(vec); 60662306a36Sopenharmony_ci if (ret < 0) { 60762306a36Sopenharmony_ci unsigned long *nums = frame_vector_pfns(vec); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci /* 61062306a36Sopenharmony_ci * Failed to convert to pages... Check the memory is physically 61162306a36Sopenharmony_ci * contiguous and use direct mapping 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci for (i = 1; i < n_pages; i++) 61462306a36Sopenharmony_ci if (nums[i-1] + 1 != nums[i]) 61562306a36Sopenharmony_ci goto fail_pfnvec; 61662306a36Sopenharmony_ci buf->dma_addr = dma_map_resource(buf->dev, 61762306a36Sopenharmony_ci __pfn_to_phys(nums[0]), size, buf->dma_dir, 0); 61862306a36Sopenharmony_ci if (dma_mapping_error(buf->dev, buf->dma_addr)) { 61962306a36Sopenharmony_ci ret = -ENOMEM; 62062306a36Sopenharmony_ci goto fail_pfnvec; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci goto out; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 62662306a36Sopenharmony_ci if (!sgt) { 62762306a36Sopenharmony_ci pr_err("failed to allocate sg table\n"); 62862306a36Sopenharmony_ci ret = -ENOMEM; 62962306a36Sopenharmony_ci goto fail_pfnvec; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci ret = sg_alloc_table_from_pages(sgt, frame_vector_pages(vec), n_pages, 63362306a36Sopenharmony_ci offset, size, GFP_KERNEL); 63462306a36Sopenharmony_ci if (ret) { 63562306a36Sopenharmony_ci pr_err("failed to initialize sg table\n"); 63662306a36Sopenharmony_ci goto fail_sgt; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci /* 64062306a36Sopenharmony_ci * No need to sync to the device, this will happen later when the 64162306a36Sopenharmony_ci * prepare() memop is called. 64262306a36Sopenharmony_ci */ 64362306a36Sopenharmony_ci if (dma_map_sgtable(buf->dev, sgt, buf->dma_dir, 64462306a36Sopenharmony_ci DMA_ATTR_SKIP_CPU_SYNC)) { 64562306a36Sopenharmony_ci pr_err("failed to map scatterlist\n"); 64662306a36Sopenharmony_ci ret = -EIO; 64762306a36Sopenharmony_ci goto fail_sgt_init; 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci contig_size = vb2_dc_get_contiguous_size(sgt); 65162306a36Sopenharmony_ci if (contig_size < size) { 65262306a36Sopenharmony_ci pr_err("contiguous mapping is too small %lu/%lu\n", 65362306a36Sopenharmony_ci contig_size, size); 65462306a36Sopenharmony_ci ret = -EFAULT; 65562306a36Sopenharmony_ci goto fail_map_sg; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci buf->dma_addr = sg_dma_address(sgt->sgl); 65962306a36Sopenharmony_ci buf->dma_sgt = sgt; 66062306a36Sopenharmony_ci buf->non_coherent_mem = 1; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ciout: 66362306a36Sopenharmony_ci buf->size = size; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci return buf; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_cifail_map_sg: 66862306a36Sopenharmony_ci dma_unmap_sgtable(buf->dev, sgt, buf->dma_dir, DMA_ATTR_SKIP_CPU_SYNC); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_cifail_sgt_init: 67162306a36Sopenharmony_ci sg_free_table(sgt); 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cifail_sgt: 67462306a36Sopenharmony_ci kfree(sgt); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_cifail_pfnvec: 67762306a36Sopenharmony_ci vb2_destroy_framevec(vec); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cifail_buf: 68062306a36Sopenharmony_ci kfree(buf); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return ERR_PTR(ret); 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/*********************************************/ 68662306a36Sopenharmony_ci/* callbacks for DMABUF buffers */ 68762306a36Sopenharmony_ci/*********************************************/ 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic int vb2_dc_map_dmabuf(void *mem_priv) 69062306a36Sopenharmony_ci{ 69162306a36Sopenharmony_ci struct vb2_dc_buf *buf = mem_priv; 69262306a36Sopenharmony_ci struct sg_table *sgt; 69362306a36Sopenharmony_ci unsigned long contig_size; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci if (WARN_ON(!buf->db_attach)) { 69662306a36Sopenharmony_ci pr_err("trying to pin a non attached buffer\n"); 69762306a36Sopenharmony_ci return -EINVAL; 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (WARN_ON(buf->dma_sgt)) { 70162306a36Sopenharmony_ci pr_err("dmabuf buffer is already pinned\n"); 70262306a36Sopenharmony_ci return 0; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* get the associated scatterlist for this buffer */ 70662306a36Sopenharmony_ci sgt = dma_buf_map_attachment_unlocked(buf->db_attach, buf->dma_dir); 70762306a36Sopenharmony_ci if (IS_ERR(sgt)) { 70862306a36Sopenharmony_ci pr_err("Error getting dmabuf scatterlist\n"); 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* checking if dmabuf is big enough to store contiguous chunk */ 71362306a36Sopenharmony_ci contig_size = vb2_dc_get_contiguous_size(sgt); 71462306a36Sopenharmony_ci if (contig_size < buf->size) { 71562306a36Sopenharmony_ci pr_err("contiguous chunk is too small %lu/%lu\n", 71662306a36Sopenharmony_ci contig_size, buf->size); 71762306a36Sopenharmony_ci dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, 71862306a36Sopenharmony_ci buf->dma_dir); 71962306a36Sopenharmony_ci return -EFAULT; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci buf->dma_addr = sg_dma_address(sgt->sgl); 72362306a36Sopenharmony_ci buf->dma_sgt = sgt; 72462306a36Sopenharmony_ci buf->vaddr = NULL; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return 0; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic void vb2_dc_unmap_dmabuf(void *mem_priv) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci struct vb2_dc_buf *buf = mem_priv; 73262306a36Sopenharmony_ci struct sg_table *sgt = buf->dma_sgt; 73362306a36Sopenharmony_ci struct iosys_map map = IOSYS_MAP_INIT_VADDR(buf->vaddr); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci if (WARN_ON(!buf->db_attach)) { 73662306a36Sopenharmony_ci pr_err("trying to unpin a not attached buffer\n"); 73762306a36Sopenharmony_ci return; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (WARN_ON(!sgt)) { 74162306a36Sopenharmony_ci pr_err("dmabuf buffer is already unpinned\n"); 74262306a36Sopenharmony_ci return; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci if (buf->vaddr) { 74662306a36Sopenharmony_ci dma_buf_vunmap_unlocked(buf->db_attach->dmabuf, &map); 74762306a36Sopenharmony_ci buf->vaddr = NULL; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci dma_buf_unmap_attachment_unlocked(buf->db_attach, sgt, buf->dma_dir); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci buf->dma_addr = 0; 75262306a36Sopenharmony_ci buf->dma_sgt = NULL; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic void vb2_dc_detach_dmabuf(void *mem_priv) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct vb2_dc_buf *buf = mem_priv; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci /* if vb2 works correctly you should never detach mapped buffer */ 76062306a36Sopenharmony_ci if (WARN_ON(buf->dma_addr)) 76162306a36Sopenharmony_ci vb2_dc_unmap_dmabuf(buf); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci /* detach this attachment */ 76462306a36Sopenharmony_ci dma_buf_detach(buf->db_attach->dmabuf, buf->db_attach); 76562306a36Sopenharmony_ci kfree(buf); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic void *vb2_dc_attach_dmabuf(struct vb2_buffer *vb, struct device *dev, 76962306a36Sopenharmony_ci struct dma_buf *dbuf, unsigned long size) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct vb2_dc_buf *buf; 77262306a36Sopenharmony_ci struct dma_buf_attachment *dba; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (dbuf->size < size) 77562306a36Sopenharmony_ci return ERR_PTR(-EFAULT); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (WARN_ON(!dev)) 77862306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci buf = kzalloc(sizeof(*buf), GFP_KERNEL); 78162306a36Sopenharmony_ci if (!buf) 78262306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci buf->dev = dev; 78562306a36Sopenharmony_ci buf->vb = vb; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci /* create attachment for the dmabuf with the user device */ 78862306a36Sopenharmony_ci dba = dma_buf_attach(dbuf, buf->dev); 78962306a36Sopenharmony_ci if (IS_ERR(dba)) { 79062306a36Sopenharmony_ci pr_err("failed to attach dmabuf\n"); 79162306a36Sopenharmony_ci kfree(buf); 79262306a36Sopenharmony_ci return dba; 79362306a36Sopenharmony_ci } 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci buf->dma_dir = vb->vb2_queue->dma_dir; 79662306a36Sopenharmony_ci buf->size = size; 79762306a36Sopenharmony_ci buf->db_attach = dba; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return buf; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/*********************************************/ 80362306a36Sopenharmony_ci/* DMA CONTIG exported functions */ 80462306a36Sopenharmony_ci/*********************************************/ 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ciconst struct vb2_mem_ops vb2_dma_contig_memops = { 80762306a36Sopenharmony_ci .alloc = vb2_dc_alloc, 80862306a36Sopenharmony_ci .put = vb2_dc_put, 80962306a36Sopenharmony_ci .get_dmabuf = vb2_dc_get_dmabuf, 81062306a36Sopenharmony_ci .cookie = vb2_dc_cookie, 81162306a36Sopenharmony_ci .vaddr = vb2_dc_vaddr, 81262306a36Sopenharmony_ci .mmap = vb2_dc_mmap, 81362306a36Sopenharmony_ci .get_userptr = vb2_dc_get_userptr, 81462306a36Sopenharmony_ci .put_userptr = vb2_dc_put_userptr, 81562306a36Sopenharmony_ci .prepare = vb2_dc_prepare, 81662306a36Sopenharmony_ci .finish = vb2_dc_finish, 81762306a36Sopenharmony_ci .map_dmabuf = vb2_dc_map_dmabuf, 81862306a36Sopenharmony_ci .unmap_dmabuf = vb2_dc_unmap_dmabuf, 81962306a36Sopenharmony_ci .attach_dmabuf = vb2_dc_attach_dmabuf, 82062306a36Sopenharmony_ci .detach_dmabuf = vb2_dc_detach_dmabuf, 82162306a36Sopenharmony_ci .num_users = vb2_dc_num_users, 82262306a36Sopenharmony_ci}; 82362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vb2_dma_contig_memops); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci/** 82662306a36Sopenharmony_ci * vb2_dma_contig_set_max_seg_size() - configure DMA max segment size 82762306a36Sopenharmony_ci * @dev: device for configuring DMA parameters 82862306a36Sopenharmony_ci * @size: size of DMA max segment size to set 82962306a36Sopenharmony_ci * 83062306a36Sopenharmony_ci * To allow mapping the scatter-list into a single chunk in the DMA 83162306a36Sopenharmony_ci * address space, the device is required to have the DMA max segment 83262306a36Sopenharmony_ci * size parameter set to a value larger than the buffer size. Otherwise, 83362306a36Sopenharmony_ci * the DMA-mapping subsystem will split the mapping into max segment 83462306a36Sopenharmony_ci * size chunks. This function sets the DMA max segment size 83562306a36Sopenharmony_ci * parameter to let DMA-mapping map a buffer as a single chunk in DMA 83662306a36Sopenharmony_ci * address space. 83762306a36Sopenharmony_ci * This code assumes that the DMA-mapping subsystem will merge all 83862306a36Sopenharmony_ci * scatterlist segments if this is really possible (for example when 83962306a36Sopenharmony_ci * an IOMMU is available and enabled). 84062306a36Sopenharmony_ci * Ideally, this parameter should be set by the generic bus code, but it 84162306a36Sopenharmony_ci * is left with the default 64KiB value due to historical litmiations in 84262306a36Sopenharmony_ci * other subsystems (like limited USB host drivers) and there no good 84362306a36Sopenharmony_ci * place to set it to the proper value. 84462306a36Sopenharmony_ci * This function should be called from the drivers, which are known to 84562306a36Sopenharmony_ci * operate on platforms with IOMMU and provide access to shared buffers 84662306a36Sopenharmony_ci * (either USERPTR or DMABUF). This should be done before initializing 84762306a36Sopenharmony_ci * videobuf2 queue. 84862306a36Sopenharmony_ci */ 84962306a36Sopenharmony_ciint vb2_dma_contig_set_max_seg_size(struct device *dev, unsigned int size) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci if (!dev->dma_parms) { 85262306a36Sopenharmony_ci dev_err(dev, "Failed to set max_seg_size: dma_parms is NULL\n"); 85362306a36Sopenharmony_ci return -ENODEV; 85462306a36Sopenharmony_ci } 85562306a36Sopenharmony_ci if (dma_get_max_seg_size(dev) < size) 85662306a36Sopenharmony_ci return dma_set_max_seg_size(dev, size); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci return 0; 85962306a36Sopenharmony_ci} 86062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vb2_dma_contig_set_max_seg_size); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ciMODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); 86362306a36Sopenharmony_ciMODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>"); 86462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 86562306a36Sopenharmony_ciMODULE_IMPORT_NS(DMA_BUF); 866