1/* 2 * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd 3 * Author:Mark Yao <mark.yao@rock-chips.com> 4 * 5 * This software is licensed under the terms of the GNU General Public 6 * License version 2, as published by the Free Software Foundation, and 7 * may be copied, distributed, and modified under those terms. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 */ 14 15#include <drm/drm.h> 16#include <drm/drmP.h> 17#include <drm/drm_gem.h> 18#include <drm/drm_vma_manager.h> 19 20#include <linux/dma-attrs.h> 21 22#include "rockchip_drm_drv.h" 23#include "rockchip_drm_gem.h" 24 25static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj, 26 bool alloc_kmap) 27{ 28 struct drm_gem_object *obj = &rk_obj->base; 29 struct drm_device *drm = obj->dev; 30 31 init_dma_attrs(&rk_obj->dma_attrs); 32 dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs); 33 34 if (!alloc_kmap) 35 dma_set_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs); 36 37 rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size, 38 &rk_obj->dma_addr, GFP_KERNEL, 39 &rk_obj->dma_attrs); 40 if (!rk_obj->kvaddr) { 41 DRM_ERROR("failed to allocate %#x byte dma buffer", obj->size); 42 return -ENOMEM; 43 } 44 45 return 0; 46} 47 48static void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj) 49{ 50 struct drm_gem_object *obj = &rk_obj->base; 51 struct drm_device *drm = obj->dev; 52 53 dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr, 54 &rk_obj->dma_attrs); 55} 56 57static int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj, 58 struct vm_area_struct *vma) 59 60{ 61 int ret; 62 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 63 struct drm_device *drm = obj->dev; 64 65 /* 66 * dma_alloc_attrs() allocated a struct page table for rk_obj, so clear 67 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). 68 */ 69 vma->vm_flags &= ~VM_PFNMAP; 70 vma->vm_pgoff = 0; 71 72 ret = dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, 73 obj->size, &rk_obj->dma_attrs); 74 if (ret) 75 drm_gem_vm_close(vma); 76 77 return ret; 78} 79 80int rockchip_gem_mmap_buf(struct drm_gem_object *obj, 81 struct vm_area_struct *vma) 82{ 83 int ret; 84 85 ret = drm_gem_mmap_obj(obj, obj->size, vma); 86 if (ret) 87 return ret; 88 89 return rockchip_drm_gem_object_mmap(obj, vma); 90} 91 92/* drm driver mmap file operations */ 93int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) 94{ 95 struct drm_gem_object *obj; 96 int ret; 97 98 ret = drm_gem_mmap(filp, vma); 99 if (ret) 100 return ret; 101 102 obj = vma->vm_private_data; 103 104 return rockchip_drm_gem_object_mmap(obj, vma); 105} 106 107struct rockchip_gem_object * 108 rockchip_gem_create_object(struct drm_device *drm, unsigned int size, 109 bool alloc_kmap) 110{ 111 struct rockchip_gem_object *rk_obj; 112 struct drm_gem_object *obj; 113 int ret; 114 115 size = round_up(size, PAGE_SIZE); 116 117 rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL); 118 if (!rk_obj) 119 return ERR_PTR(-ENOMEM); 120 121 obj = &rk_obj->base; 122 123 drm_gem_private_object_init(drm, obj, size); 124 125 ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap); 126 if (ret) 127 goto err_free_rk_obj; 128 129 return rk_obj; 130 131err_free_rk_obj: 132 kfree(rk_obj); 133 return ERR_PTR(ret); 134} 135 136/* 137 * rockchip_gem_free_object - (struct drm_driver)->gem_free_object callback 138 * function 139 */ 140void rockchip_gem_free_object(struct drm_gem_object *obj) 141{ 142 struct rockchip_gem_object *rk_obj; 143 144 drm_gem_free_mmap_offset(obj); 145 146 rk_obj = to_rockchip_obj(obj); 147 148 rockchip_gem_free_buf(rk_obj); 149 150 kfree(rk_obj); 151} 152 153/* 154 * rockchip_gem_create_with_handle - allocate an object with the given 155 * size and create a gem handle on it 156 * 157 * returns a struct rockchip_gem_object* on success or ERR_PTR values 158 * on failure. 159 */ 160static struct rockchip_gem_object * 161rockchip_gem_create_with_handle(struct drm_file *file_priv, 162 struct drm_device *drm, unsigned int size, 163 unsigned int *handle) 164{ 165 struct rockchip_gem_object *rk_obj; 166 struct drm_gem_object *obj; 167 int ret; 168 169 rk_obj = rockchip_gem_create_object(drm, size, false); 170 if (IS_ERR(rk_obj)) 171 return ERR_CAST(rk_obj); 172 173 obj = &rk_obj->base; 174 175 /* 176 * allocate a id of idr table where the obj is registered 177 * and handle has the id what user can see. 178 */ 179 ret = drm_gem_handle_create(file_priv, obj, handle); 180 if (ret) 181 goto err_handle_create; 182 183 /* drop reference from allocate - handle holds it now. */ 184 drm_gem_object_unreference_unlocked(obj); 185 186 return rk_obj; 187 188err_handle_create: 189 rockchip_gem_free_object(obj); 190 191 return ERR_PTR(ret); 192} 193 194int rockchip_gem_dumb_map_offset(struct drm_file *file_priv, 195 struct drm_device *dev, uint32_t handle, 196 uint64_t *offset) 197{ 198 struct drm_gem_object *obj; 199 int ret; 200 201 obj = drm_gem_object_lookup(dev, file_priv, handle); 202 if (!obj) { 203 DRM_ERROR("failed to lookup gem object.\n"); 204 return -EINVAL; 205 } 206 207 ret = drm_gem_create_mmap_offset(obj); 208 if (ret) 209 goto out; 210 211 *offset = drm_vma_node_offset_addr(&obj->vma_node); 212 DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); 213 214out: 215 drm_gem_object_unreference_unlocked(obj); 216 217 return 0; 218} 219 220/* 221 * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback 222 * function 223 * 224 * This aligns the pitch and size arguments to the minimum required. wrap 225 * this into your own function if you need bigger alignment. 226 */ 227int rockchip_gem_dumb_create(struct drm_file *file_priv, 228 struct drm_device *dev, 229 struct drm_mode_create_dumb *args) 230{ 231 struct rockchip_gem_object *rk_obj; 232 int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 233 234 /* 235 * align to 64 bytes since Mali requires it. 236 */ 237 min_pitch = ALIGN(min_pitch, 64); 238 239 if (args->pitch < min_pitch) 240 args->pitch = min_pitch; 241 242 if (args->size < args->pitch * args->height) 243 args->size = args->pitch * args->height; 244 245 rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, 246 &args->handle); 247 248 return PTR_ERR_OR_ZERO(rk_obj); 249} 250 251/* 252 * Allocate a sg_table for this GEM object. 253 * Note: Both the table's contents, and the sg_table itself must be freed by 254 * the caller. 255 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. 256 */ 257struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) 258{ 259 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 260 struct drm_device *drm = obj->dev; 261 struct sg_table *sgt; 262 int ret; 263 264 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 265 if (!sgt) 266 return ERR_PTR(-ENOMEM); 267 268 ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr, 269 rk_obj->dma_addr, obj->size, 270 &rk_obj->dma_attrs); 271 if (ret) { 272 DRM_ERROR("failed to allocate sgt, %d\n", ret); 273 kfree(sgt); 274 return ERR_PTR(ret); 275 } 276 277 return sgt; 278} 279 280void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) 281{ 282 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 283 284 if (dma_get_attr(DMA_ATTR_NO_KERNEL_MAPPING, &rk_obj->dma_attrs)) 285 return NULL; 286 287 return rk_obj->kvaddr; 288} 289 290void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) 291{ 292 /* Nothing to do */ 293} 294