diff options
Diffstat (limited to 'omap')
| -rw-r--r-- | omap/omap_drm.c | 150 | ||||
| -rw-r--r-- | omap/omap_drmif.h | 2 | 
2 files changed, 126 insertions, 26 deletions
diff --git a/omap/omap_drm.c b/omap/omap_drm.c index 1d37e451..cd8e8bc4 100644 --- a/omap/omap_drm.c +++ b/omap/omap_drm.c @@ -32,12 +32,15 @@  #include <stdlib.h>  #include <linux/stddef.h> +#include <linux/types.h>  #include <errno.h>  #include <sys/mman.h>  #include <fcntl.h>  #include <unistd.h> +#include <pthread.h>  #include <xf86drm.h> +#include <xf86atomic.h>  #include "omap_drm.h"  #include "omap_drmif.h" @@ -46,8 +49,23 @@  #define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)  #define PAGE_SIZE 4096 +static pthread_mutex_t table_lock = PTHREAD_MUTEX_INITIALIZER; +static void * dev_table; +  struct omap_device {  	int fd; +	atomic_t refcnt; + +	/* The handle_table is used to track GEM bo handles associated w/ +	 * this fd.  This is needed, in particular, when importing +	 * dmabuf's because we don't want multiple 'struct omap_bo's +	 * floating around with the same handle.  Otherwise, when the +	 * first one is omap_bo_del()'d the handle becomes no longer +	 * valid, and the remaining 'struct omap_bo's are left pointing +	 * to an invalid handle (and possible a GEM bo that is already +	 * free'd). +	 */ +	void *handle_table;  };  /* a GEM buffer object allocated from the DRM device */ @@ -59,19 +77,57 @@ struct omap_bo {  	uint32_t	name;		/* flink global handle (DRI2 name) */  	uint64_t	offset;		/* offset to mmap() */  	int		fd;		/* dmabuf handle */ +	atomic_t	refcnt;  }; -struct omap_device * omap_device_new(int fd) +static struct omap_device * omap_device_new_impl(int fd)  {  	struct omap_device *dev = calloc(sizeof(*dev), 1);  	if (!dev)  		return NULL;  	dev->fd = fd; +	atomic_set(&dev->refcnt, 1); +	dev->handle_table = drmHashCreate(); +	return dev; +} + +struct omap_device * omap_device_new(int fd) +{ +	struct omap_device *dev = NULL; + +	pthread_mutex_lock(&table_lock); + +	if (!dev_table) +		dev_table = drmHashCreate(); + +	if (drmHashLookup(dev_table, fd, (void **)&dev)) { +		/* not found, create new device */ +		dev = omap_device_new_impl(fd); +		drmHashInsert(dev_table, fd, dev); +	} else { +		/* found, just incr refcnt */ +		dev = omap_device_ref(dev); +	} + +	pthread_mutex_unlock(&table_lock); + +	return dev; +} + +struct omap_device * omap_device_ref(struct omap_device *dev) +{ +	atomic_inc(&dev->refcnt);  	return dev;  }  void omap_device_del(struct omap_device *dev)  { +	if (!atomic_dec_and_test(&dev->refcnt)) +		return; +	pthread_mutex_lock(&table_lock); +	drmHashDestroy(dev->handle_table); +	drmHashDelete(dev_table, dev->fd); +	pthread_mutex_unlock(&table_lock);  	free(dev);  } @@ -101,6 +157,38 @@ int omap_set_param(struct omap_device *dev, uint64_t param, uint64_t value)  	return drmCommandWrite(dev->fd, DRM_OMAP_SET_PARAM, &req, sizeof(req));  } +/* lookup a buffer from it's handle, call w/ table_lock held: */ +static struct omap_bo * lookup_bo(struct omap_device *dev, +		uint32_t handle) +{ +	struct omap_bo *bo = NULL; +	if (!drmHashLookup(dev->handle_table, handle, (void **)&bo)) { +		/* found, incr refcnt and return: */ +		bo = omap_bo_ref(bo); +	} +	return bo; +} + +/* allocate a new buffer object, call w/ table_lock held */ +static struct omap_bo * bo_from_handle(struct omap_device *dev, +		uint32_t handle) +{ +	struct omap_bo *bo = calloc(sizeof(*bo), 1); +	if (!bo) { +		struct drm_gem_close req = { +				.handle = handle, +		}; +		drmIoctl(dev->fd, DRM_IOCTL_GEM_CLOSE, &req); +		return NULL; +	} +	bo->dev = omap_device_ref(dev); +	bo->handle = handle; +	atomic_set(&bo->refcnt, 1); +	/* add ourselves to the handle table: */ +	drmHashInsert(dev->handle_table, handle, bo); +	return bo; +} +  /* allocate a new buffer object */  static struct omap_bo * omap_bo_new_impl(struct omap_device *dev,  		union omap_gem_size size, uint32_t flags) @@ -115,12 +203,13 @@ static struct omap_bo * omap_bo_new_impl(struct omap_device *dev,  		goto fail;  	} -	bo = calloc(sizeof(*bo), 1); -	if (!bo) { +	if (drmCommandWriteRead(dev->fd, DRM_OMAP_GEM_NEW, &req, sizeof(req))) {  		goto fail;  	} -	bo->dev = dev; +	pthread_mutex_lock(&table_lock); +	bo = bo_from_handle(dev, req.handle); +	pthread_mutex_unlock(&table_lock);  	if (flags & OMAP_BO_TILED) {  		bo->size = round_up(size.tiled.width, PAGE_SIZE) * size.tiled.height; @@ -128,12 +217,6 @@ static struct omap_bo * omap_bo_new_impl(struct omap_device *dev,  		bo->size = size.bytes;  	} -	if (drmCommandWriteRead(dev->fd, DRM_OMAP_GEM_NEW, &req, sizeof(req))) { -		goto fail; -	} - -	bo->handle = req.handle; -  	return bo;  fail: @@ -171,6 +254,12 @@ struct omap_bo * omap_bo_new_tiled(struct omap_device *dev,  	return omap_bo_new_impl(dev, gsize, flags);  } +struct omap_bo * omap_bo_ref(struct omap_bo *bo) +{ +	atomic_inc(&bo->refcnt); +	return bo; +} +  /* get buffer info */  static int get_buffer_info(struct omap_bo *bo)  { @@ -193,23 +282,24 @@ static int get_buffer_info(struct omap_bo *bo)  /* import a buffer object from DRI2 name */  struct omap_bo * omap_bo_from_name(struct omap_device *dev, uint32_t name)  { -	struct omap_bo *bo; +	struct omap_bo *bo = NULL;  	struct drm_gem_open req = {  			.name = name,  	}; -	bo = calloc(sizeof(*bo), 1); -	if (!bo) { -		goto fail; -	} +	pthread_mutex_lock(&table_lock);  	if (drmIoctl(dev->fd, DRM_IOCTL_GEM_OPEN, &req)) {  		goto fail;  	} -	bo->dev = dev; -	bo->name = name; -	bo->handle = req.handle; +	bo = lookup_bo(dev, req.handle); +	if (!bo) { +		bo = bo_from_handle(dev, req.handle); +		bo->name = name; +	} + +	pthread_mutex_unlock(&table_lock);  	return bo; @@ -224,24 +314,25 @@ fail:   */  struct omap_bo * omap_bo_from_dmabuf(struct omap_device *dev, int fd)  { -	struct omap_bo *bo; +	struct omap_bo *bo = NULL;  	struct drm_prime_handle req = {  			.fd = fd,  	};  	int ret; -	bo = calloc(sizeof(*bo), 1); -	if (!bo) { -		goto fail; -	} +	pthread_mutex_lock(&table_lock);  	ret = drmIoctl(dev->fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &req);  	if (ret) {  		goto fail;  	} -	bo->dev = dev; -	bo->handle = req.handle; +	bo = lookup_bo(dev, req.handle); +	if (!bo) { +		bo = bo_from_handle(dev, req.handle); +	} + +	pthread_mutex_unlock(&table_lock);  	return bo; @@ -257,6 +348,9 @@ void omap_bo_del(struct omap_bo *bo)  		return;  	} +	if (!atomic_dec_and_test(&bo->refcnt)) +		return; +  	if (bo->map) {  		munmap(bo->map, bo->size);  	} @@ -269,10 +363,14 @@ void omap_bo_del(struct omap_bo *bo)  		struct drm_gem_close req = {  				.handle = bo->handle,  		}; - +		pthread_mutex_lock(&table_lock); +		drmHashDelete(bo->dev->handle_table, bo->handle);  		drmIoctl(bo->dev->fd, DRM_IOCTL_GEM_CLOSE, &req); +		pthread_mutex_unlock(&table_lock);  	} +	omap_device_del(bo->dev); +  	free(bo);  } diff --git a/omap/omap_drmif.h b/omap/omap_drmif.h index 284b9ccc..e62d1278 100644 --- a/omap/omap_drmif.h +++ b/omap/omap_drmif.h @@ -38,6 +38,7 @@ struct omap_device;   */  struct omap_device * omap_device_new(int fd); +struct omap_device * omap_device_ref(struct omap_device *dev);  void omap_device_del(struct omap_device *dev);  int omap_get_param(struct omap_device *dev, uint64_t param, uint64_t *value);  int omap_set_param(struct omap_device *dev, uint64_t param, uint64_t value); @@ -49,6 +50,7 @@ struct omap_bo * omap_bo_new(struct omap_device *dev,  		uint32_t size, uint32_t flags);  struct omap_bo * omap_bo_new_tiled(struct omap_device *dev,  		uint32_t width, uint32_t height, uint32_t flags); +struct omap_bo * omap_bo_ref(struct omap_bo *bo);  struct omap_bo * omap_bo_from_name(struct omap_device *dev, uint32_t name);  struct omap_bo * omap_bo_from_dmabuf(struct omap_device *dev, int fd);  void omap_bo_del(struct omap_bo *bo);  | 
