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 (IS_ERR(rk_obj->kvaddr)) { 38 int ret = PTR_ERR(rk_obj->kvaddr); 39 40 DRM_ERROR("failed to allocate %#x byte dma buffer, %d", 41 obj->size, ret); 42 return ret; 43 } 44 45 return 0; 46 } 47 48 static 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 57 int rockchip_gem_mmap_buf(struct drm_gem_object *obj, 58 struct vm_area_struct *vma) 59 { 60 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 61 struct drm_device *drm = obj->dev; 62 unsigned long vm_size; 63 64 vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 65 vm_size = vma->vm_end - vma->vm_start; 66 67 if (vm_size > obj->size) 68 return -EINVAL; 69 70 return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr, 71 obj->size, &rk_obj->dma_attrs); 72 } 73 74 /* drm driver mmap file operations */ 75 int rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma) 76 { 77 struct drm_file *priv = filp->private_data; 78 struct drm_device *dev = priv->minor->dev; 79 struct drm_gem_object *obj; 80 struct drm_vma_offset_node *node; 81 int ret; 82 83 if (drm_device_is_unplugged(dev)) 84 return -ENODEV; 85 86 mutex_lock(&dev->struct_mutex); 87 88 node = drm_vma_offset_exact_lookup(dev->vma_offset_manager, 89 vma->vm_pgoff, 90 vma_pages(vma)); 91 if (!node) { 92 mutex_unlock(&dev->struct_mutex); 93 DRM_ERROR("failed to find vma node.\n"); 94 return -EINVAL; 95 } else if (!drm_vma_node_is_allowed(node, filp)) { 96 mutex_unlock(&dev->struct_mutex); 97 return -EACCES; 98 } 99 100 obj = container_of(node, struct drm_gem_object, vma_node); 101 ret = rockchip_gem_mmap_buf(obj, vma); 102 103 mutex_unlock(&dev->struct_mutex); 104 105 return ret; 106 } 107 108 struct rockchip_gem_object * 109 rockchip_gem_create_object(struct drm_device *drm, unsigned int size) 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); 126 if (ret) 127 goto err_free_rk_obj; 128 129 return rk_obj; 130 131 err_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 */ 140 void 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 */ 160 static struct rockchip_gem_object * 161 rockchip_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); 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 188 err_handle_create: 189 rockchip_gem_free_object(obj); 190 191 return ERR_PTR(ret); 192 } 193 194 int 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 mutex_lock(&dev->struct_mutex); 202 203 obj = drm_gem_object_lookup(dev, file_priv, handle); 204 if (!obj) { 205 DRM_ERROR("failed to lookup gem object.\n"); 206 ret = -EINVAL; 207 goto unlock; 208 } 209 210 ret = drm_gem_create_mmap_offset(obj); 211 if (ret) 212 goto out; 213 214 *offset = drm_vma_node_offset_addr(&obj->vma_node); 215 DRM_DEBUG_KMS("offset = 0x%llx\n", *offset); 216 217 out: 218 drm_gem_object_unreference(obj); 219 unlock: 220 mutex_unlock(&dev->struct_mutex); 221 return ret; 222 } 223 224 /* 225 * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback 226 * function 227 * 228 * This aligns the pitch and size arguments to the minimum required. wrap 229 * this into your own function if you need bigger alignment. 230 */ 231 int rockchip_gem_dumb_create(struct drm_file *file_priv, 232 struct drm_device *dev, 233 struct drm_mode_create_dumb *args) 234 { 235 struct rockchip_gem_object *rk_obj; 236 int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 237 238 /* 239 * align to 64 bytes since Mali requires it. 240 */ 241 min_pitch = ALIGN(min_pitch, 64); 242 243 if (args->pitch < min_pitch) 244 args->pitch = min_pitch; 245 246 if (args->size < args->pitch * args->height) 247 args->size = args->pitch * args->height; 248 249 rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size, 250 &args->handle); 251 252 return PTR_ERR_OR_ZERO(rk_obj); 253 } 254 255 /* 256 * Allocate a sg_table for this GEM object. 257 * Note: Both the table's contents, and the sg_table itself must be freed by 258 * the caller. 259 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. 260 */ 261 struct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj) 262 { 263 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 264 struct drm_device *drm = obj->dev; 265 struct sg_table *sgt; 266 int ret; 267 268 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 269 if (!sgt) 270 return ERR_PTR(-ENOMEM); 271 272 ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr, 273 rk_obj->dma_addr, obj->size, 274 &rk_obj->dma_attrs); 275 if (ret) { 276 DRM_ERROR("failed to allocate sgt, %d\n", ret); 277 kfree(sgt); 278 return ERR_PTR(ret); 279 } 280 281 return sgt; 282 } 283 284 void *rockchip_gem_prime_vmap(struct drm_gem_object *obj) 285 { 286 struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj); 287 288 return rk_obj->kvaddr; 289 } 290 291 void rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) 292 { 293 /* Nothing to do */ 294 } 295