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