162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH 462306a36Sopenharmony_ci * Copyright (C) 2012-2016 NVIDIA CORPORATION. All rights reserved. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/bitops.h> 862306a36Sopenharmony_ci#include <linux/host1x.h> 962306a36Sopenharmony_ci#include <linux/idr.h> 1062306a36Sopenharmony_ci#include <linux/iommu.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/pm_runtime.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <drm/drm_aperture.h> 1662306a36Sopenharmony_ci#include <drm/drm_atomic.h> 1762306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h> 1862306a36Sopenharmony_ci#include <drm/drm_debugfs.h> 1962306a36Sopenharmony_ci#include <drm/drm_drv.h> 2062306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 2162306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 2262306a36Sopenharmony_ci#include <drm/drm_ioctl.h> 2362306a36Sopenharmony_ci#include <drm/drm_prime.h> 2462306a36Sopenharmony_ci#include <drm/drm_vblank.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) 2762306a36Sopenharmony_ci#include <asm/dma-iommu.h> 2862306a36Sopenharmony_ci#endif 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include "dc.h" 3162306a36Sopenharmony_ci#include "drm.h" 3262306a36Sopenharmony_ci#include "gem.h" 3362306a36Sopenharmony_ci#include "uapi.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define DRIVER_NAME "tegra" 3662306a36Sopenharmony_ci#define DRIVER_DESC "NVIDIA Tegra graphics" 3762306a36Sopenharmony_ci#define DRIVER_DATE "20120330" 3862306a36Sopenharmony_ci#define DRIVER_MAJOR 1 3962306a36Sopenharmony_ci#define DRIVER_MINOR 0 4062306a36Sopenharmony_ci#define DRIVER_PATCHLEVEL 0 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define CARVEOUT_SZ SZ_64M 4362306a36Sopenharmony_ci#define CDMA_GATHER_FETCHES_MAX_NB 16383 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int tegra_atomic_check(struct drm_device *drm, 4662306a36Sopenharmony_ci struct drm_atomic_state *state) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int err; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci err = drm_atomic_helper_check(drm, state); 5162306a36Sopenharmony_ci if (err < 0) 5262306a36Sopenharmony_ci return err; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return tegra_display_hub_atomic_check(drm, state); 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { 5862306a36Sopenharmony_ci .fb_create = tegra_fb_create, 5962306a36Sopenharmony_ci .atomic_check = tegra_atomic_check, 6062306a36Sopenharmony_ci .atomic_commit = drm_atomic_helper_commit, 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic void tegra_atomic_post_commit(struct drm_device *drm, 6462306a36Sopenharmony_ci struct drm_atomic_state *old_state) 6562306a36Sopenharmony_ci{ 6662306a36Sopenharmony_ci struct drm_crtc_state *old_crtc_state __maybe_unused; 6762306a36Sopenharmony_ci struct drm_crtc *crtc; 6862306a36Sopenharmony_ci unsigned int i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) 7162306a36Sopenharmony_ci tegra_crtc_atomic_post_commit(crtc, old_state); 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci struct drm_device *drm = old_state->dev; 7762306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (tegra->hub) { 8062306a36Sopenharmony_ci bool fence_cookie = dma_fence_begin_signalling(); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci drm_atomic_helper_commit_modeset_disables(drm, old_state); 8362306a36Sopenharmony_ci tegra_display_hub_atomic_commit(drm, old_state); 8462306a36Sopenharmony_ci drm_atomic_helper_commit_planes(drm, old_state, 0); 8562306a36Sopenharmony_ci drm_atomic_helper_commit_modeset_enables(drm, old_state); 8662306a36Sopenharmony_ci drm_atomic_helper_commit_hw_done(old_state); 8762306a36Sopenharmony_ci dma_fence_end_signalling(fence_cookie); 8862306a36Sopenharmony_ci drm_atomic_helper_wait_for_vblanks(drm, old_state); 8962306a36Sopenharmony_ci drm_atomic_helper_cleanup_planes(drm, old_state); 9062306a36Sopenharmony_ci } else { 9162306a36Sopenharmony_ci drm_atomic_helper_commit_tail_rpm(old_state); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci tegra_atomic_post_commit(drm, old_state); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic const struct drm_mode_config_helper_funcs 9862306a36Sopenharmony_citegra_drm_mode_config_helpers = { 9962306a36Sopenharmony_ci .atomic_commit_tail = tegra_atomic_commit_tail, 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci struct tegra_drm_file *fpriv; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); 10762306a36Sopenharmony_ci if (!fpriv) 10862306a36Sopenharmony_ci return -ENOMEM; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci idr_init_base(&fpriv->legacy_contexts, 1); 11162306a36Sopenharmony_ci xa_init_flags(&fpriv->contexts, XA_FLAGS_ALLOC1); 11262306a36Sopenharmony_ci xa_init(&fpriv->syncpoints); 11362306a36Sopenharmony_ci mutex_init(&fpriv->lock); 11462306a36Sopenharmony_ci filp->driver_priv = fpriv; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void tegra_drm_context_free(struct tegra_drm_context *context) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci context->client->ops->close_channel(context); 12262306a36Sopenharmony_ci pm_runtime_put(context->client->base.dev); 12362306a36Sopenharmony_ci kfree(context); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int host1x_reloc_copy_from_user(struct host1x_reloc *dest, 12762306a36Sopenharmony_ci struct drm_tegra_reloc __user *src, 12862306a36Sopenharmony_ci struct drm_device *drm, 12962306a36Sopenharmony_ci struct drm_file *file) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci u32 cmdbuf, target; 13262306a36Sopenharmony_ci int err; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci err = get_user(cmdbuf, &src->cmdbuf.handle); 13562306a36Sopenharmony_ci if (err < 0) 13662306a36Sopenharmony_ci return err; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset); 13962306a36Sopenharmony_ci if (err < 0) 14062306a36Sopenharmony_ci return err; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci err = get_user(target, &src->target.handle); 14362306a36Sopenharmony_ci if (err < 0) 14462306a36Sopenharmony_ci return err; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci err = get_user(dest->target.offset, &src->target.offset); 14762306a36Sopenharmony_ci if (err < 0) 14862306a36Sopenharmony_ci return err; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci err = get_user(dest->shift, &src->shift); 15162306a36Sopenharmony_ci if (err < 0) 15262306a36Sopenharmony_ci return err; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci dest->flags = HOST1X_RELOC_READ | HOST1X_RELOC_WRITE; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dest->cmdbuf.bo = tegra_gem_lookup(file, cmdbuf); 15762306a36Sopenharmony_ci if (!dest->cmdbuf.bo) 15862306a36Sopenharmony_ci return -ENOENT; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci dest->target.bo = tegra_gem_lookup(file, target); 16162306a36Sopenharmony_ci if (!dest->target.bo) 16262306a36Sopenharmony_ci return -ENOENT; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ciint tegra_drm_submit(struct tegra_drm_context *context, 16862306a36Sopenharmony_ci struct drm_tegra_submit *args, struct drm_device *drm, 16962306a36Sopenharmony_ci struct drm_file *file) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct host1x_client *client = &context->client->base; 17262306a36Sopenharmony_ci unsigned int num_cmdbufs = args->num_cmdbufs; 17362306a36Sopenharmony_ci unsigned int num_relocs = args->num_relocs; 17462306a36Sopenharmony_ci struct drm_tegra_cmdbuf __user *user_cmdbufs; 17562306a36Sopenharmony_ci struct drm_tegra_reloc __user *user_relocs; 17662306a36Sopenharmony_ci struct drm_tegra_syncpt __user *user_syncpt; 17762306a36Sopenharmony_ci struct drm_tegra_syncpt syncpt; 17862306a36Sopenharmony_ci struct host1x *host1x = dev_get_drvdata(drm->dev->parent); 17962306a36Sopenharmony_ci struct drm_gem_object **refs; 18062306a36Sopenharmony_ci struct host1x_syncpt *sp = NULL; 18162306a36Sopenharmony_ci struct host1x_job *job; 18262306a36Sopenharmony_ci unsigned int num_refs; 18362306a36Sopenharmony_ci int err; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci user_cmdbufs = u64_to_user_ptr(args->cmdbufs); 18662306a36Sopenharmony_ci user_relocs = u64_to_user_ptr(args->relocs); 18762306a36Sopenharmony_ci user_syncpt = u64_to_user_ptr(args->syncpts); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* We don't yet support other than one syncpt_incr struct per submit */ 19062306a36Sopenharmony_ci if (args->num_syncpts != 1) 19162306a36Sopenharmony_ci return -EINVAL; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci /* We don't yet support waitchks */ 19462306a36Sopenharmony_ci if (args->num_waitchks != 0) 19562306a36Sopenharmony_ci return -EINVAL; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci job = host1x_job_alloc(context->channel, args->num_cmdbufs, 19862306a36Sopenharmony_ci args->num_relocs, false); 19962306a36Sopenharmony_ci if (!job) 20062306a36Sopenharmony_ci return -ENOMEM; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci job->num_relocs = args->num_relocs; 20362306a36Sopenharmony_ci job->client = client; 20462306a36Sopenharmony_ci job->class = client->class; 20562306a36Sopenharmony_ci job->serialize = true; 20662306a36Sopenharmony_ci job->syncpt_recovery = true; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * Track referenced BOs so that they can be unreferenced after the 21062306a36Sopenharmony_ci * submission is complete. 21162306a36Sopenharmony_ci */ 21262306a36Sopenharmony_ci num_refs = num_cmdbufs + num_relocs * 2; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL); 21562306a36Sopenharmony_ci if (!refs) { 21662306a36Sopenharmony_ci err = -ENOMEM; 21762306a36Sopenharmony_ci goto put; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci /* reuse as an iterator later */ 22162306a36Sopenharmony_ci num_refs = 0; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci while (num_cmdbufs) { 22462306a36Sopenharmony_ci struct drm_tegra_cmdbuf cmdbuf; 22562306a36Sopenharmony_ci struct host1x_bo *bo; 22662306a36Sopenharmony_ci struct tegra_bo *obj; 22762306a36Sopenharmony_ci u64 offset; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci if (copy_from_user(&cmdbuf, user_cmdbufs, sizeof(cmdbuf))) { 23062306a36Sopenharmony_ci err = -EFAULT; 23162306a36Sopenharmony_ci goto fail; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * The maximum number of CDMA gather fetches is 16383, a higher 23662306a36Sopenharmony_ci * value means the words count is malformed. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (cmdbuf.words > CDMA_GATHER_FETCHES_MAX_NB) { 23962306a36Sopenharmony_ci err = -EINVAL; 24062306a36Sopenharmony_ci goto fail; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci bo = tegra_gem_lookup(file, cmdbuf.handle); 24462306a36Sopenharmony_ci if (!bo) { 24562306a36Sopenharmony_ci err = -ENOENT; 24662306a36Sopenharmony_ci goto fail; 24762306a36Sopenharmony_ci } 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci offset = (u64)cmdbuf.offset + (u64)cmdbuf.words * sizeof(u32); 25062306a36Sopenharmony_ci obj = host1x_to_tegra_bo(bo); 25162306a36Sopenharmony_ci refs[num_refs++] = &obj->gem; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci /* 25462306a36Sopenharmony_ci * Gather buffer base address must be 4-bytes aligned, 25562306a36Sopenharmony_ci * unaligned offset is malformed and cause commands stream 25662306a36Sopenharmony_ci * corruption on the buffer address relocation. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_ci if (offset & 3 || offset > obj->gem.size) { 25962306a36Sopenharmony_ci err = -EINVAL; 26062306a36Sopenharmony_ci goto fail; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); 26462306a36Sopenharmony_ci num_cmdbufs--; 26562306a36Sopenharmony_ci user_cmdbufs++; 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci /* copy and resolve relocations from submit */ 26962306a36Sopenharmony_ci while (num_relocs--) { 27062306a36Sopenharmony_ci struct host1x_reloc *reloc; 27162306a36Sopenharmony_ci struct tegra_bo *obj; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci err = host1x_reloc_copy_from_user(&job->relocs[num_relocs], 27462306a36Sopenharmony_ci &user_relocs[num_relocs], drm, 27562306a36Sopenharmony_ci file); 27662306a36Sopenharmony_ci if (err < 0) 27762306a36Sopenharmony_ci goto fail; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci reloc = &job->relocs[num_relocs]; 28062306a36Sopenharmony_ci obj = host1x_to_tegra_bo(reloc->cmdbuf.bo); 28162306a36Sopenharmony_ci refs[num_refs++] = &obj->gem; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* 28462306a36Sopenharmony_ci * The unaligned cmdbuf offset will cause an unaligned write 28562306a36Sopenharmony_ci * during of the relocations patching, corrupting the commands 28662306a36Sopenharmony_ci * stream. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci if (reloc->cmdbuf.offset & 3 || 28962306a36Sopenharmony_ci reloc->cmdbuf.offset >= obj->gem.size) { 29062306a36Sopenharmony_ci err = -EINVAL; 29162306a36Sopenharmony_ci goto fail; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci obj = host1x_to_tegra_bo(reloc->target.bo); 29562306a36Sopenharmony_ci refs[num_refs++] = &obj->gem; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (reloc->target.offset >= obj->gem.size) { 29862306a36Sopenharmony_ci err = -EINVAL; 29962306a36Sopenharmony_ci goto fail; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) { 30462306a36Sopenharmony_ci err = -EFAULT; 30562306a36Sopenharmony_ci goto fail; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Syncpoint ref will be dropped on job release. */ 30962306a36Sopenharmony_ci sp = host1x_syncpt_get_by_id(host1x, syncpt.id); 31062306a36Sopenharmony_ci if (!sp) { 31162306a36Sopenharmony_ci err = -ENOENT; 31262306a36Sopenharmony_ci goto fail; 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci job->is_addr_reg = context->client->ops->is_addr_reg; 31662306a36Sopenharmony_ci job->is_valid_class = context->client->ops->is_valid_class; 31762306a36Sopenharmony_ci job->syncpt_incrs = syncpt.incrs; 31862306a36Sopenharmony_ci job->syncpt = sp; 31962306a36Sopenharmony_ci job->timeout = 10000; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (args->timeout && args->timeout < 10000) 32262306a36Sopenharmony_ci job->timeout = args->timeout; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci err = host1x_job_pin(job, context->client->base.dev); 32562306a36Sopenharmony_ci if (err) 32662306a36Sopenharmony_ci goto fail; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci err = host1x_job_submit(job); 32962306a36Sopenharmony_ci if (err) { 33062306a36Sopenharmony_ci host1x_job_unpin(job); 33162306a36Sopenharmony_ci goto fail; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci args->fence = job->syncpt_end; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_cifail: 33762306a36Sopenharmony_ci while (num_refs--) 33862306a36Sopenharmony_ci drm_gem_object_put(refs[num_refs]); 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci kfree(refs); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciput: 34362306a36Sopenharmony_ci host1x_job_put(job); 34462306a36Sopenharmony_ci return err; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci#ifdef CONFIG_DRM_TEGRA_STAGING 34962306a36Sopenharmony_cistatic int tegra_gem_create(struct drm_device *drm, void *data, 35062306a36Sopenharmony_ci struct drm_file *file) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct drm_tegra_gem_create *args = data; 35362306a36Sopenharmony_ci struct tegra_bo *bo; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags, 35662306a36Sopenharmony_ci &args->handle); 35762306a36Sopenharmony_ci if (IS_ERR(bo)) 35862306a36Sopenharmony_ci return PTR_ERR(bo); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic int tegra_gem_mmap(struct drm_device *drm, void *data, 36462306a36Sopenharmony_ci struct drm_file *file) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct drm_tegra_gem_mmap *args = data; 36762306a36Sopenharmony_ci struct drm_gem_object *gem; 36862306a36Sopenharmony_ci struct tegra_bo *bo; 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci gem = drm_gem_object_lookup(file, args->handle); 37162306a36Sopenharmony_ci if (!gem) 37262306a36Sopenharmony_ci return -EINVAL; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci bo = to_tegra_bo(gem); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node); 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci drm_gem_object_put(gem); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic int tegra_syncpt_read(struct drm_device *drm, void *data, 38462306a36Sopenharmony_ci struct drm_file *file) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci struct host1x *host = dev_get_drvdata(drm->dev->parent); 38762306a36Sopenharmony_ci struct drm_tegra_syncpt_read *args = data; 38862306a36Sopenharmony_ci struct host1x_syncpt *sp; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci sp = host1x_syncpt_get_by_id_noref(host, args->id); 39162306a36Sopenharmony_ci if (!sp) 39262306a36Sopenharmony_ci return -EINVAL; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci args->value = host1x_syncpt_read_min(sp); 39562306a36Sopenharmony_ci return 0; 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic int tegra_syncpt_incr(struct drm_device *drm, void *data, 39962306a36Sopenharmony_ci struct drm_file *file) 40062306a36Sopenharmony_ci{ 40162306a36Sopenharmony_ci struct host1x *host1x = dev_get_drvdata(drm->dev->parent); 40262306a36Sopenharmony_ci struct drm_tegra_syncpt_incr *args = data; 40362306a36Sopenharmony_ci struct host1x_syncpt *sp; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci sp = host1x_syncpt_get_by_id_noref(host1x, args->id); 40662306a36Sopenharmony_ci if (!sp) 40762306a36Sopenharmony_ci return -EINVAL; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return host1x_syncpt_incr(sp); 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic int tegra_syncpt_wait(struct drm_device *drm, void *data, 41362306a36Sopenharmony_ci struct drm_file *file) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct host1x *host1x = dev_get_drvdata(drm->dev->parent); 41662306a36Sopenharmony_ci struct drm_tegra_syncpt_wait *args = data; 41762306a36Sopenharmony_ci struct host1x_syncpt *sp; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci sp = host1x_syncpt_get_by_id_noref(host1x, args->id); 42062306a36Sopenharmony_ci if (!sp) 42162306a36Sopenharmony_ci return -EINVAL; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci return host1x_syncpt_wait(sp, args->thresh, 42462306a36Sopenharmony_ci msecs_to_jiffies(args->timeout), 42562306a36Sopenharmony_ci &args->value); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int tegra_client_open(struct tegra_drm_file *fpriv, 42962306a36Sopenharmony_ci struct tegra_drm_client *client, 43062306a36Sopenharmony_ci struct tegra_drm_context *context) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int err; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci err = pm_runtime_resume_and_get(client->base.dev); 43562306a36Sopenharmony_ci if (err) 43662306a36Sopenharmony_ci return err; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci err = client->ops->open_channel(client, context); 43962306a36Sopenharmony_ci if (err < 0) { 44062306a36Sopenharmony_ci pm_runtime_put(client->base.dev); 44162306a36Sopenharmony_ci return err; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci err = idr_alloc(&fpriv->legacy_contexts, context, 1, 0, GFP_KERNEL); 44562306a36Sopenharmony_ci if (err < 0) { 44662306a36Sopenharmony_ci client->ops->close_channel(context); 44762306a36Sopenharmony_ci pm_runtime_put(client->base.dev); 44862306a36Sopenharmony_ci return err; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci context->client = client; 45262306a36Sopenharmony_ci context->id = err; 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci return 0; 45562306a36Sopenharmony_ci} 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_cistatic int tegra_open_channel(struct drm_device *drm, void *data, 45862306a36Sopenharmony_ci struct drm_file *file) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct tegra_drm_file *fpriv = file->driver_priv; 46162306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 46262306a36Sopenharmony_ci struct drm_tegra_open_channel *args = data; 46362306a36Sopenharmony_ci struct tegra_drm_context *context; 46462306a36Sopenharmony_ci struct tegra_drm_client *client; 46562306a36Sopenharmony_ci int err = -ENODEV; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci context = kzalloc(sizeof(*context), GFP_KERNEL); 46862306a36Sopenharmony_ci if (!context) 46962306a36Sopenharmony_ci return -ENOMEM; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci mutex_lock(&fpriv->lock); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci list_for_each_entry(client, &tegra->clients, list) 47462306a36Sopenharmony_ci if (client->base.class == args->client) { 47562306a36Sopenharmony_ci err = tegra_client_open(fpriv, client, context); 47662306a36Sopenharmony_ci if (err < 0) 47762306a36Sopenharmony_ci break; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci args->context = context->id; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (err < 0) 48462306a36Sopenharmony_ci kfree(context); 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci mutex_unlock(&fpriv->lock); 48762306a36Sopenharmony_ci return err; 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic int tegra_close_channel(struct drm_device *drm, void *data, 49162306a36Sopenharmony_ci struct drm_file *file) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci struct tegra_drm_file *fpriv = file->driver_priv; 49462306a36Sopenharmony_ci struct drm_tegra_close_channel *args = data; 49562306a36Sopenharmony_ci struct tegra_drm_context *context; 49662306a36Sopenharmony_ci int err = 0; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci mutex_lock(&fpriv->lock); 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci context = idr_find(&fpriv->legacy_contexts, args->context); 50162306a36Sopenharmony_ci if (!context) { 50262306a36Sopenharmony_ci err = -EINVAL; 50362306a36Sopenharmony_ci goto unlock; 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci idr_remove(&fpriv->legacy_contexts, context->id); 50762306a36Sopenharmony_ci tegra_drm_context_free(context); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciunlock: 51062306a36Sopenharmony_ci mutex_unlock(&fpriv->lock); 51162306a36Sopenharmony_ci return err; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic int tegra_get_syncpt(struct drm_device *drm, void *data, 51562306a36Sopenharmony_ci struct drm_file *file) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct tegra_drm_file *fpriv = file->driver_priv; 51862306a36Sopenharmony_ci struct drm_tegra_get_syncpt *args = data; 51962306a36Sopenharmony_ci struct tegra_drm_context *context; 52062306a36Sopenharmony_ci struct host1x_syncpt *syncpt; 52162306a36Sopenharmony_ci int err = 0; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mutex_lock(&fpriv->lock); 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci context = idr_find(&fpriv->legacy_contexts, args->context); 52662306a36Sopenharmony_ci if (!context) { 52762306a36Sopenharmony_ci err = -ENODEV; 52862306a36Sopenharmony_ci goto unlock; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci if (args->index >= context->client->base.num_syncpts) { 53262306a36Sopenharmony_ci err = -EINVAL; 53362306a36Sopenharmony_ci goto unlock; 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci syncpt = context->client->base.syncpts[args->index]; 53762306a36Sopenharmony_ci args->id = host1x_syncpt_id(syncpt); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ciunlock: 54062306a36Sopenharmony_ci mutex_unlock(&fpriv->lock); 54162306a36Sopenharmony_ci return err; 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic int tegra_submit(struct drm_device *drm, void *data, 54562306a36Sopenharmony_ci struct drm_file *file) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct tegra_drm_file *fpriv = file->driver_priv; 54862306a36Sopenharmony_ci struct drm_tegra_submit *args = data; 54962306a36Sopenharmony_ci struct tegra_drm_context *context; 55062306a36Sopenharmony_ci int err; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci mutex_lock(&fpriv->lock); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci context = idr_find(&fpriv->legacy_contexts, args->context); 55562306a36Sopenharmony_ci if (!context) { 55662306a36Sopenharmony_ci err = -ENODEV; 55762306a36Sopenharmony_ci goto unlock; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci err = context->client->ops->submit(context, args, drm, file); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ciunlock: 56362306a36Sopenharmony_ci mutex_unlock(&fpriv->lock); 56462306a36Sopenharmony_ci return err; 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int tegra_get_syncpt_base(struct drm_device *drm, void *data, 56862306a36Sopenharmony_ci struct drm_file *file) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct tegra_drm_file *fpriv = file->driver_priv; 57162306a36Sopenharmony_ci struct drm_tegra_get_syncpt_base *args = data; 57262306a36Sopenharmony_ci struct tegra_drm_context *context; 57362306a36Sopenharmony_ci struct host1x_syncpt_base *base; 57462306a36Sopenharmony_ci struct host1x_syncpt *syncpt; 57562306a36Sopenharmony_ci int err = 0; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci mutex_lock(&fpriv->lock); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci context = idr_find(&fpriv->legacy_contexts, args->context); 58062306a36Sopenharmony_ci if (!context) { 58162306a36Sopenharmony_ci err = -ENODEV; 58262306a36Sopenharmony_ci goto unlock; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (args->syncpt >= context->client->base.num_syncpts) { 58662306a36Sopenharmony_ci err = -EINVAL; 58762306a36Sopenharmony_ci goto unlock; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci syncpt = context->client->base.syncpts[args->syncpt]; 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci base = host1x_syncpt_get_base(syncpt); 59362306a36Sopenharmony_ci if (!base) { 59462306a36Sopenharmony_ci err = -ENXIO; 59562306a36Sopenharmony_ci goto unlock; 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci args->id = host1x_syncpt_base_id(base); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ciunlock: 60162306a36Sopenharmony_ci mutex_unlock(&fpriv->lock); 60262306a36Sopenharmony_ci return err; 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic int tegra_gem_set_tiling(struct drm_device *drm, void *data, 60662306a36Sopenharmony_ci struct drm_file *file) 60762306a36Sopenharmony_ci{ 60862306a36Sopenharmony_ci struct drm_tegra_gem_set_tiling *args = data; 60962306a36Sopenharmony_ci enum tegra_bo_tiling_mode mode; 61062306a36Sopenharmony_ci struct drm_gem_object *gem; 61162306a36Sopenharmony_ci unsigned long value = 0; 61262306a36Sopenharmony_ci struct tegra_bo *bo; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci switch (args->mode) { 61562306a36Sopenharmony_ci case DRM_TEGRA_GEM_TILING_MODE_PITCH: 61662306a36Sopenharmony_ci mode = TEGRA_BO_TILING_MODE_PITCH; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci if (args->value != 0) 61962306a36Sopenharmony_ci return -EINVAL; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci break; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci case DRM_TEGRA_GEM_TILING_MODE_TILED: 62462306a36Sopenharmony_ci mode = TEGRA_BO_TILING_MODE_TILED; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (args->value != 0) 62762306a36Sopenharmony_ci return -EINVAL; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci break; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci case DRM_TEGRA_GEM_TILING_MODE_BLOCK: 63262306a36Sopenharmony_ci mode = TEGRA_BO_TILING_MODE_BLOCK; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci if (args->value > 5) 63562306a36Sopenharmony_ci return -EINVAL; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci value = args->value; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci default: 64162306a36Sopenharmony_ci return -EINVAL; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci gem = drm_gem_object_lookup(file, args->handle); 64562306a36Sopenharmony_ci if (!gem) 64662306a36Sopenharmony_ci return -ENOENT; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci bo = to_tegra_bo(gem); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci bo->tiling.mode = mode; 65162306a36Sopenharmony_ci bo->tiling.value = value; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci drm_gem_object_put(gem); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci return 0; 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int tegra_gem_get_tiling(struct drm_device *drm, void *data, 65962306a36Sopenharmony_ci struct drm_file *file) 66062306a36Sopenharmony_ci{ 66162306a36Sopenharmony_ci struct drm_tegra_gem_get_tiling *args = data; 66262306a36Sopenharmony_ci struct drm_gem_object *gem; 66362306a36Sopenharmony_ci struct tegra_bo *bo; 66462306a36Sopenharmony_ci int err = 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci gem = drm_gem_object_lookup(file, args->handle); 66762306a36Sopenharmony_ci if (!gem) 66862306a36Sopenharmony_ci return -ENOENT; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci bo = to_tegra_bo(gem); 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci switch (bo->tiling.mode) { 67362306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_PITCH: 67462306a36Sopenharmony_ci args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH; 67562306a36Sopenharmony_ci args->value = 0; 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_TILED: 67962306a36Sopenharmony_ci args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED; 68062306a36Sopenharmony_ci args->value = 0; 68162306a36Sopenharmony_ci break; 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci case TEGRA_BO_TILING_MODE_BLOCK: 68462306a36Sopenharmony_ci args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK; 68562306a36Sopenharmony_ci args->value = bo->tiling.value; 68662306a36Sopenharmony_ci break; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci default: 68962306a36Sopenharmony_ci err = -EINVAL; 69062306a36Sopenharmony_ci break; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci drm_gem_object_put(gem); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return err; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int tegra_gem_set_flags(struct drm_device *drm, void *data, 69962306a36Sopenharmony_ci struct drm_file *file) 70062306a36Sopenharmony_ci{ 70162306a36Sopenharmony_ci struct drm_tegra_gem_set_flags *args = data; 70262306a36Sopenharmony_ci struct drm_gem_object *gem; 70362306a36Sopenharmony_ci struct tegra_bo *bo; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci if (args->flags & ~DRM_TEGRA_GEM_FLAGS) 70662306a36Sopenharmony_ci return -EINVAL; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci gem = drm_gem_object_lookup(file, args->handle); 70962306a36Sopenharmony_ci if (!gem) 71062306a36Sopenharmony_ci return -ENOENT; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci bo = to_tegra_bo(gem); 71362306a36Sopenharmony_ci bo->flags = 0; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP) 71662306a36Sopenharmony_ci bo->flags |= TEGRA_BO_BOTTOM_UP; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci drm_gem_object_put(gem); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci return 0; 72162306a36Sopenharmony_ci} 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_cistatic int tegra_gem_get_flags(struct drm_device *drm, void *data, 72462306a36Sopenharmony_ci struct drm_file *file) 72562306a36Sopenharmony_ci{ 72662306a36Sopenharmony_ci struct drm_tegra_gem_get_flags *args = data; 72762306a36Sopenharmony_ci struct drm_gem_object *gem; 72862306a36Sopenharmony_ci struct tegra_bo *bo; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci gem = drm_gem_object_lookup(file, args->handle); 73162306a36Sopenharmony_ci if (!gem) 73262306a36Sopenharmony_ci return -ENOENT; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci bo = to_tegra_bo(gem); 73562306a36Sopenharmony_ci args->flags = 0; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci if (bo->flags & TEGRA_BO_BOTTOM_UP) 73862306a36Sopenharmony_ci args->flags |= DRM_TEGRA_GEM_BOTTOM_UP; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci drm_gem_object_put(gem); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_ci} 74462306a36Sopenharmony_ci#endif 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic const struct drm_ioctl_desc tegra_drm_ioctls[] = { 74762306a36Sopenharmony_ci#ifdef CONFIG_DRM_TEGRA_STAGING 74862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_OPEN, tegra_drm_ioctl_channel_open, 74962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 75062306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_CLOSE, tegra_drm_ioctl_channel_close, 75162306a36Sopenharmony_ci DRM_RENDER_ALLOW), 75262306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_MAP, tegra_drm_ioctl_channel_map, 75362306a36Sopenharmony_ci DRM_RENDER_ALLOW), 75462306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_UNMAP, tegra_drm_ioctl_channel_unmap, 75562306a36Sopenharmony_ci DRM_RENDER_ALLOW), 75662306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_CHANNEL_SUBMIT, tegra_drm_ioctl_channel_submit, 75762306a36Sopenharmony_ci DRM_RENDER_ALLOW), 75862306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_ALLOCATE, tegra_drm_ioctl_syncpoint_allocate, 75962306a36Sopenharmony_ci DRM_RENDER_ALLOW), 76062306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_FREE, tegra_drm_ioctl_syncpoint_free, 76162306a36Sopenharmony_ci DRM_RENDER_ALLOW), 76262306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SYNCPOINT_WAIT, tegra_drm_ioctl_syncpoint_wait, 76362306a36Sopenharmony_ci DRM_RENDER_ALLOW), 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_RENDER_ALLOW), 76662306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_RENDER_ALLOW), 76762306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, 76862306a36Sopenharmony_ci DRM_RENDER_ALLOW), 76962306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, 77062306a36Sopenharmony_ci DRM_RENDER_ALLOW), 77162306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, 77262306a36Sopenharmony_ci DRM_RENDER_ALLOW), 77362306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, 77462306a36Sopenharmony_ci DRM_RENDER_ALLOW), 77562306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, 77662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 77762306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, 77862306a36Sopenharmony_ci DRM_RENDER_ALLOW), 77962306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, 78062306a36Sopenharmony_ci DRM_RENDER_ALLOW), 78162306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, 78262306a36Sopenharmony_ci DRM_RENDER_ALLOW), 78362306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, 78462306a36Sopenharmony_ci DRM_RENDER_ALLOW), 78562306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, 78662306a36Sopenharmony_ci DRM_RENDER_ALLOW), 78762306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, 78862306a36Sopenharmony_ci DRM_RENDER_ALLOW), 78962306a36Sopenharmony_ci DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, 79062306a36Sopenharmony_ci DRM_RENDER_ALLOW), 79162306a36Sopenharmony_ci#endif 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct file_operations tegra_drm_fops = { 79562306a36Sopenharmony_ci .owner = THIS_MODULE, 79662306a36Sopenharmony_ci .open = drm_open, 79762306a36Sopenharmony_ci .release = drm_release, 79862306a36Sopenharmony_ci .unlocked_ioctl = drm_ioctl, 79962306a36Sopenharmony_ci .mmap = tegra_drm_mmap, 80062306a36Sopenharmony_ci .poll = drm_poll, 80162306a36Sopenharmony_ci .read = drm_read, 80262306a36Sopenharmony_ci .compat_ioctl = drm_compat_ioctl, 80362306a36Sopenharmony_ci .llseek = noop_llseek, 80462306a36Sopenharmony_ci}; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic int tegra_drm_context_cleanup(int id, void *p, void *data) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct tegra_drm_context *context = p; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci tegra_drm_context_free(context); 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return 0; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic void tegra_drm_postclose(struct drm_device *drm, struct drm_file *file) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci struct tegra_drm_file *fpriv = file->driver_priv; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci mutex_lock(&fpriv->lock); 82062306a36Sopenharmony_ci idr_for_each(&fpriv->legacy_contexts, tegra_drm_context_cleanup, NULL); 82162306a36Sopenharmony_ci tegra_drm_uapi_close_file(fpriv); 82262306a36Sopenharmony_ci mutex_unlock(&fpriv->lock); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci idr_destroy(&fpriv->legacy_contexts); 82562306a36Sopenharmony_ci mutex_destroy(&fpriv->lock); 82662306a36Sopenharmony_ci kfree(fpriv); 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 83062306a36Sopenharmony_cistatic int tegra_debugfs_framebuffers(struct seq_file *s, void *data) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *)s->private; 83362306a36Sopenharmony_ci struct drm_device *drm = node->minor->dev; 83462306a36Sopenharmony_ci struct drm_framebuffer *fb; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci mutex_lock(&drm->mode_config.fb_lock); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci list_for_each_entry(fb, &drm->mode_config.fb_list, head) { 83962306a36Sopenharmony_ci seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n", 84062306a36Sopenharmony_ci fb->base.id, fb->width, fb->height, 84162306a36Sopenharmony_ci fb->format->depth, 84262306a36Sopenharmony_ci fb->format->cpp[0] * 8, 84362306a36Sopenharmony_ci drm_framebuffer_read_refcount(fb)); 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci mutex_unlock(&drm->mode_config.fb_lock); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int tegra_debugfs_iova(struct seq_file *s, void *data) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct drm_info_node *node = (struct drm_info_node *)s->private; 85462306a36Sopenharmony_ci struct drm_device *drm = node->minor->dev; 85562306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 85662306a36Sopenharmony_ci struct drm_printer p = drm_seq_file_printer(s); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (tegra->domain) { 85962306a36Sopenharmony_ci mutex_lock(&tegra->mm_lock); 86062306a36Sopenharmony_ci drm_mm_print(&tegra->mm, &p); 86162306a36Sopenharmony_ci mutex_unlock(&tegra->mm_lock); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_ci return 0; 86562306a36Sopenharmony_ci} 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_cistatic struct drm_info_list tegra_debugfs_list[] = { 86862306a36Sopenharmony_ci { "framebuffers", tegra_debugfs_framebuffers, 0 }, 86962306a36Sopenharmony_ci { "iova", tegra_debugfs_iova, 0 }, 87062306a36Sopenharmony_ci}; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic void tegra_debugfs_init(struct drm_minor *minor) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci drm_debugfs_create_files(tegra_debugfs_list, 87562306a36Sopenharmony_ci ARRAY_SIZE(tegra_debugfs_list), 87662306a36Sopenharmony_ci minor->debugfs_root, minor); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci#endif 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic const struct drm_driver tegra_drm_driver = { 88162306a36Sopenharmony_ci .driver_features = DRIVER_MODESET | DRIVER_GEM | 88262306a36Sopenharmony_ci DRIVER_ATOMIC | DRIVER_RENDER | DRIVER_SYNCOBJ, 88362306a36Sopenharmony_ci .open = tegra_drm_open, 88462306a36Sopenharmony_ci .postclose = tegra_drm_postclose, 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci#if defined(CONFIG_DEBUG_FS) 88762306a36Sopenharmony_ci .debugfs_init = tegra_debugfs_init, 88862306a36Sopenharmony_ci#endif 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci .gem_prime_import = tegra_gem_prime_import, 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci .dumb_create = tegra_bo_dumb_create, 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci .ioctls = tegra_drm_ioctls, 89562306a36Sopenharmony_ci .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), 89662306a36Sopenharmony_ci .fops = &tegra_drm_fops, 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci .name = DRIVER_NAME, 89962306a36Sopenharmony_ci .desc = DRIVER_DESC, 90062306a36Sopenharmony_ci .date = DRIVER_DATE, 90162306a36Sopenharmony_ci .major = DRIVER_MAJOR, 90262306a36Sopenharmony_ci .minor = DRIVER_MINOR, 90362306a36Sopenharmony_ci .patchlevel = DRIVER_PATCHLEVEL, 90462306a36Sopenharmony_ci}; 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ciint tegra_drm_register_client(struct tegra_drm *tegra, 90762306a36Sopenharmony_ci struct tegra_drm_client *client) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci /* 91062306a36Sopenharmony_ci * When MLOCKs are implemented, change to allocate a shared channel 91162306a36Sopenharmony_ci * only when MLOCKs are disabled. 91262306a36Sopenharmony_ci */ 91362306a36Sopenharmony_ci client->shared_channel = host1x_channel_request(&client->base); 91462306a36Sopenharmony_ci if (!client->shared_channel) 91562306a36Sopenharmony_ci return -EBUSY; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci mutex_lock(&tegra->clients_lock); 91862306a36Sopenharmony_ci list_add_tail(&client->list, &tegra->clients); 91962306a36Sopenharmony_ci client->drm = tegra; 92062306a36Sopenharmony_ci mutex_unlock(&tegra->clients_lock); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci return 0; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ciint tegra_drm_unregister_client(struct tegra_drm *tegra, 92662306a36Sopenharmony_ci struct tegra_drm_client *client) 92762306a36Sopenharmony_ci{ 92862306a36Sopenharmony_ci mutex_lock(&tegra->clients_lock); 92962306a36Sopenharmony_ci list_del_init(&client->list); 93062306a36Sopenharmony_ci client->drm = NULL; 93162306a36Sopenharmony_ci mutex_unlock(&tegra->clients_lock); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci if (client->shared_channel) 93462306a36Sopenharmony_ci host1x_channel_put(client->shared_channel); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci return 0; 93762306a36Sopenharmony_ci} 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ciint host1x_client_iommu_attach(struct host1x_client *client) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci struct iommu_domain *domain = iommu_get_domain_for_dev(client->dev); 94262306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 94362306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 94462306a36Sopenharmony_ci struct iommu_group *group = NULL; 94562306a36Sopenharmony_ci int err; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_ARM_DMA_USE_IOMMU) 94862306a36Sopenharmony_ci if (client->dev->archdata.mapping) { 94962306a36Sopenharmony_ci struct dma_iommu_mapping *mapping = 95062306a36Sopenharmony_ci to_dma_iommu_mapping(client->dev); 95162306a36Sopenharmony_ci arm_iommu_detach_device(client->dev); 95262306a36Sopenharmony_ci arm_iommu_release_mapping(mapping); 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci domain = iommu_get_domain_for_dev(client->dev); 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci#endif 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci /* 95962306a36Sopenharmony_ci * If the host1x client is already attached to an IOMMU domain that is 96062306a36Sopenharmony_ci * not the shared IOMMU domain, don't try to attach it to a different 96162306a36Sopenharmony_ci * domain. This allows using the IOMMU-backed DMA API. 96262306a36Sopenharmony_ci */ 96362306a36Sopenharmony_ci if (domain && domain != tegra->domain) 96462306a36Sopenharmony_ci return 0; 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci if (tegra->domain) { 96762306a36Sopenharmony_ci group = iommu_group_get(client->dev); 96862306a36Sopenharmony_ci if (!group) 96962306a36Sopenharmony_ci return -ENODEV; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (domain != tegra->domain) { 97262306a36Sopenharmony_ci err = iommu_attach_group(tegra->domain, group); 97362306a36Sopenharmony_ci if (err < 0) { 97462306a36Sopenharmony_ci iommu_group_put(group); 97562306a36Sopenharmony_ci return err; 97662306a36Sopenharmony_ci } 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci tegra->use_explicit_iommu = true; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci client->group = group; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci return 0; 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_civoid host1x_client_iommu_detach(struct host1x_client *client) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(client->host); 99062306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 99162306a36Sopenharmony_ci struct iommu_domain *domain; 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (client->group) { 99462306a36Sopenharmony_ci /* 99562306a36Sopenharmony_ci * Devices that are part of the same group may no longer be 99662306a36Sopenharmony_ci * attached to a domain at this point because their group may 99762306a36Sopenharmony_ci * have been detached by an earlier client. 99862306a36Sopenharmony_ci */ 99962306a36Sopenharmony_ci domain = iommu_get_domain_for_dev(client->dev); 100062306a36Sopenharmony_ci if (domain) 100162306a36Sopenharmony_ci iommu_detach_group(tegra->domain, client->group); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci iommu_group_put(client->group); 100462306a36Sopenharmony_ci client->group = NULL; 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci} 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_civoid *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct iova *alloc; 101162306a36Sopenharmony_ci void *virt; 101262306a36Sopenharmony_ci gfp_t gfp; 101362306a36Sopenharmony_ci int err; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci if (tegra->domain) 101662306a36Sopenharmony_ci size = iova_align(&tegra->carveout.domain, size); 101762306a36Sopenharmony_ci else 101862306a36Sopenharmony_ci size = PAGE_ALIGN(size); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci gfp = GFP_KERNEL | __GFP_ZERO; 102162306a36Sopenharmony_ci if (!tegra->domain) { 102262306a36Sopenharmony_ci /* 102362306a36Sopenharmony_ci * Many units only support 32-bit addresses, even on 64-bit 102462306a36Sopenharmony_ci * SoCs. If there is no IOMMU to translate into a 32-bit IO 102562306a36Sopenharmony_ci * virtual address space, force allocations to be in the 102662306a36Sopenharmony_ci * lower 32-bit range. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_ci gfp |= GFP_DMA; 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci virt = (void *)__get_free_pages(gfp, get_order(size)); 103262306a36Sopenharmony_ci if (!virt) 103362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci if (!tegra->domain) { 103662306a36Sopenharmony_ci /* 103762306a36Sopenharmony_ci * If IOMMU is disabled, devices address physical memory 103862306a36Sopenharmony_ci * directly. 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_ci *dma = virt_to_phys(virt); 104162306a36Sopenharmony_ci return virt; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci alloc = alloc_iova(&tegra->carveout.domain, 104562306a36Sopenharmony_ci size >> tegra->carveout.shift, 104662306a36Sopenharmony_ci tegra->carveout.limit, true); 104762306a36Sopenharmony_ci if (!alloc) { 104862306a36Sopenharmony_ci err = -EBUSY; 104962306a36Sopenharmony_ci goto free_pages; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci *dma = iova_dma_addr(&tegra->carveout.domain, alloc); 105362306a36Sopenharmony_ci err = iommu_map(tegra->domain, *dma, virt_to_phys(virt), 105462306a36Sopenharmony_ci size, IOMMU_READ | IOMMU_WRITE, GFP_KERNEL); 105562306a36Sopenharmony_ci if (err < 0) 105662306a36Sopenharmony_ci goto free_iova; 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci return virt; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_cifree_iova: 106162306a36Sopenharmony_ci __free_iova(&tegra->carveout.domain, alloc); 106262306a36Sopenharmony_cifree_pages: 106362306a36Sopenharmony_ci free_pages((unsigned long)virt, get_order(size)); 106462306a36Sopenharmony_ci 106562306a36Sopenharmony_ci return ERR_PTR(err); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_civoid tegra_drm_free(struct tegra_drm *tegra, size_t size, void *virt, 106962306a36Sopenharmony_ci dma_addr_t dma) 107062306a36Sopenharmony_ci{ 107162306a36Sopenharmony_ci if (tegra->domain) 107262306a36Sopenharmony_ci size = iova_align(&tegra->carveout.domain, size); 107362306a36Sopenharmony_ci else 107462306a36Sopenharmony_ci size = PAGE_ALIGN(size); 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci if (tegra->domain) { 107762306a36Sopenharmony_ci iommu_unmap(tegra->domain, dma, size); 107862306a36Sopenharmony_ci free_iova(&tegra->carveout.domain, 107962306a36Sopenharmony_ci iova_pfn(&tegra->carveout.domain, dma)); 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci free_pages((unsigned long)virt, get_order(size)); 108362306a36Sopenharmony_ci} 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_cistatic bool host1x_drm_wants_iommu(struct host1x_device *dev) 108662306a36Sopenharmony_ci{ 108762306a36Sopenharmony_ci struct host1x *host1x = dev_get_drvdata(dev->dev.parent); 108862306a36Sopenharmony_ci struct iommu_domain *domain; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci /* Our IOMMU usage policy doesn't currently play well with GART */ 109162306a36Sopenharmony_ci if (of_machine_is_compatible("nvidia,tegra20")) 109262306a36Sopenharmony_ci return false; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* 109562306a36Sopenharmony_ci * If the Tegra DRM clients are backed by an IOMMU, push buffers are 109662306a36Sopenharmony_ci * likely to be allocated beyond the 32-bit boundary if sufficient 109762306a36Sopenharmony_ci * system memory is available. This is problematic on earlier Tegra 109862306a36Sopenharmony_ci * generations where host1x supports a maximum of 32 address bits in 109962306a36Sopenharmony_ci * the GATHER opcode. In this case, unless host1x is behind an IOMMU 110062306a36Sopenharmony_ci * as well it won't be able to process buffers allocated beyond the 110162306a36Sopenharmony_ci * 32-bit boundary. 110262306a36Sopenharmony_ci * 110362306a36Sopenharmony_ci * The DMA API will use bounce buffers in this case, so that could 110462306a36Sopenharmony_ci * perhaps still be made to work, even if less efficient, but there 110562306a36Sopenharmony_ci * is another catch: in order to perform cache maintenance on pages 110662306a36Sopenharmony_ci * allocated for discontiguous buffers we need to map and unmap the 110762306a36Sopenharmony_ci * SG table representing these buffers. This is fine for something 110862306a36Sopenharmony_ci * small like a push buffer, but it exhausts the bounce buffer pool 110962306a36Sopenharmony_ci * (typically on the order of a few MiB) for framebuffers (many MiB 111062306a36Sopenharmony_ci * for any modern resolution). 111162306a36Sopenharmony_ci * 111262306a36Sopenharmony_ci * Work around this by making sure that Tegra DRM clients only use 111362306a36Sopenharmony_ci * an IOMMU if the parent host1x also uses an IOMMU. 111462306a36Sopenharmony_ci * 111562306a36Sopenharmony_ci * Note that there's still a small gap here that we don't cover: if 111662306a36Sopenharmony_ci * the DMA API is backed by an IOMMU there's no way to control which 111762306a36Sopenharmony_ci * device is attached to an IOMMU and which isn't, except via wiring 111862306a36Sopenharmony_ci * up the device tree appropriately. This is considered an problem 111962306a36Sopenharmony_ci * of integration, so care must be taken for the DT to be consistent. 112062306a36Sopenharmony_ci */ 112162306a36Sopenharmony_ci domain = iommu_get_domain_for_dev(dev->dev.parent); 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci /* 112462306a36Sopenharmony_ci * Tegra20 and Tegra30 don't support addressing memory beyond the 112562306a36Sopenharmony_ci * 32-bit boundary, so the regular GATHER opcodes will always be 112662306a36Sopenharmony_ci * sufficient and whether or not the host1x is attached to an IOMMU 112762306a36Sopenharmony_ci * doesn't matter. 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci if (!domain && host1x_get_dma_mask(host1x) <= DMA_BIT_MASK(32)) 113062306a36Sopenharmony_ci return true; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci return domain != NULL; 113362306a36Sopenharmony_ci} 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_cistatic int host1x_drm_probe(struct host1x_device *dev) 113662306a36Sopenharmony_ci{ 113762306a36Sopenharmony_ci struct tegra_drm *tegra; 113862306a36Sopenharmony_ci struct drm_device *drm; 113962306a36Sopenharmony_ci int err; 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_ci drm = drm_dev_alloc(&tegra_drm_driver, &dev->dev); 114262306a36Sopenharmony_ci if (IS_ERR(drm)) 114362306a36Sopenharmony_ci return PTR_ERR(drm); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci tegra = kzalloc(sizeof(*tegra), GFP_KERNEL); 114662306a36Sopenharmony_ci if (!tegra) { 114762306a36Sopenharmony_ci err = -ENOMEM; 114862306a36Sopenharmony_ci goto put; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci if (host1x_drm_wants_iommu(dev) && iommu_present(&platform_bus_type)) { 115262306a36Sopenharmony_ci tegra->domain = iommu_domain_alloc(&platform_bus_type); 115362306a36Sopenharmony_ci if (!tegra->domain) { 115462306a36Sopenharmony_ci err = -ENOMEM; 115562306a36Sopenharmony_ci goto free; 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci 115862306a36Sopenharmony_ci err = iova_cache_get(); 115962306a36Sopenharmony_ci if (err < 0) 116062306a36Sopenharmony_ci goto domain; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci mutex_init(&tegra->clients_lock); 116462306a36Sopenharmony_ci INIT_LIST_HEAD(&tegra->clients); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci dev_set_drvdata(&dev->dev, drm); 116762306a36Sopenharmony_ci drm->dev_private = tegra; 116862306a36Sopenharmony_ci tegra->drm = drm; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci drm_mode_config_init(drm); 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci drm->mode_config.min_width = 0; 117362306a36Sopenharmony_ci drm->mode_config.min_height = 0; 117462306a36Sopenharmony_ci drm->mode_config.max_width = 0; 117562306a36Sopenharmony_ci drm->mode_config.max_height = 0; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci drm->mode_config.normalize_zpos = true; 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci drm->mode_config.funcs = &tegra_drm_mode_config_funcs; 118062306a36Sopenharmony_ci drm->mode_config.helper_private = &tegra_drm_mode_config_helpers; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci drm_kms_helper_poll_init(drm); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci err = host1x_device_init(dev); 118562306a36Sopenharmony_ci if (err < 0) 118662306a36Sopenharmony_ci goto poll; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci /* 118962306a36Sopenharmony_ci * Now that all display controller have been initialized, the maximum 119062306a36Sopenharmony_ci * supported resolution is known and the bitmask for horizontal and 119162306a36Sopenharmony_ci * vertical bitfields can be computed. 119262306a36Sopenharmony_ci */ 119362306a36Sopenharmony_ci tegra->hmask = drm->mode_config.max_width - 1; 119462306a36Sopenharmony_ci tegra->vmask = drm->mode_config.max_height - 1; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (tegra->use_explicit_iommu) { 119762306a36Sopenharmony_ci u64 carveout_start, carveout_end, gem_start, gem_end; 119862306a36Sopenharmony_ci u64 dma_mask = dma_get_mask(&dev->dev); 119962306a36Sopenharmony_ci dma_addr_t start, end; 120062306a36Sopenharmony_ci unsigned long order; 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_ci start = tegra->domain->geometry.aperture_start & dma_mask; 120362306a36Sopenharmony_ci end = tegra->domain->geometry.aperture_end & dma_mask; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci gem_start = start; 120662306a36Sopenharmony_ci gem_end = end - CARVEOUT_SZ; 120762306a36Sopenharmony_ci carveout_start = gem_end + 1; 120862306a36Sopenharmony_ci carveout_end = end; 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci order = __ffs(tegra->domain->pgsize_bitmap); 121162306a36Sopenharmony_ci init_iova_domain(&tegra->carveout.domain, 1UL << order, 121262306a36Sopenharmony_ci carveout_start >> order); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci tegra->carveout.shift = iova_shift(&tegra->carveout.domain); 121562306a36Sopenharmony_ci tegra->carveout.limit = carveout_end >> tegra->carveout.shift; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci drm_mm_init(&tegra->mm, gem_start, gem_end - gem_start + 1); 121862306a36Sopenharmony_ci mutex_init(&tegra->mm_lock); 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci DRM_DEBUG_DRIVER("IOMMU apertures:\n"); 122162306a36Sopenharmony_ci DRM_DEBUG_DRIVER(" GEM: %#llx-%#llx\n", gem_start, gem_end); 122262306a36Sopenharmony_ci DRM_DEBUG_DRIVER(" Carveout: %#llx-%#llx\n", carveout_start, 122362306a36Sopenharmony_ci carveout_end); 122462306a36Sopenharmony_ci } else if (tegra->domain) { 122562306a36Sopenharmony_ci iommu_domain_free(tegra->domain); 122662306a36Sopenharmony_ci tegra->domain = NULL; 122762306a36Sopenharmony_ci iova_cache_put(); 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci if (tegra->hub) { 123162306a36Sopenharmony_ci err = tegra_display_hub_prepare(tegra->hub); 123262306a36Sopenharmony_ci if (err < 0) 123362306a36Sopenharmony_ci goto device; 123462306a36Sopenharmony_ci } 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci /* syncpoints are used for full 32-bit hardware VBLANK counters */ 123762306a36Sopenharmony_ci drm->max_vblank_count = 0xffffffff; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci err = drm_vblank_init(drm, drm->mode_config.num_crtc); 124062306a36Sopenharmony_ci if (err < 0) 124162306a36Sopenharmony_ci goto hub; 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci drm_mode_config_reset(drm); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* 124662306a36Sopenharmony_ci * Only take over from a potential firmware framebuffer if any CRTCs 124762306a36Sopenharmony_ci * have been registered. This must not be a fatal error because there 124862306a36Sopenharmony_ci * are other accelerators that are exposed via this driver. 124962306a36Sopenharmony_ci * 125062306a36Sopenharmony_ci * Another case where this happens is on Tegra234 where the display 125162306a36Sopenharmony_ci * hardware is no longer part of the host1x complex, so this driver 125262306a36Sopenharmony_ci * will not expose any modesetting features. 125362306a36Sopenharmony_ci */ 125462306a36Sopenharmony_ci if (drm->mode_config.num_crtc > 0) { 125562306a36Sopenharmony_ci err = drm_aperture_remove_framebuffers(&tegra_drm_driver); 125662306a36Sopenharmony_ci if (err < 0) 125762306a36Sopenharmony_ci goto hub; 125862306a36Sopenharmony_ci } else { 125962306a36Sopenharmony_ci /* 126062306a36Sopenharmony_ci * Indicate to userspace that this doesn't expose any display 126162306a36Sopenharmony_ci * capabilities. 126262306a36Sopenharmony_ci */ 126362306a36Sopenharmony_ci drm->driver_features &= ~(DRIVER_MODESET | DRIVER_ATOMIC); 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci err = drm_dev_register(drm, 0); 126762306a36Sopenharmony_ci if (err < 0) 126862306a36Sopenharmony_ci goto hub; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci tegra_fbdev_setup(drm); 127162306a36Sopenharmony_ci 127262306a36Sopenharmony_ci return 0; 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cihub: 127562306a36Sopenharmony_ci if (tegra->hub) 127662306a36Sopenharmony_ci tegra_display_hub_cleanup(tegra->hub); 127762306a36Sopenharmony_cidevice: 127862306a36Sopenharmony_ci if (tegra->domain) { 127962306a36Sopenharmony_ci mutex_destroy(&tegra->mm_lock); 128062306a36Sopenharmony_ci drm_mm_takedown(&tegra->mm); 128162306a36Sopenharmony_ci put_iova_domain(&tegra->carveout.domain); 128262306a36Sopenharmony_ci iova_cache_put(); 128362306a36Sopenharmony_ci } 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci host1x_device_exit(dev); 128662306a36Sopenharmony_cipoll: 128762306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 128862306a36Sopenharmony_ci drm_mode_config_cleanup(drm); 128962306a36Sopenharmony_cidomain: 129062306a36Sopenharmony_ci if (tegra->domain) 129162306a36Sopenharmony_ci iommu_domain_free(tegra->domain); 129262306a36Sopenharmony_cifree: 129362306a36Sopenharmony_ci kfree(tegra); 129462306a36Sopenharmony_ciput: 129562306a36Sopenharmony_ci drm_dev_put(drm); 129662306a36Sopenharmony_ci return err; 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic int host1x_drm_remove(struct host1x_device *dev) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(&dev->dev); 130262306a36Sopenharmony_ci struct tegra_drm *tegra = drm->dev_private; 130362306a36Sopenharmony_ci int err; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_ci drm_dev_unregister(drm); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci drm_kms_helper_poll_fini(drm); 130862306a36Sopenharmony_ci drm_atomic_helper_shutdown(drm); 130962306a36Sopenharmony_ci drm_mode_config_cleanup(drm); 131062306a36Sopenharmony_ci 131162306a36Sopenharmony_ci if (tegra->hub) 131262306a36Sopenharmony_ci tegra_display_hub_cleanup(tegra->hub); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci err = host1x_device_exit(dev); 131562306a36Sopenharmony_ci if (err < 0) 131662306a36Sopenharmony_ci dev_err(&dev->dev, "host1x device cleanup failed: %d\n", err); 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci if (tegra->domain) { 131962306a36Sopenharmony_ci mutex_destroy(&tegra->mm_lock); 132062306a36Sopenharmony_ci drm_mm_takedown(&tegra->mm); 132162306a36Sopenharmony_ci put_iova_domain(&tegra->carveout.domain); 132262306a36Sopenharmony_ci iova_cache_put(); 132362306a36Sopenharmony_ci iommu_domain_free(tegra->domain); 132462306a36Sopenharmony_ci } 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_ci kfree(tegra); 132762306a36Sopenharmony_ci drm_dev_put(drm); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci return 0; 133062306a36Sopenharmony_ci} 133162306a36Sopenharmony_ci 133262306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 133362306a36Sopenharmony_cistatic int host1x_drm_suspend(struct device *dev) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci return drm_mode_config_helper_suspend(drm); 133862306a36Sopenharmony_ci} 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic int host1x_drm_resume(struct device *dev) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci struct drm_device *drm = dev_get_drvdata(dev); 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci return drm_mode_config_helper_resume(drm); 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci#endif 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend, 134962306a36Sopenharmony_ci host1x_drm_resume); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic const struct of_device_id host1x_drm_subdevs[] = { 135262306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-dc", }, 135362306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-hdmi", }, 135462306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-gr2d", }, 135562306a36Sopenharmony_ci { .compatible = "nvidia,tegra20-gr3d", }, 135662306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-dc", }, 135762306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-hdmi", }, 135862306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-gr2d", }, 135962306a36Sopenharmony_ci { .compatible = "nvidia,tegra30-gr3d", }, 136062306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-dc", }, 136162306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-dsi", }, 136262306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-hdmi", }, 136362306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-gr2d", }, 136462306a36Sopenharmony_ci { .compatible = "nvidia,tegra114-gr3d", }, 136562306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-dc", }, 136662306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-sor", }, 136762306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-hdmi", }, 136862306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-dsi", }, 136962306a36Sopenharmony_ci { .compatible = "nvidia,tegra124-vic", }, 137062306a36Sopenharmony_ci { .compatible = "nvidia,tegra132-dsi", }, 137162306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-dc", }, 137262306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-dsi", }, 137362306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-sor", }, 137462306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-sor1", }, 137562306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-vic", }, 137662306a36Sopenharmony_ci { .compatible = "nvidia,tegra210-nvdec", }, 137762306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-display", }, 137862306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-dc", }, 137962306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-sor", }, 138062306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-sor1", }, 138162306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-vic", }, 138262306a36Sopenharmony_ci { .compatible = "nvidia,tegra186-nvdec", }, 138362306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-display", }, 138462306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-dc", }, 138562306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-sor", }, 138662306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-vic", }, 138762306a36Sopenharmony_ci { .compatible = "nvidia,tegra194-nvdec", }, 138862306a36Sopenharmony_ci { .compatible = "nvidia,tegra234-vic", }, 138962306a36Sopenharmony_ci { .compatible = "nvidia,tegra234-nvdec", }, 139062306a36Sopenharmony_ci { /* sentinel */ } 139162306a36Sopenharmony_ci}; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_cistatic struct host1x_driver host1x_drm_driver = { 139462306a36Sopenharmony_ci .driver = { 139562306a36Sopenharmony_ci .name = "drm", 139662306a36Sopenharmony_ci .pm = &host1x_drm_pm_ops, 139762306a36Sopenharmony_ci }, 139862306a36Sopenharmony_ci .probe = host1x_drm_probe, 139962306a36Sopenharmony_ci .remove = host1x_drm_remove, 140062306a36Sopenharmony_ci .subdevs = host1x_drm_subdevs, 140162306a36Sopenharmony_ci}; 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_cistatic struct platform_driver * const drivers[] = { 140462306a36Sopenharmony_ci &tegra_display_hub_driver, 140562306a36Sopenharmony_ci &tegra_dc_driver, 140662306a36Sopenharmony_ci &tegra_hdmi_driver, 140762306a36Sopenharmony_ci &tegra_dsi_driver, 140862306a36Sopenharmony_ci &tegra_dpaux_driver, 140962306a36Sopenharmony_ci &tegra_sor_driver, 141062306a36Sopenharmony_ci &tegra_gr2d_driver, 141162306a36Sopenharmony_ci &tegra_gr3d_driver, 141262306a36Sopenharmony_ci &tegra_vic_driver, 141362306a36Sopenharmony_ci &tegra_nvdec_driver, 141462306a36Sopenharmony_ci}; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cistatic int __init host1x_drm_init(void) 141762306a36Sopenharmony_ci{ 141862306a36Sopenharmony_ci int err; 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci if (drm_firmware_drivers_only()) 142162306a36Sopenharmony_ci return -ENODEV; 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci err = host1x_driver_register(&host1x_drm_driver); 142462306a36Sopenharmony_ci if (err < 0) 142562306a36Sopenharmony_ci return err; 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci err = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); 142862306a36Sopenharmony_ci if (err < 0) 142962306a36Sopenharmony_ci goto unregister_host1x; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci return 0; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ciunregister_host1x: 143462306a36Sopenharmony_ci host1x_driver_unregister(&host1x_drm_driver); 143562306a36Sopenharmony_ci return err; 143662306a36Sopenharmony_ci} 143762306a36Sopenharmony_cimodule_init(host1x_drm_init); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_cistatic void __exit host1x_drm_exit(void) 144062306a36Sopenharmony_ci{ 144162306a36Sopenharmony_ci platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); 144262306a36Sopenharmony_ci host1x_driver_unregister(&host1x_drm_driver); 144362306a36Sopenharmony_ci} 144462306a36Sopenharmony_cimodule_exit(host1x_drm_exit); 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); 144762306a36Sopenharmony_ciMODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); 144862306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1449