minigbm: More sophisticated gbm_bo_map/gbm_bo_unmap

We previously added the gbm_bo_map/gbm_bo_unmap (see CL:393927)
entry points since we wanted to do driver-specific detiling during
screenshot capture tests.  We ignored most the parameters and mapped
the entire buffer.  This CL adds the ability to:

1) Return the starting address within a byte given a specific x, y
   in the buffer.

2) Handle the case where there are more than one kernel buffers
   per buffer object.  Currently, only the Exynos driver would use
   this capability.

BUG=chromium:653284
TEST=Ran cros_gralloc with modified code

CQ-DEPEND=CL:393927

Change-Id: I19d75d2f16489c0184e96305fb643f18477e1cdb
Reviewed-on: https://chromium-review.googlesource.com/395066
Commit-Ready: Gurchetan Singh <gurchetansingh@chromium.org>
Tested-by: Gurchetan Singh <gurchetansingh@chromium.org>
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
diff --git a/drv.c b/drv.c
index f2376a2..fdae850 100644
--- a/drv.c
+++ b/drv.c
@@ -106,31 +106,37 @@
 	drv->fd = fd;
 	drv->backend = drv_get_backend(fd);
 
-	if (!drv->backend) {
-		free(drv);
-		return NULL;
-	}
+	if (!drv->backend)
+		goto free_driver;
 
-	if (pthread_mutex_init(&drv->table_lock, NULL)) {
-		free(drv);
-		return NULL;
-	}
+	if (pthread_mutex_init(&drv->table_lock, NULL))
+		goto free_driver;
 
 	drv->buffer_table = drmHashCreate();
-	if (!drv->buffer_table) {
-		free(drv);
-		return NULL;
-	}
+	if (!drv->buffer_table)
+		goto free_lock;
+
+	drv->map_table = drmHashCreate();
+	if (!drv->map_table)
+		goto free_buffer_table;
 
 	if (drv->backend->init) {
 		ret = drv->backend->init(drv);
-		if (ret) {
-			free(drv);
-			return NULL;
-		}
+		if (ret)
+			goto free_map_table;
 	}
 
 	return drv;
+
+free_map_table:
+	drmHashDestroy(drv->map_table);
+free_buffer_table:
+	drmHashDestroy(drv->buffer_table);
+free_lock:
+	pthread_mutex_destroy(&drv->table_lock);
+free_driver:
+	free(drv);
+	return NULL;
 }
 
 void drv_destroy(struct driver *drv)
@@ -140,6 +146,7 @@
 
 	pthread_mutex_destroy(&drv->table_lock);
 	drmHashDestroy(drv->buffer_table);
+	drmHashDestroy(drv->map_table);
 
 	free(drv);
 }
@@ -251,32 +258,6 @@
 	free(bo);
 }
 
-void *
-drv_bo_map(struct bo *bo)
-{
-	assert(drv_num_buffers_per_bo(bo) == 1);
-
-	if (!bo->map_data || bo->map_data == MAP_FAILED)
-		bo->map_data = bo->drv->backend->bo_map(bo);
-
-	return bo->map_data;
-}
-
-int
-drv_bo_unmap(struct bo *bo)
-{
-	int ret = -1;
-
-	assert(drv_num_buffers_per_bo(bo) == 1);
-
-	if (bo->map_data && bo->map_data != MAP_FAILED)
-		ret = munmap(bo->map_data, bo->total_size);
-
-	bo->map_data = NULL;
-
-	return ret;
-}
-
 struct bo *drv_bo_import(struct driver *drv, struct drv_import_fd_data *data)
 {
 	int ret;
@@ -324,6 +305,74 @@
 	return bo;
 }
 
+void *drv_bo_map(struct bo *bo, uint32_t x, uint32_t y, uint32_t width,
+		 uint32_t height, uint32_t flags, void **map_data, size_t plane)
+{
+	void *ptr;
+	uint8_t *addr;
+	size_t offset;
+	struct map_info *data;
+
+	assert(width > 0);
+	assert(height > 0);
+	assert(x + width <= drv_bo_get_width(bo));
+	assert(y + height <= drv_bo_get_height(bo));
+
+	pthread_mutex_lock(&bo->drv->table_lock);
+
+	if (!drmHashLookup(bo->drv->map_table, bo->handles[plane].u32, &ptr)) {
+		data = (struct map_info *) ptr;
+		data->refcount++;
+		goto success;
+	}
+
+	data = calloc(1, sizeof(*data));
+	addr = bo->drv->backend->bo_map(bo, data, plane);
+	if (addr == MAP_FAILED) {
+		*map_data = NULL;
+		free(data);
+		pthread_mutex_unlock(&bo->drv->table_lock);
+		return MAP_FAILED;
+	}
+
+	data->refcount = 1;
+	data->addr = addr;
+	data->handle = bo->handles[plane].u32;
+	drmHashInsert(bo->drv->buffer_table, bo->handles[plane].u32,
+		      (void *) data);
+
+success:
+	*map_data = (void *) data;
+	offset = drv_bo_get_plane_stride(bo, plane) * y;
+	offset += drv_stride_from_format(bo->format, x, plane);
+	addr = (uint8_t *) data->addr;
+	addr += drv_bo_get_plane_offset(bo, plane) + offset;
+	pthread_mutex_unlock(&bo->drv->table_lock);
+
+	return (void *) addr;
+}
+
+int drv_bo_unmap(struct bo *bo, void *map_data)
+{
+	struct map_info *data = map_data;
+	int ret = 0;
+
+	assert(data);
+	assert(data->refcount >= 0);
+
+	pthread_mutex_lock(&bo->drv->table_lock);
+
+	if (!--data->refcount) {
+		ret = munmap(data->addr, data->length);
+		drmHashDelete(bo->drv->map_table, data->handle);
+		free(data);
+	}
+
+	pthread_mutex_unlock(&bo->drv->table_lock);
+
+	return ret;
+}
+
 uint32_t drv_bo_get_width(struct bo *bo)
 {
 	return bo->width;