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