diff options
Diffstat (limited to 'drivers/gpu/drm/tegra/drm.c')
-rw-r--r-- | drivers/gpu/drm/tegra/drm.c | 184 |
1 files changed, 70 insertions, 114 deletions
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c index 7fcf4a242840..a2bd5876c633 100644 --- a/drivers/gpu/drm/tegra/drm.c +++ b/drivers/gpu/drm/tegra/drm.c @@ -38,51 +38,11 @@ static int tegra_atomic_check(struct drm_device *drm, { int err; - err = drm_atomic_helper_check_modeset(drm, state); + err = drm_atomic_helper_check(drm, state); if (err < 0) return err; - err = drm_atomic_normalize_zpos(drm, state); - if (err < 0) - return err; - - err = drm_atomic_helper_check_planes(drm, state); - if (err < 0) - return err; - - if (state->legacy_cursor_update) - state->async_update = !drm_atomic_helper_async_check(drm, state); - - return 0; -} - -static struct drm_atomic_state * -tegra_atomic_state_alloc(struct drm_device *drm) -{ - struct tegra_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL); - - if (!state || drm_atomic_state_init(drm, &state->base) < 0) { - kfree(state); - return NULL; - } - - return &state->base; -} - -static void tegra_atomic_state_clear(struct drm_atomic_state *state) -{ - struct tegra_atomic_state *tegra = to_tegra_atomic_state(state); - - drm_atomic_state_default_clear(state); - tegra->clk_disp = NULL; - tegra->dc = NULL; - tegra->rate = 0; -} - -static void tegra_atomic_state_free(struct drm_atomic_state *state) -{ - drm_atomic_state_default_release(state); - kfree(state); + return tegra_display_hub_atomic_check(drm, state); } static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { @@ -92,9 +52,6 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { #endif .atomic_check = tegra_atomic_check, .atomic_commit = drm_atomic_helper_commit, - .atomic_state_alloc = tegra_atomic_state_alloc, - .atomic_state_clear = tegra_atomic_state_clear, - .atomic_state_free = tegra_atomic_state_free, }; static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) @@ -141,6 +98,10 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) goto free; } + err = iova_cache_get(); + if (err < 0) + goto domain; + geometry = &tegra->domain->geometry; gem_start = geometry->aperture_start; gem_end = geometry->aperture_end - CARVEOUT_SZ; @@ -179,6 +140,8 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags) drm->mode_config.allow_fb_modifiers = true; + drm->mode_config.normalize_zpos = true; + drm->mode_config.funcs = &tegra_drm_mode_config_funcs; drm->mode_config.helper_private = &tegra_drm_mode_config_helpers; @@ -232,11 +195,14 @@ config: drm_mode_config_cleanup(drm); if (tegra->domain) { - iommu_domain_free(tegra->domain); - drm_mm_takedown(&tegra->mm); mutex_destroy(&tegra->mm_lock); + drm_mm_takedown(&tegra->mm); put_iova_domain(&tegra->carveout.domain); + iova_cache_put(); } +domain: + if (tegra->domain) + iommu_domain_free(tegra->domain); free: kfree(tegra); return err; @@ -258,10 +224,11 @@ static void tegra_drm_unload(struct drm_device *drm) return; if (tegra->domain) { - iommu_domain_free(tegra->domain); - drm_mm_takedown(&tegra->mm); mutex_destroy(&tegra->mm_lock); + drm_mm_takedown(&tegra->mm); put_iova_domain(&tegra->carveout.domain); + iova_cache_put(); + iommu_domain_free(tegra->domain); } kfree(tegra); @@ -341,46 +308,15 @@ static int host1x_reloc_copy_from_user(struct host1x_reloc *dest, return 0; } -static int host1x_waitchk_copy_from_user(struct host1x_waitchk *dest, - struct drm_tegra_waitchk __user *src, - struct drm_file *file) -{ - u32 cmdbuf; - int err; - - err = get_user(cmdbuf, &src->handle); - if (err < 0) - return err; - - err = get_user(dest->offset, &src->offset); - if (err < 0) - return err; - - err = get_user(dest->syncpt_id, &src->syncpt); - if (err < 0) - return err; - - err = get_user(dest->thresh, &src->thresh); - if (err < 0) - return err; - - dest->bo = host1x_bo_lookup(file, cmdbuf); - if (!dest->bo) - return -ENOENT; - - return 0; -} - int tegra_drm_submit(struct tegra_drm_context *context, struct drm_tegra_submit *args, struct drm_device *drm, struct drm_file *file) { + struct host1x_client *client = &context->client->base; unsigned int num_cmdbufs = args->num_cmdbufs; unsigned int num_relocs = args->num_relocs; - unsigned int num_waitchks = args->num_waitchks; struct drm_tegra_cmdbuf __user *user_cmdbufs; struct drm_tegra_reloc __user *user_relocs; - struct drm_tegra_waitchk __user *user_waitchks; struct drm_tegra_syncpt __user *user_syncpt; struct drm_tegra_syncpt syncpt; struct host1x *host1x = dev_get_drvdata(drm->dev->parent); @@ -392,7 +328,6 @@ int tegra_drm_submit(struct tegra_drm_context *context, user_cmdbufs = u64_to_user_ptr(args->cmdbufs); user_relocs = u64_to_user_ptr(args->relocs); - user_waitchks = u64_to_user_ptr(args->waitchks); user_syncpt = u64_to_user_ptr(args->syncpts); /* We don't yet support other than one syncpt_incr struct per submit */ @@ -404,21 +339,20 @@ int tegra_drm_submit(struct tegra_drm_context *context, return -EINVAL; job = host1x_job_alloc(context->channel, args->num_cmdbufs, - args->num_relocs, args->num_waitchks); + args->num_relocs); if (!job) return -ENOMEM; job->num_relocs = args->num_relocs; - job->num_waitchk = args->num_waitchks; - job->client = (u32)args->context; - job->class = context->client->base.class; + job->client = client; + job->class = client->class; job->serialize = true; /* * Track referenced BOs so that they can be unreferenced after the * submission is complete. */ - num_refs = num_cmdbufs + num_relocs * 2 + num_waitchks; + num_refs = num_cmdbufs + num_relocs * 2; refs = kmalloc_array(num_refs, sizeof(*refs), GFP_KERNEL); if (!refs) { @@ -464,7 +398,7 @@ int tegra_drm_submit(struct tegra_drm_context *context, * unaligned offset is malformed and cause commands stream * corruption on the buffer address relocation. */ - if (offset & 3 || offset >= obj->gem.size) { + if (offset & 3 || offset > obj->gem.size) { err = -EINVAL; goto fail; } @@ -479,13 +413,13 @@ int tegra_drm_submit(struct tegra_drm_context *context, struct host1x_reloc *reloc; struct tegra_bo *obj; - err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs], + err = host1x_reloc_copy_from_user(&job->relocs[num_relocs], &user_relocs[num_relocs], drm, file); if (err < 0) goto fail; - reloc = &job->relocarray[num_relocs]; + reloc = &job->relocs[num_relocs]; obj = host1x_to_tegra_bo(reloc->cmdbuf.bo); refs[num_refs++] = &obj->gem; @@ -509,30 +443,6 @@ int tegra_drm_submit(struct tegra_drm_context *context, } } - /* copy and resolve waitchks from submit */ - while (num_waitchks--) { - struct host1x_waitchk *wait = &job->waitchk[num_waitchks]; - struct tegra_bo *obj; - - err = host1x_waitchk_copy_from_user( - wait, &user_waitchks[num_waitchks], file); - if (err < 0) - goto fail; - - obj = host1x_to_tegra_bo(wait->bo); - refs[num_refs++] = &obj->gem; - - /* - * The unaligned offset will cause an unaligned write during - * of the waitchks patching, corrupting the commands stream. - */ - if (wait->offset & 3 || - wait->offset >= obj->gem.size) { - err = -EINVAL; - goto fail; - } - } - if (copy_from_user(&syncpt, user_syncpt, sizeof(syncpt))) { err = -EFAULT; goto fail; @@ -1142,6 +1052,52 @@ int tegra_drm_unregister_client(struct tegra_drm *tegra, return 0; } +struct iommu_group *host1x_client_iommu_attach(struct host1x_client *client, + bool shared) +{ + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + struct iommu_group *group = NULL; + int err; + + if (tegra->domain) { + group = iommu_group_get(client->dev); + if (!group) { + dev_err(client->dev, "failed to get IOMMU group\n"); + return ERR_PTR(-ENODEV); + } + + if (!shared || (shared && (group != tegra->group))) { + err = iommu_attach_group(tegra->domain, group); + if (err < 0) { + iommu_group_put(group); + return ERR_PTR(err); + } + + if (shared && !tegra->group) + tegra->group = group; + } + } + + return group; +} + +void host1x_client_iommu_detach(struct host1x_client *client, + struct iommu_group *group) +{ + struct drm_device *drm = dev_get_drvdata(client->parent); + struct tegra_drm *tegra = drm->dev_private; + + if (group) { + if (group == tegra->group) { + iommu_detach_group(tegra->domain, group); + tegra->group = NULL; + } + + iommu_group_put(group); + } +} + void *tegra_drm_alloc(struct tegra_drm *tegra, size_t size, dma_addr_t *dma) { struct iova *alloc; |