blob: c1bb0d90145489b206a16b65116028fbfbc9f8bb [file] [log] [blame]
Thierry Redingd8f4a9e2012-11-15 21:28:22 +00001/*
2 * Copyright (C) 2012 Avionic Design GmbH
Terje Bergstromd43f81c2013-03-22 16:34:09 +02003 * Copyright (C) 2012-2013 NVIDIA CORPORATION. All rights reserved.
Thierry Redingd8f4a9e2012-11-15 21:28:22 +00004 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9
Thierry Reding776dc382013-10-14 14:43:22 +020010#include <linux/host1x.h>
Thierry Redingdf06b752014-06-26 21:41:53 +020011#include <linux/iommu.h>
Thierry Reding776dc382013-10-14 14:43:22 +020012
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000013#include "drm.h"
Arto Merilainende2ba662013-03-22 16:34:08 +020014#include "gem.h"
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000015
16#define DRIVER_NAME "tegra"
17#define DRIVER_DESC "NVIDIA Tegra graphics"
18#define DRIVER_DATE "20120330"
19#define DRIVER_MAJOR 0
20#define DRIVER_MINOR 0
21#define DRIVER_PATCHLEVEL 0
22
Thierry Reding08943e62013-09-26 16:08:18 +020023struct tegra_drm_file {
24 struct list_head contexts;
25};
26
Thierry Redingf9914212014-11-26 13:03:57 +010027static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
28 .fb_create = tegra_fb_create,
29#ifdef CONFIG_DRM_TEGRA_FBDEV
30 .output_poll_changed = tegra_fb_output_poll_changed,
31#endif
32};
33
Thierry Reding776dc382013-10-14 14:43:22 +020034static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000035{
Thierry Reding776dc382013-10-14 14:43:22 +020036 struct host1x_device *device = to_host1x_device(drm->dev);
Thierry Reding386a2a72013-09-24 13:22:17 +020037 struct tegra_drm *tegra;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000038 int err;
39
Thierry Reding776dc382013-10-14 14:43:22 +020040 tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
Thierry Reding386a2a72013-09-24 13:22:17 +020041 if (!tegra)
Terje Bergstrom692e6d72013-03-22 16:34:07 +020042 return -ENOMEM;
43
Thierry Redingdf06b752014-06-26 21:41:53 +020044 if (iommu_present(&platform_bus_type)) {
45 tegra->domain = iommu_domain_alloc(&platform_bus_type);
Dan Carpenterbf19b882014-12-04 14:00:35 +030046 if (!tegra->domain) {
47 err = -ENOMEM;
Thierry Redingdf06b752014-06-26 21:41:53 +020048 goto free;
49 }
50
51 DRM_DEBUG("IOMMU context initialized\n");
52 drm_mm_init(&tegra->mm, 0, SZ_2G);
53 }
54
Thierry Reding386a2a72013-09-24 13:22:17 +020055 mutex_init(&tegra->clients_lock);
56 INIT_LIST_HEAD(&tegra->clients);
Thierry Reding386a2a72013-09-24 13:22:17 +020057 drm->dev_private = tegra;
58 tegra->drm = drm;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000059
60 drm_mode_config_init(drm);
61
Thierry Redingf9914212014-11-26 13:03:57 +010062 drm->mode_config.min_width = 0;
63 drm->mode_config.min_height = 0;
64
65 drm->mode_config.max_width = 4096;
66 drm->mode_config.max_height = 4096;
67
68 drm->mode_config.funcs = &tegra_drm_mode_funcs;
69
Thierry Redinge2215322014-06-27 17:19:25 +020070 err = tegra_drm_fb_prepare(drm);
71 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010072 goto config;
Thierry Redinge2215322014-06-27 17:19:25 +020073
74 drm_kms_helper_poll_init(drm);
75
Thierry Reding776dc382013-10-14 14:43:22 +020076 err = host1x_device_init(device);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000077 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010078 goto fbdev;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000079
Thierry Reding9d441892014-11-24 17:02:53 +010080 drm_mode_config_reset(drm);
81
Thierry Reding603f0cc2013-04-22 21:22:14 +020082 /*
83 * We don't use the drm_irq_install() helpers provided by the DRM
84 * core, so we need to set this manually in order to allow the
85 * DRM_IOCTL_WAIT_VBLANK to operate correctly.
86 */
Ville Syrjälä44238432013-10-04 14:53:37 +030087 drm->irq_enabled = true;
Thierry Reding603f0cc2013-04-22 21:22:14 +020088
Thierry Reding6e5ff992012-11-28 11:45:47 +010089 err = drm_vblank_init(drm, drm->mode_config.num_crtc);
90 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010091 goto device;
Thierry Reding6e5ff992012-11-28 11:45:47 +010092
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000093 err = tegra_drm_fb_init(drm);
94 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010095 goto vblank;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000096
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000097 return 0;
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010098
99vblank:
100 drm_vblank_cleanup(drm);
101device:
102 host1x_device_exit(device);
103fbdev:
104 drm_kms_helper_poll_fini(drm);
105 tegra_drm_fb_free(drm);
106config:
107 drm_mode_config_cleanup(drm);
Thierry Redingdf06b752014-06-26 21:41:53 +0200108
109 if (tegra->domain) {
110 iommu_domain_free(tegra->domain);
111 drm_mm_takedown(&tegra->mm);
112 }
113free:
Thierry Reding1d1e6fe2014-11-06 14:12:08 +0100114 kfree(tegra);
115 return err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000116}
117
118static int tegra_drm_unload(struct drm_device *drm)
119{
Thierry Reding776dc382013-10-14 14:43:22 +0200120 struct host1x_device *device = to_host1x_device(drm->dev);
Thierry Redingdf06b752014-06-26 21:41:53 +0200121 struct tegra_drm *tegra = drm->dev_private;
Thierry Reding776dc382013-10-14 14:43:22 +0200122 int err;
123
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000124 drm_kms_helper_poll_fini(drm);
125 tegra_drm_fb_exit(drm);
Thierry Redingf002abc2013-10-14 14:06:02 +0200126 drm_mode_config_cleanup(drm);
Thierry Reding4aa3df72014-11-24 16:27:13 +0100127 drm_vblank_cleanup(drm);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000128
Thierry Reding776dc382013-10-14 14:43:22 +0200129 err = host1x_device_exit(device);
130 if (err < 0)
131 return err;
132
Thierry Redingdf06b752014-06-26 21:41:53 +0200133 if (tegra->domain) {
134 iommu_domain_free(tegra->domain);
135 drm_mm_takedown(&tegra->mm);
136 }
137
Thierry Reding1053f4dd2014-11-04 16:17:55 +0100138 kfree(tegra);
139
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000140 return 0;
141}
142
143static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
144{
Thierry Reding08943e62013-09-26 16:08:18 +0200145 struct tegra_drm_file *fpriv;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200146
147 fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
148 if (!fpriv)
149 return -ENOMEM;
150
151 INIT_LIST_HEAD(&fpriv->contexts);
152 filp->driver_priv = fpriv;
153
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000154 return 0;
155}
156
Thierry Redingc88c3632013-09-26 16:08:22 +0200157static void tegra_drm_context_free(struct tegra_drm_context *context)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200158{
159 context->client->ops->close_channel(context);
160 kfree(context);
161}
162
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000163static void tegra_drm_lastclose(struct drm_device *drm)
164{
Paul Bolle6e601632014-02-09 14:01:33 +0100165#ifdef CONFIG_DRM_TEGRA_FBDEV
Thierry Reding386a2a72013-09-24 13:22:17 +0200166 struct tegra_drm *tegra = drm->dev_private;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000167
Thierry Reding386a2a72013-09-24 13:22:17 +0200168 tegra_fbdev_restore_mode(tegra->fbdev);
Thierry Reding60c2f702013-10-31 13:28:50 +0100169#endif
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000170}
171
Thierry Redingc40f0f12013-10-10 11:00:33 +0200172static struct host1x_bo *
173host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
174{
175 struct drm_gem_object *gem;
176 struct tegra_bo *bo;
177
178 gem = drm_gem_object_lookup(drm, file, handle);
179 if (!gem)
180 return NULL;
181
182 mutex_lock(&drm->struct_mutex);
183 drm_gem_object_unreference(gem);
184 mutex_unlock(&drm->struct_mutex);
185
186 bo = to_tegra_bo(gem);
187 return &bo->base;
188}
189
Thierry Reding961e3be2014-06-10 10:25:00 +0200190static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
191 struct drm_tegra_reloc __user *src,
192 struct drm_device *drm,
193 struct drm_file *file)
194{
195 u32 cmdbuf, target;
196 int err;
197
198 err = get_user(cmdbuf, &src->cmdbuf.handle);
199 if (err < 0)
200 return err;
201
202 err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
203 if (err < 0)
204 return err;
205
206 err = get_user(target, &src->target.handle);
207 if (err < 0)
208 return err;
209
210 err = get_user(dest->target.offset, &src->cmdbuf.offset);
211 if (err < 0)
212 return err;
213
214 err = get_user(dest->shift, &src->shift);
215 if (err < 0)
216 return err;
217
218 dest->cmdbuf.bo = host1x_bo_lookup(drm, file, cmdbuf);
219 if (!dest->cmdbuf.bo)
220 return -ENOENT;
221
222 dest->target.bo = host1x_bo_lookup(drm, file, target);
223 if (!dest->target.bo)
224 return -ENOENT;
225
226 return 0;
227}
228
Thierry Redingc40f0f12013-10-10 11:00:33 +0200229int tegra_drm_submit(struct tegra_drm_context *context,
230 struct drm_tegra_submit *args, struct drm_device *drm,
231 struct drm_file *file)
232{
233 unsigned int num_cmdbufs = args->num_cmdbufs;
234 unsigned int num_relocs = args->num_relocs;
235 unsigned int num_waitchks = args->num_waitchks;
236 struct drm_tegra_cmdbuf __user *cmdbufs =
Thierry Redinga7ed68f2013-11-08 13:15:43 +0100237 (void __user *)(uintptr_t)args->cmdbufs;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200238 struct drm_tegra_reloc __user *relocs =
Thierry Redinga7ed68f2013-11-08 13:15:43 +0100239 (void __user *)(uintptr_t)args->relocs;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200240 struct drm_tegra_waitchk __user *waitchks =
Thierry Redinga7ed68f2013-11-08 13:15:43 +0100241 (void __user *)(uintptr_t)args->waitchks;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200242 struct drm_tegra_syncpt syncpt;
243 struct host1x_job *job;
244 int err;
245
246 /* We don't yet support other than one syncpt_incr struct per submit */
247 if (args->num_syncpts != 1)
248 return -EINVAL;
249
250 job = host1x_job_alloc(context->channel, args->num_cmdbufs,
251 args->num_relocs, args->num_waitchks);
252 if (!job)
253 return -ENOMEM;
254
255 job->num_relocs = args->num_relocs;
256 job->num_waitchk = args->num_waitchks;
257 job->client = (u32)args->context;
258 job->class = context->client->base.class;
259 job->serialize = true;
260
261 while (num_cmdbufs) {
262 struct drm_tegra_cmdbuf cmdbuf;
263 struct host1x_bo *bo;
264
Dan Carpenter9a991602013-11-08 13:07:37 +0300265 if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) {
266 err = -EFAULT;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200267 goto fail;
Dan Carpenter9a991602013-11-08 13:07:37 +0300268 }
Thierry Redingc40f0f12013-10-10 11:00:33 +0200269
270 bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
271 if (!bo) {
272 err = -ENOENT;
273 goto fail;
274 }
275
276 host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
277 num_cmdbufs--;
278 cmdbufs++;
279 }
280
Thierry Reding961e3be2014-06-10 10:25:00 +0200281 /* copy and resolve relocations from submit */
Thierry Redingc40f0f12013-10-10 11:00:33 +0200282 while (num_relocs--) {
Thierry Reding961e3be2014-06-10 10:25:00 +0200283 err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
284 &relocs[num_relocs], drm,
285 file);
286 if (err < 0)
Thierry Redingc40f0f12013-10-10 11:00:33 +0200287 goto fail;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200288 }
289
Dan Carpenter9a991602013-11-08 13:07:37 +0300290 if (copy_from_user(job->waitchk, waitchks,
291 sizeof(*waitchks) * num_waitchks)) {
292 err = -EFAULT;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200293 goto fail;
Dan Carpenter9a991602013-11-08 13:07:37 +0300294 }
Thierry Redingc40f0f12013-10-10 11:00:33 +0200295
Dan Carpenter9a991602013-11-08 13:07:37 +0300296 if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts,
297 sizeof(syncpt))) {
298 err = -EFAULT;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200299 goto fail;
Dan Carpenter9a991602013-11-08 13:07:37 +0300300 }
Thierry Redingc40f0f12013-10-10 11:00:33 +0200301
302 job->is_addr_reg = context->client->ops->is_addr_reg;
303 job->syncpt_incrs = syncpt.incrs;
304 job->syncpt_id = syncpt.id;
305 job->timeout = 10000;
306
307 if (args->timeout && args->timeout < 10000)
308 job->timeout = args->timeout;
309
310 err = host1x_job_pin(job, context->client->base.dev);
311 if (err)
312 goto fail;
313
314 err = host1x_job_submit(job);
315 if (err)
316 goto fail_submit;
317
318 args->fence = job->syncpt_end;
319
320 host1x_job_put(job);
321 return 0;
322
323fail_submit:
324 host1x_job_unpin(job);
325fail:
326 host1x_job_put(job);
327 return err;
328}
329
330
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200331#ifdef CONFIG_DRM_TEGRA_STAGING
Thierry Redingc88c3632013-09-26 16:08:22 +0200332static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200333{
Thierry Redingc88c3632013-09-26 16:08:22 +0200334 return (struct tegra_drm_context *)(uintptr_t)context;
335}
336
337static bool tegra_drm_file_owns_context(struct tegra_drm_file *file,
338 struct tegra_drm_context *context)
339{
340 struct tegra_drm_context *ctx;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200341
342 list_for_each_entry(ctx, &file->contexts, list)
343 if (ctx == context)
344 return true;
345
346 return false;
347}
348
349static int tegra_gem_create(struct drm_device *drm, void *data,
350 struct drm_file *file)
351{
352 struct drm_tegra_gem_create *args = data;
353 struct tegra_bo *bo;
354
Thierry Reding773af772013-10-04 22:34:01 +0200355 bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200356 &args->handle);
357 if (IS_ERR(bo))
358 return PTR_ERR(bo);
359
360 return 0;
361}
362
363static int tegra_gem_mmap(struct drm_device *drm, void *data,
364 struct drm_file *file)
365{
366 struct drm_tegra_gem_mmap *args = data;
367 struct drm_gem_object *gem;
368 struct tegra_bo *bo;
369
370 gem = drm_gem_object_lookup(drm, file, args->handle);
371 if (!gem)
372 return -EINVAL;
373
374 bo = to_tegra_bo(gem);
375
David Herrmann2bc7b0c2013-08-13 14:19:58 +0200376 args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200377
378 drm_gem_object_unreference(gem);
379
380 return 0;
381}
382
383static int tegra_syncpt_read(struct drm_device *drm, void *data,
384 struct drm_file *file)
385{
Thierry Reding776dc382013-10-14 14:43:22 +0200386 struct host1x *host = dev_get_drvdata(drm->dev->parent);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200387 struct drm_tegra_syncpt_read *args = data;
Thierry Reding776dc382013-10-14 14:43:22 +0200388 struct host1x_syncpt *sp;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200389
Thierry Reding776dc382013-10-14 14:43:22 +0200390 sp = host1x_syncpt_get(host, args->id);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200391 if (!sp)
392 return -EINVAL;
393
394 args->value = host1x_syncpt_read_min(sp);
395 return 0;
396}
397
398static int tegra_syncpt_incr(struct drm_device *drm, void *data,
399 struct drm_file *file)
400{
Thierry Reding776dc382013-10-14 14:43:22 +0200401 struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200402 struct drm_tegra_syncpt_incr *args = data;
Thierry Reding776dc382013-10-14 14:43:22 +0200403 struct host1x_syncpt *sp;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200404
Thierry Reding776dc382013-10-14 14:43:22 +0200405 sp = host1x_syncpt_get(host1x, args->id);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200406 if (!sp)
407 return -EINVAL;
408
Arto Merilainenebae30b2013-05-29 13:26:08 +0300409 return host1x_syncpt_incr(sp);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200410}
411
412static int tegra_syncpt_wait(struct drm_device *drm, void *data,
413 struct drm_file *file)
414{
Thierry Reding776dc382013-10-14 14:43:22 +0200415 struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200416 struct drm_tegra_syncpt_wait *args = data;
Thierry Reding776dc382013-10-14 14:43:22 +0200417 struct host1x_syncpt *sp;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200418
Thierry Reding776dc382013-10-14 14:43:22 +0200419 sp = host1x_syncpt_get(host1x, args->id);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200420 if (!sp)
421 return -EINVAL;
422
423 return host1x_syncpt_wait(sp, args->thresh, args->timeout,
424 &args->value);
425}
426
427static int tegra_open_channel(struct drm_device *drm, void *data,
428 struct drm_file *file)
429{
Thierry Reding08943e62013-09-26 16:08:18 +0200430 struct tegra_drm_file *fpriv = file->driver_priv;
Thierry Reding386a2a72013-09-24 13:22:17 +0200431 struct tegra_drm *tegra = drm->dev_private;
432 struct drm_tegra_open_channel *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200433 struct tegra_drm_context *context;
Thierry Reding53fa7f72013-09-24 15:35:40 +0200434 struct tegra_drm_client *client;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200435 int err = -ENODEV;
436
437 context = kzalloc(sizeof(*context), GFP_KERNEL);
438 if (!context)
439 return -ENOMEM;
440
Thierry Reding776dc382013-10-14 14:43:22 +0200441 list_for_each_entry(client, &tegra->clients, list)
Thierry Reding53fa7f72013-09-24 15:35:40 +0200442 if (client->base.class == args->client) {
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200443 err = client->ops->open_channel(client, context);
444 if (err)
445 break;
446
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200447 list_add(&context->list, &fpriv->contexts);
448 args->context = (uintptr_t)context;
Thierry Reding53fa7f72013-09-24 15:35:40 +0200449 context->client = client;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200450 return 0;
451 }
452
453 kfree(context);
454 return err;
455}
456
457static int tegra_close_channel(struct drm_device *drm, void *data,
458 struct drm_file *file)
459{
Thierry Reding08943e62013-09-26 16:08:18 +0200460 struct tegra_drm_file *fpriv = file->driver_priv;
Thierry Reding776dc382013-10-14 14:43:22 +0200461 struct drm_tegra_close_channel *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200462 struct tegra_drm_context *context;
463
464 context = tegra_drm_get_context(args->context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200465
Thierry Reding08943e62013-09-26 16:08:18 +0200466 if (!tegra_drm_file_owns_context(fpriv, context))
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200467 return -EINVAL;
468
469 list_del(&context->list);
Thierry Redingc88c3632013-09-26 16:08:22 +0200470 tegra_drm_context_free(context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200471
472 return 0;
473}
474
475static int tegra_get_syncpt(struct drm_device *drm, void *data,
476 struct drm_file *file)
477{
Thierry Reding08943e62013-09-26 16:08:18 +0200478 struct tegra_drm_file *fpriv = file->driver_priv;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200479 struct drm_tegra_get_syncpt *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200480 struct tegra_drm_context *context;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200481 struct host1x_syncpt *syncpt;
482
Thierry Redingc88c3632013-09-26 16:08:22 +0200483 context = tegra_drm_get_context(args->context);
484
Thierry Reding08943e62013-09-26 16:08:18 +0200485 if (!tegra_drm_file_owns_context(fpriv, context))
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200486 return -ENODEV;
487
Thierry Reding53fa7f72013-09-24 15:35:40 +0200488 if (args->index >= context->client->base.num_syncpts)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200489 return -EINVAL;
490
Thierry Reding53fa7f72013-09-24 15:35:40 +0200491 syncpt = context->client->base.syncpts[args->index];
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200492 args->id = host1x_syncpt_id(syncpt);
493
494 return 0;
495}
496
497static int tegra_submit(struct drm_device *drm, void *data,
498 struct drm_file *file)
499{
Thierry Reding08943e62013-09-26 16:08:18 +0200500 struct tegra_drm_file *fpriv = file->driver_priv;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200501 struct drm_tegra_submit *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200502 struct tegra_drm_context *context;
503
504 context = tegra_drm_get_context(args->context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200505
Thierry Reding08943e62013-09-26 16:08:18 +0200506 if (!tegra_drm_file_owns_context(fpriv, context))
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200507 return -ENODEV;
508
509 return context->client->ops->submit(context, args, drm, file);
510}
Arto Merilainenc54a1692013-10-14 15:21:54 +0300511
512static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
513 struct drm_file *file)
514{
515 struct tegra_drm_file *fpriv = file->driver_priv;
516 struct drm_tegra_get_syncpt_base *args = data;
517 struct tegra_drm_context *context;
518 struct host1x_syncpt_base *base;
519 struct host1x_syncpt *syncpt;
520
521 context = tegra_drm_get_context(args->context);
522
523 if (!tegra_drm_file_owns_context(fpriv, context))
524 return -ENODEV;
525
526 if (args->syncpt >= context->client->base.num_syncpts)
527 return -EINVAL;
528
529 syncpt = context->client->base.syncpts[args->syncpt];
530
531 base = host1x_syncpt_get_base(syncpt);
532 if (!base)
533 return -ENXIO;
534
535 args->id = host1x_syncpt_base_id(base);
536
537 return 0;
538}
Thierry Reding7678d712014-06-03 14:56:57 +0200539
540static int tegra_gem_set_tiling(struct drm_device *drm, void *data,
541 struct drm_file *file)
542{
543 struct drm_tegra_gem_set_tiling *args = data;
544 enum tegra_bo_tiling_mode mode;
545 struct drm_gem_object *gem;
546 unsigned long value = 0;
547 struct tegra_bo *bo;
548
549 switch (args->mode) {
550 case DRM_TEGRA_GEM_TILING_MODE_PITCH:
551 mode = TEGRA_BO_TILING_MODE_PITCH;
552
553 if (args->value != 0)
554 return -EINVAL;
555
556 break;
557
558 case DRM_TEGRA_GEM_TILING_MODE_TILED:
559 mode = TEGRA_BO_TILING_MODE_TILED;
560
561 if (args->value != 0)
562 return -EINVAL;
563
564 break;
565
566 case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
567 mode = TEGRA_BO_TILING_MODE_BLOCK;
568
569 if (args->value > 5)
570 return -EINVAL;
571
572 value = args->value;
573 break;
574
575 default:
576 return -EINVAL;
577 }
578
579 gem = drm_gem_object_lookup(drm, file, args->handle);
580 if (!gem)
581 return -ENOENT;
582
583 bo = to_tegra_bo(gem);
584
585 bo->tiling.mode = mode;
586 bo->tiling.value = value;
587
588 drm_gem_object_unreference(gem);
589
590 return 0;
591}
592
593static int tegra_gem_get_tiling(struct drm_device *drm, void *data,
594 struct drm_file *file)
595{
596 struct drm_tegra_gem_get_tiling *args = data;
597 struct drm_gem_object *gem;
598 struct tegra_bo *bo;
599 int err = 0;
600
601 gem = drm_gem_object_lookup(drm, file, args->handle);
602 if (!gem)
603 return -ENOENT;
604
605 bo = to_tegra_bo(gem);
606
607 switch (bo->tiling.mode) {
608 case TEGRA_BO_TILING_MODE_PITCH:
609 args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
610 args->value = 0;
611 break;
612
613 case TEGRA_BO_TILING_MODE_TILED:
614 args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
615 args->value = 0;
616 break;
617
618 case TEGRA_BO_TILING_MODE_BLOCK:
619 args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
620 args->value = bo->tiling.value;
621 break;
622
623 default:
624 err = -EINVAL;
625 break;
626 }
627
628 drm_gem_object_unreference(gem);
629
630 return err;
631}
Thierry Reding7b129082014-06-10 12:04:03 +0200632
633static int tegra_gem_set_flags(struct drm_device *drm, void *data,
634 struct drm_file *file)
635{
636 struct drm_tegra_gem_set_flags *args = data;
637 struct drm_gem_object *gem;
638 struct tegra_bo *bo;
639
640 if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
641 return -EINVAL;
642
643 gem = drm_gem_object_lookup(drm, file, args->handle);
644 if (!gem)
645 return -ENOENT;
646
647 bo = to_tegra_bo(gem);
648 bo->flags = 0;
649
650 if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
651 bo->flags |= TEGRA_BO_BOTTOM_UP;
652
653 drm_gem_object_unreference(gem);
654
655 return 0;
656}
657
658static int tegra_gem_get_flags(struct drm_device *drm, void *data,
659 struct drm_file *file)
660{
661 struct drm_tegra_gem_get_flags *args = data;
662 struct drm_gem_object *gem;
663 struct tegra_bo *bo;
664
665 gem = drm_gem_object_lookup(drm, file, args->handle);
666 if (!gem)
667 return -ENOENT;
668
669 bo = to_tegra_bo(gem);
670 args->flags = 0;
671
672 if (bo->flags & TEGRA_BO_BOTTOM_UP)
673 args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;
674
675 drm_gem_object_unreference(gem);
676
677 return 0;
678}
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200679#endif
680
Rob Clarkbaa70942013-08-02 13:27:49 -0400681static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200682#ifdef CONFIG_DRM_TEGRA_STAGING
Thierry Redingbd4f2362014-06-03 14:59:29 +0200683 DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED),
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200684 DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED),
685 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED),
686 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED),
687 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED),
688 DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED),
689 DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED),
690 DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED),
691 DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED),
Arto Merilainenc54a1692013-10-14 15:21:54 +0300692 DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED),
Thierry Reding7678d712014-06-03 14:56:57 +0200693 DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, DRM_UNLOCKED),
694 DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, DRM_UNLOCKED),
Thierry Reding7b129082014-06-10 12:04:03 +0200695 DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, DRM_UNLOCKED),
696 DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, DRM_UNLOCKED),
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200697#endif
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000698};
699
700static const struct file_operations tegra_drm_fops = {
701 .owner = THIS_MODULE,
702 .open = drm_open,
703 .release = drm_release,
704 .unlocked_ioctl = drm_ioctl,
Arto Merilainende2ba662013-03-22 16:34:08 +0200705 .mmap = tegra_drm_mmap,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000706 .poll = drm_poll,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000707 .read = drm_read,
708#ifdef CONFIG_COMPAT
709 .compat_ioctl = drm_compat_ioctl,
710#endif
711 .llseek = noop_llseek,
712};
713
Thierry Redinged7dae52014-12-16 16:03:13 +0100714static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm,
715 unsigned int pipe)
Thierry Reding6e5ff992012-11-28 11:45:47 +0100716{
717 struct drm_crtc *crtc;
718
719 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
Thierry Redinged7dae52014-12-16 16:03:13 +0100720 if (pipe == drm_crtc_index(crtc))
Thierry Reding6e5ff992012-11-28 11:45:47 +0100721 return crtc;
722 }
723
724 return NULL;
725}
726
Thierry Redinged7dae52014-12-16 16:03:13 +0100727static u32 tegra_drm_get_vblank_counter(struct drm_device *drm, int pipe)
Thierry Reding6e5ff992012-11-28 11:45:47 +0100728{
Thierry Redinged7dae52014-12-16 16:03:13 +0100729 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
730
731 if (!crtc)
732 return 0;
733
Thierry Reding6e5ff992012-11-28 11:45:47 +0100734 /* TODO: implement real hardware counter using syncpoints */
Thierry Redinged7dae52014-12-16 16:03:13 +0100735 return drm_crtc_vblank_count(crtc);
Thierry Reding6e5ff992012-11-28 11:45:47 +0100736}
737
738static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
739{
740 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
741 struct tegra_dc *dc = to_tegra_dc(crtc);
742
743 if (!crtc)
744 return -ENODEV;
745
746 tegra_dc_enable_vblank(dc);
747
748 return 0;
749}
750
751static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
752{
753 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
754 struct tegra_dc *dc = to_tegra_dc(crtc);
755
756 if (crtc)
757 tegra_dc_disable_vblank(dc);
758}
759
Thierry Reding3c03c462012-11-28 12:00:18 +0100760static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
761{
Thierry Reding08943e62013-09-26 16:08:18 +0200762 struct tegra_drm_file *fpriv = file->driver_priv;
Thierry Redingc88c3632013-09-26 16:08:22 +0200763 struct tegra_drm_context *context, *tmp;
Thierry Reding3c03c462012-11-28 12:00:18 +0100764 struct drm_crtc *crtc;
765
766 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
767 tegra_dc_cancel_page_flip(crtc, file);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200768
769 list_for_each_entry_safe(context, tmp, &fpriv->contexts, list)
Thierry Redingc88c3632013-09-26 16:08:22 +0200770 tegra_drm_context_free(context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200771
772 kfree(fpriv);
Thierry Reding3c03c462012-11-28 12:00:18 +0100773}
774
Thierry Redinge450fcc2013-02-13 16:13:16 +0100775#ifdef CONFIG_DEBUG_FS
776static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
777{
778 struct drm_info_node *node = (struct drm_info_node *)s->private;
779 struct drm_device *drm = node->minor->dev;
780 struct drm_framebuffer *fb;
781
782 mutex_lock(&drm->mode_config.fb_lock);
783
784 list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
785 seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
786 fb->base.id, fb->width, fb->height, fb->depth,
787 fb->bits_per_pixel,
788 atomic_read(&fb->refcount.refcount));
789 }
790
791 mutex_unlock(&drm->mode_config.fb_lock);
792
793 return 0;
794}
795
796static struct drm_info_list tegra_debugfs_list[] = {
797 { "framebuffers", tegra_debugfs_framebuffers, 0 },
798};
799
800static int tegra_debugfs_init(struct drm_minor *minor)
801{
802 return drm_debugfs_create_files(tegra_debugfs_list,
803 ARRAY_SIZE(tegra_debugfs_list),
804 minor->debugfs_root, minor);
805}
806
807static void tegra_debugfs_cleanup(struct drm_minor *minor)
808{
809 drm_debugfs_remove_files(tegra_debugfs_list,
810 ARRAY_SIZE(tegra_debugfs_list), minor);
811}
812#endif
813
Thierry Reding9b57f5f2013-11-08 13:17:14 +0100814static struct drm_driver tegra_drm_driver = {
Thierry Reding38003912013-12-12 10:00:43 +0100815 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000816 .load = tegra_drm_load,
817 .unload = tegra_drm_unload,
818 .open = tegra_drm_open,
Thierry Reding3c03c462012-11-28 12:00:18 +0100819 .preclose = tegra_drm_preclose,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000820 .lastclose = tegra_drm_lastclose,
821
Thierry Reding6e5ff992012-11-28 11:45:47 +0100822 .get_vblank_counter = tegra_drm_get_vblank_counter,
823 .enable_vblank = tegra_drm_enable_vblank,
824 .disable_vblank = tegra_drm_disable_vblank,
825
Thierry Redinge450fcc2013-02-13 16:13:16 +0100826#if defined(CONFIG_DEBUG_FS)
827 .debugfs_init = tegra_debugfs_init,
828 .debugfs_cleanup = tegra_debugfs_cleanup,
829#endif
830
Arto Merilainende2ba662013-03-22 16:34:08 +0200831 .gem_free_object = tegra_bo_free_object,
832 .gem_vm_ops = &tegra_bo_vm_ops,
Thierry Reding38003912013-12-12 10:00:43 +0100833
834 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
835 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
836 .gem_prime_export = tegra_gem_prime_export,
837 .gem_prime_import = tegra_gem_prime_import,
838
Arto Merilainende2ba662013-03-22 16:34:08 +0200839 .dumb_create = tegra_bo_dumb_create,
840 .dumb_map_offset = tegra_bo_dumb_map_offset,
Daniel Vetter43387b32013-07-16 09:12:04 +0200841 .dumb_destroy = drm_gem_dumb_destroy,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000842
843 .ioctls = tegra_drm_ioctls,
844 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
845 .fops = &tegra_drm_fops,
846
847 .name = DRIVER_NAME,
848 .desc = DRIVER_DESC,
849 .date = DRIVER_DATE,
850 .major = DRIVER_MAJOR,
851 .minor = DRIVER_MINOR,
852 .patchlevel = DRIVER_PATCHLEVEL,
853};
Thierry Reding776dc382013-10-14 14:43:22 +0200854
855int tegra_drm_register_client(struct tegra_drm *tegra,
856 struct tegra_drm_client *client)
857{
858 mutex_lock(&tegra->clients_lock);
859 list_add_tail(&client->list, &tegra->clients);
860 mutex_unlock(&tegra->clients_lock);
861
862 return 0;
863}
864
865int tegra_drm_unregister_client(struct tegra_drm *tegra,
866 struct tegra_drm_client *client)
867{
868 mutex_lock(&tegra->clients_lock);
869 list_del_init(&client->list);
870 mutex_unlock(&tegra->clients_lock);
871
872 return 0;
873}
874
Thierry Reding9910f5c2014-05-22 09:57:15 +0200875static int host1x_drm_probe(struct host1x_device *dev)
Thierry Reding776dc382013-10-14 14:43:22 +0200876{
Thierry Reding9910f5c2014-05-22 09:57:15 +0200877 struct drm_driver *driver = &tegra_drm_driver;
878 struct drm_device *drm;
879 int err;
880
881 drm = drm_dev_alloc(driver, &dev->dev);
882 if (!drm)
883 return -ENOMEM;
884
885 drm_dev_set_unique(drm, dev_name(&dev->dev));
886 dev_set_drvdata(&dev->dev, drm);
887
888 err = drm_dev_register(drm, 0);
889 if (err < 0)
890 goto unref;
891
892 DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
893 driver->major, driver->minor, driver->patchlevel,
894 driver->date, drm->primary->index);
895
896 return 0;
897
898unref:
899 drm_dev_unref(drm);
900 return err;
Thierry Reding776dc382013-10-14 14:43:22 +0200901}
902
Thierry Reding9910f5c2014-05-22 09:57:15 +0200903static int host1x_drm_remove(struct host1x_device *dev)
Thierry Reding776dc382013-10-14 14:43:22 +0200904{
Thierry Reding9910f5c2014-05-22 09:57:15 +0200905 struct drm_device *drm = dev_get_drvdata(&dev->dev);
906
907 drm_dev_unregister(drm);
908 drm_dev_unref(drm);
Thierry Reding776dc382013-10-14 14:43:22 +0200909
910 return 0;
911}
912
913static const struct of_device_id host1x_drm_subdevs[] = {
914 { .compatible = "nvidia,tegra20-dc", },
915 { .compatible = "nvidia,tegra20-hdmi", },
916 { .compatible = "nvidia,tegra20-gr2d", },
Thierry Reding5f60ed02013-02-28 08:08:01 +0100917 { .compatible = "nvidia,tegra20-gr3d", },
Thierry Reding776dc382013-10-14 14:43:22 +0200918 { .compatible = "nvidia,tegra30-dc", },
919 { .compatible = "nvidia,tegra30-hdmi", },
920 { .compatible = "nvidia,tegra30-gr2d", },
Thierry Reding5f60ed02013-02-28 08:08:01 +0100921 { .compatible = "nvidia,tegra30-gr3d", },
Thierry Redingdec72732013-09-03 08:45:46 +0200922 { .compatible = "nvidia,tegra114-dsi", },
Mikko Perttunen7d1d28a2013-09-30 16:54:47 +0200923 { .compatible = "nvidia,tegra114-hdmi", },
Thierry Reding5f60ed02013-02-28 08:08:01 +0100924 { .compatible = "nvidia,tegra114-gr3d", },
Thierry Reding8620fc62013-12-12 11:03:59 +0100925 { .compatible = "nvidia,tegra124-dc", },
Thierry Reding6b6b6042013-11-15 16:06:05 +0100926 { .compatible = "nvidia,tegra124-sor", },
Thierry Redingfb7be702013-11-15 16:07:32 +0100927 { .compatible = "nvidia,tegra124-hdmi", },
Thierry Reding776dc382013-10-14 14:43:22 +0200928 { /* sentinel */ }
929};
930
931static struct host1x_driver host1x_drm_driver = {
Thierry Redingf4c5cf82014-12-18 15:29:14 +0100932 .driver = {
933 .name = "drm",
934 },
Thierry Reding776dc382013-10-14 14:43:22 +0200935 .probe = host1x_drm_probe,
936 .remove = host1x_drm_remove,
937 .subdevs = host1x_drm_subdevs,
938};
939
940static int __init host1x_drm_init(void)
941{
942 int err;
943
944 err = host1x_driver_register(&host1x_drm_driver);
945 if (err < 0)
946 return err;
947
948 err = platform_driver_register(&tegra_dc_driver);
949 if (err < 0)
950 goto unregister_host1x;
951
Thierry Redingdec72732013-09-03 08:45:46 +0200952 err = platform_driver_register(&tegra_dsi_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200953 if (err < 0)
954 goto unregister_dc;
955
Thierry Reding6b6b6042013-11-15 16:06:05 +0100956 err = platform_driver_register(&tegra_sor_driver);
Thierry Redingdec72732013-09-03 08:45:46 +0200957 if (err < 0)
958 goto unregister_dsi;
959
Thierry Reding6b6b6042013-11-15 16:06:05 +0100960 err = platform_driver_register(&tegra_hdmi_driver);
961 if (err < 0)
962 goto unregister_sor;
963
964 err = platform_driver_register(&tegra_dpaux_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200965 if (err < 0)
966 goto unregister_hdmi;
967
Thierry Reding6b6b6042013-11-15 16:06:05 +0100968 err = platform_driver_register(&tegra_gr2d_driver);
969 if (err < 0)
970 goto unregister_dpaux;
971
Thierry Reding5f60ed02013-02-28 08:08:01 +0100972 err = platform_driver_register(&tegra_gr3d_driver);
973 if (err < 0)
974 goto unregister_gr2d;
975
Thierry Reding776dc382013-10-14 14:43:22 +0200976 return 0;
977
Thierry Reding5f60ed02013-02-28 08:08:01 +0100978unregister_gr2d:
979 platform_driver_unregister(&tegra_gr2d_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +0100980unregister_dpaux:
981 platform_driver_unregister(&tegra_dpaux_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200982unregister_hdmi:
983 platform_driver_unregister(&tegra_hdmi_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +0100984unregister_sor:
985 platform_driver_unregister(&tegra_sor_driver);
Thierry Redingdec72732013-09-03 08:45:46 +0200986unregister_dsi:
987 platform_driver_unregister(&tegra_dsi_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200988unregister_dc:
989 platform_driver_unregister(&tegra_dc_driver);
990unregister_host1x:
991 host1x_driver_unregister(&host1x_drm_driver);
992 return err;
993}
994module_init(host1x_drm_init);
995
996static void __exit host1x_drm_exit(void)
997{
Thierry Reding5f60ed02013-02-28 08:08:01 +0100998 platform_driver_unregister(&tegra_gr3d_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200999 platform_driver_unregister(&tegra_gr2d_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +01001000 platform_driver_unregister(&tegra_dpaux_driver);
Thierry Reding776dc382013-10-14 14:43:22 +02001001 platform_driver_unregister(&tegra_hdmi_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +01001002 platform_driver_unregister(&tegra_sor_driver);
Thierry Redingdec72732013-09-03 08:45:46 +02001003 platform_driver_unregister(&tegra_dsi_driver);
Thierry Reding776dc382013-10-14 14:43:22 +02001004 platform_driver_unregister(&tegra_dc_driver);
1005 host1x_driver_unregister(&host1x_drm_driver);
1006}
1007module_exit(host1x_drm_exit);
1008
1009MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
1010MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
1011MODULE_LICENSE("GPL v2");