rockchip_drm_gem.c 6.6 KB
Newer Older
M
Mark Yao 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
 * Author:Mark Yao <mark.yao@rock-chips.com>
 *
 * This software is licensed under the terms of the GNU General Public
 * License version 2, as published by the Free Software Foundation, and
 * may be copied, distributed, and modified under those terms.
 *
 * 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.
 */

#include <drm/drm.h>
#include <drm/drmP.h>
#include <drm/drm_gem.h>
#include <drm/drm_vma_manager.h>

#include "rockchip_drm_drv.h"
#include "rockchip_drm_gem.h"

23 24
static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
				  bool alloc_kmap)
M
Mark Yao 已提交
25 26 27 28
{
	struct drm_gem_object *obj = &rk_obj->base;
	struct drm_device *drm = obj->dev;

29
	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
M
Mark Yao 已提交
30

31
	if (!alloc_kmap)
32
		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
33

M
Mark Yao 已提交
34 35
	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
					 &rk_obj->dma_addr, GFP_KERNEL,
36
					 rk_obj->dma_attrs);
37
	if (!rk_obj->kvaddr) {
38
		DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
39
		return -ENOMEM;
M
Mark Yao 已提交
40 41 42 43 44 45 46 47 48 49 50
	}

	return 0;
}

static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
{
	struct drm_gem_object *obj = &rk_obj->base;
	struct drm_device *drm = obj->dev;

	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
51
		       rk_obj->dma_attrs);
M
Mark Yao 已提交
52 53
}

54 55 56
static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
					struct vm_area_struct *vma)

M
Mark Yao 已提交
57
{
58
	int ret;
M
Mark Yao 已提交
59 60 61
	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
	struct drm_device *drm = obj->dev;

62 63 64 65 66
	/*
	 * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear
	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
	 */
	vma->vm_flags &= ~VM_PFNMAP;
67
	vma->vm_pgoff = 0;
M
Mark Yao 已提交
68

69
	ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
70
			     obj->size, rk_obj->dma_attrs);
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
	if (ret)
		drm_gem_vm_close(vma);

	return ret;
}

int rockchip_gem_mmap_buf(struct drm_gem_object *obj,
			  struct vm_area_struct *vma)
{
	int ret;

	ret = drm_gem_mmap_obj(obj, obj->size, vma);
	if (ret)
		return ret;

	return rockchip_drm_gem_object_mmap(obj, vma);
M
Mark Yao 已提交
87 88 89 90 91 92 93 94
}

/* drm driver mmap file operations */
int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
	struct drm_gem_object *obj;
	int ret;

95 96 97
	ret = drm_gem_mmap(filp, vma);
	if (ret)
		return ret;
M
Mark Yao 已提交
98

99
	obj = vma->vm_private_data;
M
Mark Yao 已提交
100

101
	return rockchip_drm_gem_object_mmap(obj, vma);
M
Mark Yao 已提交
102 103 104
}

struct rockchip_gem_object *
105 106
	rockchip_gem_create_object(struct drm_device *drm, unsigned int size,
				   bool alloc_kmap)
M
Mark Yao 已提交
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
{
	struct rockchip_gem_object *rk_obj;
	struct drm_gem_object *obj;
	int ret;

	size = round_up(size, PAGE_SIZE);

	rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
	if (!rk_obj)
		return ERR_PTR(-ENOMEM);

	obj = &rk_obj->base;

	drm_gem_private_object_init(drm, obj, size);

122
	ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
M
Mark Yao 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
	if (ret)
		goto err_free_rk_obj;

	return rk_obj;

err_free_rk_obj:
	kfree(rk_obj);
	return ERR_PTR(ret);
}

/*
 * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback
 * function
 */
void rockchip_gem_free_object(struct drm_gem_object *obj)
{
	struct rockchip_gem_object *rk_obj;

	drm_gem_free_mmap_offset(obj);

	rk_obj = to_rockchip_obj(obj);

	rockchip_gem_free_buf(rk_obj);

	kfree(rk_obj);
}

/*
 * rockchip_gem_create_with_handle - allocate an object with the given
 * size and create a gem handle on it
 *
 * returns a struct rockchip_gem_object* on success or ERR_PTR values
 * on failure.
 */
static struct rockchip_gem_object *
rockchip_gem_create_with_handle(struct drm_file *file_priv,
				struct drm_device *drm, unsigned int size,
				unsigned int *handle)
{
	struct rockchip_gem_object *rk_obj;
	struct drm_gem_object *obj;
	int ret;

166
	rk_obj = rockchip_gem_create_object(drm, size, false);
M
Mark Yao 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	if (IS_ERR(rk_obj))
		return ERR_CAST(rk_obj);

	obj = &rk_obj->base;

	/*
	 * allocate a id of idr table where the obj is registered
	 * and handle has the id what user can see.
	 */
	ret = drm_gem_handle_create(file_priv, obj, handle);
	if (ret)
		goto err_handle_create;

	/* drop reference from allocate - handle holds it now. */
	drm_gem_object_unreference_unlocked(obj);

	return rk_obj;

err_handle_create:
	rockchip_gem_free_object(obj);

	return ERR_PTR(ret);
}

int rockchip_gem_dumb_map_offset(struct drm_file *file_priv,
				 struct drm_device *dev, uint32_t handle,
				 uint64_t *offset)
{
	struct drm_gem_object *obj;
	int ret;

198
	obj = drm_gem_object_lookup(file_priv, handle);
M
Mark Yao 已提交
199 200
	if (!obj) {
		DRM_ERROR("failed to lookup gem object.\n");
201
		return -EINVAL;
M
Mark Yao 已提交
202 203 204 205 206 207 208 209 210 211
	}

	ret = drm_gem_create_mmap_offset(obj);
	if (ret)
		goto out;

	*offset = drm_vma_node_offset_addr(&obj->vma_node);
	DRM_DEBUG_KMS("offset = 0x%llx\n", *offset);

out:
212 213 214
	drm_gem_object_unreference_unlocked(obj);

	return 0;
M
Mark Yao 已提交
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
}

/*
 * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
 * function
 *
 * This aligns the pitch and size arguments to the minimum required. wrap
 * this into your own function if you need bigger alignment.
 */
int rockchip_gem_dumb_create(struct drm_file *file_priv,
			     struct drm_device *dev,
			     struct drm_mode_create_dumb *args)
{
	struct rockchip_gem_object *rk_obj;
	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);

	/*
	 * align to 64 bytes since Mali requires it.
	 */
234 235
	args->pitch = ALIGN(min_pitch, 64);
	args->size = args->pitch * args->height;
M
Mark Yao 已提交
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261

	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
						 &args->handle);

	return PTR_ERR_OR_ZERO(rk_obj);
}

/*
 * Allocate a sg_table for this GEM object.
 * Note: Both the table's contents, and the sg_table itself must be freed by
 *       the caller.
 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
 */
struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
{
	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
	struct drm_device *drm = obj->dev;
	struct sg_table *sgt;
	int ret;

	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
	if (!sgt)
		return ERR_PTR(-ENOMEM);

	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
				    rk_obj->dma_addr, obj->size,
262
				    rk_obj->dma_attrs);
M
Mark Yao 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275
	if (ret) {
		DRM_ERROR("failed to allocate sgt, %d\n", ret);
		kfree(sgt);
		return ERR_PTR(ret);
	}

	return sgt;
}

void *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
{
	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);

276
	if (rk_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
277 278
		return NULL;

M
Mark Yao 已提交
279 280 281 282 283 284 285
	return rk_obj->kvaddr;
}

void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
{
	/* Nothing to do */
}