drm/tegra: Introduce tegra_drm_submit()
[cascardo/linux.git] / drivers / gpu / drm / tegra / drm.c
index c2db409..6f42f16 100644 (file)
@@ -72,13 +72,13 @@ static int tegra_drm_unload(struct drm_device *drm)
 
        drm_kms_helper_poll_fini(drm);
        tegra_drm_fb_exit(drm);
+       drm_vblank_cleanup(drm);
+       drm_mode_config_cleanup(drm);
 
        err = host1x_device_exit(device);
        if (err < 0)
                return err;
 
-       drm_mode_config_cleanup(drm);
-
        return 0;
 }
 
@@ -109,6 +109,135 @@ static void tegra_drm_lastclose(struct drm_device *drm)
        tegra_fbdev_restore_mode(tegra->fbdev);
 }
 
+static struct host1x_bo *
+host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
+{
+       struct drm_gem_object *gem;
+       struct tegra_bo *bo;
+
+       gem = drm_gem_object_lookup(drm, file, handle);
+       if (!gem)
+               return NULL;
+
+       mutex_lock(&drm->struct_mutex);
+       drm_gem_object_unreference(gem);
+       mutex_unlock(&drm->struct_mutex);
+
+       bo = to_tegra_bo(gem);
+       return &bo->base;
+}
+
+int tegra_drm_submit(struct tegra_drm_context *context,
+                    struct drm_tegra_submit *args, struct drm_device *drm,
+                    struct drm_file *file)
+{
+       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 *cmdbufs =
+               (void * __user)(uintptr_t)args->cmdbufs;
+       struct drm_tegra_reloc __user *relocs =
+               (void * __user)(uintptr_t)args->relocs;
+       struct drm_tegra_waitchk __user *waitchks =
+               (void * __user)(uintptr_t)args->waitchks;
+       struct drm_tegra_syncpt syncpt;
+       struct host1x_job *job;
+       int err;
+
+       /* We don't yet support other than one syncpt_incr struct per submit */
+       if (args->num_syncpts != 1)
+               return -EINVAL;
+
+       job = host1x_job_alloc(context->channel, args->num_cmdbufs,
+                              args->num_relocs, args->num_waitchks);
+       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->serialize = true;
+
+       while (num_cmdbufs) {
+               struct drm_tegra_cmdbuf cmdbuf;
+               struct host1x_bo *bo;
+
+               err = copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf));
+               if (err)
+                       goto fail;
+
+               bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
+               if (!bo) {
+                       err = -ENOENT;
+                       goto fail;
+               }
+
+               host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
+               num_cmdbufs--;
+               cmdbufs++;
+       }
+
+       err = copy_from_user(job->relocarray, relocs,
+                            sizeof(*relocs) * num_relocs);
+       if (err)
+               goto fail;
+
+       while (num_relocs--) {
+               struct host1x_reloc *reloc = &job->relocarray[num_relocs];
+               struct host1x_bo *cmdbuf, *target;
+
+               cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf);
+               target = host1x_bo_lookup(drm, file, (u32)reloc->target);
+
+               reloc->cmdbuf = cmdbuf;
+               reloc->target = target;
+
+               if (!reloc->target || !reloc->cmdbuf) {
+                       err = -ENOENT;
+                       goto fail;
+               }
+       }
+
+       err = copy_from_user(job->waitchk, waitchks,
+                            sizeof(*waitchks) * num_waitchks);
+       if (err)
+               goto fail;
+
+       err = copy_from_user(&syncpt, (void * __user)(uintptr_t)args->syncpts,
+                            sizeof(syncpt));
+       if (err)
+               goto fail;
+
+       job->is_addr_reg = context->client->ops->is_addr_reg;
+       job->syncpt_incrs = syncpt.incrs;
+       job->syncpt_id = syncpt.id;
+       job->timeout = 10000;
+
+       if (args->timeout && args->timeout < 10000)
+               job->timeout = args->timeout;
+
+       err = host1x_job_pin(job, context->client->base.dev);
+       if (err)
+               goto fail;
+
+       err = host1x_job_submit(job);
+       if (err)
+               goto fail_submit;
+
+       args->fence = job->syncpt_end;
+
+       host1x_job_put(job);
+       return 0;
+
+fail_submit:
+       host1x_job_unpin(job);
+fail:
+       host1x_job_put(job);
+       return err;
+}
+
+
 #ifdef CONFIG_DRM_TEGRA_STAGING
 static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
 {
@@ -489,6 +618,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
        { .compatible = "nvidia,tegra30-dc", },
        { .compatible = "nvidia,tegra30-hdmi", },
        { .compatible = "nvidia,tegra30-gr2d", },
+       { .compatible = "nvidia,tegra114-hdmi", },
        { /* sentinel */ }
 };