[PATCH 1/4] [RFC] drm/exynos: DMABUF: Added support for exporting non-contig buffers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



With this change, the exynos drm dmabuf module can export and
import dmabuf of gem objects with non-continuous memory.

The exynos_map_dmabuf function can create SGT of a non-contiguous
buffer by calling dma_get_pages to retrieve the allocated pages
and then maps the SGT to the caller's address space.

Signed-off-by: Prathyush K <prathyush.k@xxxxxxxxxxx>
---
 drivers/gpu/drm/exynos/exynos_drm_dmabuf.c |   98 +++++++++++++++++++++++-----
 1 files changed, 81 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
index cbb6ad4..54b88bd 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_dmabuf.c
@@ -56,6 +56,59 @@ static void exynos_dmabuf_detach(struct dma_buf *dmabuf,
 	dma_buf_put(dmabuf);
 }
 
+
+static struct sg_table *drm_dc_pages_to_sgt(struct page **pages,
+	unsigned long n_pages, size_t offset, size_t offset2, dma_addr_t daddr)
+{
+	struct sg_table *sgt;
+	struct scatterlist *s;
+	int i, j, cur_page, chunks, ret;
+
+	sgt = kzalloc(sizeof *sgt, GFP_KERNEL);
+	if (!sgt)
+		return ERR_PTR(-ENOMEM);
+
+	/* compute number of chunks */
+	chunks = 1;
+	for (i = 1; i < n_pages; ++i)
+		if (pages[i] != pages[i - 1] + 1)
+			++chunks;
+
+	ret = sg_alloc_table(sgt, chunks, GFP_KERNEL);
+	if (ret) {
+		kfree(sgt);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* merging chunks and putting them into the scatterlist */
+	cur_page = 0;
+	for_each_sg(sgt->sgl, s, sgt->orig_nents, i) {
+		size_t size = PAGE_SIZE;
+
+		for (j = cur_page + 1; j < n_pages; ++j) {
+			if (pages[j] != pages[j - 1] + 1)
+				break;
+			size += PAGE_SIZE;
+		}
+
+		/* cut offset if chunk starts at the first page */
+		if (cur_page == 0)
+			size -= offset;
+		/* cut offset2 if chunk ends at the last page */
+		if (j == n_pages)
+			size -= offset2;
+
+		sg_set_page(s, pages[cur_page], size, offset);
+		s->dma_address = daddr;
+		daddr +=  size;
+		offset = 0;
+		cur_page = j;
+	}
+
+	return sgt;
+}
+
+
 static struct sg_table *exynos_map_dmabuf(struct dma_buf_attachment *attach,
 					enum dma_data_direction direction)
 {
@@ -64,6 +117,8 @@ static struct sg_table *exynos_map_dmabuf(struct dma_buf_attachment *attach,
 	struct exynos_drm_gem_buf *buffer;
 	struct sg_table *sgt;
 	int ret;
+	int size, n_pages;
+	struct page **pages = NULL;
 
 	DRM_DEBUG_KMS("%s\n", __FILE__);
 
@@ -71,27 +126,37 @@ static struct sg_table *exynos_map_dmabuf(struct dma_buf_attachment *attach,
 
 	buffer = exynos_gem_obj->buffer;
 
-	/* TODO. consider physically non-continuous memory with IOMMU. */
+	size = buffer->size;
+	n_pages = PAGE_ALIGN(size) >> PAGE_SHIFT;
 
-	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
-	if (!sgt) {
-		DRM_DEBUG_KMS("failed to allocate sg table.\n");
-		return ERR_PTR(-ENOMEM);
+	pages = kmalloc(n_pages * sizeof pages[0], GFP_KERNEL);
+	if (!pages) {
+		DRM_DEBUG_KMS("failed to alloc page table\n");
+		return NULL;
 	}
 
-	ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
+	ret = dma_get_pages(attach->dev, buffer->kvaddr,
+				buffer->dma_addr, pages, n_pages);
 	if (ret < 0) {
-		DRM_DEBUG_KMS("failed to allocate scatter list.\n");
-		kfree(sgt);
-		sgt = NULL;
-		return ERR_PTR(-ENOMEM);
+		DRM_DEBUG_KMS("failed to get buffer pages from DMA API\n");
+		return NULL;
 	}
+	if (ret != n_pages) {
+		DRM_DEBUG_KMS("failed to get all pages from DMA API\n");
+		return NULL;
+	}
+
+	sgt = drm_dc_pages_to_sgt(pages, n_pages, 0, 0, buffer->dma_addr);
+	if (IS_ERR(sgt)) {
+		DRM_DEBUG_KMS("failed to prepare sg table\n");
+		return NULL;
+	}
+
+	sgt->nents = dma_map_sg(attach->dev, sgt->sgl,
+				sgt->orig_nents, DMA_BIDIRECTIONAL);
 
-	sg_init_table(sgt->sgl, 1);
-	sg_dma_len(sgt->sgl) = buffer->size;
-	sg_set_page(sgt->sgl, pfn_to_page(PFN_DOWN(buffer->dma_addr)),
-			buffer->size, 0);
-	sg_dma_address(sgt->sgl) = buffer->dma_addr;
+	/* pages are no longer needed */
+	kfree(pages);
 
 	/*
 	 * increase reference count of this buffer.
@@ -303,8 +368,6 @@ int exynos_dmabuf_prime_fd_to_handle(struct drm_device *drm_dev,
 	if (ret < 0)
 		goto fail_handle;
 
-	/* consider physically non-continuous memory with IOMMU. */
-
 	buffer->dma_addr = sg_dma_address(sgt->sgl);
 	buffer->size = sg_dma_len(sgt->sgl);
 
@@ -316,6 +379,7 @@ int exynos_dmabuf_prime_fd_to_handle(struct drm_device *drm_dev,
 	atomic_set(&buffer->shared_refcount, 1);
 
 	exynos_gem_obj->base.import_attach = attach;
+	exynos_gem_obj->buffer = buffer;
 
 	ret = drm_prime_insert_fd_handle_mapping(&file_priv->prime,
 							dmabuf, *handle);
-- 
1.7.0.4

_______________________________________________
dri-devel mailing list
dri-devel@xxxxxxxxxxxxxxxxxxxxx
http://lists.freedesktop.org/mailman/listinfo/dri-devel


[Index of Archives]     [Linux DRI Users]     [Linux Intel Graphics]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]
  Powered by Linux