brintos

brintos / linux-shallow public Read only

0
0
Text · 23.3 KiB · 671eed4 Raw
842 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright 2018 Marty E. Plummer <hanetzer@startmail.com> */3/* Copyright 2019 Linaro, Ltd., Rob Herring <robh@kernel.org> */4/* Copyright 2019 Collabora ltd. */5 6#include <linux/module.h>7#include <linux/of.h>8#include <linux/pagemap.h>9#include <linux/platform_device.h>10#include <linux/pm_runtime.h>11#include <drm/panfrost_drm.h>12#include <drm/drm_drv.h>13#include <drm/drm_ioctl.h>14#include <drm/drm_syncobj.h>15#include <drm/drm_utils.h>16 17#include "panfrost_device.h"18#include "panfrost_gem.h"19#include "panfrost_mmu.h"20#include "panfrost_job.h"21#include "panfrost_gpu.h"22#include "panfrost_perfcnt.h"23 24static bool unstable_ioctls;25module_param_unsafe(unstable_ioctls, bool, 0600);26 27static int panfrost_ioctl_get_param(struct drm_device *ddev, void *data, struct drm_file *file)28{29	struct drm_panfrost_get_param *param = data;30	struct panfrost_device *pfdev = ddev->dev_private;31 32	if (param->pad != 0)33		return -EINVAL;34 35#define PANFROST_FEATURE(name, member)			\36	case DRM_PANFROST_PARAM_ ## name:		\37		param->value = pfdev->features.member;	\38		break39#define PANFROST_FEATURE_ARRAY(name, member, max)			\40	case DRM_PANFROST_PARAM_ ## name ## 0 ...			\41		DRM_PANFROST_PARAM_ ## name ## max:			\42		param->value = pfdev->features.member[param->param -	\43			DRM_PANFROST_PARAM_ ## name ## 0];		\44		break45 46	switch (param->param) {47		PANFROST_FEATURE(GPU_PROD_ID, id);48		PANFROST_FEATURE(GPU_REVISION, revision);49		PANFROST_FEATURE(SHADER_PRESENT, shader_present);50		PANFROST_FEATURE(TILER_PRESENT, tiler_present);51		PANFROST_FEATURE(L2_PRESENT, l2_present);52		PANFROST_FEATURE(STACK_PRESENT, stack_present);53		PANFROST_FEATURE(AS_PRESENT, as_present);54		PANFROST_FEATURE(JS_PRESENT, js_present);55		PANFROST_FEATURE(L2_FEATURES, l2_features);56		PANFROST_FEATURE(CORE_FEATURES, core_features);57		PANFROST_FEATURE(TILER_FEATURES, tiler_features);58		PANFROST_FEATURE(MEM_FEATURES, mem_features);59		PANFROST_FEATURE(MMU_FEATURES, mmu_features);60		PANFROST_FEATURE(THREAD_FEATURES, thread_features);61		PANFROST_FEATURE(MAX_THREADS, max_threads);62		PANFROST_FEATURE(THREAD_MAX_WORKGROUP_SZ,63				thread_max_workgroup_sz);64		PANFROST_FEATURE(THREAD_MAX_BARRIER_SZ,65				thread_max_barrier_sz);66		PANFROST_FEATURE(COHERENCY_FEATURES, coherency_features);67		PANFROST_FEATURE(AFBC_FEATURES, afbc_features);68		PANFROST_FEATURE_ARRAY(TEXTURE_FEATURES, texture_features, 3);69		PANFROST_FEATURE_ARRAY(JS_FEATURES, js_features, 15);70		PANFROST_FEATURE(NR_CORE_GROUPS, nr_core_groups);71		PANFROST_FEATURE(THREAD_TLS_ALLOC, thread_tls_alloc);72	default:73		return -EINVAL;74	}75 76	return 0;77}78 79static int panfrost_ioctl_create_bo(struct drm_device *dev, void *data,80		struct drm_file *file)81{82	struct panfrost_file_priv *priv = file->driver_priv;83	struct panfrost_gem_object *bo;84	struct drm_panfrost_create_bo *args = data;85	struct panfrost_gem_mapping *mapping;86	int ret;87 88	if (!args->size || args->pad ||89	    (args->flags & ~(PANFROST_BO_NOEXEC | PANFROST_BO_HEAP)))90		return -EINVAL;91 92	/* Heaps should never be executable */93	if ((args->flags & PANFROST_BO_HEAP) &&94	    !(args->flags & PANFROST_BO_NOEXEC))95		return -EINVAL;96 97	bo = panfrost_gem_create(dev, args->size, args->flags);98	if (IS_ERR(bo))99		return PTR_ERR(bo);100 101	ret = drm_gem_handle_create(file, &bo->base.base, &args->handle);102	if (ret)103		goto out;104 105	mapping = panfrost_gem_mapping_get(bo, priv);106	if (mapping) {107		args->offset = mapping->mmnode.start << PAGE_SHIFT;108		panfrost_gem_mapping_put(mapping);109	} else {110		/* This can only happen if the handle from111		 * drm_gem_handle_create() has already been guessed and freed112		 * by user space113		 */114		ret = -EINVAL;115	}116 117out:118	drm_gem_object_put(&bo->base.base);119	return ret;120}121 122/**123 * panfrost_lookup_bos() - Sets up job->bo[] with the GEM objects124 * referenced by the job.125 * @dev: DRM device126 * @file_priv: DRM file for this fd127 * @args: IOCTL args128 * @job: job being set up129 *130 * Resolve handles from userspace to BOs and attach them to job.131 *132 * Note that this function doesn't need to unreference the BOs on133 * failure, because that will happen at panfrost_job_cleanup() time.134 */135static int136panfrost_lookup_bos(struct drm_device *dev,137		  struct drm_file *file_priv,138		  struct drm_panfrost_submit *args,139		  struct panfrost_job *job)140{141	struct panfrost_file_priv *priv = file_priv->driver_priv;142	struct panfrost_gem_object *bo;143	unsigned int i;144	int ret;145 146	job->bo_count = args->bo_handle_count;147 148	if (!job->bo_count)149		return 0;150 151	ret = drm_gem_objects_lookup(file_priv,152				     (void __user *)(uintptr_t)args->bo_handles,153				     job->bo_count, &job->bos);154	if (ret)155		return ret;156 157	job->mappings = kvmalloc_array(job->bo_count,158				       sizeof(struct panfrost_gem_mapping *),159				       GFP_KERNEL | __GFP_ZERO);160	if (!job->mappings)161		return -ENOMEM;162 163	for (i = 0; i < job->bo_count; i++) {164		struct panfrost_gem_mapping *mapping;165 166		bo = to_panfrost_bo(job->bos[i]);167		mapping = panfrost_gem_mapping_get(bo, priv);168		if (!mapping) {169			ret = -EINVAL;170			break;171		}172 173		atomic_inc(&bo->gpu_usecount);174		job->mappings[i] = mapping;175	}176 177	return ret;178}179 180/**181 * panfrost_copy_in_sync() - Sets up job->deps with the sync objects182 * referenced by the job.183 * @dev: DRM device184 * @file_priv: DRM file for this fd185 * @args: IOCTL args186 * @job: job being set up187 *188 * Resolve syncobjs from userspace to fences and attach them to job.189 *190 * Note that this function doesn't need to unreference the fences on191 * failure, because that will happen at panfrost_job_cleanup() time.192 */193static int194panfrost_copy_in_sync(struct drm_device *dev,195		  struct drm_file *file_priv,196		  struct drm_panfrost_submit *args,197		  struct panfrost_job *job)198{199	u32 *handles;200	int ret = 0;201	int i, in_fence_count;202 203	in_fence_count = args->in_sync_count;204 205	if (!in_fence_count)206		return 0;207 208	handles = kvmalloc_array(in_fence_count, sizeof(u32), GFP_KERNEL);209	if (!handles) {210		ret = -ENOMEM;211		DRM_DEBUG("Failed to allocate incoming syncobj handles\n");212		goto fail;213	}214 215	if (copy_from_user(handles,216			   (void __user *)(uintptr_t)args->in_syncs,217			   in_fence_count * sizeof(u32))) {218		ret = -EFAULT;219		DRM_DEBUG("Failed to copy in syncobj handles\n");220		goto fail;221	}222 223	for (i = 0; i < in_fence_count; i++) {224		ret = drm_sched_job_add_syncobj_dependency(&job->base, file_priv,225							   handles[i], 0);226		if (ret)227			goto fail;228	}229 230fail:231	kvfree(handles);232	return ret;233}234 235static int panfrost_ioctl_submit(struct drm_device *dev, void *data,236		struct drm_file *file)237{238	struct panfrost_device *pfdev = dev->dev_private;239	struct panfrost_file_priv *file_priv = file->driver_priv;240	struct drm_panfrost_submit *args = data;241	struct drm_syncobj *sync_out = NULL;242	struct panfrost_job *job;243	int ret = 0, slot;244 245	if (!args->jc)246		return -EINVAL;247 248	if (args->requirements && args->requirements != PANFROST_JD_REQ_FS)249		return -EINVAL;250 251	if (args->out_sync > 0) {252		sync_out = drm_syncobj_find(file, args->out_sync);253		if (!sync_out)254			return -ENODEV;255	}256 257	job = kzalloc(sizeof(*job), GFP_KERNEL);258	if (!job) {259		ret = -ENOMEM;260		goto out_put_syncout;261	}262 263	kref_init(&job->refcount);264 265	job->pfdev = pfdev;266	job->jc = args->jc;267	job->requirements = args->requirements;268	job->flush_id = panfrost_gpu_get_latest_flush_id(pfdev);269	job->mmu = file_priv->mmu;270	job->engine_usage = &file_priv->engine_usage;271 272	slot = panfrost_job_get_slot(job);273 274	ret = drm_sched_job_init(&job->base,275				 &file_priv->sched_entity[slot],276				 1, NULL);277	if (ret)278		goto out_put_job;279 280	ret = panfrost_copy_in_sync(dev, file, args, job);281	if (ret)282		goto out_cleanup_job;283 284	ret = panfrost_lookup_bos(dev, file, args, job);285	if (ret)286		goto out_cleanup_job;287 288	ret = panfrost_job_push(job);289	if (ret)290		goto out_cleanup_job;291 292	/* Update the return sync object for the job */293	if (sync_out)294		drm_syncobj_replace_fence(sync_out, job->render_done_fence);295 296out_cleanup_job:297	if (ret)298		drm_sched_job_cleanup(&job->base);299out_put_job:300	panfrost_job_put(job);301out_put_syncout:302	if (sync_out)303		drm_syncobj_put(sync_out);304 305	return ret;306}307 308static int309panfrost_ioctl_wait_bo(struct drm_device *dev, void *data,310		       struct drm_file *file_priv)311{312	long ret;313	struct drm_panfrost_wait_bo *args = data;314	struct drm_gem_object *gem_obj;315	unsigned long timeout = drm_timeout_abs_to_jiffies(args->timeout_ns);316 317	if (args->pad)318		return -EINVAL;319 320	gem_obj = drm_gem_object_lookup(file_priv, args->handle);321	if (!gem_obj)322		return -ENOENT;323 324	ret = dma_resv_wait_timeout(gem_obj->resv, DMA_RESV_USAGE_READ,325				    true, timeout);326	if (!ret)327		ret = timeout ? -ETIMEDOUT : -EBUSY;328 329	drm_gem_object_put(gem_obj);330 331	return ret;332}333 334static int panfrost_ioctl_mmap_bo(struct drm_device *dev, void *data,335		      struct drm_file *file_priv)336{337	struct drm_panfrost_mmap_bo *args = data;338	struct drm_gem_object *gem_obj;339	int ret;340 341	if (args->flags != 0) {342		DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);343		return -EINVAL;344	}345 346	gem_obj = drm_gem_object_lookup(file_priv, args->handle);347	if (!gem_obj) {348		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);349		return -ENOENT;350	}351 352	/* Don't allow mmapping of heap objects as pages are not pinned. */353	if (to_panfrost_bo(gem_obj)->is_heap) {354		ret = -EINVAL;355		goto out;356	}357 358	ret = drm_gem_create_mmap_offset(gem_obj);359	if (ret == 0)360		args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);361 362out:363	drm_gem_object_put(gem_obj);364	return ret;365}366 367static int panfrost_ioctl_get_bo_offset(struct drm_device *dev, void *data,368			    struct drm_file *file_priv)369{370	struct panfrost_file_priv *priv = file_priv->driver_priv;371	struct drm_panfrost_get_bo_offset *args = data;372	struct panfrost_gem_mapping *mapping;373	struct drm_gem_object *gem_obj;374	struct panfrost_gem_object *bo;375 376	gem_obj = drm_gem_object_lookup(file_priv, args->handle);377	if (!gem_obj) {378		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);379		return -ENOENT;380	}381	bo = to_panfrost_bo(gem_obj);382 383	mapping = panfrost_gem_mapping_get(bo, priv);384	drm_gem_object_put(gem_obj);385 386	if (!mapping)387		return -EINVAL;388 389	args->offset = mapping->mmnode.start << PAGE_SHIFT;390	panfrost_gem_mapping_put(mapping);391	return 0;392}393 394static int panfrost_ioctl_madvise(struct drm_device *dev, void *data,395				  struct drm_file *file_priv)396{397	struct panfrost_file_priv *priv = file_priv->driver_priv;398	struct drm_panfrost_madvise *args = data;399	struct panfrost_device *pfdev = dev->dev_private;400	struct drm_gem_object *gem_obj;401	struct panfrost_gem_object *bo;402	int ret = 0;403 404	gem_obj = drm_gem_object_lookup(file_priv, args->handle);405	if (!gem_obj) {406		DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);407		return -ENOENT;408	}409 410	bo = to_panfrost_bo(gem_obj);411 412	ret = dma_resv_lock_interruptible(bo->base.base.resv, NULL);413	if (ret)414		goto out_put_object;415 416	mutex_lock(&pfdev->shrinker_lock);417	mutex_lock(&bo->mappings.lock);418	if (args->madv == PANFROST_MADV_DONTNEED) {419		struct panfrost_gem_mapping *first;420 421		first = list_first_entry(&bo->mappings.list,422					 struct panfrost_gem_mapping,423					 node);424 425		/*426		 * If we want to mark the BO purgeable, there must be only one427		 * user: the caller FD.428		 * We could do something smarter and mark the BO purgeable only429		 * when all its users have marked it purgeable, but globally430		 * visible/shared BOs are likely to never be marked purgeable431		 * anyway, so let's not bother.432		 */433		if (!list_is_singular(&bo->mappings.list) ||434		    WARN_ON_ONCE(first->mmu != priv->mmu)) {435			ret = -EINVAL;436			goto out_unlock_mappings;437		}438	}439 440	args->retained = drm_gem_shmem_madvise(&bo->base, args->madv);441 442	if (args->retained) {443		if (args->madv == PANFROST_MADV_DONTNEED)444			list_move_tail(&bo->base.madv_list,445				       &pfdev->shrinker_list);446		else if (args->madv == PANFROST_MADV_WILLNEED)447			list_del_init(&bo->base.madv_list);448	}449 450out_unlock_mappings:451	mutex_unlock(&bo->mappings.lock);452	mutex_unlock(&pfdev->shrinker_lock);453	dma_resv_unlock(bo->base.base.resv);454out_put_object:455	drm_gem_object_put(gem_obj);456	return ret;457}458 459int panfrost_unstable_ioctl_check(void)460{461	if (!unstable_ioctls)462		return -ENOSYS;463 464	return 0;465}466 467static int468panfrost_open(struct drm_device *dev, struct drm_file *file)469{470	int ret;471	struct panfrost_device *pfdev = dev->dev_private;472	struct panfrost_file_priv *panfrost_priv;473 474	panfrost_priv = kzalloc(sizeof(*panfrost_priv), GFP_KERNEL);475	if (!panfrost_priv)476		return -ENOMEM;477 478	panfrost_priv->pfdev = pfdev;479	file->driver_priv = panfrost_priv;480 481	panfrost_priv->mmu = panfrost_mmu_ctx_create(pfdev);482	if (IS_ERR(panfrost_priv->mmu)) {483		ret = PTR_ERR(panfrost_priv->mmu);484		goto err_free;485	}486 487	ret = panfrost_job_open(panfrost_priv);488	if (ret)489		goto err_job;490 491	return 0;492 493err_job:494	panfrost_mmu_ctx_put(panfrost_priv->mmu);495err_free:496	kfree(panfrost_priv);497	return ret;498}499 500static void501panfrost_postclose(struct drm_device *dev, struct drm_file *file)502{503	struct panfrost_file_priv *panfrost_priv = file->driver_priv;504 505	panfrost_perfcnt_close(file);506	panfrost_job_close(panfrost_priv);507 508	panfrost_mmu_ctx_put(panfrost_priv->mmu);509	kfree(panfrost_priv);510}511 512static const struct drm_ioctl_desc panfrost_drm_driver_ioctls[] = {513#define PANFROST_IOCTL(n, func, flags) \514	DRM_IOCTL_DEF_DRV(PANFROST_##n, panfrost_ioctl_##func, flags)515 516	PANFROST_IOCTL(SUBMIT,		submit,		DRM_RENDER_ALLOW),517	PANFROST_IOCTL(WAIT_BO,		wait_bo,	DRM_RENDER_ALLOW),518	PANFROST_IOCTL(CREATE_BO,	create_bo,	DRM_RENDER_ALLOW),519	PANFROST_IOCTL(MMAP_BO,		mmap_bo,	DRM_RENDER_ALLOW),520	PANFROST_IOCTL(GET_PARAM,	get_param,	DRM_RENDER_ALLOW),521	PANFROST_IOCTL(GET_BO_OFFSET,	get_bo_offset,	DRM_RENDER_ALLOW),522	PANFROST_IOCTL(PERFCNT_ENABLE,	perfcnt_enable,	DRM_RENDER_ALLOW),523	PANFROST_IOCTL(PERFCNT_DUMP,	perfcnt_dump,	DRM_RENDER_ALLOW),524	PANFROST_IOCTL(MADVISE,		madvise,	DRM_RENDER_ALLOW),525};526 527static void panfrost_gpu_show_fdinfo(struct panfrost_device *pfdev,528				     struct panfrost_file_priv *panfrost_priv,529				     struct drm_printer *p)530{531	int i;532 533	/*534	 * IMPORTANT NOTE: drm-cycles and drm-engine measurements are not535	 * accurate, as they only provide a rough estimation of the number of536	 * GPU cycles and CPU time spent in a given context. This is due to two537	 * different factors:538	 * - Firstly, we must consider the time the CPU and then the kernel539	 *   takes to process the GPU interrupt, which means additional time and540	 *   GPU cycles will be added in excess to the real figure.541	 * - Secondly, the pipelining done by the Job Manager (2 job slots per542	 *   engine) implies there is no way to know exactly how much time each543	 *   job spent on the GPU.544	 */545 546	static const char * const engine_names[] = {547		"fragment", "vertex-tiler", "compute-only"548	};549 550	BUILD_BUG_ON(ARRAY_SIZE(engine_names) != NUM_JOB_SLOTS);551 552	for (i = 0; i < NUM_JOB_SLOTS - 1; i++) {553		if (pfdev->profile_mode) {554			drm_printf(p, "drm-engine-%s:\t%llu ns\n",555				   engine_names[i], panfrost_priv->engine_usage.elapsed_ns[i]);556			drm_printf(p, "drm-cycles-%s:\t%llu\n",557				   engine_names[i], panfrost_priv->engine_usage.cycles[i]);558		}559		drm_printf(p, "drm-maxfreq-%s:\t%lu Hz\n",560			   engine_names[i], pfdev->pfdevfreq.fast_rate);561		drm_printf(p, "drm-curfreq-%s:\t%lu Hz\n",562			   engine_names[i], pfdev->pfdevfreq.current_frequency);563	}564}565 566static void panfrost_show_fdinfo(struct drm_printer *p, struct drm_file *file)567{568	struct drm_device *dev = file->minor->dev;569	struct panfrost_device *pfdev = dev->dev_private;570 571	panfrost_gpu_show_fdinfo(pfdev, file->driver_priv, p);572 573	drm_show_memory_stats(p, file);574}575 576static const struct file_operations panfrost_drm_driver_fops = {577	.owner = THIS_MODULE,578	DRM_GEM_FOPS,579	.show_fdinfo = drm_show_fdinfo,580};581 582/*583 * Panfrost driver version:584 * - 1.0 - initial interface585 * - 1.1 - adds HEAP and NOEXEC flags for CREATE_BO586 * - 1.2 - adds AFBC_FEATURES query587 */588static const struct drm_driver panfrost_drm_driver = {589	.driver_features	= DRIVER_RENDER | DRIVER_GEM | DRIVER_SYNCOBJ,590	.open			= panfrost_open,591	.postclose		= panfrost_postclose,592	.show_fdinfo		= panfrost_show_fdinfo,593	.ioctls			= panfrost_drm_driver_ioctls,594	.num_ioctls		= ARRAY_SIZE(panfrost_drm_driver_ioctls),595	.fops			= &panfrost_drm_driver_fops,596	.name			= "panfrost",597	.desc			= "panfrost DRM",598	.date			= "20180908",599	.major			= 1,600	.minor			= 2,601 602	.gem_create_object	= panfrost_gem_create_object,603	.gem_prime_import_sg_table = panfrost_gem_prime_import_sg_table,604};605 606static int panfrost_probe(struct platform_device *pdev)607{608	struct panfrost_device *pfdev;609	struct drm_device *ddev;610	int err;611 612	pfdev = devm_kzalloc(&pdev->dev, sizeof(*pfdev), GFP_KERNEL);613	if (!pfdev)614		return -ENOMEM;615 616	pfdev->pdev = pdev;617	pfdev->dev = &pdev->dev;618 619	platform_set_drvdata(pdev, pfdev);620 621	pfdev->comp = of_device_get_match_data(&pdev->dev);622	if (!pfdev->comp)623		return -ENODEV;624 625	pfdev->coherent = device_get_dma_attr(&pdev->dev) == DEV_DMA_COHERENT;626 627	/* Allocate and initialize the DRM device. */628	ddev = drm_dev_alloc(&panfrost_drm_driver, &pdev->dev);629	if (IS_ERR(ddev))630		return PTR_ERR(ddev);631 632	ddev->dev_private = pfdev;633	pfdev->ddev = ddev;634 635	mutex_init(&pfdev->shrinker_lock);636	INIT_LIST_HEAD(&pfdev->shrinker_list);637 638	err = panfrost_device_init(pfdev);639	if (err) {640		if (err != -EPROBE_DEFER)641			dev_err(&pdev->dev, "Fatal error during GPU init\n");642		goto err_out0;643	}644 645	pm_runtime_set_active(pfdev->dev);646	pm_runtime_mark_last_busy(pfdev->dev);647	pm_runtime_enable(pfdev->dev);648	pm_runtime_set_autosuspend_delay(pfdev->dev, 50); /* ~3 frames */649	pm_runtime_use_autosuspend(pfdev->dev);650 651	/*652	 * Register the DRM device with the core and the connectors with653	 * sysfs654	 */655	err = drm_dev_register(ddev, 0);656	if (err < 0)657		goto err_out1;658 659	err = panfrost_gem_shrinker_init(ddev);660	if (err)661		goto err_out2;662 663	return 0;664 665err_out2:666	drm_dev_unregister(ddev);667err_out1:668	pm_runtime_disable(pfdev->dev);669	panfrost_device_fini(pfdev);670	pm_runtime_set_suspended(pfdev->dev);671err_out0:672	drm_dev_put(ddev);673	return err;674}675 676static void panfrost_remove(struct platform_device *pdev)677{678	struct panfrost_device *pfdev = platform_get_drvdata(pdev);679	struct drm_device *ddev = pfdev->ddev;680 681	drm_dev_unregister(ddev);682	panfrost_gem_shrinker_cleanup(ddev);683 684	pm_runtime_get_sync(pfdev->dev);685	pm_runtime_disable(pfdev->dev);686	panfrost_device_fini(pfdev);687	pm_runtime_set_suspended(pfdev->dev);688 689	drm_dev_put(ddev);690}691 692static ssize_t profiling_show(struct device *dev,693			      struct device_attribute *attr, char *buf)694{695	struct panfrost_device *pfdev = dev_get_drvdata(dev);696 697	return sysfs_emit(buf, "%d\n", pfdev->profile_mode);698}699 700static ssize_t profiling_store(struct device *dev,701			       struct device_attribute *attr,702			       const char *buf, size_t len)703{704	struct panfrost_device *pfdev = dev_get_drvdata(dev);705	bool value;706	int err;707 708	err = kstrtobool(buf, &value);709	if (err)710		return err;711 712	pfdev->profile_mode = value;713 714	return len;715}716 717static DEVICE_ATTR_RW(profiling);718 719static struct attribute *panfrost_attrs[] = {720	&dev_attr_profiling.attr,721	NULL,722};723 724ATTRIBUTE_GROUPS(panfrost);725 726/*727 * The OPP core wants the supply names to be NULL terminated, but we need the728 * correct num_supplies value for regulator core. Hence, we NULL terminate here729 * and then initialize num_supplies with ARRAY_SIZE - 1.730 */731static const char * const default_supplies[] = { "mali", NULL };732static const struct panfrost_compatible default_data = {733	.num_supplies = ARRAY_SIZE(default_supplies) - 1,734	.supply_names = default_supplies,735	.num_pm_domains = 1, /* optional */736	.pm_domain_names = NULL,737};738 739static const struct panfrost_compatible amlogic_data = {740	.num_supplies = ARRAY_SIZE(default_supplies) - 1,741	.supply_names = default_supplies,742	.vendor_quirk = panfrost_gpu_amlogic_quirk,743};744 745/*746 * The old data with two power supplies for MT8183 is here only to747 * keep retro-compatibility with older devicetrees, as DVFS will748 * not work with this one.749 *750 * On new devicetrees please use the _b variant with a single and751 * coupled regulators instead.752 */753static const char * const mediatek_mt8183_supplies[] = { "mali", "sram", NULL };754static const char * const mediatek_mt8183_pm_domains[] = { "core0", "core1", "core2" };755static const struct panfrost_compatible mediatek_mt8183_data = {756	.num_supplies = ARRAY_SIZE(mediatek_mt8183_supplies) - 1,757	.supply_names = mediatek_mt8183_supplies,758	.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),759	.pm_domain_names = mediatek_mt8183_pm_domains,760};761 762static const char * const mediatek_mt8183_b_supplies[] = { "mali", NULL };763static const struct panfrost_compatible mediatek_mt8183_b_data = {764	.num_supplies = ARRAY_SIZE(mediatek_mt8183_b_supplies) - 1,765	.supply_names = mediatek_mt8183_b_supplies,766	.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),767	.pm_domain_names = mediatek_mt8183_pm_domains,768	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),769};770 771static const char * const mediatek_mt8186_pm_domains[] = { "core0", "core1" };772static const struct panfrost_compatible mediatek_mt8186_data = {773	.num_supplies = ARRAY_SIZE(mediatek_mt8183_b_supplies) - 1,774	.supply_names = mediatek_mt8183_b_supplies,775	.num_pm_domains = ARRAY_SIZE(mediatek_mt8186_pm_domains),776	.pm_domain_names = mediatek_mt8186_pm_domains,777	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),778};779 780/* MT8188 uses the same power domains and power supplies as MT8183 */781static const struct panfrost_compatible mediatek_mt8188_data = {782	.num_supplies = ARRAY_SIZE(mediatek_mt8183_b_supplies) - 1,783	.supply_names = mediatek_mt8183_b_supplies,784	.num_pm_domains = ARRAY_SIZE(mediatek_mt8183_pm_domains),785	.pm_domain_names = mediatek_mt8183_pm_domains,786	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),787};788 789static const char * const mediatek_mt8192_supplies[] = { "mali", NULL };790static const char * const mediatek_mt8192_pm_domains[] = { "core0", "core1", "core2",791							   "core3", "core4" };792static const struct panfrost_compatible mediatek_mt8192_data = {793	.num_supplies = ARRAY_SIZE(mediatek_mt8192_supplies) - 1,794	.supply_names = mediatek_mt8192_supplies,795	.num_pm_domains = ARRAY_SIZE(mediatek_mt8192_pm_domains),796	.pm_domain_names = mediatek_mt8192_pm_domains,797	.pm_features = BIT(GPU_PM_CLK_DIS) | BIT(GPU_PM_VREG_OFF),798};799 800static const struct of_device_id dt_match[] = {801	/* Set first to probe before the generic compatibles */802	{ .compatible = "amlogic,meson-gxm-mali",803	  .data = &amlogic_data, },804	{ .compatible = "amlogic,meson-g12a-mali",805	  .data = &amlogic_data, },806	{ .compatible = "arm,mali-t604", .data = &default_data, },807	{ .compatible = "arm,mali-t624", .data = &default_data, },808	{ .compatible = "arm,mali-t628", .data = &default_data, },809	{ .compatible = "arm,mali-t720", .data = &default_data, },810	{ .compatible = "arm,mali-t760", .data = &default_data, },811	{ .compatible = "arm,mali-t820", .data = &default_data, },812	{ .compatible = "arm,mali-t830", .data = &default_data, },813	{ .compatible = "arm,mali-t860", .data = &default_data, },814	{ .compatible = "arm,mali-t880", .data = &default_data, },815	{ .compatible = "arm,mali-bifrost", .data = &default_data, },816	{ .compatible = "arm,mali-valhall-jm", .data = &default_data, },817	{ .compatible = "mediatek,mt8183-mali", .data = &mediatek_mt8183_data },818	{ .compatible = "mediatek,mt8183b-mali", .data = &mediatek_mt8183_b_data },819	{ .compatible = "mediatek,mt8186-mali", .data = &mediatek_mt8186_data },820	{ .compatible = "mediatek,mt8188-mali", .data = &mediatek_mt8188_data },821	{ .compatible = "mediatek,mt8192-mali", .data = &mediatek_mt8192_data },822	{}823};824MODULE_DEVICE_TABLE(of, dt_match);825 826static struct platform_driver panfrost_driver = {827	.probe		= panfrost_probe,828	.remove_new	= panfrost_remove,829	.driver		= {830		.name	= "panfrost",831		.pm	= pm_ptr(&panfrost_pm_ops),832		.of_match_table = dt_match,833		.dev_groups = panfrost_groups,834	},835};836module_platform_driver(panfrost_driver);837 838MODULE_AUTHOR("Panfrost Project Developers");839MODULE_DESCRIPTION("Panfrost DRM Driver");840MODULE_LICENSE("GPL v2");841MODULE_SOFTDEP("pre: governor_simpleondemand");842