| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | /*
 | 
					
						
							| 
									
										
										
										
											2013-02-11 12:43:09 -05:00
										 |  |  |  * drivers/gpu/drm/omapdrm/omap_gem_dmabuf.c | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2011 Texas Instruments | 
					
						
							|  |  |  |  * Author: Rob Clark <rob.clark@linaro.org> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms of the GNU General Public License version 2 as published by | 
					
						
							|  |  |  |  * the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope that it will be useful, but WITHOUT | 
					
						
							|  |  |  |  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
					
						
							|  |  |  |  * more details. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along with | 
					
						
							|  |  |  |  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "omap_drv.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/dma-buf.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct sg_table *omap_gem_map_dma_buf( | 
					
						
							|  |  |  | 		struct dma_buf_attachment *attachment, | 
					
						
							|  |  |  | 		enum dma_data_direction dir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = attachment->dmabuf->priv; | 
					
						
							|  |  |  | 	struct sg_table *sg; | 
					
						
							|  |  |  | 	dma_addr_t paddr; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sg = kzalloc(sizeof(*sg), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!sg) | 
					
						
							|  |  |  | 		return ERR_PTR(-ENOMEM); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* camera, etc, need physically contiguous.. but we need a
 | 
					
						
							|  |  |  | 	 * better way to know this.. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	ret = omap_gem_get_paddr(obj, &paddr, true); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = sg_alloc_table(sg, 1, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sg_init_table(sg->sgl, 1); | 
					
						
							|  |  |  | 	sg_dma_len(sg->sgl) = obj->size; | 
					
						
							|  |  |  | 	sg_set_page(sg->sgl, pfn_to_page(PFN_DOWN(paddr)), obj->size, 0); | 
					
						
							|  |  |  | 	sg_dma_address(sg->sgl) = paddr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | 	/* this should be after _get_paddr() to ensure we have pages attached */ | 
					
						
							|  |  |  | 	omap_gem_dma_sync(obj, dir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | 	return sg; | 
					
						
							| 
									
										
										
										
											2013-01-15 20:46:50 +01:00
										 |  |  | out: | 
					
						
							|  |  |  | 	kfree(sg); | 
					
						
							|  |  |  | 	return ERR_PTR(ret); | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gem_unmap_dma_buf(struct dma_buf_attachment *attachment, | 
					
						
							|  |  |  | 		struct sg_table *sg, enum dma_data_direction dir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = attachment->dmabuf->priv; | 
					
						
							|  |  |  | 	omap_gem_put_paddr(obj); | 
					
						
							|  |  |  | 	sg_free_table(sg); | 
					
						
							|  |  |  | 	kfree(sg); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gem_dmabuf_release(struct dma_buf *buffer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							|  |  |  | 	/* release reference that was taken when dmabuf was exported
 | 
					
						
							|  |  |  | 	 * in omap_gem_prime_set().. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	drm_gem_object_unreference_unlocked(obj); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int omap_gem_dmabuf_begin_cpu_access(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		size_t start, size_t len, enum dma_data_direction dir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							|  |  |  | 	struct page **pages; | 
					
						
							|  |  |  | 	if (omap_gem_flags(obj) & OMAP_BO_TILED) { | 
					
						
							|  |  |  | 		/* TODO we would need to pin at least part of the buffer to
 | 
					
						
							|  |  |  | 		 * get de-tiled view.  For now just reject it. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* make sure we have the pages: */ | 
					
						
							|  |  |  | 	return omap_gem_get_pages(obj, &pages, true); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gem_dmabuf_end_cpu_access(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		size_t start, size_t len, enum dma_data_direction dir) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							|  |  |  | 	omap_gem_put_pages(obj); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *omap_gem_dmabuf_kmap_atomic(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		unsigned long page_num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							|  |  |  | 	struct page **pages; | 
					
						
							|  |  |  | 	omap_gem_get_pages(obj, &pages, false); | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | 	omap_gem_cpu_sync(obj, page_num); | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | 	return kmap_atomic(pages[page_num]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gem_dmabuf_kunmap_atomic(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		unsigned long page_num, void *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	kunmap_atomic(addr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void *omap_gem_dmabuf_kmap(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		unsigned long page_num) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							|  |  |  | 	struct page **pages; | 
					
						
							|  |  |  | 	omap_gem_get_pages(obj, &pages, false); | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | 	omap_gem_cpu_sync(obj, page_num); | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | 	return kmap(pages[page_num]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void omap_gem_dmabuf_kunmap(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		unsigned long page_num, void *addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							|  |  |  | 	struct page **pages; | 
					
						
							|  |  |  | 	omap_gem_get_pages(obj, &pages, false); | 
					
						
							|  |  |  | 	kunmap(pages[page_num]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | static int omap_gem_dmabuf_mmap(struct dma_buf *buffer, | 
					
						
							|  |  |  | 		struct vm_area_struct *vma) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj = buffer->priv; | 
					
						
							| 
									
										
										
										
											2013-06-27 08:39:58 +09:00
										 |  |  | 	struct drm_device *dev = obj->dev; | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (WARN_ON(!obj->filp)) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-27 08:39:58 +09:00
										 |  |  | 	mutex_lock(&dev->struct_mutex); | 
					
						
							| 
									
										
										
										
											2013-04-16 14:21:23 +02:00
										 |  |  | 	ret = drm_gem_mmap_obj(obj, omap_gem_mmap_size(obj), vma); | 
					
						
							| 
									
										
										
										
											2013-06-27 08:39:58 +09:00
										 |  |  | 	mutex_unlock(&dev->struct_mutex); | 
					
						
							| 
									
										
										
										
											2013-04-16 14:21:23 +02:00
										 |  |  | 	if (ret < 0) | 
					
						
							|  |  |  | 		return ret; | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return omap_gem_mmap_obj(obj, vma); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-10 10:44:00 +03:00
										 |  |  | static struct dma_buf_ops omap_dmabuf_ops = { | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | 		.map_dma_buf = omap_gem_map_dma_buf, | 
					
						
							|  |  |  | 		.unmap_dma_buf = omap_gem_unmap_dma_buf, | 
					
						
							|  |  |  | 		.release = omap_gem_dmabuf_release, | 
					
						
							|  |  |  | 		.begin_cpu_access = omap_gem_dmabuf_begin_cpu_access, | 
					
						
							|  |  |  | 		.end_cpu_access = omap_gem_dmabuf_end_cpu_access, | 
					
						
							|  |  |  | 		.kmap_atomic = omap_gem_dmabuf_kmap_atomic, | 
					
						
							|  |  |  | 		.kunmap_atomic = omap_gem_dmabuf_kunmap_atomic, | 
					
						
							|  |  |  | 		.kmap = omap_gem_dmabuf_kmap, | 
					
						
							|  |  |  | 		.kunmap = omap_gem_dmabuf_kunmap, | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:25 -06:00
										 |  |  | 		.mmap = omap_gem_dmabuf_mmap, | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-14 19:40:14 +09:00
										 |  |  | struct dma_buf *omap_gem_prime_export(struct drm_device *dev, | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | 		struct drm_gem_object *obj, int flags) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-07-01 12:57:26 +02:00
										 |  |  | 	return dma_buf_export(obj, &omap_dmabuf_ops, obj->size, flags, NULL); | 
					
						
							| 
									
										
										
										
											2012-04-10 13:19:55 -05:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:26 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-14 19:40:14 +09:00
										 |  |  | struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:26 -06:00
										 |  |  | 		struct dma_buf *buffer) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct drm_gem_object *obj; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* is this one of own objects? */ | 
					
						
							|  |  |  | 	if (buffer->ops == &omap_dmabuf_ops) { | 
					
						
							|  |  |  | 		obj = buffer->priv; | 
					
						
							|  |  |  | 		/* is it from our device? */ | 
					
						
							|  |  |  | 		if (obj->dev == dev) { | 
					
						
							| 
									
										
										
										
											2012-09-27 15:30:06 +09:00
										 |  |  | 			/*
 | 
					
						
							|  |  |  | 			 * Importing dmabuf exported from out own gem increases | 
					
						
							|  |  |  | 			 * refcount on gem itself instead of f_count of dmabuf. | 
					
						
							|  |  |  | 			 */ | 
					
						
							| 
									
										
										
										
											2012-05-17 02:37:26 -06:00
										 |  |  | 			drm_gem_object_reference(obj); | 
					
						
							|  |  |  | 			return obj; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * TODO add support for importing buffers from other devices.. | 
					
						
							|  |  |  | 	 * for now we don't need this but would be nice to add eventually | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	return ERR_PTR(-EINVAL); | 
					
						
							|  |  |  | } |