blob: 6c9df794b3be42dde9a2df18ffd263a1b2763eab [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 Reding776dc382013-10-14 14:43:22 +020027static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000028{
Thierry Reding776dc382013-10-14 14:43:22 +020029 struct host1x_device *device = to_host1x_device(drm->dev);
Thierry Reding386a2a72013-09-24 13:22:17 +020030 struct tegra_drm *tegra;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000031 int err;
32
Thierry Reding776dc382013-10-14 14:43:22 +020033 tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
Thierry Reding386a2a72013-09-24 13:22:17 +020034 if (!tegra)
Terje Bergstrom692e6d72013-03-22 16:34:07 +020035 return -ENOMEM;
36
Thierry Redingdf06b752014-06-26 21:41:53 +020037 if (iommu_present(&platform_bus_type)) {
38 tegra->domain = iommu_domain_alloc(&platform_bus_type);
39 if (IS_ERR(tegra->domain)) {
40 err = PTR_ERR(tegra->domain);
41 goto free;
42 }
43
44 DRM_DEBUG("IOMMU context initialized\n");
45 drm_mm_init(&tegra->mm, 0, SZ_2G);
46 }
47
Thierry Reding386a2a72013-09-24 13:22:17 +020048 mutex_init(&tegra->clients_lock);
49 INIT_LIST_HEAD(&tegra->clients);
Thierry Reding386a2a72013-09-24 13:22:17 +020050 drm->dev_private = tegra;
51 tegra->drm = drm;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000052
53 drm_mode_config_init(drm);
54
Thierry Redinge2215322014-06-27 17:19:25 +020055 err = tegra_drm_fb_prepare(drm);
56 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010057 goto config;
Thierry Redinge2215322014-06-27 17:19:25 +020058
59 drm_kms_helper_poll_init(drm);
60
Thierry Reding776dc382013-10-14 14:43:22 +020061 err = host1x_device_init(device);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000062 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010063 goto fbdev;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000064
Thierry Reding603f0cc2013-04-22 21:22:14 +020065 /*
66 * We don't use the drm_irq_install() helpers provided by the DRM
67 * core, so we need to set this manually in order to allow the
68 * DRM_IOCTL_WAIT_VBLANK to operate correctly.
69 */
Ville Syrjälä44238432013-10-04 14:53:37 +030070 drm->irq_enabled = true;
Thierry Reding603f0cc2013-04-22 21:22:14 +020071
Thierry Reding6e5ff992012-11-28 11:45:47 +010072 err = drm_vblank_init(drm, drm->mode_config.num_crtc);
73 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010074 goto device;
Thierry Reding6e5ff992012-11-28 11:45:47 +010075
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000076 err = tegra_drm_fb_init(drm);
77 if (err < 0)
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010078 goto vblank;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000079
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000080 return 0;
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010081
82vblank:
83 drm_vblank_cleanup(drm);
84device:
85 host1x_device_exit(device);
86fbdev:
87 drm_kms_helper_poll_fini(drm);
88 tegra_drm_fb_free(drm);
89config:
90 drm_mode_config_cleanup(drm);
Thierry Redingdf06b752014-06-26 21:41:53 +020091
92 if (tegra->domain) {
93 iommu_domain_free(tegra->domain);
94 drm_mm_takedown(&tegra->mm);
95 }
96free:
Thierry Reding1d1e6fe2014-11-06 14:12:08 +010097 kfree(tegra);
98 return err;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +000099}
100
101static int tegra_drm_unload(struct drm_device *drm)
102{
Thierry Reding776dc382013-10-14 14:43:22 +0200103 struct host1x_device *device = to_host1x_device(drm->dev);
Thierry Redingdf06b752014-06-26 21:41:53 +0200104 struct tegra_drm *tegra = drm->dev_private;
Thierry Reding776dc382013-10-14 14:43:22 +0200105 int err;
106
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000107 drm_kms_helper_poll_fini(drm);
108 tegra_drm_fb_exit(drm);
Thierry Redingf002abc2013-10-14 14:06:02 +0200109 drm_vblank_cleanup(drm);
110 drm_mode_config_cleanup(drm);
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000111
Thierry Reding776dc382013-10-14 14:43:22 +0200112 err = host1x_device_exit(device);
113 if (err < 0)
114 return err;
115
Thierry Redingdf06b752014-06-26 21:41:53 +0200116 if (tegra->domain) {
117 iommu_domain_free(tegra->domain);
118 drm_mm_takedown(&tegra->mm);
119 }
120
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000121 return 0;
122}
123
124static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
125{
Thierry Reding08943e62013-09-26 16:08:18 +0200126 struct tegra_drm_file *fpriv;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200127
128 fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
129 if (!fpriv)
130 return -ENOMEM;
131
132 INIT_LIST_HEAD(&fpriv->contexts);
133 filp->driver_priv = fpriv;
134
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000135 return 0;
136}
137
Thierry Redingc88c3632013-09-26 16:08:22 +0200138static void tegra_drm_context_free(struct tegra_drm_context *context)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200139{
140 context->client->ops->close_channel(context);
141 kfree(context);
142}
143
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000144static void tegra_drm_lastclose(struct drm_device *drm)
145{
Paul Bolle6e601632014-02-09 14:01:33 +0100146#ifdef CONFIG_DRM_TEGRA_FBDEV
Thierry Reding386a2a72013-09-24 13:22:17 +0200147 struct tegra_drm *tegra = drm->dev_private;
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000148
Thierry Reding386a2a72013-09-24 13:22:17 +0200149 tegra_fbdev_restore_mode(tegra->fbdev);
Thierry Reding60c2f702013-10-31 13:28:50 +0100150#endif
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000151}
152
Thierry Redingc40f0f12013-10-10 11:00:33 +0200153static struct host1x_bo *
154host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle)
155{
156 struct drm_gem_object *gem;
157 struct tegra_bo *bo;
158
159 gem = drm_gem_object_lookup(drm, file, handle);
160 if (!gem)
161 return NULL;
162
163 mutex_lock(&drm->struct_mutex);
164 drm_gem_object_unreference(gem);
165 mutex_unlock(&drm->struct_mutex);
166
167 bo = to_tegra_bo(gem);
168 return &bo->base;
169}
170
Thierry Reding961e3be2014-06-10 10:25:00 +0200171static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
172 struct drm_tegra_reloc __user *src,
173 struct drm_device *drm,
174 struct drm_file *file)
175{
176 u32 cmdbuf, target;
177 int err;
178
179 err = get_user(cmdbuf, &src->cmdbuf.handle);
180 if (err < 0)
181 return err;
182
183 err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
184 if (err < 0)
185 return err;
186
187 err = get_user(target, &src->target.handle);
188 if (err < 0)
189 return err;
190
191 err = get_user(dest->target.offset, &src->cmdbuf.offset);
192 if (err < 0)
193 return err;
194
195 err = get_user(dest->shift, &src->shift);
196 if (err < 0)
197 return err;
198
199 dest->cmdbuf.bo = host1x_bo_lookup(drm, file, cmdbuf);
200 if (!dest->cmdbuf.bo)
201 return -ENOENT;
202
203 dest->target.bo = host1x_bo_lookup(drm, file, target);
204 if (!dest->target.bo)
205 return -ENOENT;
206
207 return 0;
208}
209
Thierry Redingc40f0f12013-10-10 11:00:33 +0200210int tegra_drm_submit(struct tegra_drm_context *context,
211 struct drm_tegra_submit *args, struct drm_device *drm,
212 struct drm_file *file)
213{
214 unsigned int num_cmdbufs = args->num_cmdbufs;
215 unsigned int num_relocs = args->num_relocs;
216 unsigned int num_waitchks = args->num_waitchks;
217 struct drm_tegra_cmdbuf __user *cmdbufs =
Thierry Redinga7ed68f2013-11-08 13:15:43 +0100218 (void __user *)(uintptr_t)args->cmdbufs;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200219 struct drm_tegra_reloc __user *relocs =
Thierry Redinga7ed68f2013-11-08 13:15:43 +0100220 (void __user *)(uintptr_t)args->relocs;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200221 struct drm_tegra_waitchk __user *waitchks =
Thierry Redinga7ed68f2013-11-08 13:15:43 +0100222 (void __user *)(uintptr_t)args->waitchks;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200223 struct drm_tegra_syncpt syncpt;
224 struct host1x_job *job;
225 int err;
226
227 /* We don't yet support other than one syncpt_incr struct per submit */
228 if (args->num_syncpts != 1)
229 return -EINVAL;
230
231 job = host1x_job_alloc(context->channel, args->num_cmdbufs,
232 args->num_relocs, args->num_waitchks);
233 if (!job)
234 return -ENOMEM;
235
236 job->num_relocs = args->num_relocs;
237 job->num_waitchk = args->num_waitchks;
238 job->client = (u32)args->context;
239 job->class = context->client->base.class;
240 job->serialize = true;
241
242 while (num_cmdbufs) {
243 struct drm_tegra_cmdbuf cmdbuf;
244 struct host1x_bo *bo;
245
Dan Carpenter9a991602013-11-08 13:07:37 +0300246 if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) {
247 err = -EFAULT;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200248 goto fail;
Dan Carpenter9a991602013-11-08 13:07:37 +0300249 }
Thierry Redingc40f0f12013-10-10 11:00:33 +0200250
251 bo = host1x_bo_lookup(drm, file, cmdbuf.handle);
252 if (!bo) {
253 err = -ENOENT;
254 goto fail;
255 }
256
257 host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
258 num_cmdbufs--;
259 cmdbufs++;
260 }
261
Thierry Reding961e3be2014-06-10 10:25:00 +0200262 /* copy and resolve relocations from submit */
Thierry Redingc40f0f12013-10-10 11:00:33 +0200263 while (num_relocs--) {
Thierry Reding961e3be2014-06-10 10:25:00 +0200264 err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
265 &relocs[num_relocs], drm,
266 file);
267 if (err < 0)
Thierry Redingc40f0f12013-10-10 11:00:33 +0200268 goto fail;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200269 }
270
Dan Carpenter9a991602013-11-08 13:07:37 +0300271 if (copy_from_user(job->waitchk, waitchks,
272 sizeof(*waitchks) * num_waitchks)) {
273 err = -EFAULT;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200274 goto fail;
Dan Carpenter9a991602013-11-08 13:07:37 +0300275 }
Thierry Redingc40f0f12013-10-10 11:00:33 +0200276
Dan Carpenter9a991602013-11-08 13:07:37 +0300277 if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts,
278 sizeof(syncpt))) {
279 err = -EFAULT;
Thierry Redingc40f0f12013-10-10 11:00:33 +0200280 goto fail;
Dan Carpenter9a991602013-11-08 13:07:37 +0300281 }
Thierry Redingc40f0f12013-10-10 11:00:33 +0200282
283 job->is_addr_reg = context->client->ops->is_addr_reg;
284 job->syncpt_incrs = syncpt.incrs;
285 job->syncpt_id = syncpt.id;
286 job->timeout = 10000;
287
288 if (args->timeout && args->timeout < 10000)
289 job->timeout = args->timeout;
290
291 err = host1x_job_pin(job, context->client->base.dev);
292 if (err)
293 goto fail;
294
295 err = host1x_job_submit(job);
296 if (err)
297 goto fail_submit;
298
299 args->fence = job->syncpt_end;
300
301 host1x_job_put(job);
302 return 0;
303
304fail_submit:
305 host1x_job_unpin(job);
306fail:
307 host1x_job_put(job);
308 return err;
309}
310
311
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200312#ifdef CONFIG_DRM_TEGRA_STAGING
Thierry Redingc88c3632013-09-26 16:08:22 +0200313static struct tegra_drm_context *tegra_drm_get_context(__u64 context)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200314{
Thierry Redingc88c3632013-09-26 16:08:22 +0200315 return (struct tegra_drm_context *)(uintptr_t)context;
316}
317
318static bool tegra_drm_file_owns_context(struct tegra_drm_file *file,
319 struct tegra_drm_context *context)
320{
321 struct tegra_drm_context *ctx;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200322
323 list_for_each_entry(ctx, &file->contexts, list)
324 if (ctx == context)
325 return true;
326
327 return false;
328}
329
330static int tegra_gem_create(struct drm_device *drm, void *data,
331 struct drm_file *file)
332{
333 struct drm_tegra_gem_create *args = data;
334 struct tegra_bo *bo;
335
Thierry Reding773af772013-10-04 22:34:01 +0200336 bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200337 &args->handle);
338 if (IS_ERR(bo))
339 return PTR_ERR(bo);
340
341 return 0;
342}
343
344static int tegra_gem_mmap(struct drm_device *drm, void *data,
345 struct drm_file *file)
346{
347 struct drm_tegra_gem_mmap *args = data;
348 struct drm_gem_object *gem;
349 struct tegra_bo *bo;
350
351 gem = drm_gem_object_lookup(drm, file, args->handle);
352 if (!gem)
353 return -EINVAL;
354
355 bo = to_tegra_bo(gem);
356
David Herrmann2bc7b0c2013-08-13 14:19:58 +0200357 args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200358
359 drm_gem_object_unreference(gem);
360
361 return 0;
362}
363
364static int tegra_syncpt_read(struct drm_device *drm, void *data,
365 struct drm_file *file)
366{
Thierry Reding776dc382013-10-14 14:43:22 +0200367 struct host1x *host = dev_get_drvdata(drm->dev->parent);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200368 struct drm_tegra_syncpt_read *args = data;
Thierry Reding776dc382013-10-14 14:43:22 +0200369 struct host1x_syncpt *sp;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200370
Thierry Reding776dc382013-10-14 14:43:22 +0200371 sp = host1x_syncpt_get(host, args->id);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200372 if (!sp)
373 return -EINVAL;
374
375 args->value = host1x_syncpt_read_min(sp);
376 return 0;
377}
378
379static int tegra_syncpt_incr(struct drm_device *drm, void *data,
380 struct drm_file *file)
381{
Thierry Reding776dc382013-10-14 14:43:22 +0200382 struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200383 struct drm_tegra_syncpt_incr *args = data;
Thierry Reding776dc382013-10-14 14:43:22 +0200384 struct host1x_syncpt *sp;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200385
Thierry Reding776dc382013-10-14 14:43:22 +0200386 sp = host1x_syncpt_get(host1x, args->id);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200387 if (!sp)
388 return -EINVAL;
389
Arto Merilainenebae30b2013-05-29 13:26:08 +0300390 return host1x_syncpt_incr(sp);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200391}
392
393static int tegra_syncpt_wait(struct drm_device *drm, void *data,
394 struct drm_file *file)
395{
Thierry Reding776dc382013-10-14 14:43:22 +0200396 struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200397 struct drm_tegra_syncpt_wait *args = data;
Thierry Reding776dc382013-10-14 14:43:22 +0200398 struct host1x_syncpt *sp;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200399
Thierry Reding776dc382013-10-14 14:43:22 +0200400 sp = host1x_syncpt_get(host1x, args->id);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200401 if (!sp)
402 return -EINVAL;
403
404 return host1x_syncpt_wait(sp, args->thresh, args->timeout,
405 &args->value);
406}
407
408static int tegra_open_channel(struct drm_device *drm, void *data,
409 struct drm_file *file)
410{
Thierry Reding08943e62013-09-26 16:08:18 +0200411 struct tegra_drm_file *fpriv = file->driver_priv;
Thierry Reding386a2a72013-09-24 13:22:17 +0200412 struct tegra_drm *tegra = drm->dev_private;
413 struct drm_tegra_open_channel *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200414 struct tegra_drm_context *context;
Thierry Reding53fa7f72013-09-24 15:35:40 +0200415 struct tegra_drm_client *client;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200416 int err = -ENODEV;
417
418 context = kzalloc(sizeof(*context), GFP_KERNEL);
419 if (!context)
420 return -ENOMEM;
421
Thierry Reding776dc382013-10-14 14:43:22 +0200422 list_for_each_entry(client, &tegra->clients, list)
Thierry Reding53fa7f72013-09-24 15:35:40 +0200423 if (client->base.class == args->client) {
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200424 err = client->ops->open_channel(client, context);
425 if (err)
426 break;
427
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200428 list_add(&context->list, &fpriv->contexts);
429 args->context = (uintptr_t)context;
Thierry Reding53fa7f72013-09-24 15:35:40 +0200430 context->client = client;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200431 return 0;
432 }
433
434 kfree(context);
435 return err;
436}
437
438static int tegra_close_channel(struct drm_device *drm, void *data,
439 struct drm_file *file)
440{
Thierry Reding08943e62013-09-26 16:08:18 +0200441 struct tegra_drm_file *fpriv = file->driver_priv;
Thierry Reding776dc382013-10-14 14:43:22 +0200442 struct drm_tegra_close_channel *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200443 struct tegra_drm_context *context;
444
445 context = tegra_drm_get_context(args->context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200446
Thierry Reding08943e62013-09-26 16:08:18 +0200447 if (!tegra_drm_file_owns_context(fpriv, context))
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200448 return -EINVAL;
449
450 list_del(&context->list);
Thierry Redingc88c3632013-09-26 16:08:22 +0200451 tegra_drm_context_free(context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200452
453 return 0;
454}
455
456static int tegra_get_syncpt(struct drm_device *drm, void *data,
457 struct drm_file *file)
458{
Thierry Reding08943e62013-09-26 16:08:18 +0200459 struct tegra_drm_file *fpriv = file->driver_priv;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200460 struct drm_tegra_get_syncpt *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200461 struct tegra_drm_context *context;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200462 struct host1x_syncpt *syncpt;
463
Thierry Redingc88c3632013-09-26 16:08:22 +0200464 context = tegra_drm_get_context(args->context);
465
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 -ENODEV;
468
Thierry Reding53fa7f72013-09-24 15:35:40 +0200469 if (args->index >= context->client->base.num_syncpts)
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200470 return -EINVAL;
471
Thierry Reding53fa7f72013-09-24 15:35:40 +0200472 syncpt = context->client->base.syncpts[args->index];
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200473 args->id = host1x_syncpt_id(syncpt);
474
475 return 0;
476}
477
478static int tegra_submit(struct drm_device *drm, void *data,
479 struct drm_file *file)
480{
Thierry Reding08943e62013-09-26 16:08:18 +0200481 struct tegra_drm_file *fpriv = file->driver_priv;
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200482 struct drm_tegra_submit *args = data;
Thierry Redingc88c3632013-09-26 16:08:22 +0200483 struct tegra_drm_context *context;
484
485 context = tegra_drm_get_context(args->context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200486
Thierry Reding08943e62013-09-26 16:08:18 +0200487 if (!tegra_drm_file_owns_context(fpriv, context))
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200488 return -ENODEV;
489
490 return context->client->ops->submit(context, args, drm, file);
491}
Arto Merilainenc54a1692013-10-14 15:21:54 +0300492
493static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
494 struct drm_file *file)
495{
496 struct tegra_drm_file *fpriv = file->driver_priv;
497 struct drm_tegra_get_syncpt_base *args = data;
498 struct tegra_drm_context *context;
499 struct host1x_syncpt_base *base;
500 struct host1x_syncpt *syncpt;
501
502 context = tegra_drm_get_context(args->context);
503
504 if (!tegra_drm_file_owns_context(fpriv, context))
505 return -ENODEV;
506
507 if (args->syncpt >= context->client->base.num_syncpts)
508 return -EINVAL;
509
510 syncpt = context->client->base.syncpts[args->syncpt];
511
512 base = host1x_syncpt_get_base(syncpt);
513 if (!base)
514 return -ENXIO;
515
516 args->id = host1x_syncpt_base_id(base);
517
518 return 0;
519}
Thierry Reding7678d712014-06-03 14:56:57 +0200520
521static int tegra_gem_set_tiling(struct drm_device *drm, void *data,
522 struct drm_file *file)
523{
524 struct drm_tegra_gem_set_tiling *args = data;
525 enum tegra_bo_tiling_mode mode;
526 struct drm_gem_object *gem;
527 unsigned long value = 0;
528 struct tegra_bo *bo;
529
530 switch (args->mode) {
531 case DRM_TEGRA_GEM_TILING_MODE_PITCH:
532 mode = TEGRA_BO_TILING_MODE_PITCH;
533
534 if (args->value != 0)
535 return -EINVAL;
536
537 break;
538
539 case DRM_TEGRA_GEM_TILING_MODE_TILED:
540 mode = TEGRA_BO_TILING_MODE_TILED;
541
542 if (args->value != 0)
543 return -EINVAL;
544
545 break;
546
547 case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
548 mode = TEGRA_BO_TILING_MODE_BLOCK;
549
550 if (args->value > 5)
551 return -EINVAL;
552
553 value = args->value;
554 break;
555
556 default:
557 return -EINVAL;
558 }
559
560 gem = drm_gem_object_lookup(drm, file, args->handle);
561 if (!gem)
562 return -ENOENT;
563
564 bo = to_tegra_bo(gem);
565
566 bo->tiling.mode = mode;
567 bo->tiling.value = value;
568
569 drm_gem_object_unreference(gem);
570
571 return 0;
572}
573
574static int tegra_gem_get_tiling(struct drm_device *drm, void *data,
575 struct drm_file *file)
576{
577 struct drm_tegra_gem_get_tiling *args = data;
578 struct drm_gem_object *gem;
579 struct tegra_bo *bo;
580 int err = 0;
581
582 gem = drm_gem_object_lookup(drm, file, args->handle);
583 if (!gem)
584 return -ENOENT;
585
586 bo = to_tegra_bo(gem);
587
588 switch (bo->tiling.mode) {
589 case TEGRA_BO_TILING_MODE_PITCH:
590 args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
591 args->value = 0;
592 break;
593
594 case TEGRA_BO_TILING_MODE_TILED:
595 args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
596 args->value = 0;
597 break;
598
599 case TEGRA_BO_TILING_MODE_BLOCK:
600 args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
601 args->value = bo->tiling.value;
602 break;
603
604 default:
605 err = -EINVAL;
606 break;
607 }
608
609 drm_gem_object_unreference(gem);
610
611 return err;
612}
Thierry Reding7b129082014-06-10 12:04:03 +0200613
614static int tegra_gem_set_flags(struct drm_device *drm, void *data,
615 struct drm_file *file)
616{
617 struct drm_tegra_gem_set_flags *args = data;
618 struct drm_gem_object *gem;
619 struct tegra_bo *bo;
620
621 if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
622 return -EINVAL;
623
624 gem = drm_gem_object_lookup(drm, file, args->handle);
625 if (!gem)
626 return -ENOENT;
627
628 bo = to_tegra_bo(gem);
629 bo->flags = 0;
630
631 if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
632 bo->flags |= TEGRA_BO_BOTTOM_UP;
633
634 drm_gem_object_unreference(gem);
635
636 return 0;
637}
638
639static int tegra_gem_get_flags(struct drm_device *drm, void *data,
640 struct drm_file *file)
641{
642 struct drm_tegra_gem_get_flags *args = data;
643 struct drm_gem_object *gem;
644 struct tegra_bo *bo;
645
646 gem = drm_gem_object_lookup(drm, file, args->handle);
647 if (!gem)
648 return -ENOENT;
649
650 bo = to_tegra_bo(gem);
651 args->flags = 0;
652
653 if (bo->flags & TEGRA_BO_BOTTOM_UP)
654 args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;
655
656 drm_gem_object_unreference(gem);
657
658 return 0;
659}
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200660#endif
661
Rob Clarkbaa70942013-08-02 13:27:49 -0400662static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200663#ifdef CONFIG_DRM_TEGRA_STAGING
Thierry Redingbd4f2362014-06-03 14:59:29 +0200664 DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED),
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200665 DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED),
666 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED),
667 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED),
668 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED),
669 DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED),
670 DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED),
671 DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED),
672 DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED),
Arto Merilainenc54a1692013-10-14 15:21:54 +0300673 DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED),
Thierry Reding7678d712014-06-03 14:56:57 +0200674 DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, DRM_UNLOCKED),
675 DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, DRM_UNLOCKED),
Thierry Reding7b129082014-06-10 12:04:03 +0200676 DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, DRM_UNLOCKED),
677 DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, DRM_UNLOCKED),
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200678#endif
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000679};
680
681static const struct file_operations tegra_drm_fops = {
682 .owner = THIS_MODULE,
683 .open = drm_open,
684 .release = drm_release,
685 .unlocked_ioctl = drm_ioctl,
Arto Merilainende2ba662013-03-22 16:34:08 +0200686 .mmap = tegra_drm_mmap,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000687 .poll = drm_poll,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000688 .read = drm_read,
689#ifdef CONFIG_COMPAT
690 .compat_ioctl = drm_compat_ioctl,
691#endif
692 .llseek = noop_llseek,
693};
694
Thierry Reding6e5ff992012-11-28 11:45:47 +0100695static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe)
696{
697 struct drm_crtc *crtc;
698
699 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) {
700 struct tegra_dc *dc = to_tegra_dc(crtc);
701
702 if (dc->pipe == pipe)
703 return crtc;
704 }
705
706 return NULL;
707}
708
709static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc)
710{
711 /* TODO: implement real hardware counter using syncpoints */
712 return drm_vblank_count(dev, crtc);
713}
714
715static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe)
716{
717 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
718 struct tegra_dc *dc = to_tegra_dc(crtc);
719
720 if (!crtc)
721 return -ENODEV;
722
723 tegra_dc_enable_vblank(dc);
724
725 return 0;
726}
727
728static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe)
729{
730 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe);
731 struct tegra_dc *dc = to_tegra_dc(crtc);
732
733 if (crtc)
734 tegra_dc_disable_vblank(dc);
735}
736
Thierry Reding3c03c462012-11-28 12:00:18 +0100737static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
738{
Thierry Reding08943e62013-09-26 16:08:18 +0200739 struct tegra_drm_file *fpriv = file->driver_priv;
Thierry Redingc88c3632013-09-26 16:08:22 +0200740 struct tegra_drm_context *context, *tmp;
Thierry Reding3c03c462012-11-28 12:00:18 +0100741 struct drm_crtc *crtc;
742
743 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
744 tegra_dc_cancel_page_flip(crtc, file);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200745
746 list_for_each_entry_safe(context, tmp, &fpriv->contexts, list)
Thierry Redingc88c3632013-09-26 16:08:22 +0200747 tegra_drm_context_free(context);
Terje Bergstromd43f81c2013-03-22 16:34:09 +0200748
749 kfree(fpriv);
Thierry Reding3c03c462012-11-28 12:00:18 +0100750}
751
Thierry Redinge450fcc2013-02-13 16:13:16 +0100752#ifdef CONFIG_DEBUG_FS
753static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
754{
755 struct drm_info_node *node = (struct drm_info_node *)s->private;
756 struct drm_device *drm = node->minor->dev;
757 struct drm_framebuffer *fb;
758
759 mutex_lock(&drm->mode_config.fb_lock);
760
761 list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
762 seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
763 fb->base.id, fb->width, fb->height, fb->depth,
764 fb->bits_per_pixel,
765 atomic_read(&fb->refcount.refcount));
766 }
767
768 mutex_unlock(&drm->mode_config.fb_lock);
769
770 return 0;
771}
772
773static struct drm_info_list tegra_debugfs_list[] = {
774 { "framebuffers", tegra_debugfs_framebuffers, 0 },
775};
776
777static int tegra_debugfs_init(struct drm_minor *minor)
778{
779 return drm_debugfs_create_files(tegra_debugfs_list,
780 ARRAY_SIZE(tegra_debugfs_list),
781 minor->debugfs_root, minor);
782}
783
784static void tegra_debugfs_cleanup(struct drm_minor *minor)
785{
786 drm_debugfs_remove_files(tegra_debugfs_list,
787 ARRAY_SIZE(tegra_debugfs_list), minor);
788}
789#endif
790
Thierry Reding9b57f5f2013-11-08 13:17:14 +0100791static struct drm_driver tegra_drm_driver = {
Thierry Reding38003912013-12-12 10:00:43 +0100792 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000793 .load = tegra_drm_load,
794 .unload = tegra_drm_unload,
795 .open = tegra_drm_open,
Thierry Reding3c03c462012-11-28 12:00:18 +0100796 .preclose = tegra_drm_preclose,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000797 .lastclose = tegra_drm_lastclose,
798
Thierry Reding6e5ff992012-11-28 11:45:47 +0100799 .get_vblank_counter = tegra_drm_get_vblank_counter,
800 .enable_vblank = tegra_drm_enable_vblank,
801 .disable_vblank = tegra_drm_disable_vblank,
802
Thierry Redinge450fcc2013-02-13 16:13:16 +0100803#if defined(CONFIG_DEBUG_FS)
804 .debugfs_init = tegra_debugfs_init,
805 .debugfs_cleanup = tegra_debugfs_cleanup,
806#endif
807
Arto Merilainende2ba662013-03-22 16:34:08 +0200808 .gem_free_object = tegra_bo_free_object,
809 .gem_vm_ops = &tegra_bo_vm_ops,
Thierry Reding38003912013-12-12 10:00:43 +0100810
811 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
812 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
813 .gem_prime_export = tegra_gem_prime_export,
814 .gem_prime_import = tegra_gem_prime_import,
815
Arto Merilainende2ba662013-03-22 16:34:08 +0200816 .dumb_create = tegra_bo_dumb_create,
817 .dumb_map_offset = tegra_bo_dumb_map_offset,
Daniel Vetter43387b32013-07-16 09:12:04 +0200818 .dumb_destroy = drm_gem_dumb_destroy,
Thierry Redingd8f4a9e2012-11-15 21:28:22 +0000819
820 .ioctls = tegra_drm_ioctls,
821 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
822 .fops = &tegra_drm_fops,
823
824 .name = DRIVER_NAME,
825 .desc = DRIVER_DESC,
826 .date = DRIVER_DATE,
827 .major = DRIVER_MAJOR,
828 .minor = DRIVER_MINOR,
829 .patchlevel = DRIVER_PATCHLEVEL,
830};
Thierry Reding776dc382013-10-14 14:43:22 +0200831
832int tegra_drm_register_client(struct tegra_drm *tegra,
833 struct tegra_drm_client *client)
834{
835 mutex_lock(&tegra->clients_lock);
836 list_add_tail(&client->list, &tegra->clients);
837 mutex_unlock(&tegra->clients_lock);
838
839 return 0;
840}
841
842int tegra_drm_unregister_client(struct tegra_drm *tegra,
843 struct tegra_drm_client *client)
844{
845 mutex_lock(&tegra->clients_lock);
846 list_del_init(&client->list);
847 mutex_unlock(&tegra->clients_lock);
848
849 return 0;
850}
851
Thierry Reding9910f5c2014-05-22 09:57:15 +0200852static int host1x_drm_probe(struct host1x_device *dev)
Thierry Reding776dc382013-10-14 14:43:22 +0200853{
Thierry Reding9910f5c2014-05-22 09:57:15 +0200854 struct drm_driver *driver = &tegra_drm_driver;
855 struct drm_device *drm;
856 int err;
857
858 drm = drm_dev_alloc(driver, &dev->dev);
859 if (!drm)
860 return -ENOMEM;
861
862 drm_dev_set_unique(drm, dev_name(&dev->dev));
863 dev_set_drvdata(&dev->dev, drm);
864
865 err = drm_dev_register(drm, 0);
866 if (err < 0)
867 goto unref;
868
869 DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", driver->name,
870 driver->major, driver->minor, driver->patchlevel,
871 driver->date, drm->primary->index);
872
873 return 0;
874
875unref:
876 drm_dev_unref(drm);
877 return err;
Thierry Reding776dc382013-10-14 14:43:22 +0200878}
879
Thierry Reding9910f5c2014-05-22 09:57:15 +0200880static int host1x_drm_remove(struct host1x_device *dev)
Thierry Reding776dc382013-10-14 14:43:22 +0200881{
Thierry Reding9910f5c2014-05-22 09:57:15 +0200882 struct drm_device *drm = dev_get_drvdata(&dev->dev);
883
884 drm_dev_unregister(drm);
885 drm_dev_unref(drm);
Thierry Reding776dc382013-10-14 14:43:22 +0200886
887 return 0;
888}
889
890static const struct of_device_id host1x_drm_subdevs[] = {
891 { .compatible = "nvidia,tegra20-dc", },
892 { .compatible = "nvidia,tegra20-hdmi", },
893 { .compatible = "nvidia,tegra20-gr2d", },
Thierry Reding5f60ed02013-02-28 08:08:01 +0100894 { .compatible = "nvidia,tegra20-gr3d", },
Thierry Reding776dc382013-10-14 14:43:22 +0200895 { .compatible = "nvidia,tegra30-dc", },
896 { .compatible = "nvidia,tegra30-hdmi", },
897 { .compatible = "nvidia,tegra30-gr2d", },
Thierry Reding5f60ed02013-02-28 08:08:01 +0100898 { .compatible = "nvidia,tegra30-gr3d", },
Thierry Redingdec72732013-09-03 08:45:46 +0200899 { .compatible = "nvidia,tegra114-dsi", },
Mikko Perttunen7d1d28a2013-09-30 16:54:47 +0200900 { .compatible = "nvidia,tegra114-hdmi", },
Thierry Reding5f60ed02013-02-28 08:08:01 +0100901 { .compatible = "nvidia,tegra114-gr3d", },
Thierry Reding8620fc62013-12-12 11:03:59 +0100902 { .compatible = "nvidia,tegra124-dc", },
Thierry Reding6b6b6042013-11-15 16:06:05 +0100903 { .compatible = "nvidia,tegra124-sor", },
Thierry Redingfb7be702013-11-15 16:07:32 +0100904 { .compatible = "nvidia,tegra124-hdmi", },
Thierry Reding776dc382013-10-14 14:43:22 +0200905 { /* sentinel */ }
906};
907
908static struct host1x_driver host1x_drm_driver = {
909 .name = "drm",
910 .probe = host1x_drm_probe,
911 .remove = host1x_drm_remove,
912 .subdevs = host1x_drm_subdevs,
913};
914
915static int __init host1x_drm_init(void)
916{
917 int err;
918
919 err = host1x_driver_register(&host1x_drm_driver);
920 if (err < 0)
921 return err;
922
923 err = platform_driver_register(&tegra_dc_driver);
924 if (err < 0)
925 goto unregister_host1x;
926
Thierry Redingdec72732013-09-03 08:45:46 +0200927 err = platform_driver_register(&tegra_dsi_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200928 if (err < 0)
929 goto unregister_dc;
930
Thierry Reding6b6b6042013-11-15 16:06:05 +0100931 err = platform_driver_register(&tegra_sor_driver);
Thierry Redingdec72732013-09-03 08:45:46 +0200932 if (err < 0)
933 goto unregister_dsi;
934
Thierry Reding6b6b6042013-11-15 16:06:05 +0100935 err = platform_driver_register(&tegra_hdmi_driver);
936 if (err < 0)
937 goto unregister_sor;
938
939 err = platform_driver_register(&tegra_dpaux_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200940 if (err < 0)
941 goto unregister_hdmi;
942
Thierry Reding6b6b6042013-11-15 16:06:05 +0100943 err = platform_driver_register(&tegra_gr2d_driver);
944 if (err < 0)
945 goto unregister_dpaux;
946
Thierry Reding5f60ed02013-02-28 08:08:01 +0100947 err = platform_driver_register(&tegra_gr3d_driver);
948 if (err < 0)
949 goto unregister_gr2d;
950
Thierry Reding776dc382013-10-14 14:43:22 +0200951 return 0;
952
Thierry Reding5f60ed02013-02-28 08:08:01 +0100953unregister_gr2d:
954 platform_driver_unregister(&tegra_gr2d_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +0100955unregister_dpaux:
956 platform_driver_unregister(&tegra_dpaux_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200957unregister_hdmi:
958 platform_driver_unregister(&tegra_hdmi_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +0100959unregister_sor:
960 platform_driver_unregister(&tegra_sor_driver);
Thierry Redingdec72732013-09-03 08:45:46 +0200961unregister_dsi:
962 platform_driver_unregister(&tegra_dsi_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200963unregister_dc:
964 platform_driver_unregister(&tegra_dc_driver);
965unregister_host1x:
966 host1x_driver_unregister(&host1x_drm_driver);
967 return err;
968}
969module_init(host1x_drm_init);
970
971static void __exit host1x_drm_exit(void)
972{
Thierry Reding5f60ed02013-02-28 08:08:01 +0100973 platform_driver_unregister(&tegra_gr3d_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200974 platform_driver_unregister(&tegra_gr2d_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +0100975 platform_driver_unregister(&tegra_dpaux_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200976 platform_driver_unregister(&tegra_hdmi_driver);
Thierry Reding6b6b6042013-11-15 16:06:05 +0100977 platform_driver_unregister(&tegra_sor_driver);
Thierry Redingdec72732013-09-03 08:45:46 +0200978 platform_driver_unregister(&tegra_dsi_driver);
Thierry Reding776dc382013-10-14 14:43:22 +0200979 platform_driver_unregister(&tegra_dc_driver);
980 host1x_driver_unregister(&host1x_drm_driver);
981}
982module_exit(host1x_drm_exit);
983
984MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
985MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
986MODULE_LICENSE("GPL v2");