xref: /linux/drivers/gpu/drm/panfrost/panfrost_drv.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
1f3ba9122SRob Herring // SPDX-License-Identifier: GPL-2.0
2f3ba9122SRob Herring /* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */
3f3ba9122SRob Herring /* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */
4f3ba9122SRob Herring /* Copyright 2019 Collabora ltd. */
5f3ba9122SRob Herring 
6f3ba9122SRob Herring #include <linux/module.h>
7722d4f06SRob Herring #include <linux/of.h>
8f3ba9122SRob Herring #include <linux/pagemap.h>
9722d4f06SRob Herring #include <linux/platform_device.h>
10f3ba9122SRob Herring #include <linux/pm_runtime.h>
11f3ba9122SRob Herring #include <drm/panfrost_drm.h>
12f3ba9122SRob Herring #include <drm/drm_drv.h>
13f3ba9122SRob Herring #include <drm/drm_ioctl.h>
14f3ba9122SRob Herring #include <drm/drm_syncobj.h>
15f3ba9122SRob Herring #include <drm/drm_utils.h>
16f3ba9122SRob Herring 
17f3ba9122SRob Herring #include "panfrost_device.h"
18f3ba9122SRob Herring #include "panfrost_gem.h"
19f3ba9122SRob Herring #include "panfrost_mmu.h"
20f3ba9122SRob Herring #include "panfrost_job.h"
21f3ba9122SRob Herring #include "panfrost_gpu.h"
227786fd10SBoris Brezillon #include "panfrost_perfcnt.h"
23f3ba9122SRob Herring 
2492f0ad0bSBoris Brezillon static bool unstable_ioctls;
2592f0ad0bSBoris Brezillon module_param_unsafe(unstable_ioctls, bool, 0600);
2692f0ad0bSBoris Brezillon 
panfrost_ioctl_get_param(struct drm_device * ddev,void * data,struct drm_file * file)27f3ba9122SRob Herring static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file)
28f3ba9122SRob Herring {
29f3ba9122SRob Herring 	struct drm_panfrost_get_param *param = data;
30f3ba9122SRob Herring 	struct panfrost_device *pfdev = ddev->dev_private;
31f3ba9122SRob Herring 
32f3ba9122SRob Herring 	if (param->pad != 0)
33f3ba9122SRob Herring 		return -EINVAL;
34f3ba9122SRob Herring 
354bced8beSSteven Price #define PANFROST_FEATURE(name, member)			\
364bced8beSSteven Price 	case DRM_PANFROST_PARAM_ ## name:		\
374bced8beSSteven Price 		param->value = pfdev->features.member;	\
384bced8beSSteven Price 		break
394bced8beSSteven Price #define PANFROST_FEATURE_ARRAY(name, member, max)			\
404bced8beSSteven Price 	case DRM_PANFROST_PARAM_ ## name ## 0 ...			\
414bced8beSSteven Price 		DRM_PANFROST_PARAM_ ## name ## max:			\
424bced8beSSteven Price 		param->value = pfdev->features.member[param->param -	\
434bced8beSSteven Price 			DRM_PANFROST_PARAM_ ## name ## 0];		\
444bced8beSSteven Price 		break
454bced8beSSteven Price 
46f3ba9122SRob Herring 	switch (param->param) {
474bced8beSSteven Price 		PANFROST_FEATURE(GPU_PROD_ID, id);
484bced8beSSteven Price 		PANFROST_FEATURE(GPU_REVISION, revision);
494bced8beSSteven Price 		PANFROST_FEATURE(SHADER_PRESENT, shader_present);
504bced8beSSteven Price 		PANFROST_FEATURE(TILER_PRESENT, tiler_present);
514bced8beSSteven Price 		PANFROST_FEATURE(L2_PRESENT, l2_present);
524bced8beSSteven Price 		PANFROST_FEATURE(STACK_PRESENT, stack_present);
534bced8beSSteven Price 		PANFROST_FEATURE(AS_PRESENT, as_present);
544bced8beSSteven Price 		PANFROST_FEATURE(JS_PRESENT, js_present);
554bced8beSSteven Price 		PANFROST_FEATURE(L2_FEATURES, l2_features);
564bced8beSSteven Price 		PANFROST_FEATURE(CORE_FEATURES, core_features);
574bced8beSSteven Price 		PANFROST_FEATURE(TILER_FEATURES, tiler_features);
584bced8beSSteven Price 		PANFROST_FEATURE(MEM_FEATURES, mem_features);
594bced8beSSteven Price 		PANFROST_FEATURE(MMU_FEATURES, mmu_features);
604bced8beSSteven Price 		PANFROST_FEATURE(THREAD_FEATURES, thread_features);
614bced8beSSteven Price 		PANFROST_FEATURE(MAX_THREADS, max_threads);
624bced8beSSteven Price 		PANFROST_FEATURE(THREAD_MAX_WORKGROUP_SZ,
634bced8beSSteven Price 				thread_max_workgroup_sz);
644bced8beSSteven Price 		PANFROST_FEATURE(THREAD_MAX_BARRIER_SZ,
654bced8beSSteven Price 				thread_max_barrier_sz);
664bced8beSSteven Price 		PANFROST_FEATURE(COHERENCY_FEATURES, coherency_features);
673e2926f8SAlyssa Rosenzweig 		PANFROST_FEATURE(AFBC_FEATURES, afbc_features);
684bced8beSSteven Price 		PANFROST_FEATURE_ARRAY(TEXTURE_FEATURES, texture_features, 3);
694bced8beSSteven Price 		PANFROST_FEATURE_ARRAY(JS_FEATURES, js_features, 15);
704bced8beSSteven Price 		PANFROST_FEATURE(NR_CORE_GROUPS, nr_core_groups);
714bced8beSSteven Price 		PANFROST_FEATURE(THREAD_TLS_ALLOC, thread_tls_alloc);
72f3ba9122SRob Herring 	default:
73f3ba9122SRob Herring 		return -EINVAL;
74f3ba9122SRob Herring 	}
75f3ba9122SRob Herring 
76f3ba9122SRob Herring 	return 0;
77f3ba9122SRob Herring }
78f3ba9122SRob Herring 
panfrost_ioctl_create_bo(struct drm_device * dev,void * data,struct drm_file * file)79f3ba9122SRob Herring static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,
80f3ba9122SRob Herring 		struct drm_file *file)
81f3ba9122SRob Herring {
82bdefca2dSBoris Brezillon 	struct panfrost_file_priv *priv = file->driver_priv;
83203270c0SRob Herring 	struct panfrost_gem_object *bo;
84f3ba9122SRob Herring 	struct drm_panfrost_create_bo *args = data;
85bdefca2dSBoris Brezillon 	struct panfrost_gem_mapping *mapping;
864217c6acSSteven Price 	int ret;
87f3ba9122SRob Herring 
88203270c0SRob Herring 	if (!args->size || args->pad ||
89187d2929SRob Herring 	    (args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP)))
90187d2929SRob Herring 		return -EINVAL;
91187d2929SRob Herring 
92187d2929SRob Herring 	/* Heaps should never be executable */
93187d2929SRob Herring 	if ((args->flags & PANFROST_BO_HEAP) &&
94187d2929SRob Herring 	    !(args->flags & PANFROST_BO_NOEXEC))
95f3ba9122SRob Herring 		return -EINVAL;
96f3ba9122SRob Herring 
974217c6acSSteven Price 	bo = panfrost_gem_create(dev, args->size, args->flags);
98203270c0SRob Herring 	if (IS_ERR(bo))
99203270c0SRob Herring 		return PTR_ERR(bo);
100f3ba9122SRob Herring 
1014217c6acSSteven Price 	ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);
1024217c6acSSteven Price 	if (ret)
1034217c6acSSteven Price 		goto out;
104bdefca2dSBoris Brezillon 
1054217c6acSSteven Price 	mapping = panfrost_gem_mapping_get(bo, priv);
1064217c6acSSteven Price 	if (mapping) {
107bdefca2dSBoris Brezillon 		args->offset = mapping->mmnode.start << PAGE_SHIFT;
108bdefca2dSBoris Brezillon 		panfrost_gem_mapping_put(mapping);
1094217c6acSSteven Price 	} else {
1104217c6acSSteven Price 		/* This can only happen if the handle from
1114217c6acSSteven Price 		 * drm_gem_handle_create() has already been guessed and freed
1124217c6acSSteven Price 		 * by user space
1134217c6acSSteven Price 		 */
1144217c6acSSteven Price 		ret = -EINVAL;
1154217c6acSSteven Price 	}
116f3ba9122SRob Herring 
1174217c6acSSteven Price out:
1184217c6acSSteven Price 	drm_gem_object_put(&bo->base.base);
1194217c6acSSteven Price 	return ret;
120f3ba9122SRob Herring }
121f3ba9122SRob Herring 
122f3ba9122SRob Herring /**
123f3ba9122SRob Herring  * panfrost_lookup_bos() - Sets up job->bo[] with the GEM objects
124f3ba9122SRob Herring  * referenced by the job.
125f3ba9122SRob Herring  * @dev: DRM device
126f3ba9122SRob Herring  * @file_priv: DRM file for this fd
127f3ba9122SRob Herring  * @args: IOCTL args
128f3ba9122SRob Herring  * @job: job being set up
129f3ba9122SRob Herring  *
130f3ba9122SRob Herring  * Resolve handles from userspace to BOs and attach them to job.
131f3ba9122SRob Herring  *
132f3ba9122SRob Herring  * Note that this function doesn't need to unreference the BOs on
133f3ba9122SRob Herring  * failure, because that will happen at panfrost_job_cleanup() time.
134f3ba9122SRob Herring  */
135f3ba9122SRob Herring static int
panfrost_lookup_bos(struct drm_device * dev,struct drm_file * file_priv,struct drm_panfrost_submit * args,struct panfrost_job * job)136f3ba9122SRob Herring panfrost_lookup_bos(struct drm_device *dev,
137f3ba9122SRob Herring 		  struct drm_file *file_priv,
138f3ba9122SRob Herring 		  struct drm_panfrost_submit *args,
139f3ba9122SRob Herring 		  struct panfrost_job *job)
140f3ba9122SRob Herring {
141bdefca2dSBoris Brezillon 	struct panfrost_file_priv *priv = file_priv->driver_priv;
142bdefca2dSBoris Brezillon 	struct panfrost_gem_object *bo;
143bdefca2dSBoris Brezillon 	unsigned int i;
144bdefca2dSBoris Brezillon 	int ret;
145bdefca2dSBoris Brezillon 
146f3ba9122SRob Herring 	job->bo_count = args->bo_handle_count;
147f3ba9122SRob Herring 
148f3ba9122SRob Herring 	if (!job->bo_count)
149f3ba9122SRob Herring 		return 0;
150f3ba9122SRob Herring 
151bdefca2dSBoris Brezillon 	ret = drm_gem_objects_lookup(file_priv,
152f3ba9122SRob Herring 				     (void __user *)(uintptr_t)args->bo_handles,
153f3ba9122SRob Herring 				     job->bo_count, &job->bos);
154bdefca2dSBoris Brezillon 	if (ret)
155bdefca2dSBoris Brezillon 		return ret;
156bdefca2dSBoris Brezillon 
157bdefca2dSBoris Brezillon 	job->mappings = kvmalloc_array(job->bo_count,
158bdefca2dSBoris Brezillon 				       sizeof(struct panfrost_gem_mapping *),
159bdefca2dSBoris Brezillon 				       GFP_KERNEL | __GFP_ZERO);
160bdefca2dSBoris Brezillon 	if (!job->mappings)
161bdefca2dSBoris Brezillon 		return -ENOMEM;
162bdefca2dSBoris Brezillon 
163bdefca2dSBoris Brezillon 	for (i = 0; i < job->bo_count; i++) {
164bdefca2dSBoris Brezillon 		struct panfrost_gem_mapping *mapping;
165bdefca2dSBoris Brezillon 
166bdefca2dSBoris Brezillon 		bo = to_panfrost_bo(job->bos[i]);
167bdefca2dSBoris Brezillon 		mapping = panfrost_gem_mapping_get(bo, priv);
168bdefca2dSBoris Brezillon 		if (!mapping) {
169bdefca2dSBoris Brezillon 			ret = -EINVAL;
170bdefca2dSBoris Brezillon 			break;
171bdefca2dSBoris Brezillon 		}
172bdefca2dSBoris Brezillon 
1737e0cf7e9SBoris Brezillon 		atomic_inc(&bo->gpu_usecount);
174bdefca2dSBoris Brezillon 		job->mappings[i] = mapping;
175bdefca2dSBoris Brezillon 	}
176bdefca2dSBoris Brezillon 
177bdefca2dSBoris Brezillon 	return ret;
178f3ba9122SRob Herring }
179f3ba9122SRob Herring 
180f3ba9122SRob Herring /**
1817d7a0fc4SDaniel Vetter  * panfrost_copy_in_sync() - Sets up job->deps with the sync objects
182f3ba9122SRob Herring  * referenced by the job.
183f3ba9122SRob Herring  * @dev: DRM device
184f3ba9122SRob Herring  * @file_priv: DRM file for this fd
185f3ba9122SRob Herring  * @args: IOCTL args
186f3ba9122SRob Herring  * @job: job being set up
187f3ba9122SRob Herring  *
188f3ba9122SRob Herring  * Resolve syncobjs from userspace to fences and attach them to job.
189f3ba9122SRob Herring  *
190f3ba9122SRob Herring  * Note that this function doesn't need to unreference the fences on
191f3ba9122SRob Herring  * failure, because that will happen at panfrost_job_cleanup() time.
192f3ba9122SRob Herring  */
193f3ba9122SRob Herring static int
panfrost_copy_in_sync(struct drm_device * dev,struct drm_file * file_priv,struct drm_panfrost_submit * args,struct panfrost_job * job)194f3ba9122SRob Herring panfrost_copy_in_sync(struct drm_device *dev,
195f3ba9122SRob Herring 		  struct drm_file *file_priv,
196f3ba9122SRob Herring 		  struct drm_panfrost_submit *args,
197f3ba9122SRob Herring 		  struct panfrost_job *job)
198f3ba9122SRob Herring {
199f3ba9122SRob Herring 	u32 *handles;
200f3ba9122SRob Herring 	int ret = 0;
2017d7a0fc4SDaniel Vetter 	int i, in_fence_count;
202f3ba9122SRob Herring 
2037d7a0fc4SDaniel Vetter 	in_fence_count = args->in_sync_count;
204f3ba9122SRob Herring 
2057d7a0fc4SDaniel Vetter 	if (!in_fence_count)
206f3ba9122SRob Herring 		return 0;
207f3ba9122SRob Herring 
2087d7a0fc4SDaniel Vetter 	handles = kvmalloc_array(in_fence_count, sizeof(u32), GFP_KERNEL);
209f3ba9122SRob Herring 	if (!handles) {
210f3ba9122SRob Herring 		ret = -ENOMEM;
211f3ba9122SRob Herring 		DRM_DEBUG("Failed to allocate incoming syncobj handles\n");
212f3ba9122SRob Herring 		goto fail;
213f3ba9122SRob Herring 	}
214f3ba9122SRob Herring 
215f3ba9122SRob Herring 	if (copy_from_user(handles,
216f3ba9122SRob Herring 			   (void __user *)(uintptr_t)args->in_syncs,
2177d7a0fc4SDaniel Vetter 			   in_fence_count * sizeof(u32))) {
218f3ba9122SRob Herring 		ret = -EFAULT;
219f3ba9122SRob Herring 		DRM_DEBUG("Failed to copy in syncobj handles\n");
220f3ba9122SRob Herring 		goto fail;
221f3ba9122SRob Herring 	}
222f3ba9122SRob Herring 
2237d7a0fc4SDaniel Vetter 	for (i = 0; i < in_fence_count; i++) {
2244636c4a5SMaíra Canal 		ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv,
2254636c4a5SMaíra Canal 							   handles[i], 0);
2267d7a0fc4SDaniel Vetter 		if (ret)
227f3ba9122SRob Herring 			goto fail;
228f3ba9122SRob Herring 	}
229f3ba9122SRob Herring 
230f3ba9122SRob Herring fail:
231f3ba9122SRob Herring 	kvfree(handles);
232f3ba9122SRob Herring 	return ret;
233f3ba9122SRob Herring }
234f3ba9122SRob Herring 
panfrost_ioctl_submit(struct drm_device * dev,void * data,struct drm_file * file)235f3ba9122SRob Herring static int panfrost_ioctl_submit(struct drm_device *dev, void *data,
236f3ba9122SRob Herring 		struct drm_file *file)
237f3ba9122SRob Herring {
238f3ba9122SRob Herring 	struct panfrost_device *pfdev = dev->dev_private;
2396e516fafSSteven Price 	struct panfrost_file_priv *file_priv = file->driver_priv;
240f3ba9122SRob Herring 	struct drm_panfrost_submit *args = data;
2416ff408e6STomeu Vizoso 	struct drm_syncobj *sync_out = NULL;
242f3ba9122SRob Herring 	struct panfrost_job *job;
24353516280SDaniel Vetter 	int ret = 0, slot;
244f3ba9122SRob Herring 
2456ff408e6STomeu Vizoso 	if (!args->jc)
2466ff408e6STomeu Vizoso 		return -EINVAL;
2476ff408e6STomeu Vizoso 
2486ff408e6STomeu Vizoso 	if (args->requirements && args->requirements != PANFROST_JD_REQ_FS)
2496ff408e6STomeu Vizoso 		return -EINVAL;
2506ff408e6STomeu Vizoso 
2516ff408e6STomeu Vizoso 	if (args->out_sync > 0) {
2526ff408e6STomeu Vizoso 		sync_out = drm_syncobj_find(file, args->out_sync);
2536ff408e6STomeu Vizoso 		if (!sync_out)
2546ff408e6STomeu Vizoso 			return -ENODEV;
2556ff408e6STomeu Vizoso 	}
2566ff408e6STomeu Vizoso 
257f3ba9122SRob Herring 	job = kzalloc(sizeof(*job), GFP_KERNEL);
2586ff408e6STomeu Vizoso 	if (!job) {
2596ff408e6STomeu Vizoso 		ret = -ENOMEM;
260771d2053SBoris Brezillon 		goto out_put_syncout;
2616ff408e6STomeu Vizoso 	}
262f3ba9122SRob Herring 
263f3ba9122SRob Herring 	kref_init(&job->refcount);
264f3ba9122SRob Herring 
265f3ba9122SRob Herring 	job->pfdev = pfdev;
266f3ba9122SRob Herring 	job->jc = args->jc;
267f3ba9122SRob Herring 	job->requirements = args->requirements;
268f3ba9122SRob Herring 	job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev);
2696e516fafSSteven Price 	job->mmu = file_priv->mmu;
270f11b0417SAdrián Larumbe 	job->engine_usage = &file_priv->engine_usage;
271f3ba9122SRob Herring 
27253516280SDaniel Vetter 	slot = panfrost_job_get_slot(job);
27353516280SDaniel Vetter 
27453516280SDaniel Vetter 	ret = drm_sched_job_init(&job->base,
2756e516fafSSteven Price 				 &file_priv->sched_entity[slot],
276a78422e9SDanilo Krummrich 				 1, NULL);
27753516280SDaniel Vetter 	if (ret)
278771d2053SBoris Brezillon 		goto out_put_job;
27953516280SDaniel Vetter 
280f3ba9122SRob Herring 	ret = panfrost_copy_in_sync(dev, file, args, job);
281f3ba9122SRob Herring 	if (ret)
282771d2053SBoris Brezillon 		goto out_cleanup_job;
283f3ba9122SRob Herring 
284f3ba9122SRob Herring 	ret = panfrost_lookup_bos(dev, file, args, job);
285f3ba9122SRob Herring 	if (ret)
286771d2053SBoris Brezillon 		goto out_cleanup_job;
287f3ba9122SRob Herring 
288f3ba9122SRob Herring 	ret = panfrost_job_push(job);
289f3ba9122SRob Herring 	if (ret)
290771d2053SBoris Brezillon 		goto out_cleanup_job;
291f3ba9122SRob Herring 
292f3ba9122SRob Herring 	/* Update the return sync object for the job */
2936ff408e6STomeu Vizoso 	if (sync_out)
294f3ba9122SRob Herring 		drm_syncobj_replace_fence(sync_out, job->render_done_fence);
295f3ba9122SRob Herring 
296771d2053SBoris Brezillon out_cleanup_job:
297771d2053SBoris Brezillon 	if (ret)
29853516280SDaniel Vetter 		drm_sched_job_cleanup(&job->base);
299771d2053SBoris Brezillon out_put_job:
300f3ba9122SRob Herring 	panfrost_job_put(job);
301771d2053SBoris Brezillon out_put_syncout:
302cc2e787eSTomeu Vizoso 	if (sync_out)
3036ff408e6STomeu Vizoso 		drm_syncobj_put(sync_out);
304f3ba9122SRob Herring 
305f3ba9122SRob Herring 	return ret;
306f3ba9122SRob Herring }
307f3ba9122SRob Herring 
308f3ba9122SRob Herring static int
panfrost_ioctl_wait_bo(struct drm_device * dev,void * data,struct drm_file * file_priv)309f3ba9122SRob Herring panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,
310f3ba9122SRob Herring 		       struct drm_file *file_priv)
311f3ba9122SRob Herring {
312f3ba9122SRob Herring 	long ret;
313f3ba9122SRob Herring 	struct drm_panfrost_wait_bo *args = data;
314f3ba9122SRob Herring 	struct drm_gem_object *gem_obj;
315f3ba9122SRob Herring 	unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);
316f3ba9122SRob Herring 
317f3ba9122SRob Herring 	if (args->pad)
318f3ba9122SRob Herring 		return -EINVAL;
319f3ba9122SRob Herring 
320f3ba9122SRob Herring 	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
321f3ba9122SRob Herring 	if (!gem_obj)
322f3ba9122SRob Herring 		return -ENOENT;
323f3ba9122SRob Herring 
3247bc80a54SChristian König 	ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_READ,
3257bc80a54SChristian König 				    true, timeout);
326f3ba9122SRob Herring 	if (!ret)
327f3ba9122SRob Herring 		ret = timeout ? -ETIMEDOUT : -EBUSY;
328f3ba9122SRob Herring 
329496d0cc6SEmil Velikov 	drm_gem_object_put(gem_obj);
330f3ba9122SRob Herring 
331f3ba9122SRob Herring 	return ret;
332f3ba9122SRob Herring }
333f3ba9122SRob Herring 
panfrost_ioctl_mmap_bo(struct drm_device * dev,void * data,struct drm_file * file_priv)334f3ba9122SRob Herring static int panfrost_ioctl_mmap_bo(struct drm_device *dev, void *data,
335f3ba9122SRob Herring 		      struct drm_file *file_priv)
336f3ba9122SRob Herring {
337f3ba9122SRob Herring 	struct drm_panfrost_mmap_bo *args = data;
338e6be0a99SRob Herring 	struct drm_gem_object *gem_obj;
339e6be0a99SRob Herring 	int ret;
340f3ba9122SRob Herring 
341f3ba9122SRob Herring 	if (args->flags != 0) {
342f3ba9122SRob Herring 		DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);
343f3ba9122SRob Herring 		return -EINVAL;
344f3ba9122SRob Herring 	}
345f3ba9122SRob Herring 
346e6be0a99SRob Herring 	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
347e6be0a99SRob Herring 	if (!gem_obj) {
348e6be0a99SRob Herring 		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
349e6be0a99SRob Herring 		return -ENOENT;
350e6be0a99SRob Herring 	}
351e6be0a99SRob Herring 
352187d2929SRob Herring 	/* Don't allow mmapping of heap objects as pages are not pinned. */
3533bb69dbcSBoris Brezillon 	if (to_panfrost_bo(gem_obj)->is_heap) {
3543bb69dbcSBoris Brezillon 		ret = -EINVAL;
3553bb69dbcSBoris Brezillon 		goto out;
3563bb69dbcSBoris Brezillon 	}
357187d2929SRob Herring 
358e6be0a99SRob Herring 	ret = drm_gem_create_mmap_offset(gem_obj);
359e6be0a99SRob Herring 	if (ret == 0)
360e6be0a99SRob Herring 		args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
361e6be0a99SRob Herring 
3623bb69dbcSBoris Brezillon out:
363496d0cc6SEmil Velikov 	drm_gem_object_put(gem_obj);
364e6be0a99SRob Herring 	return ret;
365f3ba9122SRob Herring }
366f3ba9122SRob Herring 
panfrost_ioctl_get_bo_offset(struct drm_device * dev,void * data,struct drm_file * file_priv)367f3ba9122SRob Herring static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,
368f3ba9122SRob Herring 			    struct drm_file *file_priv)
369f3ba9122SRob Herring {
370bdefca2dSBoris Brezillon 	struct panfrost_file_priv *priv = file_priv->driver_priv;
371f3ba9122SRob Herring 	struct drm_panfrost_get_bo_offset *args = data;
372bdefca2dSBoris Brezillon 	struct panfrost_gem_mapping *mapping;
373f3ba9122SRob Herring 	struct drm_gem_object *gem_obj;
374f3ba9122SRob Herring 	struct panfrost_gem_object *bo;
375f3ba9122SRob Herring 
376f3ba9122SRob Herring 	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
377f3ba9122SRob Herring 	if (!gem_obj) {
378f3ba9122SRob Herring 		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
379f3ba9122SRob Herring 		return -ENOENT;
380f3ba9122SRob Herring 	}
381f3ba9122SRob Herring 	bo = to_panfrost_bo(gem_obj);
382f3ba9122SRob Herring 
383bdefca2dSBoris Brezillon 	mapping = panfrost_gem_mapping_get(bo, priv);
384496d0cc6SEmil Velikov 	drm_gem_object_put(gem_obj);
385bdefca2dSBoris Brezillon 
386bdefca2dSBoris Brezillon 	if (!mapping)
387bdefca2dSBoris Brezillon 		return -EINVAL;
388bdefca2dSBoris Brezillon 
389bdefca2dSBoris Brezillon 	args->offset = mapping->mmnode.start << PAGE_SHIFT;
390bdefca2dSBoris Brezillon 	panfrost_gem_mapping_put(mapping);
391f3ba9122SRob Herring 	return 0;
392f3ba9122SRob Herring }
393f3ba9122SRob Herring 
panfrost_ioctl_madvise(struct drm_device * dev,void * data,struct drm_file * file_priv)394013b6510SRob Herring static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,
395013b6510SRob Herring 				  struct drm_file *file_priv)
396013b6510SRob Herring {
397bdefca2dSBoris Brezillon 	struct panfrost_file_priv *priv = file_priv->driver_priv;
398013b6510SRob Herring 	struct drm_panfrost_madvise *args = data;
399013b6510SRob Herring 	struct panfrost_device *pfdev = dev->dev_private;
400013b6510SRob Herring 	struct drm_gem_object *gem_obj;
401bdefca2dSBoris Brezillon 	struct panfrost_gem_object *bo;
402bdefca2dSBoris Brezillon 	int ret = 0;
403013b6510SRob Herring 
404013b6510SRob Herring 	gem_obj = drm_gem_object_lookup(file_priv, args->handle);
405013b6510SRob Herring 	if (!gem_obj) {
406013b6510SRob Herring 		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
407013b6510SRob Herring 		return -ENOENT;
408013b6510SRob Herring 	}
409013b6510SRob Herring 
410bdefca2dSBoris Brezillon 	bo = to_panfrost_bo(gem_obj);
411bdefca2dSBoris Brezillon 
41221aa27ddSDmitry Osipenko 	ret = dma_resv_lock_interruptible(bo->base.base.resv, NULL);
41321aa27ddSDmitry Osipenko 	if (ret)
41421aa27ddSDmitry Osipenko 		goto out_put_object;
41521aa27ddSDmitry Osipenko 
41670cc7795SBoris Brezillon 	mutex_lock(&pfdev->shrinker_lock);
417bdefca2dSBoris Brezillon 	mutex_lock(&bo->mappings.lock);
418bdefca2dSBoris Brezillon 	if (args->madv == PANFROST_MADV_DONTNEED) {
419bdefca2dSBoris Brezillon 		struct panfrost_gem_mapping *first;
420bdefca2dSBoris Brezillon 
421bdefca2dSBoris Brezillon 		first = list_first_entry(&bo->mappings.list,
422bdefca2dSBoris Brezillon 					 struct panfrost_gem_mapping,
423bdefca2dSBoris Brezillon 					 node);
424bdefca2dSBoris Brezillon 
425bdefca2dSBoris Brezillon 		/*
426bdefca2dSBoris Brezillon 		 * If we want to mark the BO purgeable, there must be only one
427bdefca2dSBoris Brezillon 		 * user: the caller FD.
428bdefca2dSBoris Brezillon 		 * We could do something smarter and mark the BO purgeable only
429bdefca2dSBoris Brezillon 		 * when all its users have marked it purgeable, but globally
430bdefca2dSBoris Brezillon 		 * visible/shared BOs are likely to never be marked purgeable
431bdefca2dSBoris Brezillon 		 * anyway, so let's not bother.
432bdefca2dSBoris Brezillon 		 */
433bdefca2dSBoris Brezillon 		if (!list_is_singular(&bo->mappings.list) ||
4347fdc48ccSBoris Brezillon 		    WARN_ON_ONCE(first->mmu != priv->mmu)) {
435bdefca2dSBoris Brezillon 			ret = -EINVAL;
436bdefca2dSBoris Brezillon 			goto out_unlock_mappings;
437bdefca2dSBoris Brezillon 		}
438bdefca2dSBoris Brezillon 	}
439bdefca2dSBoris Brezillon 
440a193f3b4SThomas Zimmermann 	args->retained = drm_gem_shmem_madvise(&bo->base, args->madv);
441013b6510SRob Herring 
442013b6510SRob Herring 	if (args->retained) {
443013b6510SRob Herring 		if (args->madv == PANFROST_MADV_DONTNEED)
4449fc33eaaSDmitry Osipenko 			list_move_tail(&bo->base.madv_list,
44570cc7795SBoris Brezillon 				       &pfdev->shrinker_list);
446013b6510SRob Herring 		else if (args->madv == PANFROST_MADV_WILLNEED)
447013b6510SRob Herring 			list_del_init(&bo->base.madv_list);
448013b6510SRob Herring 	}
449bdefca2dSBoris Brezillon 
450bdefca2dSBoris Brezillon out_unlock_mappings:
451bdefca2dSBoris Brezillon 	mutex_unlock(&bo->mappings.lock);
45270cc7795SBoris Brezillon 	mutex_unlock(&pfdev->shrinker_lock);
45321aa27ddSDmitry Osipenko 	dma_resv_unlock(bo->base.base.resv);
45421aa27ddSDmitry Osipenko out_put_object:
455496d0cc6SEmil Velikov 	drm_gem_object_put(gem_obj);
456bdefca2dSBoris Brezillon 	return ret;
457013b6510SRob Herring }
458013b6510SRob Herring 
panfrost_unstable_ioctl_check(void)45992f0ad0bSBoris Brezillon int panfrost_unstable_ioctl_check(void)
46092f0ad0bSBoris Brezillon {
46192f0ad0bSBoris Brezillon 	if (!unstable_ioctls)
46292f0ad0bSBoris Brezillon 		return -ENOSYS;
46392f0ad0bSBoris Brezillon 
46492f0ad0bSBoris Brezillon 	return 0;
46592f0ad0bSBoris Brezillon }
46692f0ad0bSBoris Brezillon 
467f3ba9122SRob Herring static int
panfrost_open(struct drm_device * dev,struct drm_file * file)468f3ba9122SRob Herring panfrost_open(struct drm_device *dev, struct drm_file *file)
469f3ba9122SRob Herring {
4707282f764SRob Herring 	int ret;
471f3ba9122SRob Herring 	struct panfrost_device *pfdev = dev->dev_private;
472f3ba9122SRob Herring 	struct panfrost_file_priv *panfrost_priv;
473f3ba9122SRob Herring 
474f3ba9122SRob Herring 	panfrost_priv = kzalloc(sizeof(*panfrost_priv), GFP_KERNEL);
475f3ba9122SRob Herring 	if (!panfrost_priv)
476f3ba9122SRob Herring 		return -ENOMEM;
477f3ba9122SRob Herring 
478f3ba9122SRob Herring 	panfrost_priv->pfdev = pfdev;
479f3ba9122SRob Herring 	file->driver_priv = panfrost_priv;
480f3ba9122SRob Herring 
4817fdc48ccSBoris Brezillon 	panfrost_priv->mmu = panfrost_mmu_ctx_create(pfdev);
4827fdc48ccSBoris Brezillon 	if (IS_ERR(panfrost_priv->mmu)) {
4837fdc48ccSBoris Brezillon 		ret = PTR_ERR(panfrost_priv->mmu);
4847fdc48ccSBoris Brezillon 		goto err_free;
4857fdc48ccSBoris Brezillon 	}
4867282f764SRob Herring 
4877282f764SRob Herring 	ret = panfrost_job_open(panfrost_priv);
4887282f764SRob Herring 	if (ret)
4897282f764SRob Herring 		goto err_job;
4907282f764SRob Herring 
4917282f764SRob Herring 	return 0;
4927282f764SRob Herring 
4937282f764SRob Herring err_job:
4947fdc48ccSBoris Brezillon 	panfrost_mmu_ctx_put(panfrost_priv->mmu);
4957fdc48ccSBoris Brezillon err_free:
4967282f764SRob Herring 	kfree(panfrost_priv);
4977282f764SRob Herring 	return ret;
498f3ba9122SRob Herring }
499f3ba9122SRob Herring 
500f3ba9122SRob Herring static void
panfrost_postclose(struct drm_device * dev,struct drm_file * file)501f3ba9122SRob Herring panfrost_postclose(struct drm_device *dev, struct drm_file *file)
502f3ba9122SRob Herring {
503f3ba9122SRob Herring 	struct panfrost_file_priv *panfrost_priv = file->driver_priv;
504f3ba9122SRob Herring 
5050a523998SBoris Brezillon 	panfrost_perfcnt_close(file);
506f3ba9122SRob Herring 	panfrost_job_close(panfrost_priv);
507f3ba9122SRob Herring 
5087fdc48ccSBoris Brezillon 	panfrost_mmu_ctx_put(panfrost_priv->mmu);
509f3ba9122SRob Herring 	kfree(panfrost_priv);
510f3ba9122SRob Herring }
511f3ba9122SRob Herring 
512f3ba9122SRob Herring static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {
513f3ba9122SRob Herring #define PANFROST_IOCTL(n, func, flags) \
514f3ba9122SRob Herring 	DRM_IOCTL_DEF_DRV(PANFROST_##n, panfrost_ioctl_##func, flags)
515f3ba9122SRob Herring 
516c1572b75SEmil Velikov 	PANFROST_IOCTL(SUBMIT,		submit,		DRM_RENDER_ALLOW),
517f3ba9122SRob Herring 	PANFROST_IOCTL(WAIT_BO,		wait_bo,	DRM_RENDER_ALLOW),
518f3ba9122SRob Herring 	PANFROST_IOCTL(CREATE_BO,	create_bo,	DRM_RENDER_ALLOW),
519f3ba9122SRob Herring 	PANFROST_IOCTL(MMAP_BO,		mmap_bo,	DRM_RENDER_ALLOW),
520f3ba9122SRob Herring 	PANFROST_IOCTL(GET_PARAM,	get_param,	DRM_RENDER_ALLOW),
521f3ba9122SRob Herring 	PANFROST_IOCTL(GET_BO_OFFSET,	get_bo_offset,	DRM_RENDER_ALLOW),
5227786fd10SBoris Brezillon 	PANFROST_IOCTL(PERFCNT_ENABLE,	perfcnt_enable,	DRM_RENDER_ALLOW),
5237786fd10SBoris Brezillon 	PANFROST_IOCTL(PERFCNT_DUMP,	perfcnt_dump,	DRM_RENDER_ALLOW),
524013b6510SRob Herring 	PANFROST_IOCTL(MADVISE,		madvise,	DRM_RENDER_ALLOW),
525f3ba9122SRob Herring };
526f3ba9122SRob Herring 
panfrost_gpu_show_fdinfo(struct panfrost_device * pfdev,struct panfrost_file_priv * panfrost_priv,struct drm_printer * p)527f11b0417SAdrián Larumbe static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev,
528f11b0417SAdrián Larumbe 				     struct panfrost_file_priv *panfrost_priv,
529f11b0417SAdrián Larumbe 				     struct drm_printer *p)
530f11b0417SAdrián Larumbe {
531f11b0417SAdrián Larumbe 	int i;
532f11b0417SAdrián Larumbe 
533f11b0417SAdrián Larumbe 	/*
534f11b0417SAdrián Larumbe 	 * IMPORTANT NOTE: drm-cycles and drm-engine measurements are not
535f11b0417SAdrián Larumbe 	 * accurate, as they only provide a rough estimation of the number of
536f11b0417SAdrián Larumbe 	 * GPU cycles and CPU time spent in a given context. This is due to two
537f11b0417SAdrián Larumbe 	 * different factors:
538f11b0417SAdrián Larumbe 	 * - Firstly, we must consider the time the CPU and then the kernel
539f11b0417SAdrián Larumbe 	 *   takes to process the GPU interrupt, which means additional time and
540f11b0417SAdrián Larumbe 	 *   GPU cycles will be added in excess to the real figure.
541f11b0417SAdrián Larumbe 	 * - Secondly, the pipelining done by the Job Manager (2 job slots per
542f11b0417SAdrián Larumbe 	 *   engine) implies there is no way to know exactly how much time each
543f11b0417SAdrián Larumbe 	 *   job spent on the GPU.
544f11b0417SAdrián Larumbe 	 */
545f11b0417SAdrián Larumbe 
546f11b0417SAdrián Larumbe 	static const char * const engine_names[] = {
547f11b0417SAdrián Larumbe 		"fragment", "vertex-tiler", "compute-only"
548f11b0417SAdrián Larumbe 	};
549f11b0417SAdrián Larumbe 
550f11b0417SAdrián Larumbe 	BUILD_BUG_ON(ARRAY_SIZE(engine_names) != NUM_JOB_SLOTS);
551f11b0417SAdrián Larumbe 
552f11b0417SAdrián Larumbe 	for (i = 0; i < NUM_JOB_SLOTS - 1; i++) {
553dfe4fd26SAdrián Larumbe 		if (pfdev->profile_mode) {
554f11b0417SAdrián Larumbe 			drm_printf(p, "drm-engine-%s:\t%llu ns\n",
555f11b0417SAdrián Larumbe 				   engine_names[i], panfrost_priv->engine_usage.elapsed_ns[i]);
556f11b0417SAdrián Larumbe 			drm_printf(p, "drm-cycles-%s:\t%llu\n",
557f11b0417SAdrián Larumbe 				   engine_names[i], panfrost_priv->engine_usage.cycles[i]);
558dfe4fd26SAdrián Larumbe 		}
559f11b0417SAdrián Larumbe 		drm_printf(p, "drm-maxfreq-%s:\t%lu Hz\n",
560f11b0417SAdrián Larumbe 			   engine_names[i], pfdev->pfdevfreq.fast_rate);
561f11b0417SAdrián Larumbe 		drm_printf(p, "drm-curfreq-%s:\t%lu Hz\n",
562f11b0417SAdrián Larumbe 			   engine_names[i], pfdev->pfdevfreq.current_frequency);
563f11b0417SAdrián Larumbe 	}
564f11b0417SAdrián Larumbe }
565f11b0417SAdrián Larumbe 
panfrost_show_fdinfo(struct drm_printer * p,struct drm_file * file)566f11b0417SAdrián Larumbe static void panfrost_show_fdinfo(struct drm_printer *p, struct drm_file *file)
567f11b0417SAdrián Larumbe {
568f11b0417SAdrián Larumbe 	struct drm_device *dev = file->minor->dev;
569f11b0417SAdrián Larumbe 	struct panfrost_device *pfdev = dev->dev_private;
570f11b0417SAdrián Larumbe 
571f11b0417SAdrián Larumbe 	panfrost_gpu_show_fdinfo(pfdev, file->driver_priv, p);
5729ccdac7aSAdrián Larumbe 
5739ccdac7aSAdrián Larumbe 	drm_show_memory_stats(p, file);
574f11b0417SAdrián Larumbe }
575f11b0417SAdrián Larumbe 
576f11b0417SAdrián Larumbe static const struct file_operations panfrost_drm_driver_fops = {
577f11b0417SAdrián Larumbe 	.owner = THIS_MODULE,
578f11b0417SAdrián Larumbe 	DRM_GEM_FOPS,
579f11b0417SAdrián Larumbe 	.show_fdinfo = drm_show_fdinfo,
580f11b0417SAdrián Larumbe };
581f3ba9122SRob Herring 
5821c2b9390SRob Herring /*
5831c2b9390SRob Herring  * Panfrost driver version:
5841c2b9390SRob Herring  * - 1.0 - initial interface
5851c2b9390SRob Herring  * - 1.1 - adds HEAP and NOEXEC flags for CREATE_BO
5863e2926f8SAlyssa Rosenzweig  * - 1.2 - adds AFBC_FEATURES query
5871c2b9390SRob Herring  */
58870a59dd8SDaniel Vetter static const struct drm_driver panfrost_drm_driver = {
5890424fdafSDaniel Vetter 	.driver_features	= DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,
590f3ba9122SRob Herring 	.open			= panfrost_open,
591f3ba9122SRob Herring 	.postclose		= panfrost_postclose,
592f11b0417SAdrián Larumbe 	.show_fdinfo		= panfrost_show_fdinfo,
593f3ba9122SRob Herring 	.ioctls			= panfrost_drm_driver_ioctls,
594f3ba9122SRob Herring 	.num_ioctls		= ARRAY_SIZE(panfrost_drm_driver_ioctls),
595f3ba9122SRob Herring 	.fops			= &panfrost_drm_driver_fops,
596f3ba9122SRob Herring 	.name			= "panfrost",
597f3ba9122SRob Herring 	.desc			= "panfrost DRM",
598f3ba9122SRob Herring 	.date			= "20180908",
599f3ba9122SRob Herring 	.major			= 1,
6003e2926f8SAlyssa Rosenzweig 	.minor			= 2,
601f3ba9122SRob Herring 
602f3ba9122SRob Herring 	.gem_create_object	= panfrost_gem_create_object,
603f3ba9122SRob Herring 	.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,
604f3ba9122SRob Herring };
605f3ba9122SRob Herring 
panfrost_probe(struct platform_device * pdev)606f3ba9122SRob Herring static int panfrost_probe(struct platform_device *pdev)
607f3ba9122SRob Herring {
608f3ba9122SRob Herring 	struct panfrost_device *pfdev;
609f3ba9122SRob Herring 	struct drm_device *ddev;
610f3ba9122SRob Herring 	int err;
611f3ba9122SRob Herring 
612f3ba9122SRob Herring 	pfdev = devm_kzalloc(&pdev->dev, sizeof(*pfdev), GFP_KERNEL);
613f3ba9122SRob Herring 	if (!pfdev)
614f3ba9122SRob Herring 		return -ENOMEM;
615f3ba9122SRob Herring 
616f3ba9122SRob Herring 	pfdev->pdev = pdev;
617f3ba9122SRob Herring 	pfdev->dev = &pdev->dev;
618f3ba9122SRob Herring 
619f3ba9122SRob Herring 	platform_set_drvdata(pdev, pfdev);
620f3ba9122SRob Herring 
6213e1399bcSNicolas Boichat 	pfdev->comp = of_device_get_match_data(&pdev->dev);
6223e1399bcSNicolas Boichat 	if (!pfdev->comp)
6233e1399bcSNicolas Boichat 		return -ENODEV;
6243e1399bcSNicolas Boichat 
625268af50fSRobin Murphy 	pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;
626268af50fSRobin Murphy 
62781d9d7f8STom Rix 	/* Allocate and initialize the DRM device. */
628f3ba9122SRob Herring 	ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev);
629f3ba9122SRob Herring 	if (IS_ERR(ddev))
630f3ba9122SRob Herring 		return PTR_ERR(ddev);
631f3ba9122SRob Herring 
632f3ba9122SRob Herring 	ddev->dev_private = pfdev;
633f3ba9122SRob Herring 	pfdev->ddev = ddev;
634f3ba9122SRob Herring 
635013b6510SRob Herring 	mutex_init(&pfdev->shrinker_lock);
636013b6510SRob Herring 	INIT_LIST_HEAD(&pfdev->shrinker_list);
637f3ba9122SRob Herring 
638f3ba9122SRob Herring 	err = panfrost_device_init(pfdev);
639f3ba9122SRob Herring 	if (err) {
6405450f361SRobin Murphy 		if (err != -EPROBE_DEFER)
641f3ba9122SRob Herring 			dev_err(&pdev->dev, "Fatal error during GPU init\n");
642f3ba9122SRob Herring 		goto err_out0;
643f3ba9122SRob Herring 	}
644f3ba9122SRob Herring 
64563543079SRob Herring 	pm_runtime_set_active(pfdev->dev);
64663543079SRob Herring 	pm_runtime_mark_last_busy(pfdev->dev);
64763543079SRob Herring 	pm_runtime_enable(pfdev->dev);
64863543079SRob Herring 	pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */
64963543079SRob Herring 	pm_runtime_use_autosuspend(pfdev->dev);
65063543079SRob Herring 
651f3ba9122SRob Herring 	/*
652f3ba9122SRob Herring 	 * Register the DRM device with the core and the connectors with
653f3ba9122SRob Herring 	 * sysfs
654f3ba9122SRob Herring 	 */
655f3ba9122SRob Herring 	err = drm_dev_register(ddev, 0);
656f3ba9122SRob Herring 	if (err < 0)
65725e247bbSClément Péron 		goto err_out1;
658f3ba9122SRob Herring 
659e11c4f3aSQi Zheng 	err = panfrost_gem_shrinker_init(ddev);
660e11c4f3aSQi Zheng 	if (err)
661e11c4f3aSQi Zheng 		goto err_out2;
662013b6510SRob Herring 
663f3ba9122SRob Herring 	return 0;
664f3ba9122SRob Herring 
665e11c4f3aSQi Zheng err_out2:
666e11c4f3aSQi Zheng 	drm_dev_unregister(ddev);
667f3ba9122SRob Herring err_out1:
66825e247bbSClément Péron 	pm_runtime_disable(pfdev->dev);
669f3ba9122SRob Herring 	panfrost_device_fini(pfdev);
670876b15d2SSteven Price 	pm_runtime_set_suspended(pfdev->dev);
671f3ba9122SRob Herring err_out0:
672f3ba9122SRob Herring 	drm_dev_put(ddev);
673f3ba9122SRob Herring 	return err;
674f3ba9122SRob Herring }
675f3ba9122SRob Herring 
panfrost_remove(struct platform_device * pdev)676e41977a8SUwe Kleine-König static void panfrost_remove(struct platform_device *pdev)
677f3ba9122SRob Herring {
678f3ba9122SRob Herring 	struct panfrost_device *pfdev = platform_get_drvdata(pdev);
679f3ba9122SRob Herring 	struct drm_device *ddev = pfdev->ddev;
680f3ba9122SRob Herring 
681f3ba9122SRob Herring 	drm_dev_unregister(ddev);
682013b6510SRob Herring 	panfrost_gem_shrinker_cleanup(ddev);
683aebe8c22SRob Herring 
684f3ba9122SRob Herring 	pm_runtime_get_sync(pfdev->dev);
685aebe8c22SRob Herring 	pm_runtime_disable(pfdev->dev);
686876b15d2SSteven Price 	panfrost_device_fini(pfdev);
687876b15d2SSteven Price 	pm_runtime_set_suspended(pfdev->dev);
688aebe8c22SRob Herring 
689f3ba9122SRob Herring 	drm_dev_put(ddev);
690f3ba9122SRob Herring }
691f3ba9122SRob Herring 
profiling_show(struct device * dev,struct device_attribute * attr,char * buf)692b12f3ea7SAdrián Larumbe static ssize_t profiling_show(struct device *dev,
693b12f3ea7SAdrián Larumbe 			      struct device_attribute *attr, char *buf)
694b12f3ea7SAdrián Larumbe {
695b12f3ea7SAdrián Larumbe 	struct panfrost_device *pfdev = dev_get_drvdata(dev);
696b12f3ea7SAdrián Larumbe 
697b12f3ea7SAdrián Larumbe 	return sysfs_emit(buf, "%d\n", pfdev->profile_mode);
698b12f3ea7SAdrián Larumbe }
699b12f3ea7SAdrián Larumbe 
profiling_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)700b12f3ea7SAdrián Larumbe static ssize_t profiling_store(struct device *dev,
701b12f3ea7SAdrián Larumbe 			       struct device_attribute *attr,
702b12f3ea7SAdrián Larumbe 			       const char *buf, size_t len)
703b12f3ea7SAdrián Larumbe {
704b12f3ea7SAdrián Larumbe 	struct panfrost_device *pfdev = dev_get_drvdata(dev);
705b12f3ea7SAdrián Larumbe 	bool value;
706b12f3ea7SAdrián Larumbe 	int err;
707b12f3ea7SAdrián Larumbe 
708b12f3ea7SAdrián Larumbe 	err = kstrtobool(buf, &value);
709b12f3ea7SAdrián Larumbe 	if (err)
710b12f3ea7SAdrián Larumbe 		return err;
711b12f3ea7SAdrián Larumbe 
712b12f3ea7SAdrián Larumbe 	pfdev->profile_mode = value;
713b12f3ea7SAdrián Larumbe 
714b12f3ea7SAdrián Larumbe 	return len;
715b12f3ea7SAdrián Larumbe }
716b12f3ea7SAdrián Larumbe 
717b12f3ea7SAdrián Larumbe static DEVICE_ATTR_RW(profiling);
718b12f3ea7SAdrián Larumbe 
719b12f3ea7SAdrián Larumbe static struct attribute *panfrost_attrs[] = {
720b12f3ea7SAdrián Larumbe 	&dev_attr_profiling.attr,
721b12f3ea7SAdrián Larumbe 	NULL,
722b12f3ea7SAdrián Larumbe };
723b12f3ea7SAdrián Larumbe 
724b12f3ea7SAdrián Larumbe ATTRIBUTE_GROUPS(panfrost);
725b12f3ea7SAdrián Larumbe 
72687686cc8SViresh Kumar /*
72787686cc8SViresh Kumar  * The OPP core wants the supply names to be NULL terminated, but we need the
72887686cc8SViresh Kumar  * correct num_supplies value for regulator core. Hence, we NULL terminate here
72987686cc8SViresh Kumar  * and then initialize num_supplies with ARRAY_SIZE - 1.
73087686cc8SViresh Kumar  */
73187686cc8SViresh Kumar static const char * const default_supplies[] = { "mali", NULL };
7323e1399bcSNicolas Boichat static const struct panfrost_compatible default_data = {
73387686cc8SViresh Kumar 	.num_supplies = ARRAY_SIZE(default_supplies) - 1,
7343e1399bcSNicolas Boichat 	.supply_names = default_supplies,
735506629c8SNicolas Boichat 	.num_pm_domains = 1, /* optional */
736506629c8SNicolas Boichat 	.pm_domain_names = NULL,
7373e1399bcSNicolas Boichat };
7383e1399bcSNicolas Boichat 
739afcd0c7dSNeil Armstrong static const struct panfrost_compatible amlogic_data = {
74087686cc8SViresh Kumar 	.num_supplies = ARRAY_SIZE(default_supplies) - 1,
741afcd0c7dSNeil Armstrong 	.supply_names = default_supplies,
742afcd0c7dSNeil Armstrong 	.vendor_quirk = panfrost_gpu_amlogic_quirk,
743afcd0c7dSNeil Armstrong };
744afcd0c7dSNeil Armstrong 
745ab1a072cSAngeloGioacchino Del Regno /*
746ab1a072cSAngeloGioacchino Del Regno  * The old data with two power supplies for MT8183 is here only to
747ab1a072cSAngeloGioacchino Del Regno  * keep retro-compatibility with older devicetrees, as DVFS will
748ab1a072cSAngeloGioacchino Del Regno  * not work with this one.
749ab1a072cSAngeloGioacchino Del Regno  *
750ab1a072cSAngeloGioacchino Del Regno  * On new devicetrees please use the _b variant with a single and
751ab1a072cSAngeloGioacchino Del Regno  * coupled regulators instead.
752ab1a072cSAngeloGioacchino Del Regno  */
75387686cc8SViresh Kumar static const char * const mediatek_mt8183_supplies[] = { "mali", "sram", NULL };
754d52ce709SJiapeng Chong static const char * const mediatek_mt8183_pm_domains[] = { "core0", "core1", "core2" };
7551275e417SNicolas Boichat static const struct panfrost_compatible mediatek_mt8183_data = {
75687686cc8SViresh Kumar 	.num_supplies = ARRAY_SIZE(mediatek_mt8183_supplies) - 1,
7571275e417SNicolas Boichat 	.supply_names = mediatek_mt8183_supplies,
7581275e417SNicolas Boichat 	.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),
7591275e417SNicolas Boichat 	.pm_domain_names = mediatek_mt8183_pm_domains,
7601275e417SNicolas Boichat };
7611275e417SNicolas Boichat 
762ab1a072cSAngeloGioacchino Del Regno static const char * const mediatek_mt8183_b_supplies[] = { "mali", NULL };
763ab1a072cSAngeloGioacchino Del Regno static const struct panfrost_compatible mediatek_mt8183_b_data = {
764ab1a072cSAngeloGioacchino Del Regno 	.num_supplies = ARRAY_SIZE(mediatek_mt8183_b_supplies) - 1,
765ab1a072cSAngeloGioacchino Del Regno 	.supply_names = mediatek_mt8183_b_supplies,
766ab1a072cSAngeloGioacchino Del Regno 	.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),
767ab1a072cSAngeloGioacchino Del Regno 	.pm_domain_names = mediatek_mt8183_pm_domains,
768540527b1SAngeloGioacchino Del Regno 	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),
769ab1a072cSAngeloGioacchino Del Regno };
770ab1a072cSAngeloGioacchino Del Regno 
771901cdf66SAngeloGioacchino Del Regno static const char * const mediatek_mt8186_pm_domains[] = { "core0", "core1" };
772901cdf66SAngeloGioacchino Del Regno static const struct panfrost_compatible mediatek_mt8186_data = {
773901cdf66SAngeloGioacchino Del Regno 	.num_supplies = ARRAY_SIZE(mediatek_mt8183_b_supplies) - 1,
774901cdf66SAngeloGioacchino Del Regno 	.supply_names = mediatek_mt8183_b_supplies,
775901cdf66SAngeloGioacchino Del Regno 	.num_pm_domains = ARRAY_SIZE(mediatek_mt8186_pm_domains),
776901cdf66SAngeloGioacchino Del Regno 	.pm_domain_names = mediatek_mt8186_pm_domains,
777540527b1SAngeloGioacchino Del Regno 	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),
778901cdf66SAngeloGioacchino Del Regno };
779901cdf66SAngeloGioacchino Del Regno 
78072533b67SAngeloGioacchino Del Regno /* MT8188 uses the same power domains and power supplies as MT8183 */
78172533b67SAngeloGioacchino Del Regno static const struct panfrost_compatible mediatek_mt8188_data = {
78272533b67SAngeloGioacchino Del Regno 	.num_supplies = ARRAY_SIZE(mediatek_mt8183_b_supplies) - 1,
78372533b67SAngeloGioacchino Del Regno 	.supply_names = mediatek_mt8183_b_supplies,
78472533b67SAngeloGioacchino Del Regno 	.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),
78572533b67SAngeloGioacchino Del Regno 	.pm_domain_names = mediatek_mt8183_pm_domains,
78672533b67SAngeloGioacchino Del Regno 	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),
78772533b67SAngeloGioacchino Del Regno };
78872533b67SAngeloGioacchino Del Regno 
7894c681180SAlyssa Rosenzweig static const char * const mediatek_mt8192_supplies[] = { "mali", NULL };
7904c681180SAlyssa Rosenzweig static const char * const mediatek_mt8192_pm_domains[] = { "core0", "core1", "core2",
7914c681180SAlyssa Rosenzweig 							   "core3", "core4" };
7924c681180SAlyssa Rosenzweig static const struct panfrost_compatible mediatek_mt8192_data = {
7934c681180SAlyssa Rosenzweig 	.num_supplies = ARRAY_SIZE(mediatek_mt8192_supplies) - 1,
7944c681180SAlyssa Rosenzweig 	.supply_names = mediatek_mt8192_supplies,
7954c681180SAlyssa Rosenzweig 	.num_pm_domains = ARRAY_SIZE(mediatek_mt8192_pm_domains),
7964c681180SAlyssa Rosenzweig 	.pm_domain_names = mediatek_mt8192_pm_domains,
797540527b1SAngeloGioacchino Del Regno 	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),
7984c681180SAlyssa Rosenzweig };
7994c681180SAlyssa Rosenzweig 
800f3ba9122SRob Herring static const struct of_device_id dt_match[] = {
801afcd0c7dSNeil Armstrong 	/* Set first to probe before the generic compatibles */
802afcd0c7dSNeil Armstrong 	{ .compatible = "amlogic,meson-gxm-mali",
803afcd0c7dSNeil Armstrong 	  .data = &amlogic_data, },
804afcd0c7dSNeil Armstrong 	{ .compatible = "amlogic,meson-g12a-mali",
805afcd0c7dSNeil Armstrong 	  .data = &amlogic_data, },
8063e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t604", .data = &default_data, },
8073e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t624", .data = &default_data, },
8083e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t628", .data = &default_data, },
8093e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t720", .data = &default_data, },
8103e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t760", .data = &default_data, },
8113e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t820", .data = &default_data, },
8123e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t830", .data = &default_data, },
8133e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t860", .data = &default_data, },
8143e1399bcSNicolas Boichat 	{ .compatible = "arm,mali-t880", .data = &default_data, },
81572ef7fe9STomeu Vizoso 	{ .compatible = "arm,mali-bifrost", .data = &default_data, },
816952cd974SAlyssa Rosenzweig 	{ .compatible = "arm,mali-valhall-jm", .data = &default_data, },
8171275e417SNicolas Boichat 	{ .compatible = "mediatek,mt8183-mali", .data = &mediatek_mt8183_data },
818ab1a072cSAngeloGioacchino Del Regno 	{ .compatible = "mediatek,mt8183b-mali", .data = &mediatek_mt8183_b_data },
819901cdf66SAngeloGioacchino Del Regno 	{ .compatible = "mediatek,mt8186-mali", .data = &mediatek_mt8186_data },
82072533b67SAngeloGioacchino Del Regno 	{ .compatible = "mediatek,mt8188-mali", .data = &mediatek_mt8188_data },
8214c681180SAlyssa Rosenzweig 	{ .compatible = "mediatek,mt8192-mali", .data = &mediatek_mt8192_data },
822f3ba9122SRob Herring 	{}
823f3ba9122SRob Herring };
824f3ba9122SRob Herring MODULE_DEVICE_TABLE(of, dt_match);
825f3ba9122SRob Herring 
826f3ba9122SRob Herring static struct platform_driver panfrost_driver = {
827f3ba9122SRob Herring 	.probe		= panfrost_probe,
828e41977a8SUwe Kleine-König 	.remove_new	= panfrost_remove,
829f3ba9122SRob Herring 	.driver		= {
830f3ba9122SRob Herring 		.name	= "panfrost",
83153d36818SPaul Cercueil 		.pm	= pm_ptr(&panfrost_pm_ops),
832f3ba9122SRob Herring 		.of_match_table = dt_match,
833b12f3ea7SAdrián Larumbe 		.dev_groups = panfrost_groups,
834f3ba9122SRob Herring 	},
835f3ba9122SRob Herring };
836f3ba9122SRob Herring module_platform_driver(panfrost_driver);
837f3ba9122SRob Herring 
838f3ba9122SRob Herring MODULE_AUTHOR("Panfrost Project Developers");
839f3ba9122SRob Herring MODULE_DESCRIPTION("Panfrost DRM Driver");
840f3ba9122SRob Herring MODULE_LICENSE("GPL v2");
841*80f4e627SDragan Simic MODULE_SOFTDEP("pre: governor_simpleondemand");
842