v3d_bo.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /* Copyright (C) 2015-2018 Broadcom */
  3. /**
  4. * DOC: V3D GEM BO management support
  5. *
  6. * Compared to VC4 (V3D 2.x), V3D 3.3 introduces an MMU between the
  7. * GPU and the bus, allowing us to use shmem objects for our storage
  8. * instead of CMA.
  9. *
  10. * Physically contiguous objects may still be imported to V3D, but the
  11. * driver doesn't allocate physically contiguous objects on its own.
  12. * Display engines requiring physically contiguous allocations should
  13. * look into Mesa's "renderonly" support (as used by the Mesa pl111
  14. * driver) for an example of how to integrate with V3D.
  15. *
  16. * Long term, we should support evicting pages from the MMU when under
  17. * memory pressure (thus the v3d_bo_get_pages() refcounting), but
  18. * that's not a high priority since our systems tend to not have swap.
  19. */
  20. #include <linux/dma-buf.h>
  21. #include <linux/pfn_t.h>
  22. #include "v3d_drv.h"
  23. #include "uapi/drm/v3d_drm.h"
  24. /* Pins the shmem pages, fills in the .pages and .sgt fields of the BO, and maps
  25. * it for DMA.
  26. */
  27. static int
  28. v3d_bo_get_pages(struct v3d_bo *bo)
  29. {
  30. struct drm_gem_object *obj = &bo->base;
  31. struct drm_device *dev = obj->dev;
  32. int npages = obj->size >> PAGE_SHIFT;
  33. int ret = 0;
  34. mutex_lock(&bo->lock);
  35. if (bo->pages_refcount++ != 0)
  36. goto unlock;
  37. if (!obj->import_attach) {
  38. bo->pages = drm_gem_get_pages(obj);
  39. if (IS_ERR(bo->pages)) {
  40. ret = PTR_ERR(bo->pages);
  41. goto unlock;
  42. }
  43. bo->sgt = drm_prime_pages_to_sg(bo->pages, npages);
  44. if (IS_ERR(bo->sgt)) {
  45. ret = PTR_ERR(bo->sgt);
  46. goto put_pages;
  47. }
  48. /* Map the pages for use by the GPU. */
  49. dma_map_sg(dev->dev, bo->sgt->sgl,
  50. bo->sgt->nents, DMA_BIDIRECTIONAL);
  51. } else {
  52. bo->pages = kcalloc(npages, sizeof(*bo->pages), GFP_KERNEL);
  53. if (!bo->pages)
  54. goto put_pages;
  55. drm_prime_sg_to_page_addr_arrays(bo->sgt, bo->pages,
  56. NULL, npages);
  57. /* Note that dma-bufs come in mapped. */
  58. }
  59. mutex_unlock(&bo->lock);
  60. return 0;
  61. put_pages:
  62. drm_gem_put_pages(obj, bo->pages, true, true);
  63. bo->pages = NULL;
  64. unlock:
  65. bo->pages_refcount--;
  66. mutex_unlock(&bo->lock);
  67. return ret;
  68. }
  69. static void
  70. v3d_bo_put_pages(struct v3d_bo *bo)
  71. {
  72. struct drm_gem_object *obj = &bo->base;
  73. mutex_lock(&bo->lock);
  74. if (--bo->pages_refcount == 0) {
  75. if (!obj->import_attach) {
  76. dma_unmap_sg(obj->dev->dev, bo->sgt->sgl,
  77. bo->sgt->nents, DMA_BIDIRECTIONAL);
  78. sg_free_table(bo->sgt);
  79. kfree(bo->sgt);
  80. drm_gem_put_pages(obj, bo->pages, true, true);
  81. } else {
  82. kfree(bo->pages);
  83. }
  84. }
  85. mutex_unlock(&bo->lock);
  86. }
  87. static struct v3d_bo *v3d_bo_create_struct(struct drm_device *dev,
  88. size_t unaligned_size)
  89. {
  90. struct v3d_dev *v3d = to_v3d_dev(dev);
  91. struct drm_gem_object *obj;
  92. struct v3d_bo *bo;
  93. size_t size = roundup(unaligned_size, PAGE_SIZE);
  94. int ret;
  95. if (size == 0)
  96. return ERR_PTR(-EINVAL);
  97. bo = kzalloc(sizeof(*bo), GFP_KERNEL);
  98. if (!bo)
  99. return ERR_PTR(-ENOMEM);
  100. obj = &bo->base;
  101. INIT_LIST_HEAD(&bo->vmas);
  102. INIT_LIST_HEAD(&bo->unref_head);
  103. mutex_init(&bo->lock);
  104. ret = drm_gem_object_init(dev, obj, size);
  105. if (ret)
  106. goto free_bo;
  107. spin_lock(&v3d->mm_lock);
  108. ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node,
  109. obj->size >> PAGE_SHIFT,
  110. GMP_GRANULARITY >> PAGE_SHIFT, 0, 0);
  111. spin_unlock(&v3d->mm_lock);
  112. if (ret)
  113. goto free_obj;
  114. return bo;
  115. free_obj:
  116. drm_gem_object_release(obj);
  117. free_bo:
  118. kfree(bo);
  119. return ERR_PTR(ret);
  120. }
  121. struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
  122. size_t unaligned_size)
  123. {
  124. struct v3d_dev *v3d = to_v3d_dev(dev);
  125. struct drm_gem_object *obj;
  126. struct v3d_bo *bo;
  127. int ret;
  128. bo = v3d_bo_create_struct(dev, unaligned_size);
  129. if (IS_ERR(bo))
  130. return bo;
  131. obj = &bo->base;
  132. bo->resv = &bo->_resv;
  133. reservation_object_init(bo->resv);
  134. ret = v3d_bo_get_pages(bo);
  135. if (ret)
  136. goto free_mm;
  137. v3d_mmu_insert_ptes(bo);
  138. mutex_lock(&v3d->bo_lock);
  139. v3d->bo_stats.num_allocated++;
  140. v3d->bo_stats.pages_allocated += obj->size >> PAGE_SHIFT;
  141. mutex_unlock(&v3d->bo_lock);
  142. return bo;
  143. free_mm:
  144. spin_lock(&v3d->mm_lock);
  145. drm_mm_remove_node(&bo->node);
  146. spin_unlock(&v3d->mm_lock);
  147. drm_gem_object_release(obj);
  148. kfree(bo);
  149. return ERR_PTR(ret);
  150. }
  151. /* Called DRM core on the last userspace/kernel unreference of the
  152. * BO.
  153. */
  154. void v3d_free_object(struct drm_gem_object *obj)
  155. {
  156. struct v3d_dev *v3d = to_v3d_dev(obj->dev);
  157. struct v3d_bo *bo = to_v3d_bo(obj);
  158. mutex_lock(&v3d->bo_lock);
  159. v3d->bo_stats.num_allocated--;
  160. v3d->bo_stats.pages_allocated -= obj->size >> PAGE_SHIFT;
  161. mutex_unlock(&v3d->bo_lock);
  162. reservation_object_fini(&bo->_resv);
  163. v3d_bo_put_pages(bo);
  164. if (obj->import_attach)
  165. drm_prime_gem_destroy(obj, bo->sgt);
  166. v3d_mmu_remove_ptes(bo);
  167. spin_lock(&v3d->mm_lock);
  168. drm_mm_remove_node(&bo->node);
  169. spin_unlock(&v3d->mm_lock);
  170. mutex_destroy(&bo->lock);
  171. drm_gem_object_release(obj);
  172. kfree(bo);
  173. }
  174. struct reservation_object *v3d_prime_res_obj(struct drm_gem_object *obj)
  175. {
  176. struct v3d_bo *bo = to_v3d_bo(obj);
  177. return bo->resv;
  178. }
  179. static void
  180. v3d_set_mmap_vma_flags(struct vm_area_struct *vma)
  181. {
  182. vma->vm_flags &= ~VM_PFNMAP;
  183. vma->vm_flags |= VM_MIXEDMAP;
  184. vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
  185. }
  186. vm_fault_t v3d_gem_fault(struct vm_fault *vmf)
  187. {
  188. struct vm_area_struct *vma = vmf->vma;
  189. struct drm_gem_object *obj = vma->vm_private_data;
  190. struct v3d_bo *bo = to_v3d_bo(obj);
  191. pfn_t pfn;
  192. pgoff_t pgoff;
  193. /* We don't use vmf->pgoff since that has the fake offset: */
  194. pgoff = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
  195. pfn = __pfn_to_pfn_t(page_to_pfn(bo->pages[pgoff]), PFN_DEV);
  196. return vmf_insert_mixed(vma, vmf->address, pfn);
  197. }
  198. int v3d_mmap(struct file *filp, struct vm_area_struct *vma)
  199. {
  200. int ret;
  201. ret = drm_gem_mmap(filp, vma);
  202. if (ret)
  203. return ret;
  204. v3d_set_mmap_vma_flags(vma);
  205. return ret;
  206. }
  207. int v3d_prime_mmap(struct drm_gem_object *obj, struct vm_area_struct *vma)
  208. {
  209. int ret;
  210. ret = drm_gem_mmap_obj(obj, obj->size, vma);
  211. if (ret < 0)
  212. return ret;
  213. v3d_set_mmap_vma_flags(vma);
  214. return 0;
  215. }
  216. struct sg_table *
  217. v3d_prime_get_sg_table(struct drm_gem_object *obj)
  218. {
  219. struct v3d_bo *bo = to_v3d_bo(obj);
  220. int npages = obj->size >> PAGE_SHIFT;
  221. return drm_prime_pages_to_sg(bo->pages, npages);
  222. }
  223. struct drm_gem_object *
  224. v3d_prime_import_sg_table(struct drm_device *dev,
  225. struct dma_buf_attachment *attach,
  226. struct sg_table *sgt)
  227. {
  228. struct drm_gem_object *obj;
  229. struct v3d_bo *bo;
  230. bo = v3d_bo_create_struct(dev, attach->dmabuf->size);
  231. if (IS_ERR(bo))
  232. return ERR_CAST(bo);
  233. obj = &bo->base;
  234. bo->resv = attach->dmabuf->resv;
  235. bo->sgt = sgt;
  236. obj->import_attach = attach;
  237. v3d_bo_get_pages(bo);
  238. v3d_mmu_insert_ptes(bo);
  239. return obj;
  240. }
  241. int v3d_create_bo_ioctl(struct drm_device *dev, void *data,
  242. struct drm_file *file_priv)
  243. {
  244. struct drm_v3d_create_bo *args = data;
  245. struct v3d_bo *bo = NULL;
  246. int ret;
  247. if (args->flags != 0) {
  248. DRM_INFO("unknown create_bo flags: %d\n", args->flags);
  249. return -EINVAL;
  250. }
  251. bo = v3d_bo_create(dev, file_priv, PAGE_ALIGN(args->size));
  252. if (IS_ERR(bo))
  253. return PTR_ERR(bo);
  254. args->offset = bo->node.start << PAGE_SHIFT;
  255. ret = drm_gem_handle_create(file_priv, &bo->base, &args->handle);
  256. drm_gem_object_put_unlocked(&bo->base);
  257. return ret;
  258. }
  259. int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
  260. struct drm_file *file_priv)
  261. {
  262. struct drm_v3d_mmap_bo *args = data;
  263. struct drm_gem_object *gem_obj;
  264. int ret;
  265. if (args->flags != 0) {
  266. DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);
  267. return -EINVAL;
  268. }
  269. gem_obj = drm_gem_object_lookup(file_priv, args->handle);
  270. if (!gem_obj) {
  271. DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
  272. return -ENOENT;
  273. }
  274. ret = drm_gem_create_mmap_offset(gem_obj);
  275. if (ret == 0)
  276. args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
  277. drm_gem_object_put_unlocked(gem_obj);
  278. return ret;
  279. }
  280. int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
  281. struct drm_file *file_priv)
  282. {
  283. struct drm_v3d_get_bo_offset *args = data;
  284. struct drm_gem_object *gem_obj;
  285. struct v3d_bo *bo;
  286. gem_obj = drm_gem_object_lookup(file_priv, args->handle);
  287. if (!gem_obj) {
  288. DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
  289. return -ENOENT;
  290. }
  291. bo = to_v3d_bo(gem_obj);
  292. args->offset = bo->node.start << PAGE_SHIFT;
  293. drm_gem_object_put_unlocked(gem_obj);
  294. return 0;
  295. }