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 25 static int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj) 26 { 27 struct drm_gem_object *obj = &rk_obj->base; 28 struct drm_device *drm = obj->dev; 29 30 init_dma_attrs(&rk_obj->dma_attrs); 31 dma_set_attr(DMA_ATTR_WRITE_COMBINE, &rk_obj->dma_attrs); 32 33 /* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */ 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 %#x 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 int rockchip_gem_mmap_buf(struct drm_gem_object *obj, 55 struct vm_area_struct *vma) 56 { 57 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 58 struct drm_device *drm = obj->dev; 59 unsigned long vm_size; 60 61 vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 62 vm_size = vma->vm_end - vma->vm_start; 63 64 if (vm_size > obj->size) 65 return -EINVAL; 66 67 return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, 68 obj->size, &rk_obj->dma_attrs); 69 } 70 71 /* drm driver mmap file operations */ 72 int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) 73 { 74 struct drm_file *priv = filp->private_data; 75 struct drm_device *dev = priv->minor->dev; 76 struct drm_gem_object *obj; 77 struct drm_vma_offset_node *node; 78 int ret; 79 80 if (drm_device_is_unplugged(dev)) 81 return -ENODEV; 82 83 mutex_lock(&dev->struct_mutex); 84 85 node = drm_vma_offset_exact_lookup(dev->vma_offset_manager, 86 vma->vm_pgoff, 87 vma_pages(vma)); 88 if (!node) { 89 mutex_unlock(&dev->struct_mutex); 90 DRM_ERROR("failed to find vma node.\n"); 91 return -EINVAL; 92 } else if (!drm_vma_node_is_allowed(node, filp)) { 93 mutex_unlock(&dev->struct_mutex); 94 return -EACCES; 95 } 96 97 obj = container_of(node, struct drm_gem_object, vma_node); 98 ret = rockchip_gem_mmap_buf(obj, vma); 99 100 mutex_unlock(&dev->struct_mutex); 101 102 return ret; 103 } 104 105 struct rockchip_gem_object * 106 rockchip_gem_create_object(struct drm_device *drm, unsigned int size) 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); 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); 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 mutex_lock(&dev->struct_mutex); 199 200 obj = drm_gem_object_lookup(dev, file_priv, handle); 201 if (!obj) { 202 DRM_ERROR("failed to lookup gem object.\n"); 203 ret = -EINVAL; 204 goto unlock; 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 214 out: 215 drm_gem_object_unreference(obj); 216 unlock: 217 mutex_unlock(&dev->struct_mutex); 218 return ret; 219 } 220 221 /* 222 * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback 223 * function 224 * 225 * This aligns the pitch and size arguments to the minimum required. wrap 226 * this into your own function if you need bigger alignment. 227 */ 228 int rockchip_gem_dumb_create(struct drm_file *file_priv, 229 struct drm_device *dev, 230 struct drm_mode_create_dumb *args) 231 { 232 struct rockchip_gem_object *rk_obj; 233 int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 234 235 /* 236 * align to 64 bytes since Mali requires it. 237 */ 238 min_pitch = ALIGN(min_pitch, 64); 239 240 if (args->pitch < min_pitch) 241 args->pitch = min_pitch; 242 243 if (args->size < args->pitch * args->height) 244 args->size = args->pitch * args->height; 245 246 rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, 247 &args->handle); 248 249 return PTR_ERR_OR_ZERO(rk_obj); 250 } 251 252 /* 253 * Allocate a sg_table for this GEM object. 254 * Note: Both the table's contents, and the sg_table itself must be freed by 255 * the caller. 256 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. 257 */ 258 struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) 259 { 260 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 261 struct drm_device *drm = obj->dev; 262 struct sg_table *sgt; 263 int ret; 264 265 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 266 if (!sgt) 267 return ERR_PTR(-ENOMEM); 268 269 ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr, 270 rk_obj->dma_addr, obj->size, 271 &rk_obj->dma_attrs); 272 if (ret) { 273 DRM_ERROR("failed to allocate sgt, %d\n", ret); 274 kfree(sgt); 275 return ERR_PTR(ret); 276 } 277 278 return sgt; 279 } 280 281 void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) 282 { 283 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 284 285 return rk_obj->kvaddr; 286 } 287 288 void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) 289 { 290 /* Nothing to do */ 291 } 292