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