1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016-2018, 2020-2021 The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2013 Red Hat |
5 | * Author: Rob Clark <robdclark@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/dma-mapping.h> |
9 | #include <linux/fault-inject.h> |
10 | #include <linux/of_address.h> |
11 | #include <linux/uaccess.h> |
12 | |
13 | #include <drm/drm_drv.h> |
14 | #include <drm/drm_file.h> |
15 | #include <drm/drm_ioctl.h> |
16 | #include <drm/drm_of.h> |
17 | |
18 | #include "msm_drv.h" |
19 | #include "msm_debugfs.h" |
20 | #include "msm_kms.h" |
21 | #include "adreno/adreno_gpu.h" |
22 | |
23 | /* |
24 | * MSM driver version: |
25 | * - 1.0.0 - initial interface |
26 | * - 1.1.0 - adds madvise, and support for submits with > 4 cmd buffers |
27 | * - 1.2.0 - adds explicit fence support for submit ioctl |
28 | * - 1.3.0 - adds GMEM_BASE + NR_RINGS params, SUBMITQUEUE_NEW + |
29 | * SUBMITQUEUE_CLOSE ioctls, and MSM_INFO_IOVA flag for |
30 | * MSM_GEM_INFO ioctl. |
31 | * - 1.4.0 - softpin, MSM_RELOC_BO_DUMP, and GEM_INFO support to set/get |
32 | * GEM object's debug name |
33 | * - 1.5.0 - Add SUBMITQUERY_QUERY ioctl |
34 | * - 1.6.0 - Syncobj support |
35 | * - 1.7.0 - Add MSM_PARAM_SUSPENDS to access suspend count |
36 | * - 1.8.0 - Add MSM_BO_CACHED_COHERENT for supported GPUs (a6xx) |
37 | * - 1.9.0 - Add MSM_SUBMIT_FENCE_SN_IN |
38 | * - 1.10.0 - Add MSM_SUBMIT_BO_NO_IMPLICIT |
39 | * - 1.11.0 - Add wait boost (MSM_WAIT_FENCE_BOOST, MSM_PREP_BOOST) |
40 | * - 1.12.0 - Add MSM_INFO_SET_METADATA and MSM_INFO_GET_METADATA |
41 | */ |
42 | #define MSM_VERSION_MAJOR 1 |
43 | #define MSM_VERSION_MINOR 12 |
44 | #define MSM_VERSION_PATCHLEVEL 0 |
45 | |
46 | static void msm_deinit_vram(struct drm_device *ddev); |
47 | |
48 | static char *vram = "16m" ; |
49 | MODULE_PARM_DESC(vram, "Configure VRAM size (for devices without IOMMU/GPUMMU)" ); |
50 | module_param(vram, charp, 0); |
51 | |
52 | bool dumpstate; |
53 | MODULE_PARM_DESC(dumpstate, "Dump KMS state on errors" ); |
54 | module_param(dumpstate, bool, 0600); |
55 | |
56 | static bool modeset = true; |
57 | MODULE_PARM_DESC(modeset, "Use kernel modesetting [KMS] (1=on (default), 0=disable)" ); |
58 | module_param(modeset, bool, 0600); |
59 | |
60 | #ifdef CONFIG_FAULT_INJECTION |
61 | DECLARE_FAULT_ATTR(fail_gem_alloc); |
62 | DECLARE_FAULT_ATTR(fail_gem_iova); |
63 | #endif |
64 | |
65 | static int msm_drm_uninit(struct device *dev) |
66 | { |
67 | struct platform_device *pdev = to_platform_device(dev); |
68 | struct msm_drm_private *priv = platform_get_drvdata(pdev); |
69 | struct drm_device *ddev = priv->dev; |
70 | |
71 | /* |
72 | * Shutdown the hw if we're far enough along where things might be on. |
73 | * If we run this too early, we'll end up panicking in any variety of |
74 | * places. Since we don't register the drm device until late in |
75 | * msm_drm_init, drm_dev->registered is used as an indicator that the |
76 | * shutdown will be successful. |
77 | */ |
78 | if (ddev->registered) { |
79 | drm_dev_unregister(dev: ddev); |
80 | if (priv->kms) |
81 | drm_atomic_helper_shutdown(dev: ddev); |
82 | } |
83 | |
84 | /* We must cancel and cleanup any pending vblank enable/disable |
85 | * work before msm_irq_uninstall() to avoid work re-enabling an |
86 | * irq after uninstall has disabled it. |
87 | */ |
88 | |
89 | flush_workqueue(priv->wq); |
90 | |
91 | msm_gem_shrinker_cleanup(dev: ddev); |
92 | |
93 | msm_perf_debugfs_cleanup(priv); |
94 | msm_rd_debugfs_cleanup(priv); |
95 | |
96 | if (priv->kms) |
97 | msm_drm_kms_uninit(dev); |
98 | |
99 | msm_deinit_vram(ddev); |
100 | |
101 | component_unbind_all(parent: dev, data: ddev); |
102 | |
103 | ddev->dev_private = NULL; |
104 | drm_dev_put(dev: ddev); |
105 | |
106 | destroy_workqueue(wq: priv->wq); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | bool msm_use_mmu(struct drm_device *dev) |
112 | { |
113 | struct msm_drm_private *priv = dev->dev_private; |
114 | |
115 | /* |
116 | * a2xx comes with its own MMU |
117 | * On other platforms IOMMU can be declared specified either for the |
118 | * MDP/DPU device or for its parent, MDSS device. |
119 | */ |
120 | return priv->is_a2xx || |
121 | device_iommu_mapped(dev: dev->dev) || |
122 | device_iommu_mapped(dev: dev->dev->parent); |
123 | } |
124 | |
125 | static int msm_init_vram(struct drm_device *dev) |
126 | { |
127 | struct msm_drm_private *priv = dev->dev_private; |
128 | struct device_node *node; |
129 | unsigned long size = 0; |
130 | int ret = 0; |
131 | |
132 | /* In the device-tree world, we could have a 'memory-region' |
133 | * phandle, which gives us a link to our "vram". Allocating |
134 | * is all nicely abstracted behind the dma api, but we need |
135 | * to know the entire size to allocate it all in one go. There |
136 | * are two cases: |
137 | * 1) device with no IOMMU, in which case we need exclusive |
138 | * access to a VRAM carveout big enough for all gpu |
139 | * buffers |
140 | * 2) device with IOMMU, but where the bootloader puts up |
141 | * a splash screen. In this case, the VRAM carveout |
142 | * need only be large enough for fbdev fb. But we need |
143 | * exclusive access to the buffer to avoid the kernel |
144 | * using those pages for other purposes (which appears |
145 | * as corruption on screen before we have a chance to |
146 | * load and do initial modeset) |
147 | */ |
148 | |
149 | node = of_parse_phandle(np: dev->dev->of_node, phandle_name: "memory-region" , index: 0); |
150 | if (node) { |
151 | struct resource r; |
152 | ret = of_address_to_resource(dev: node, index: 0, r: &r); |
153 | of_node_put(node); |
154 | if (ret) |
155 | return ret; |
156 | size = r.end - r.start + 1; |
157 | DRM_INFO("using VRAM carveout: %lx@%pa\n" , size, &r.start); |
158 | |
159 | /* if we have no IOMMU, then we need to use carveout allocator. |
160 | * Grab the entire DMA chunk carved out in early startup in |
161 | * mach-msm: |
162 | */ |
163 | } else if (!msm_use_mmu(dev)) { |
164 | DRM_INFO("using %s VRAM carveout\n" , vram); |
165 | size = memparse(ptr: vram, NULL); |
166 | } |
167 | |
168 | if (size) { |
169 | unsigned long attrs = 0; |
170 | void *p; |
171 | |
172 | priv->vram.size = size; |
173 | |
174 | drm_mm_init(mm: &priv->vram.mm, start: 0, size: (size >> PAGE_SHIFT) - 1); |
175 | spin_lock_init(&priv->vram.lock); |
176 | |
177 | attrs |= DMA_ATTR_NO_KERNEL_MAPPING; |
178 | attrs |= DMA_ATTR_WRITE_COMBINE; |
179 | |
180 | /* note that for no-kernel-mapping, the vaddr returned |
181 | * is bogus, but non-null if allocation succeeded: |
182 | */ |
183 | p = dma_alloc_attrs(dev: dev->dev, size, |
184 | dma_handle: &priv->vram.paddr, GFP_KERNEL, attrs); |
185 | if (!p) { |
186 | DRM_DEV_ERROR(dev->dev, "failed to allocate VRAM\n" ); |
187 | priv->vram.paddr = 0; |
188 | return -ENOMEM; |
189 | } |
190 | |
191 | DRM_DEV_INFO(dev->dev, "VRAM: %08x->%08x\n" , |
192 | (uint32_t)priv->vram.paddr, |
193 | (uint32_t)(priv->vram.paddr + size)); |
194 | } |
195 | |
196 | return ret; |
197 | } |
198 | |
199 | static void msm_deinit_vram(struct drm_device *ddev) |
200 | { |
201 | struct msm_drm_private *priv = ddev->dev_private; |
202 | unsigned long attrs = DMA_ATTR_NO_KERNEL_MAPPING; |
203 | |
204 | if (!priv->vram.paddr) |
205 | return; |
206 | |
207 | drm_mm_takedown(mm: &priv->vram.mm); |
208 | dma_free_attrs(dev: ddev->dev, size: priv->vram.size, NULL, dma_handle: priv->vram.paddr, |
209 | attrs); |
210 | } |
211 | |
212 | static int msm_drm_init(struct device *dev, const struct drm_driver *drv) |
213 | { |
214 | struct msm_drm_private *priv = dev_get_drvdata(dev); |
215 | struct drm_device *ddev; |
216 | int ret; |
217 | |
218 | if (drm_firmware_drivers_only()) |
219 | return -ENODEV; |
220 | |
221 | ddev = drm_dev_alloc(driver: drv, parent: dev); |
222 | if (IS_ERR(ptr: ddev)) { |
223 | DRM_DEV_ERROR(dev, "failed to allocate drm_device\n" ); |
224 | return PTR_ERR(ptr: ddev); |
225 | } |
226 | ddev->dev_private = priv; |
227 | priv->dev = ddev; |
228 | |
229 | priv->wq = alloc_ordered_workqueue("msm" , 0); |
230 | if (!priv->wq) { |
231 | ret = -ENOMEM; |
232 | goto err_put_dev; |
233 | } |
234 | |
235 | INIT_LIST_HEAD(list: &priv->objects); |
236 | mutex_init(&priv->obj_lock); |
237 | |
238 | /* |
239 | * Initialize the LRUs: |
240 | */ |
241 | mutex_init(&priv->lru.lock); |
242 | drm_gem_lru_init(lru: &priv->lru.unbacked, lock: &priv->lru.lock); |
243 | drm_gem_lru_init(lru: &priv->lru.pinned, lock: &priv->lru.lock); |
244 | drm_gem_lru_init(lru: &priv->lru.willneed, lock: &priv->lru.lock); |
245 | drm_gem_lru_init(lru: &priv->lru.dontneed, lock: &priv->lru.lock); |
246 | |
247 | /* Teach lockdep about lock ordering wrt. shrinker: */ |
248 | fs_reclaim_acquire(GFP_KERNEL); |
249 | might_lock(&priv->lru.lock); |
250 | fs_reclaim_release(GFP_KERNEL); |
251 | |
252 | if (priv->kms_init) { |
253 | ret = drmm_mode_config_init(dev: ddev); |
254 | if (ret) |
255 | goto err_destroy_wq; |
256 | } |
257 | |
258 | ret = msm_init_vram(dev: ddev); |
259 | if (ret) |
260 | goto err_destroy_wq; |
261 | |
262 | dma_set_max_seg_size(dev, UINT_MAX); |
263 | |
264 | /* Bind all our sub-components: */ |
265 | ret = component_bind_all(parent: dev, data: ddev); |
266 | if (ret) |
267 | goto err_deinit_vram; |
268 | |
269 | ret = msm_gem_shrinker_init(dev: ddev); |
270 | if (ret) |
271 | goto err_msm_uninit; |
272 | |
273 | if (priv->kms_init) { |
274 | ret = msm_drm_kms_init(dev, drv); |
275 | if (ret) |
276 | goto err_msm_uninit; |
277 | } else { |
278 | /* valid only for the dummy headless case, where of_node=NULL */ |
279 | WARN_ON(dev->of_node); |
280 | ddev->driver_features &= ~DRIVER_MODESET; |
281 | ddev->driver_features &= ~DRIVER_ATOMIC; |
282 | } |
283 | |
284 | ret = drm_dev_register(dev: ddev, flags: 0); |
285 | if (ret) |
286 | goto err_msm_uninit; |
287 | |
288 | ret = msm_debugfs_late_init(dev: ddev); |
289 | if (ret) |
290 | goto err_msm_uninit; |
291 | |
292 | if (priv->kms_init) { |
293 | drm_kms_helper_poll_init(dev: ddev); |
294 | msm_fbdev_setup(dev: ddev); |
295 | } |
296 | |
297 | return 0; |
298 | |
299 | err_msm_uninit: |
300 | msm_drm_uninit(dev); |
301 | |
302 | return ret; |
303 | |
304 | err_deinit_vram: |
305 | msm_deinit_vram(ddev); |
306 | err_destroy_wq: |
307 | destroy_workqueue(wq: priv->wq); |
308 | err_put_dev: |
309 | drm_dev_put(dev: ddev); |
310 | |
311 | return ret; |
312 | } |
313 | |
314 | /* |
315 | * DRM operations: |
316 | */ |
317 | |
318 | static void load_gpu(struct drm_device *dev) |
319 | { |
320 | static DEFINE_MUTEX(init_lock); |
321 | struct msm_drm_private *priv = dev->dev_private; |
322 | |
323 | mutex_lock(&init_lock); |
324 | |
325 | if (!priv->gpu) |
326 | priv->gpu = adreno_load_gpu(dev); |
327 | |
328 | mutex_unlock(lock: &init_lock); |
329 | } |
330 | |
331 | static int context_init(struct drm_device *dev, struct drm_file *file) |
332 | { |
333 | static atomic_t ident = ATOMIC_INIT(0); |
334 | struct msm_drm_private *priv = dev->dev_private; |
335 | struct msm_file_private *ctx; |
336 | |
337 | ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); |
338 | if (!ctx) |
339 | return -ENOMEM; |
340 | |
341 | INIT_LIST_HEAD(list: &ctx->submitqueues); |
342 | rwlock_init(&ctx->queuelock); |
343 | |
344 | kref_init(kref: &ctx->ref); |
345 | msm_submitqueue_init(dev, ctx); |
346 | |
347 | ctx->aspace = msm_gpu_create_private_address_space(priv->gpu, current); |
348 | file->driver_priv = ctx; |
349 | |
350 | ctx->seqno = atomic_inc_return(v: &ident); |
351 | |
352 | return 0; |
353 | } |
354 | |
355 | static int msm_open(struct drm_device *dev, struct drm_file *file) |
356 | { |
357 | /* For now, load gpu on open.. to avoid the requirement of having |
358 | * firmware in the initrd. |
359 | */ |
360 | load_gpu(dev); |
361 | |
362 | return context_init(dev, file); |
363 | } |
364 | |
365 | static void context_close(struct msm_file_private *ctx) |
366 | { |
367 | msm_submitqueue_close(ctx); |
368 | msm_file_private_put(ctx); |
369 | } |
370 | |
371 | static void msm_postclose(struct drm_device *dev, struct drm_file *file) |
372 | { |
373 | struct msm_drm_private *priv = dev->dev_private; |
374 | struct msm_file_private *ctx = file->driver_priv; |
375 | |
376 | /* |
377 | * It is not possible to set sysprof param to non-zero if gpu |
378 | * is not initialized: |
379 | */ |
380 | if (priv->gpu) |
381 | msm_file_private_set_sysprof(ctx, priv->gpu, 0); |
382 | |
383 | context_close(ctx); |
384 | } |
385 | |
386 | /* |
387 | * DRM ioctls: |
388 | */ |
389 | |
390 | static int msm_ioctl_get_param(struct drm_device *dev, void *data, |
391 | struct drm_file *file) |
392 | { |
393 | struct msm_drm_private *priv = dev->dev_private; |
394 | struct drm_msm_param *args = data; |
395 | struct msm_gpu *gpu; |
396 | |
397 | /* for now, we just have 3d pipe.. eventually this would need to |
398 | * be more clever to dispatch to appropriate gpu module: |
399 | */ |
400 | if ((args->pipe != MSM_PIPE_3D0) || (args->pad != 0)) |
401 | return -EINVAL; |
402 | |
403 | gpu = priv->gpu; |
404 | |
405 | if (!gpu) |
406 | return -ENXIO; |
407 | |
408 | return gpu->funcs->get_param(gpu, file->driver_priv, |
409 | args->param, &args->value, &args->len); |
410 | } |
411 | |
412 | static int msm_ioctl_set_param(struct drm_device *dev, void *data, |
413 | struct drm_file *file) |
414 | { |
415 | struct msm_drm_private *priv = dev->dev_private; |
416 | struct drm_msm_param *args = data; |
417 | struct msm_gpu *gpu; |
418 | |
419 | if ((args->pipe != MSM_PIPE_3D0) || (args->pad != 0)) |
420 | return -EINVAL; |
421 | |
422 | gpu = priv->gpu; |
423 | |
424 | if (!gpu) |
425 | return -ENXIO; |
426 | |
427 | return gpu->funcs->set_param(gpu, file->driver_priv, |
428 | args->param, args->value, args->len); |
429 | } |
430 | |
431 | static int msm_ioctl_gem_new(struct drm_device *dev, void *data, |
432 | struct drm_file *file) |
433 | { |
434 | struct drm_msm_gem_new *args = data; |
435 | uint32_t flags = args->flags; |
436 | |
437 | if (args->flags & ~MSM_BO_FLAGS) { |
438 | DRM_ERROR("invalid flags: %08x\n" , args->flags); |
439 | return -EINVAL; |
440 | } |
441 | |
442 | /* |
443 | * Uncached CPU mappings are deprecated, as of: |
444 | * |
445 | * 9ef364432db4 ("drm/msm: deprecate MSM_BO_UNCACHED (map as writecombine instead)") |
446 | * |
447 | * So promote them to WC. |
448 | */ |
449 | if (flags & MSM_BO_UNCACHED) { |
450 | flags &= ~MSM_BO_CACHED; |
451 | flags |= MSM_BO_WC; |
452 | } |
453 | |
454 | if (should_fail(attr: &fail_gem_alloc, size: args->size)) |
455 | return -ENOMEM; |
456 | |
457 | return msm_gem_new_handle(dev, file, args->size, |
458 | args->flags, &args->handle, NULL); |
459 | } |
460 | |
461 | static inline ktime_t to_ktime(struct drm_msm_timespec timeout) |
462 | { |
463 | return ktime_set(secs: timeout.tv_sec, nsecs: timeout.tv_nsec); |
464 | } |
465 | |
466 | static int msm_ioctl_gem_cpu_prep(struct drm_device *dev, void *data, |
467 | struct drm_file *file) |
468 | { |
469 | struct drm_msm_gem_cpu_prep *args = data; |
470 | struct drm_gem_object *obj; |
471 | ktime_t timeout = to_ktime(timeout: args->timeout); |
472 | int ret; |
473 | |
474 | if (args->op & ~MSM_PREP_FLAGS) { |
475 | DRM_ERROR("invalid op: %08x\n" , args->op); |
476 | return -EINVAL; |
477 | } |
478 | |
479 | obj = drm_gem_object_lookup(filp: file, handle: args->handle); |
480 | if (!obj) |
481 | return -ENOENT; |
482 | |
483 | ret = msm_gem_cpu_prep(obj, args->op, &timeout); |
484 | |
485 | drm_gem_object_put(obj); |
486 | |
487 | return ret; |
488 | } |
489 | |
490 | static int msm_ioctl_gem_cpu_fini(struct drm_device *dev, void *data, |
491 | struct drm_file *file) |
492 | { |
493 | struct drm_msm_gem_cpu_fini *args = data; |
494 | struct drm_gem_object *obj; |
495 | int ret; |
496 | |
497 | obj = drm_gem_object_lookup(filp: file, handle: args->handle); |
498 | if (!obj) |
499 | return -ENOENT; |
500 | |
501 | ret = msm_gem_cpu_fini(obj); |
502 | |
503 | drm_gem_object_put(obj); |
504 | |
505 | return ret; |
506 | } |
507 | |
508 | static int msm_ioctl_gem_info_iova(struct drm_device *dev, |
509 | struct drm_file *file, struct drm_gem_object *obj, |
510 | uint64_t *iova) |
511 | { |
512 | struct msm_drm_private *priv = dev->dev_private; |
513 | struct msm_file_private *ctx = file->driver_priv; |
514 | |
515 | if (!priv->gpu) |
516 | return -EINVAL; |
517 | |
518 | if (should_fail(attr: &fail_gem_iova, size: obj->size)) |
519 | return -ENOMEM; |
520 | |
521 | /* |
522 | * Don't pin the memory here - just get an address so that userspace can |
523 | * be productive |
524 | */ |
525 | return msm_gem_get_iova(obj, ctx->aspace, iova); |
526 | } |
527 | |
528 | static int msm_ioctl_gem_info_set_iova(struct drm_device *dev, |
529 | struct drm_file *file, struct drm_gem_object *obj, |
530 | uint64_t iova) |
531 | { |
532 | struct msm_drm_private *priv = dev->dev_private; |
533 | struct msm_file_private *ctx = file->driver_priv; |
534 | |
535 | if (!priv->gpu) |
536 | return -EINVAL; |
537 | |
538 | /* Only supported if per-process address space is supported: */ |
539 | if (priv->gpu->aspace == ctx->aspace) |
540 | return -EOPNOTSUPP; |
541 | |
542 | if (should_fail(attr: &fail_gem_iova, size: obj->size)) |
543 | return -ENOMEM; |
544 | |
545 | return msm_gem_set_iova(obj, ctx->aspace, iova); |
546 | } |
547 | |
548 | static int msm_ioctl_gem_info_set_metadata(struct drm_gem_object *obj, |
549 | __user void *metadata, |
550 | u32 metadata_size) |
551 | { |
552 | struct msm_gem_object *msm_obj = to_msm_bo(obj); |
553 | void *buf; |
554 | int ret; |
555 | |
556 | /* Impose a moderate upper bound on metadata size: */ |
557 | if (metadata_size > 128) { |
558 | return -EOVERFLOW; |
559 | } |
560 | |
561 | /* Use a temporary buf to keep copy_from_user() outside of gem obj lock: */ |
562 | buf = memdup_user(metadata, metadata_size); |
563 | if (IS_ERR(ptr: buf)) |
564 | return PTR_ERR(ptr: buf); |
565 | |
566 | ret = msm_gem_lock_interruptible(obj); |
567 | if (ret) |
568 | goto out; |
569 | |
570 | msm_obj->metadata = |
571 | krealloc(objp: msm_obj->metadata, new_size: metadata_size, GFP_KERNEL); |
572 | msm_obj->metadata_size = metadata_size; |
573 | memcpy(msm_obj->metadata, buf, metadata_size); |
574 | |
575 | msm_gem_unlock(obj); |
576 | |
577 | out: |
578 | kfree(objp: buf); |
579 | |
580 | return ret; |
581 | } |
582 | |
583 | static int msm_ioctl_gem_info_get_metadata(struct drm_gem_object *obj, |
584 | __user void *metadata, |
585 | u32 *metadata_size) |
586 | { |
587 | struct msm_gem_object *msm_obj = to_msm_bo(obj); |
588 | void *buf; |
589 | int ret, len; |
590 | |
591 | if (!metadata) { |
592 | /* |
593 | * Querying the size is inherently racey, but |
594 | * EXT_external_objects expects the app to confirm |
595 | * via device and driver UUIDs that the exporter and |
596 | * importer versions match. All we can do from the |
597 | * kernel side is check the length under obj lock |
598 | * when userspace tries to retrieve the metadata |
599 | */ |
600 | *metadata_size = msm_obj->metadata_size; |
601 | return 0; |
602 | } |
603 | |
604 | ret = msm_gem_lock_interruptible(obj); |
605 | if (ret) |
606 | return ret; |
607 | |
608 | /* Avoid copy_to_user() under gem obj lock: */ |
609 | len = msm_obj->metadata_size; |
610 | buf = kmemdup(msm_obj->metadata, len, GFP_KERNEL); |
611 | |
612 | msm_gem_unlock(obj); |
613 | |
614 | if (*metadata_size < len) { |
615 | ret = -ETOOSMALL; |
616 | } else if (copy_to_user(to: metadata, from: buf, n: len)) { |
617 | ret = -EFAULT; |
618 | } else { |
619 | *metadata_size = len; |
620 | } |
621 | |
622 | kfree(objp: buf); |
623 | |
624 | return 0; |
625 | } |
626 | |
627 | static int msm_ioctl_gem_info(struct drm_device *dev, void *data, |
628 | struct drm_file *file) |
629 | { |
630 | struct drm_msm_gem_info *args = data; |
631 | struct drm_gem_object *obj; |
632 | struct msm_gem_object *msm_obj; |
633 | int i, ret = 0; |
634 | |
635 | if (args->pad) |
636 | return -EINVAL; |
637 | |
638 | switch (args->info) { |
639 | case MSM_INFO_GET_OFFSET: |
640 | case MSM_INFO_GET_IOVA: |
641 | case MSM_INFO_SET_IOVA: |
642 | case MSM_INFO_GET_FLAGS: |
643 | /* value returned as immediate, not pointer, so len==0: */ |
644 | if (args->len) |
645 | return -EINVAL; |
646 | break; |
647 | case MSM_INFO_SET_NAME: |
648 | case MSM_INFO_GET_NAME: |
649 | case MSM_INFO_SET_METADATA: |
650 | case MSM_INFO_GET_METADATA: |
651 | break; |
652 | default: |
653 | return -EINVAL; |
654 | } |
655 | |
656 | obj = drm_gem_object_lookup(filp: file, handle: args->handle); |
657 | if (!obj) |
658 | return -ENOENT; |
659 | |
660 | msm_obj = to_msm_bo(obj); |
661 | |
662 | switch (args->info) { |
663 | case MSM_INFO_GET_OFFSET: |
664 | args->value = msm_gem_mmap_offset(obj); |
665 | break; |
666 | case MSM_INFO_GET_IOVA: |
667 | ret = msm_ioctl_gem_info_iova(dev, file, obj, iova: &args->value); |
668 | break; |
669 | case MSM_INFO_SET_IOVA: |
670 | ret = msm_ioctl_gem_info_set_iova(dev, file, obj, iova: args->value); |
671 | break; |
672 | case MSM_INFO_GET_FLAGS: |
673 | if (obj->import_attach) { |
674 | ret = -EINVAL; |
675 | break; |
676 | } |
677 | /* Hide internal kernel-only flags: */ |
678 | args->value = to_msm_bo(obj)->flags & MSM_BO_FLAGS; |
679 | ret = 0; |
680 | break; |
681 | case MSM_INFO_SET_NAME: |
682 | /* length check should leave room for terminating null: */ |
683 | if (args->len >= sizeof(msm_obj->name)) { |
684 | ret = -EINVAL; |
685 | break; |
686 | } |
687 | if (copy_from_user(to: msm_obj->name, u64_to_user_ptr(args->value), |
688 | n: args->len)) { |
689 | msm_obj->name[0] = '\0'; |
690 | ret = -EFAULT; |
691 | break; |
692 | } |
693 | msm_obj->name[args->len] = '\0'; |
694 | for (i = 0; i < args->len; i++) { |
695 | if (!isprint(msm_obj->name[i])) { |
696 | msm_obj->name[i] = '\0'; |
697 | break; |
698 | } |
699 | } |
700 | break; |
701 | case MSM_INFO_GET_NAME: |
702 | if (args->value && (args->len < strlen(msm_obj->name))) { |
703 | ret = -ETOOSMALL; |
704 | break; |
705 | } |
706 | args->len = strlen(msm_obj->name); |
707 | if (args->value) { |
708 | if (copy_to_user(u64_to_user_ptr(args->value), |
709 | from: msm_obj->name, n: args->len)) |
710 | ret = -EFAULT; |
711 | } |
712 | break; |
713 | case MSM_INFO_SET_METADATA: |
714 | ret = msm_ioctl_gem_info_set_metadata( |
715 | obj, u64_to_user_ptr(args->value), metadata_size: args->len); |
716 | break; |
717 | case MSM_INFO_GET_METADATA: |
718 | ret = msm_ioctl_gem_info_get_metadata( |
719 | obj, u64_to_user_ptr(args->value), metadata_size: &args->len); |
720 | break; |
721 | } |
722 | |
723 | drm_gem_object_put(obj); |
724 | |
725 | return ret; |
726 | } |
727 | |
728 | static int wait_fence(struct msm_gpu_submitqueue *queue, uint32_t fence_id, |
729 | ktime_t timeout, uint32_t flags) |
730 | { |
731 | struct dma_fence *fence; |
732 | int ret; |
733 | |
734 | if (fence_after(fence_id, queue->last_fence)) { |
735 | DRM_ERROR_RATELIMITED("waiting on invalid fence: %u (of %u)\n" , |
736 | fence_id, queue->last_fence); |
737 | return -EINVAL; |
738 | } |
739 | |
740 | /* |
741 | * Map submitqueue scoped "seqno" (which is actually an idr key) |
742 | * back to underlying dma-fence |
743 | * |
744 | * The fence is removed from the fence_idr when the submit is |
745 | * retired, so if the fence is not found it means there is nothing |
746 | * to wait for |
747 | */ |
748 | spin_lock(lock: &queue->idr_lock); |
749 | fence = idr_find(&queue->fence_idr, id: fence_id); |
750 | if (fence) |
751 | fence = dma_fence_get_rcu(fence); |
752 | spin_unlock(lock: &queue->idr_lock); |
753 | |
754 | if (!fence) |
755 | return 0; |
756 | |
757 | if (flags & MSM_WAIT_FENCE_BOOST) |
758 | dma_fence_set_deadline(fence, deadline: ktime_get()); |
759 | |
760 | ret = dma_fence_wait_timeout(fence, intr: true, timeout: timeout_to_jiffies(timeout: &timeout)); |
761 | if (ret == 0) { |
762 | ret = -ETIMEDOUT; |
763 | } else if (ret != -ERESTARTSYS) { |
764 | ret = 0; |
765 | } |
766 | |
767 | dma_fence_put(fence); |
768 | |
769 | return ret; |
770 | } |
771 | |
772 | static int msm_ioctl_wait_fence(struct drm_device *dev, void *data, |
773 | struct drm_file *file) |
774 | { |
775 | struct msm_drm_private *priv = dev->dev_private; |
776 | struct drm_msm_wait_fence *args = data; |
777 | struct msm_gpu_submitqueue *queue; |
778 | int ret; |
779 | |
780 | if (args->flags & ~MSM_WAIT_FENCE_FLAGS) { |
781 | DRM_ERROR("invalid flags: %08x\n" , args->flags); |
782 | return -EINVAL; |
783 | } |
784 | |
785 | if (!priv->gpu) |
786 | return 0; |
787 | |
788 | queue = msm_submitqueue_get(file->driver_priv, args->queueid); |
789 | if (!queue) |
790 | return -ENOENT; |
791 | |
792 | ret = wait_fence(queue, fence_id: args->fence, timeout: to_ktime(timeout: args->timeout), flags: args->flags); |
793 | |
794 | msm_submitqueue_put(queue); |
795 | |
796 | return ret; |
797 | } |
798 | |
799 | static int msm_ioctl_gem_madvise(struct drm_device *dev, void *data, |
800 | struct drm_file *file) |
801 | { |
802 | struct drm_msm_gem_madvise *args = data; |
803 | struct drm_gem_object *obj; |
804 | int ret; |
805 | |
806 | switch (args->madv) { |
807 | case MSM_MADV_DONTNEED: |
808 | case MSM_MADV_WILLNEED: |
809 | break; |
810 | default: |
811 | return -EINVAL; |
812 | } |
813 | |
814 | obj = drm_gem_object_lookup(filp: file, handle: args->handle); |
815 | if (!obj) { |
816 | return -ENOENT; |
817 | } |
818 | |
819 | ret = msm_gem_madvise(obj, args->madv); |
820 | if (ret >= 0) { |
821 | args->retained = ret; |
822 | ret = 0; |
823 | } |
824 | |
825 | drm_gem_object_put(obj); |
826 | |
827 | return ret; |
828 | } |
829 | |
830 | |
831 | static int msm_ioctl_submitqueue_new(struct drm_device *dev, void *data, |
832 | struct drm_file *file) |
833 | { |
834 | struct drm_msm_submitqueue *args = data; |
835 | |
836 | if (args->flags & ~MSM_SUBMITQUEUE_FLAGS) |
837 | return -EINVAL; |
838 | |
839 | return msm_submitqueue_create(dev, file->driver_priv, args->prio, |
840 | args->flags, &args->id); |
841 | } |
842 | |
843 | static int msm_ioctl_submitqueue_query(struct drm_device *dev, void *data, |
844 | struct drm_file *file) |
845 | { |
846 | return msm_submitqueue_query(dev, file->driver_priv, data); |
847 | } |
848 | |
849 | static int msm_ioctl_submitqueue_close(struct drm_device *dev, void *data, |
850 | struct drm_file *file) |
851 | { |
852 | u32 id = *(u32 *) data; |
853 | |
854 | return msm_submitqueue_remove(file->driver_priv, id); |
855 | } |
856 | |
857 | static const struct drm_ioctl_desc msm_ioctls[] = { |
858 | DRM_IOCTL_DEF_DRV(MSM_GET_PARAM, msm_ioctl_get_param, DRM_RENDER_ALLOW), |
859 | DRM_IOCTL_DEF_DRV(MSM_SET_PARAM, msm_ioctl_set_param, DRM_RENDER_ALLOW), |
860 | DRM_IOCTL_DEF_DRV(MSM_GEM_NEW, msm_ioctl_gem_new, DRM_RENDER_ALLOW), |
861 | DRM_IOCTL_DEF_DRV(MSM_GEM_INFO, msm_ioctl_gem_info, DRM_RENDER_ALLOW), |
862 | DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_PREP, msm_ioctl_gem_cpu_prep, DRM_RENDER_ALLOW), |
863 | DRM_IOCTL_DEF_DRV(MSM_GEM_CPU_FINI, msm_ioctl_gem_cpu_fini, DRM_RENDER_ALLOW), |
864 | DRM_IOCTL_DEF_DRV(MSM_GEM_SUBMIT, msm_ioctl_gem_submit, DRM_RENDER_ALLOW), |
865 | DRM_IOCTL_DEF_DRV(MSM_WAIT_FENCE, msm_ioctl_wait_fence, DRM_RENDER_ALLOW), |
866 | DRM_IOCTL_DEF_DRV(MSM_GEM_MADVISE, msm_ioctl_gem_madvise, DRM_RENDER_ALLOW), |
867 | DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_NEW, msm_ioctl_submitqueue_new, DRM_RENDER_ALLOW), |
868 | DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_CLOSE, msm_ioctl_submitqueue_close, DRM_RENDER_ALLOW), |
869 | DRM_IOCTL_DEF_DRV(MSM_SUBMITQUEUE_QUERY, msm_ioctl_submitqueue_query, DRM_RENDER_ALLOW), |
870 | }; |
871 | |
872 | static void msm_show_fdinfo(struct drm_printer *p, struct drm_file *file) |
873 | { |
874 | struct drm_device *dev = file->minor->dev; |
875 | struct msm_drm_private *priv = dev->dev_private; |
876 | |
877 | if (!priv->gpu) |
878 | return; |
879 | |
880 | msm_gpu_show_fdinfo(priv->gpu, file->driver_priv, p); |
881 | |
882 | drm_show_memory_stats(p, file); |
883 | } |
884 | |
885 | static const struct file_operations fops = { |
886 | .owner = THIS_MODULE, |
887 | DRM_GEM_FOPS, |
888 | .show_fdinfo = drm_show_fdinfo, |
889 | }; |
890 | |
891 | static const struct drm_driver msm_driver = { |
892 | .driver_features = DRIVER_GEM | |
893 | DRIVER_RENDER | |
894 | DRIVER_ATOMIC | |
895 | DRIVER_MODESET | |
896 | DRIVER_SYNCOBJ, |
897 | .open = msm_open, |
898 | .postclose = msm_postclose, |
899 | .dumb_create = msm_gem_dumb_create, |
900 | .dumb_map_offset = msm_gem_dumb_map_offset, |
901 | .gem_prime_import_sg_table = msm_gem_prime_import_sg_table, |
902 | #ifdef CONFIG_DEBUG_FS |
903 | .debugfs_init = msm_debugfs_init, |
904 | #endif |
905 | .show_fdinfo = msm_show_fdinfo, |
906 | .ioctls = msm_ioctls, |
907 | .num_ioctls = ARRAY_SIZE(msm_ioctls), |
908 | .fops = &fops, |
909 | .name = "msm" , |
910 | .desc = "MSM Snapdragon DRM" , |
911 | .date = "20130625" , |
912 | .major = MSM_VERSION_MAJOR, |
913 | .minor = MSM_VERSION_MINOR, |
914 | .patchlevel = MSM_VERSION_PATCHLEVEL, |
915 | }; |
916 | |
917 | /* |
918 | * Componentized driver support: |
919 | */ |
920 | |
921 | /* |
922 | * Identify what components need to be added by parsing what remote-endpoints |
923 | * our MDP output ports are connected to. In the case of LVDS on MDP4, there |
924 | * is no external component that we need to add since LVDS is within MDP4 |
925 | * itself. |
926 | */ |
927 | static int add_components_mdp(struct device *master_dev, |
928 | struct component_match **matchptr) |
929 | { |
930 | struct device_node *np = master_dev->of_node; |
931 | struct device_node *ep_node; |
932 | |
933 | for_each_endpoint_of_node(np, ep_node) { |
934 | struct device_node *intf; |
935 | struct of_endpoint ep; |
936 | int ret; |
937 | |
938 | ret = of_graph_parse_endpoint(node: ep_node, endpoint: &ep); |
939 | if (ret) { |
940 | DRM_DEV_ERROR(master_dev, "unable to parse port endpoint\n" ); |
941 | of_node_put(node: ep_node); |
942 | return ret; |
943 | } |
944 | |
945 | /* |
946 | * The LCDC/LVDS port on MDP4 is a speacial case where the |
947 | * remote-endpoint isn't a component that we need to add |
948 | */ |
949 | if (of_device_is_compatible(device: np, "qcom,mdp4" ) && |
950 | ep.port == 0) |
951 | continue; |
952 | |
953 | /* |
954 | * It's okay if some of the ports don't have a remote endpoint |
955 | * specified. It just means that the port isn't connected to |
956 | * any external interface. |
957 | */ |
958 | intf = of_graph_get_remote_port_parent(node: ep_node); |
959 | if (!intf) |
960 | continue; |
961 | |
962 | if (of_device_is_available(device: intf)) |
963 | drm_of_component_match_add(master: master_dev, matchptr, |
964 | compare: component_compare_of, node: intf); |
965 | |
966 | of_node_put(node: intf); |
967 | } |
968 | |
969 | return 0; |
970 | } |
971 | |
972 | #if !IS_REACHABLE(CONFIG_DRM_MSM_MDP5) || !IS_REACHABLE(CONFIG_DRM_MSM_DPU) |
973 | bool msm_disp_drv_should_bind(struct device *dev, bool dpu_driver) |
974 | { |
975 | /* If just a single driver is enabled, use it no matter what */ |
976 | return true; |
977 | } |
978 | #else |
979 | |
980 | static bool prefer_mdp5 = true; |
981 | MODULE_PARM_DESC(prefer_mdp5, "Select whether MDP5 or DPU driver should be preferred" ); |
982 | module_param(prefer_mdp5, bool, 0444); |
983 | |
984 | /* list all platforms supported by both mdp5 and dpu drivers */ |
985 | static const char *const msm_mdp5_dpu_migration[] = { |
986 | "qcom,sdm630-mdp5" , |
987 | "qcom,sdm660-mdp5" , |
988 | NULL, |
989 | }; |
990 | |
991 | bool msm_disp_drv_should_bind(struct device *dev, bool dpu_driver) |
992 | { |
993 | /* If it is not an MDP5 device, do not try MDP5 driver */ |
994 | if (!of_device_is_compatible(device: dev->of_node, "qcom,mdp5" )) |
995 | return dpu_driver; |
996 | |
997 | /* If it is not in the migration list, use MDP5 */ |
998 | if (!of_device_compatible_match(device: dev->of_node, compat: msm_mdp5_dpu_migration)) |
999 | return !dpu_driver; |
1000 | |
1001 | return prefer_mdp5 ? !dpu_driver : dpu_driver; |
1002 | } |
1003 | #endif |
1004 | |
1005 | /* |
1006 | * We don't know what's the best binding to link the gpu with the drm device. |
1007 | * Fow now, we just hunt for all the possible gpus that we support, and add them |
1008 | * as components. |
1009 | */ |
1010 | static const struct of_device_id msm_gpu_match[] = { |
1011 | { .compatible = "qcom,adreno" }, |
1012 | { .compatible = "qcom,adreno-3xx" }, |
1013 | { .compatible = "amd,imageon" }, |
1014 | { .compatible = "qcom,kgsl-3d0" }, |
1015 | { }, |
1016 | }; |
1017 | |
1018 | static int add_gpu_components(struct device *dev, |
1019 | struct component_match **matchptr) |
1020 | { |
1021 | struct device_node *np; |
1022 | |
1023 | np = of_find_matching_node(NULL, matches: msm_gpu_match); |
1024 | if (!np) |
1025 | return 0; |
1026 | |
1027 | if (of_device_is_available(device: np)) |
1028 | drm_of_component_match_add(master: dev, matchptr, compare: component_compare_of, node: np); |
1029 | |
1030 | of_node_put(node: np); |
1031 | |
1032 | return 0; |
1033 | } |
1034 | |
1035 | static int msm_drm_bind(struct device *dev) |
1036 | { |
1037 | return msm_drm_init(dev, drv: &msm_driver); |
1038 | } |
1039 | |
1040 | static void msm_drm_unbind(struct device *dev) |
1041 | { |
1042 | msm_drm_uninit(dev); |
1043 | } |
1044 | |
1045 | const struct component_master_ops msm_drm_ops = { |
1046 | .bind = msm_drm_bind, |
1047 | .unbind = msm_drm_unbind, |
1048 | }; |
1049 | |
1050 | int msm_drv_probe(struct device *master_dev, |
1051 | int (*kms_init)(struct drm_device *dev), |
1052 | struct msm_kms *kms) |
1053 | { |
1054 | struct msm_drm_private *priv; |
1055 | struct component_match *match = NULL; |
1056 | int ret; |
1057 | |
1058 | priv = devm_kzalloc(dev: master_dev, size: sizeof(*priv), GFP_KERNEL); |
1059 | if (!priv) |
1060 | return -ENOMEM; |
1061 | |
1062 | priv->kms = kms; |
1063 | priv->kms_init = kms_init; |
1064 | dev_set_drvdata(dev: master_dev, data: priv); |
1065 | |
1066 | /* Add mdp components if we have KMS. */ |
1067 | if (kms_init) { |
1068 | ret = add_components_mdp(master_dev, matchptr: &match); |
1069 | if (ret) |
1070 | return ret; |
1071 | } |
1072 | |
1073 | ret = add_gpu_components(dev: master_dev, matchptr: &match); |
1074 | if (ret) |
1075 | return ret; |
1076 | |
1077 | /* on all devices that I am aware of, iommu's which can map |
1078 | * any address the cpu can see are used: |
1079 | */ |
1080 | ret = dma_set_mask_and_coherent(dev: master_dev, mask: ~0); |
1081 | if (ret) |
1082 | return ret; |
1083 | |
1084 | ret = component_master_add_with_match(master_dev, &msm_drm_ops, match); |
1085 | if (ret) |
1086 | return ret; |
1087 | |
1088 | return 0; |
1089 | } |
1090 | |
1091 | /* |
1092 | * Platform driver: |
1093 | * Used only for headlesss GPU instances |
1094 | */ |
1095 | |
1096 | static int msm_pdev_probe(struct platform_device *pdev) |
1097 | { |
1098 | return msm_drv_probe(master_dev: &pdev->dev, NULL, NULL); |
1099 | } |
1100 | |
1101 | static void msm_pdev_remove(struct platform_device *pdev) |
1102 | { |
1103 | component_master_del(&pdev->dev, &msm_drm_ops); |
1104 | } |
1105 | |
1106 | static struct platform_driver msm_platform_driver = { |
1107 | .probe = msm_pdev_probe, |
1108 | .remove_new = msm_pdev_remove, |
1109 | .driver = { |
1110 | .name = "msm" , |
1111 | }, |
1112 | }; |
1113 | |
1114 | static int __init msm_drm_register(void) |
1115 | { |
1116 | if (!modeset) |
1117 | return -EINVAL; |
1118 | |
1119 | DBG("init" ); |
1120 | msm_mdp_register(); |
1121 | msm_dpu_register(); |
1122 | msm_dsi_register(); |
1123 | msm_hdmi_register(); |
1124 | msm_dp_register(); |
1125 | adreno_register(); |
1126 | msm_mdp4_register(); |
1127 | msm_mdss_register(); |
1128 | return platform_driver_register(&msm_platform_driver); |
1129 | } |
1130 | |
1131 | static void __exit msm_drm_unregister(void) |
1132 | { |
1133 | DBG("fini" ); |
1134 | platform_driver_unregister(&msm_platform_driver); |
1135 | msm_mdss_unregister(); |
1136 | msm_mdp4_unregister(); |
1137 | msm_dp_unregister(); |
1138 | msm_hdmi_unregister(); |
1139 | adreno_unregister(); |
1140 | msm_dsi_unregister(); |
1141 | msm_mdp_unregister(); |
1142 | msm_dpu_unregister(); |
1143 | } |
1144 | |
1145 | module_init(msm_drm_register); |
1146 | module_exit(msm_drm_unregister); |
1147 | |
1148 | MODULE_AUTHOR("Rob Clark <robdclark@gmail.com" ); |
1149 | MODULE_DESCRIPTION("MSM DRM Driver" ); |
1150 | MODULE_LICENSE("GPL" ); |
1151 | |