1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2015 MediaTek Inc. 4 */ 5 6 #include <linux/dma-buf.h> 7 8 #include <drm/drm.h> 9 #include <drm/drm_device.h> 10 #include <drm/drm_gem.h> 11 #include <drm/drm_gem_cma_helper.h> 12 #include <drm/drm_prime.h> 13 14 #include "mtk_drm_drv.h" 15 #include "mtk_drm_gem.h" 16 17 static const struct drm_gem_object_funcs mtk_drm_gem_object_funcs = { 18 .free = mtk_drm_gem_free_object, 19 .get_sg_table = mtk_gem_prime_get_sg_table, 20 .vmap = mtk_drm_gem_prime_vmap, 21 .vunmap = mtk_drm_gem_prime_vunmap, 22 .vm_ops = &drm_gem_cma_vm_ops, 23 }; 24 25 static struct mtk_drm_gem_obj *mtk_drm_gem_init(struct drm_device *dev, 26 unsigned long size) 27 { 28 struct mtk_drm_gem_obj *mtk_gem_obj; 29 int ret; 30 31 size = round_up(size, PAGE_SIZE); 32 33 mtk_gem_obj = kzalloc(sizeof(*mtk_gem_obj), GFP_KERNEL); 34 if (!mtk_gem_obj) 35 return ERR_PTR(-ENOMEM); 36 37 mtk_gem_obj->base.funcs = &mtk_drm_gem_object_funcs; 38 39 ret = drm_gem_object_init(dev, &mtk_gem_obj->base, size); 40 if (ret < 0) { 41 DRM_ERROR("failed to initialize gem object\n"); 42 kfree(mtk_gem_obj); 43 return ERR_PTR(ret); 44 } 45 46 return mtk_gem_obj; 47 } 48 49 struct mtk_drm_gem_obj *mtk_drm_gem_create(struct drm_device *dev, 50 size_t size, bool alloc_kmap) 51 { 52 struct mtk_drm_private *priv = dev->dev_private; 53 struct mtk_drm_gem_obj *mtk_gem; 54 struct drm_gem_object *obj; 55 int ret; 56 57 mtk_gem = mtk_drm_gem_init(dev, size); 58 if (IS_ERR(mtk_gem)) 59 return ERR_CAST(mtk_gem); 60 61 obj = &mtk_gem->base; 62 63 mtk_gem->dma_attrs = DMA_ATTR_WRITE_COMBINE; 64 65 if (!alloc_kmap) 66 mtk_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; 67 68 mtk_gem->cookie = dma_alloc_attrs(priv->dma_dev, obj->size, 69 &mtk_gem->dma_addr, GFP_KERNEL, 70 mtk_gem->dma_attrs); 71 if (!mtk_gem->cookie) { 72 DRM_ERROR("failed to allocate %zx byte dma buffer", obj->size); 73 ret = -ENOMEM; 74 goto err_gem_free; 75 } 76 77 if (alloc_kmap) 78 mtk_gem->kvaddr = mtk_gem->cookie; 79 80 DRM_DEBUG_DRIVER("cookie = %p dma_addr = %pad size = %zu\n", 81 mtk_gem->cookie, &mtk_gem->dma_addr, 82 size); 83 84 return mtk_gem; 85 86 err_gem_free: 87 drm_gem_object_release(obj); 88 kfree(mtk_gem); 89 return ERR_PTR(ret); 90 } 91 92 void mtk_drm_gem_free_object(struct drm_gem_object *obj) 93 { 94 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); 95 struct mtk_drm_private *priv = obj->dev->dev_private; 96 97 if (mtk_gem->sg) 98 drm_prime_gem_destroy(obj, mtk_gem->sg); 99 else 100 dma_free_attrs(priv->dma_dev, obj->size, mtk_gem->cookie, 101 mtk_gem->dma_addr, mtk_gem->dma_attrs); 102 103 /* release file pointer to gem object. */ 104 drm_gem_object_release(obj); 105 106 kfree(mtk_gem); 107 } 108 109 int mtk_drm_gem_dumb_create(struct drm_file *file_priv, struct drm_device *dev, 110 struct drm_mode_create_dumb *args) 111 { 112 struct mtk_drm_gem_obj *mtk_gem; 113 int ret; 114 115 args->pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 116 args->size = args->pitch * args->height; 117 118 mtk_gem = mtk_drm_gem_create(dev, args->size, false); 119 if (IS_ERR(mtk_gem)) 120 return PTR_ERR(mtk_gem); 121 122 /* 123 * allocate a id of idr table where the obj is registered 124 * and handle has the id what user can see. 125 */ 126 ret = drm_gem_handle_create(file_priv, &mtk_gem->base, &args->handle); 127 if (ret) 128 goto err_handle_create; 129 130 /* drop reference from allocate - handle holds it now. */ 131 drm_gem_object_put(&mtk_gem->base); 132 133 return 0; 134 135 err_handle_create: 136 mtk_drm_gem_free_object(&mtk_gem->base); 137 return ret; 138 } 139 140 static int mtk_drm_gem_object_mmap(struct drm_gem_object *obj, 141 struct vm_area_struct *vma) 142 143 { 144 int ret; 145 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); 146 struct mtk_drm_private *priv = obj->dev->dev_private; 147 148 /* 149 * dma_alloc_attrs() allocated a struct page table for mtk_gem, so clear 150 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap(). 151 */ 152 vma->vm_flags &= ~VM_PFNMAP; 153 154 ret = dma_mmap_attrs(priv->dma_dev, vma, mtk_gem->cookie, 155 mtk_gem->dma_addr, obj->size, mtk_gem->dma_attrs); 156 if (ret) 157 drm_gem_vm_close(vma); 158 159 return ret; 160 } 161 162 int mtk_drm_gem_mmap_buf(struct drm_gem_object *obj, struct vm_area_struct *vma) 163 { 164 int ret; 165 166 ret = drm_gem_mmap_obj(obj, obj->size, vma); 167 if (ret) 168 return ret; 169 170 return mtk_drm_gem_object_mmap(obj, vma); 171 } 172 173 int mtk_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) 174 { 175 struct drm_gem_object *obj; 176 int ret; 177 178 ret = drm_gem_mmap(filp, vma); 179 if (ret) 180 return ret; 181 182 obj = vma->vm_private_data; 183 184 /* 185 * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the 186 * whole buffer from the start. 187 */ 188 vma->vm_pgoff = 0; 189 190 return mtk_drm_gem_object_mmap(obj, vma); 191 } 192 193 /* 194 * Allocate a sg_table for this GEM object. 195 * Note: Both the table's contents, and the sg_table itself must be freed by 196 * the caller. 197 * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error. 198 */ 199 struct sg_table *mtk_gem_prime_get_sg_table(struct drm_gem_object *obj) 200 { 201 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); 202 struct mtk_drm_private *priv = obj->dev->dev_private; 203 struct sg_table *sgt; 204 int ret; 205 206 sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 207 if (!sgt) 208 return ERR_PTR(-ENOMEM); 209 210 ret = dma_get_sgtable_attrs(priv->dma_dev, sgt, mtk_gem->cookie, 211 mtk_gem->dma_addr, obj->size, 212 mtk_gem->dma_attrs); 213 if (ret) { 214 DRM_ERROR("failed to allocate sgt, %d\n", ret); 215 kfree(sgt); 216 return ERR_PTR(ret); 217 } 218 219 return sgt; 220 } 221 222 struct drm_gem_object *mtk_gem_prime_import_sg_table(struct drm_device *dev, 223 struct dma_buf_attachment *attach, struct sg_table *sg) 224 { 225 struct mtk_drm_gem_obj *mtk_gem; 226 int ret; 227 struct scatterlist *s; 228 unsigned int i; 229 dma_addr_t expected; 230 231 mtk_gem = mtk_drm_gem_init(dev, attach->dmabuf->size); 232 233 if (IS_ERR(mtk_gem)) 234 return ERR_CAST(mtk_gem); 235 236 expected = sg_dma_address(sg->sgl); 237 for_each_sg(sg->sgl, s, sg->nents, i) { 238 if (!sg_dma_len(s)) 239 break; 240 241 if (sg_dma_address(s) != expected) { 242 DRM_ERROR("sg_table is not contiguous"); 243 ret = -EINVAL; 244 goto err_gem_free; 245 } 246 expected = sg_dma_address(s) + sg_dma_len(s); 247 } 248 249 mtk_gem->dma_addr = sg_dma_address(sg->sgl); 250 mtk_gem->sg = sg; 251 252 return &mtk_gem->base; 253 254 err_gem_free: 255 kfree(mtk_gem); 256 return ERR_PTR(ret); 257 } 258 259 void *mtk_drm_gem_prime_vmap(struct drm_gem_object *obj) 260 { 261 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); 262 struct sg_table *sgt; 263 struct sg_page_iter iter; 264 unsigned int npages; 265 unsigned int i = 0; 266 267 if (mtk_gem->kvaddr) 268 return mtk_gem->kvaddr; 269 270 sgt = mtk_gem_prime_get_sg_table(obj); 271 if (IS_ERR(sgt)) 272 return NULL; 273 274 npages = obj->size >> PAGE_SHIFT; 275 mtk_gem->pages = kcalloc(npages, sizeof(*mtk_gem->pages), GFP_KERNEL); 276 if (!mtk_gem->pages) 277 goto out; 278 279 for_each_sg_page(sgt->sgl, &iter, sgt->orig_nents, 0) { 280 mtk_gem->pages[i++] = sg_page_iter_page(&iter); 281 if (i > npages) 282 break; 283 } 284 mtk_gem->kvaddr = vmap(mtk_gem->pages, npages, VM_MAP, 285 pgprot_writecombine(PAGE_KERNEL)); 286 287 out: 288 kfree(sgt); 289 290 return mtk_gem->kvaddr; 291 } 292 293 void mtk_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) 294 { 295 struct mtk_drm_gem_obj *mtk_gem = to_mtk_gem_obj(obj); 296 297 if (!mtk_gem->pages) 298 return; 299 300 vunmap(vaddr); 301 mtk_gem->kvaddr = 0; 302 kfree(mtk_gem->pages); 303 } 304