drm.c 26.7 KB
Newer Older
T
Thierry Reding 已提交
1 2
/*
 * Copyright (C) 2012 Avionic Design GmbH
T
Terje Bergstrom 已提交
3
 * Copyright (C) 2012-2013 NVIDIA CORPORATION.  All rights reserved.
T
Thierry Reding 已提交
4 5 6 7 8 9
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

10
#include <linux/host1x.h>
11
#include <linux/idr.h>
T
Thierry Reding 已提交
12
#include <linux/iommu.h>
13

14
#include <drm/drm_atomic.h>
15 16
#include <drm/drm_atomic_helper.h>

T
Thierry Reding 已提交
17
#include "drm.h"
18
#include "gem.h"
T
Thierry Reding 已提交
19 20 21 22 23 24 25 26

#define DRIVER_NAME "tegra"
#define DRIVER_DESC "NVIDIA Tegra graphics"
#define DRIVER_DATE "20120330"
#define DRIVER_MAJOR 0
#define DRIVER_MINOR 0
#define DRIVER_PATCHLEVEL 0

27
struct tegra_drm_file {
28 29
	struct idr contexts;
	struct mutex lock;
30 31
};

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
static void tegra_atomic_schedule(struct tegra_drm *tegra,
				  struct drm_atomic_state *state)
{
	tegra->commit.state = state;
	schedule_work(&tegra->commit.work);
}

static void tegra_atomic_complete(struct tegra_drm *tegra,
				  struct drm_atomic_state *state)
{
	struct drm_device *drm = tegra->drm;

	/*
	 * Everything below can be run asynchronously without the need to grab
	 * any modeset locks at all under one condition: It must be guaranteed
	 * that the asynchronous work has either been cancelled (if the driver
	 * supports it, which at least requires that the framebuffers get
	 * cleaned up with drm_atomic_helper_cleanup_planes()) or completed
	 * before the new state gets committed on the software side with
	 * drm_atomic_helper_swap_state().
	 *
	 * This scheme allows new atomic state updates to be prepared and
	 * checked in parallel to the asynchronous completion of the previous
	 * update. Which is important since compositors need to figure out the
	 * composition of the next frame right after having submitted the
	 * current layout.
	 */

60 61
	drm_atomic_helper_commit_modeset_disables(drm, state);
	drm_atomic_helper_commit_modeset_enables(drm, state);
62 63
	drm_atomic_helper_commit_planes(drm, state,
					DRM_PLANE_COMMIT_ACTIVE_ONLY);
64 65 66 67

	drm_atomic_helper_wait_for_vblanks(drm, state);

	drm_atomic_helper_cleanup_planes(drm, state);
68
	drm_atomic_state_put(state);
69 70 71 72 73 74 75 76 77 78 79
}

static void tegra_atomic_work(struct work_struct *work)
{
	struct tegra_drm *tegra = container_of(work, struct tegra_drm,
					       commit.work);

	tegra_atomic_complete(tegra, tegra->commit.state);
}

static int tegra_atomic_commit(struct drm_device *drm,
80
			       struct drm_atomic_state *state, bool nonblock)
81 82 83 84 85 86 87 88
{
	struct tegra_drm *tegra = drm->dev_private;
	int err;

	err = drm_atomic_helper_prepare_planes(drm, state);
	if (err)
		return err;

89
	/* serialize outstanding nonblocking commits */
90 91 92 93 94 95 96 97 98
	mutex_lock(&tegra->commit.lock);
	flush_work(&tegra->commit.work);

	/*
	 * This is the point of no return - everything below never fails except
	 * when the hw goes bonghits. Which means we can commit the new state on
	 * the software side now.
	 */

99
	drm_atomic_helper_swap_state(state, true);
100

101
	drm_atomic_state_get(state);
102
	if (nonblock)
103 104 105 106 107 108 109 110
		tegra_atomic_schedule(tegra, state);
	else
		tegra_atomic_complete(tegra, state);

	mutex_unlock(&tegra->commit.lock);
	return 0;
}

111 112
static const struct drm_mode_config_funcs tegra_drm_mode_funcs = {
	.fb_create = tegra_fb_create,
113
#ifdef CONFIG_DRM_FBDEV_EMULATION
114 115
	.output_poll_changed = tegra_fb_output_poll_changed,
#endif
116
	.atomic_check = drm_atomic_helper_check,
117
	.atomic_commit = tegra_atomic_commit,
118 119
};

120
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
121
{
122
	struct host1x_device *device = to_host1x_device(drm->dev);
123
	struct tegra_drm *tegra;
124 125
	int err;

126
	tegra = kzalloc(sizeof(*tegra), GFP_KERNEL);
127
	if (!tegra)
128 129
		return -ENOMEM;

T
Thierry Reding 已提交
130
	if (iommu_present(&platform_bus_type)) {
131 132 133
		struct iommu_domain_geometry *geometry;
		u64 start, end;

T
Thierry Reding 已提交
134
		tegra->domain = iommu_domain_alloc(&platform_bus_type);
135 136
		if (!tegra->domain) {
			err = -ENOMEM;
T
Thierry Reding 已提交
137 138 139
			goto free;
		}

140 141 142 143
		geometry = &tegra->domain->geometry;
		start = geometry->aperture_start;
		end = geometry->aperture_end;

144 145
		DRM_DEBUG_DRIVER("IOMMU aperture initialized (%#llx-%#llx)\n",
				 start, end);
146
		drm_mm_init(&tegra->mm, start, end - start + 1);
147
		mutex_init(&tegra->mm_lock);
T
Thierry Reding 已提交
148 149
	}

150 151
	mutex_init(&tegra->clients_lock);
	INIT_LIST_HEAD(&tegra->clients);
152 153 154 155

	mutex_init(&tegra->commit.lock);
	INIT_WORK(&tegra->commit.work, tegra_atomic_work);

156 157
	drm->dev_private = tegra;
	tegra->drm = drm;
T
Thierry Reding 已提交
158 159 160

	drm_mode_config_init(drm);

161 162 163 164 165 166 167 168
	drm->mode_config.min_width = 0;
	drm->mode_config.min_height = 0;

	drm->mode_config.max_width = 4096;
	drm->mode_config.max_height = 4096;

	drm->mode_config.funcs = &tegra_drm_mode_funcs;

169 170
	err = tegra_drm_fb_prepare(drm);
	if (err < 0)
171
		goto config;
172 173 174

	drm_kms_helper_poll_init(drm);

175
	err = host1x_device_init(device);
T
Thierry Reding 已提交
176
	if (err < 0)
177
		goto fbdev;
T
Thierry Reding 已提交
178

179 180 181 182 183
	/*
	 * We don't use the drm_irq_install() helpers provided by the DRM
	 * core, so we need to set this manually in order to allow the
	 * DRM_IOCTL_WAIT_VBLANK to operate correctly.
	 */
V
Ville Syrjälä 已提交
184
	drm->irq_enabled = true;
185

186 187 188
	/* syncpoints are used for full 32-bit hardware VBLANK counters */
	drm->max_vblank_count = 0xffffffff;

189 190
	err = drm_vblank_init(drm, drm->mode_config.num_crtc);
	if (err < 0)
191
		goto device;
192

193 194
	drm_mode_config_reset(drm);

T
Thierry Reding 已提交
195 196
	err = tegra_drm_fb_init(drm);
	if (err < 0)
197
		goto vblank;
T
Thierry Reding 已提交
198 199

	return 0;
200 201 202 203 204 205 206 207 208 209

vblank:
	drm_vblank_cleanup(drm);
device:
	host1x_device_exit(device);
fbdev:
	drm_kms_helper_poll_fini(drm);
	tegra_drm_fb_free(drm);
config:
	drm_mode_config_cleanup(drm);
T
Thierry Reding 已提交
210 211 212 213

	if (tegra->domain) {
		iommu_domain_free(tegra->domain);
		drm_mm_takedown(&tegra->mm);
214
		mutex_destroy(&tegra->mm_lock);
T
Thierry Reding 已提交
215 216
	}
free:
217 218
	kfree(tegra);
	return err;
T
Thierry Reding 已提交
219 220
}

221
static void tegra_drm_unload(struct drm_device *drm)
T
Thierry Reding 已提交
222
{
223
	struct host1x_device *device = to_host1x_device(drm->dev);
T
Thierry Reding 已提交
224
	struct tegra_drm *tegra = drm->dev_private;
225 226
	int err;

T
Thierry Reding 已提交
227 228
	drm_kms_helper_poll_fini(drm);
	tegra_drm_fb_exit(drm);
229
	drm_mode_config_cleanup(drm);
230
	drm_vblank_cleanup(drm);
T
Thierry Reding 已提交
231

232 233
	err = host1x_device_exit(device);
	if (err < 0)
234
		return;
235

T
Thierry Reding 已提交
236 237 238
	if (tegra->domain) {
		iommu_domain_free(tegra->domain);
		drm_mm_takedown(&tegra->mm);
239
		mutex_destroy(&tegra->mm_lock);
T
Thierry Reding 已提交
240 241
	}

T
Thierry Reding 已提交
242
	kfree(tegra);
T
Thierry Reding 已提交
243 244 245 246
}

static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp)
{
247
	struct tegra_drm_file *fpriv;
T
Terje Bergstrom 已提交
248 249 250 251 252

	fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL);
	if (!fpriv)
		return -ENOMEM;

253 254
	idr_init(&fpriv->contexts);
	mutex_init(&fpriv->lock);
T
Terje Bergstrom 已提交
255 256
	filp->driver_priv = fpriv;

T
Thierry Reding 已提交
257 258 259
	return 0;
}

260
static void tegra_drm_context_free(struct tegra_drm_context *context)
T
Terje Bergstrom 已提交
261 262 263 264 265
{
	context->client->ops->close_channel(context);
	kfree(context);
}

T
Thierry Reding 已提交
266 267
static void tegra_drm_lastclose(struct drm_device *drm)
{
268
#ifdef CONFIG_DRM_FBDEV_EMULATION
269
	struct tegra_drm *tegra = drm->dev_private;
T
Thierry Reding 已提交
270

271
	tegra_fbdev_restore_mode(tegra->fbdev);
272
#endif
T
Thierry Reding 已提交
273 274
}

275
static struct host1x_bo *
276
host1x_bo_lookup(struct drm_file *file, u32 handle)
277 278 279 280
{
	struct drm_gem_object *gem;
	struct tegra_bo *bo;

281
	gem = drm_gem_object_lookup(file, handle);
282 283 284
	if (!gem)
		return NULL;

285
	drm_gem_object_unreference_unlocked(gem);
286 287 288 289 290

	bo = to_tegra_bo(gem);
	return &bo->base;
}

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310
static int host1x_reloc_copy_from_user(struct host1x_reloc *dest,
				       struct drm_tegra_reloc __user *src,
				       struct drm_device *drm,
				       struct drm_file *file)
{
	u32 cmdbuf, target;
	int err;

	err = get_user(cmdbuf, &src->cmdbuf.handle);
	if (err < 0)
		return err;

	err = get_user(dest->cmdbuf.offset, &src->cmdbuf.offset);
	if (err < 0)
		return err;

	err = get_user(target, &src->target.handle);
	if (err < 0)
		return err;

311
	err = get_user(dest->target.offset, &src->target.offset);
312 313 314 315 316 317 318
	if (err < 0)
		return err;

	err = get_user(dest->shift, &src->shift);
	if (err < 0)
		return err;

319
	dest->cmdbuf.bo = host1x_bo_lookup(file, cmdbuf);
320 321 322
	if (!dest->cmdbuf.bo)
		return -ENOENT;

323
	dest->target.bo = host1x_bo_lookup(file, target);
324 325 326 327 328 329
	if (!dest->target.bo)
		return -ENOENT;

	return 0;
}

330 331 332 333 334 335 336 337
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 =
338
		(void __user *)(uintptr_t)args->cmdbufs;
339
	struct drm_tegra_reloc __user *relocs =
340
		(void __user *)(uintptr_t)args->relocs;
341
	struct drm_tegra_waitchk __user *waitchks =
342
		(void __user *)(uintptr_t)args->waitchks;
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365
	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;

366 367
		if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) {
			err = -EFAULT;
368
			goto fail;
369
		}
370

371
		bo = host1x_bo_lookup(file, cmdbuf.handle);
372 373 374 375 376 377 378 379 380 381
		if (!bo) {
			err = -ENOENT;
			goto fail;
		}

		host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset);
		num_cmdbufs--;
		cmdbufs++;
	}

382
	/* copy and resolve relocations from submit */
383
	while (num_relocs--) {
384 385 386 387
		err = host1x_reloc_copy_from_user(&job->relocarray[num_relocs],
						  &relocs[num_relocs], drm,
						  file);
		if (err < 0)
388 389 390
			goto fail;
	}

391 392 393
	if (copy_from_user(job->waitchk, waitchks,
			   sizeof(*waitchks) * num_waitchks)) {
		err = -EFAULT;
394
		goto fail;
395
	}
396

397 398 399
	if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts,
			   sizeof(syncpt))) {
		err = -EFAULT;
400
		goto fail;
401
	}
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431

	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;
}


T
Terje Bergstrom 已提交
432
#ifdef CONFIG_DRM_TEGRA_STAGING
433 434
static struct tegra_drm_context *
tegra_drm_file_get_context(struct tegra_drm_file *file, u32 id)
435
{
436
	struct tegra_drm_context *context;
T
Terje Bergstrom 已提交
437

438 439 440
	mutex_lock(&file->lock);
	context = idr_find(&file->contexts, id);
	mutex_unlock(&file->lock);
T
Terje Bergstrom 已提交
441

442
	return context;
T
Terje Bergstrom 已提交
443 444 445 446 447 448 449 450
}

static int tegra_gem_create(struct drm_device *drm, void *data,
			    struct drm_file *file)
{
	struct drm_tegra_gem_create *args = data;
	struct tegra_bo *bo;

451
	bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags,
T
Terje Bergstrom 已提交
452 453 454 455 456 457 458 459 460 461 462 463 464 465
					 &args->handle);
	if (IS_ERR(bo))
		return PTR_ERR(bo);

	return 0;
}

static int tegra_gem_mmap(struct drm_device *drm, void *data,
			  struct drm_file *file)
{
	struct drm_tegra_gem_mmap *args = data;
	struct drm_gem_object *gem;
	struct tegra_bo *bo;

466
	gem = drm_gem_object_lookup(file, args->handle);
T
Terje Bergstrom 已提交
467 468 469 470 471
	if (!gem)
		return -EINVAL;

	bo = to_tegra_bo(gem);

472
	args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node);
T
Terje Bergstrom 已提交
473

474
	drm_gem_object_unreference_unlocked(gem);
T
Terje Bergstrom 已提交
475 476 477 478 479 480 481

	return 0;
}

static int tegra_syncpt_read(struct drm_device *drm, void *data,
			     struct drm_file *file)
{
482
	struct host1x *host = dev_get_drvdata(drm->dev->parent);
T
Terje Bergstrom 已提交
483
	struct drm_tegra_syncpt_read *args = data;
484
	struct host1x_syncpt *sp;
T
Terje Bergstrom 已提交
485

486
	sp = host1x_syncpt_get(host, args->id);
T
Terje Bergstrom 已提交
487 488 489 490 491 492 493 494 495 496
	if (!sp)
		return -EINVAL;

	args->value = host1x_syncpt_read_min(sp);
	return 0;
}

static int tegra_syncpt_incr(struct drm_device *drm, void *data,
			     struct drm_file *file)
{
497
	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
T
Terje Bergstrom 已提交
498
	struct drm_tegra_syncpt_incr *args = data;
499
	struct host1x_syncpt *sp;
T
Terje Bergstrom 已提交
500

501
	sp = host1x_syncpt_get(host1x, args->id);
T
Terje Bergstrom 已提交
502 503 504
	if (!sp)
		return -EINVAL;

505
	return host1x_syncpt_incr(sp);
T
Terje Bergstrom 已提交
506 507 508 509 510
}

static int tegra_syncpt_wait(struct drm_device *drm, void *data,
			     struct drm_file *file)
{
511
	struct host1x *host1x = dev_get_drvdata(drm->dev->parent);
T
Terje Bergstrom 已提交
512
	struct drm_tegra_syncpt_wait *args = data;
513
	struct host1x_syncpt *sp;
T
Terje Bergstrom 已提交
514

515
	sp = host1x_syncpt_get(host1x, args->id);
T
Terje Bergstrom 已提交
516 517 518 519 520 521 522
	if (!sp)
		return -EINVAL;

	return host1x_syncpt_wait(sp, args->thresh, args->timeout,
				  &args->value);
}

523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
static int tegra_client_open(struct tegra_drm_file *fpriv,
			     struct tegra_drm_client *client,
			     struct tegra_drm_context *context)
{
	int err;

	err = client->ops->open_channel(client, context);
	if (err < 0)
		return err;

	err = idr_alloc(&fpriv->contexts, context, 0, 0, GFP_KERNEL);
	if (err < 0) {
		client->ops->close_channel(context);
		return err;
	}

	context->client = client;
	context->id = err;

	return 0;
}

T
Terje Bergstrom 已提交
545 546 547
static int tegra_open_channel(struct drm_device *drm, void *data,
			      struct drm_file *file)
{
548
	struct tegra_drm_file *fpriv = file->driver_priv;
549
	struct tegra_drm *tegra = drm->dev_private;
T
Terje Bergstrom 已提交
550
	struct drm_tegra_open_channel *args = data;
551
	struct tegra_drm_context *context;
552
	struct tegra_drm_client *client;
T
Terje Bergstrom 已提交
553 554 555 556 557 558
	int err = -ENODEV;

	context = kzalloc(sizeof(*context), GFP_KERNEL);
	if (!context)
		return -ENOMEM;

559 560
	mutex_lock(&fpriv->lock);

561
	list_for_each_entry(client, &tegra->clients, list)
562
		if (client->base.class == args->client) {
563 564
			err = tegra_client_open(fpriv, client, context);
			if (err < 0)
T
Terje Bergstrom 已提交
565 566
				break;

567 568
			args->context = context->id;
			break;
T
Terje Bergstrom 已提交
569 570
		}

571 572 573 574
	if (err < 0)
		kfree(context);

	mutex_unlock(&fpriv->lock);
T
Terje Bergstrom 已提交
575 576 577 578 579 580
	return err;
}

static int tegra_close_channel(struct drm_device *drm, void *data,
			       struct drm_file *file)
{
581
	struct tegra_drm_file *fpriv = file->driver_priv;
582
	struct drm_tegra_close_channel *args = data;
583
	struct tegra_drm_context *context;
584
	int err = 0;
585

586
	mutex_lock(&fpriv->lock);
T
Terje Bergstrom 已提交
587

588 589 590 591 592
	context = tegra_drm_file_get_context(fpriv, args->context);
	if (!context) {
		err = -EINVAL;
		goto unlock;
	}
T
Terje Bergstrom 已提交
593

594
	idr_remove(&fpriv->contexts, context->id);
595
	tegra_drm_context_free(context);
T
Terje Bergstrom 已提交
596

597 598 599
unlock:
	mutex_unlock(&fpriv->lock);
	return err;
T
Terje Bergstrom 已提交
600 601 602 603 604
}

static int tegra_get_syncpt(struct drm_device *drm, void *data,
			    struct drm_file *file)
{
605
	struct tegra_drm_file *fpriv = file->driver_priv;
T
Terje Bergstrom 已提交
606
	struct drm_tegra_get_syncpt *args = data;
607
	struct tegra_drm_context *context;
T
Terje Bergstrom 已提交
608
	struct host1x_syncpt *syncpt;
609
	int err = 0;
T
Terje Bergstrom 已提交
610

611
	mutex_lock(&fpriv->lock);
612

613 614 615 616 617
	context = tegra_drm_file_get_context(fpriv, args->context);
	if (!context) {
		err = -ENODEV;
		goto unlock;
	}
T
Terje Bergstrom 已提交
618

619 620 621 622
	if (args->index >= context->client->base.num_syncpts) {
		err = -EINVAL;
		goto unlock;
	}
T
Terje Bergstrom 已提交
623

624
	syncpt = context->client->base.syncpts[args->index];
T
Terje Bergstrom 已提交
625 626
	args->id = host1x_syncpt_id(syncpt);

627 628 629
unlock:
	mutex_unlock(&fpriv->lock);
	return err;
T
Terje Bergstrom 已提交
630 631 632 633 634
}

static int tegra_submit(struct drm_device *drm, void *data,
			struct drm_file *file)
{
635
	struct tegra_drm_file *fpriv = file->driver_priv;
T
Terje Bergstrom 已提交
636
	struct drm_tegra_submit *args = data;
637
	struct tegra_drm_context *context;
638
	int err;
639

640
	mutex_lock(&fpriv->lock);
T
Terje Bergstrom 已提交
641

642 643 644 645 646 647 648
	context = tegra_drm_file_get_context(fpriv, args->context);
	if (!context) {
		err = -ENODEV;
		goto unlock;
	}

	err = context->client->ops->submit(context, args, drm, file);
T
Terje Bergstrom 已提交
649

650 651 652
unlock:
	mutex_unlock(&fpriv->lock);
	return err;
T
Terje Bergstrom 已提交
653
}
654 655 656 657 658 659 660 661 662

static int tegra_get_syncpt_base(struct drm_device *drm, void *data,
				 struct drm_file *file)
{
	struct tegra_drm_file *fpriv = file->driver_priv;
	struct drm_tegra_get_syncpt_base *args = data;
	struct tegra_drm_context *context;
	struct host1x_syncpt_base *base;
	struct host1x_syncpt *syncpt;
663
	int err = 0;
664

665
	mutex_lock(&fpriv->lock);
666

667 668 669 670 671
	context = tegra_drm_file_get_context(fpriv, args->context);
	if (!context) {
		err = -ENODEV;
		goto unlock;
	}
672

673 674 675 676
	if (args->syncpt >= context->client->base.num_syncpts) {
		err = -EINVAL;
		goto unlock;
	}
677 678 679 680

	syncpt = context->client->base.syncpts[args->syncpt];

	base = host1x_syncpt_get_base(syncpt);
681 682 683 684
	if (!base) {
		err = -ENXIO;
		goto unlock;
	}
685 686 687

	args->id = host1x_syncpt_base_id(base);

688 689 690
unlock:
	mutex_unlock(&fpriv->lock);
	return err;
691
}
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731

static int tegra_gem_set_tiling(struct drm_device *drm, void *data,
				struct drm_file *file)
{
	struct drm_tegra_gem_set_tiling *args = data;
	enum tegra_bo_tiling_mode mode;
	struct drm_gem_object *gem;
	unsigned long value = 0;
	struct tegra_bo *bo;

	switch (args->mode) {
	case DRM_TEGRA_GEM_TILING_MODE_PITCH:
		mode = TEGRA_BO_TILING_MODE_PITCH;

		if (args->value != 0)
			return -EINVAL;

		break;

	case DRM_TEGRA_GEM_TILING_MODE_TILED:
		mode = TEGRA_BO_TILING_MODE_TILED;

		if (args->value != 0)
			return -EINVAL;

		break;

	case DRM_TEGRA_GEM_TILING_MODE_BLOCK:
		mode = TEGRA_BO_TILING_MODE_BLOCK;

		if (args->value > 5)
			return -EINVAL;

		value = args->value;
		break;

	default:
		return -EINVAL;
	}

732
	gem = drm_gem_object_lookup(file, args->handle);
733 734 735 736 737 738 739 740
	if (!gem)
		return -ENOENT;

	bo = to_tegra_bo(gem);

	bo->tiling.mode = mode;
	bo->tiling.value = value;

741
	drm_gem_object_unreference_unlocked(gem);
742 743 744 745 746 747 748 749 750 751 752 753

	return 0;
}

static int tegra_gem_get_tiling(struct drm_device *drm, void *data,
				struct drm_file *file)
{
	struct drm_tegra_gem_get_tiling *args = data;
	struct drm_gem_object *gem;
	struct tegra_bo *bo;
	int err = 0;

754
	gem = drm_gem_object_lookup(file, args->handle);
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780
	if (!gem)
		return -ENOENT;

	bo = to_tegra_bo(gem);

	switch (bo->tiling.mode) {
	case TEGRA_BO_TILING_MODE_PITCH:
		args->mode = DRM_TEGRA_GEM_TILING_MODE_PITCH;
		args->value = 0;
		break;

	case TEGRA_BO_TILING_MODE_TILED:
		args->mode = DRM_TEGRA_GEM_TILING_MODE_TILED;
		args->value = 0;
		break;

	case TEGRA_BO_TILING_MODE_BLOCK:
		args->mode = DRM_TEGRA_GEM_TILING_MODE_BLOCK;
		args->value = bo->tiling.value;
		break;

	default:
		err = -EINVAL;
		break;
	}

781
	drm_gem_object_unreference_unlocked(gem);
782 783 784

	return err;
}
785 786 787 788 789 790 791 792 793 794 795

static int tegra_gem_set_flags(struct drm_device *drm, void *data,
			       struct drm_file *file)
{
	struct drm_tegra_gem_set_flags *args = data;
	struct drm_gem_object *gem;
	struct tegra_bo *bo;

	if (args->flags & ~DRM_TEGRA_GEM_FLAGS)
		return -EINVAL;

796
	gem = drm_gem_object_lookup(file, args->handle);
797 798 799 800 801 802 803 804 805
	if (!gem)
		return -ENOENT;

	bo = to_tegra_bo(gem);
	bo->flags = 0;

	if (args->flags & DRM_TEGRA_GEM_BOTTOM_UP)
		bo->flags |= TEGRA_BO_BOTTOM_UP;

806
	drm_gem_object_unreference_unlocked(gem);
807 808 809 810 811 812 813 814 815 816 817

	return 0;
}

static int tegra_gem_get_flags(struct drm_device *drm, void *data,
			       struct drm_file *file)
{
	struct drm_tegra_gem_get_flags *args = data;
	struct drm_gem_object *gem;
	struct tegra_bo *bo;

818
	gem = drm_gem_object_lookup(file, args->handle);
819 820 821 822 823 824 825 826 827
	if (!gem)
		return -ENOENT;

	bo = to_tegra_bo(gem);
	args->flags = 0;

	if (bo->flags & TEGRA_BO_BOTTOM_UP)
		args->flags |= DRM_TEGRA_GEM_BOTTOM_UP;

828
	drm_gem_object_unreference_unlocked(gem);
829 830 831

	return 0;
}
T
Terje Bergstrom 已提交
832 833
#endif

R
Rob Clark 已提交
834
static const struct drm_ioctl_desc tegra_drm_ioctls[] = {
T
Terje Bergstrom 已提交
835
#ifdef CONFIG_DRM_TEGRA_STAGING
836 837 838 839 840 841 842 843 844 845 846 847 848 849
	DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_TILING, tegra_gem_set_tiling, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_TILING, tegra_gem_get_tiling, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GEM_SET_FLAGS, tegra_gem_set_flags, 0),
	DRM_IOCTL_DEF_DRV(TEGRA_GEM_GET_FLAGS, tegra_gem_get_flags, 0),
T
Terje Bergstrom 已提交
850
#endif
T
Thierry Reding 已提交
851 852 853 854 855 856 857
};

static const struct file_operations tegra_drm_fops = {
	.owner = THIS_MODULE,
	.open = drm_open,
	.release = drm_release,
	.unlocked_ioctl = drm_ioctl,
858
	.mmap = tegra_drm_mmap,
T
Thierry Reding 已提交
859 860 861 862 863 864
	.poll = drm_poll,
	.read = drm_read,
	.compat_ioctl = drm_compat_ioctl,
	.llseek = noop_llseek,
};

865 866
static u32 tegra_drm_get_vblank_counter(struct drm_device *drm,
					unsigned int pipe)
867
{
868
	struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
869
	struct tegra_dc *dc = to_tegra_dc(crtc);
870 871 872 873

	if (!crtc)
		return 0;

874
	return tegra_dc_get_vblank_counter(dc);
875 876
}

877
static int tegra_drm_enable_vblank(struct drm_device *drm, unsigned int pipe)
878
{
879
	struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
880 881 882 883 884 885 886 887 888 889
	struct tegra_dc *dc = to_tegra_dc(crtc);

	if (!crtc)
		return -ENODEV;

	tegra_dc_enable_vblank(dc);

	return 0;
}

890
static void tegra_drm_disable_vblank(struct drm_device *drm, unsigned int pipe)
891
{
892
	struct drm_crtc *crtc = drm_crtc_from_index(drm, pipe);
893 894 895 896 897 898
	struct tegra_dc *dc = to_tegra_dc(crtc);

	if (crtc)
		tegra_dc_disable_vblank(dc);
}

899 900 901 902 903 904 905 906 907
static int tegra_drm_context_cleanup(int id, void *p, void *data)
{
	struct tegra_drm_context *context = p;

	tegra_drm_context_free(context);

	return 0;
}

908 909
static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file)
{
910
	struct tegra_drm_file *fpriv = file->driver_priv;
911

912 913 914
	mutex_lock(&fpriv->lock);
	idr_for_each(&fpriv->contexts, tegra_drm_context_cleanup, NULL);
	mutex_unlock(&fpriv->lock);
T
Terje Bergstrom 已提交
915

916 917
	idr_destroy(&fpriv->contexts);
	mutex_destroy(&fpriv->lock);
T
Terje Bergstrom 已提交
918
	kfree(fpriv);
919 920
}

921 922 923 924 925 926 927 928 929 930 931
#ifdef CONFIG_DEBUG_FS
static int tegra_debugfs_framebuffers(struct seq_file *s, void *data)
{
	struct drm_info_node *node = (struct drm_info_node *)s->private;
	struct drm_device *drm = node->minor->dev;
	struct drm_framebuffer *fb;

	mutex_lock(&drm->mode_config.fb_lock);

	list_for_each_entry(fb, &drm->mode_config.fb_list, head) {
		seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n",
V
Ville Syrjälä 已提交
932 933
			   fb->base.id, fb->width, fb->height,
			   fb->format->depth,
V
Ville Syrjälä 已提交
934
			   fb->format->cpp[0] * 8,
935
			   drm_framebuffer_read_refcount(fb));
936 937 938 939 940 941 942
	}

	mutex_unlock(&drm->mode_config.fb_lock);

	return 0;
}

943 944 945 946 947
static int tegra_debugfs_iova(struct seq_file *s, void *data)
{
	struct drm_info_node *node = (struct drm_info_node *)s->private;
	struct drm_device *drm = node->minor->dev;
	struct tegra_drm *tegra = drm->dev_private;
D
Daniel Vetter 已提交
948
	struct drm_printer p = drm_seq_file_printer(s);
949

950
	mutex_lock(&tegra->mm_lock);
D
Daniel Vetter 已提交
951
	drm_mm_print(&tegra->mm, &p);
952
	mutex_unlock(&tegra->mm_lock);
D
Daniel Vetter 已提交
953 954

	return 0;
955 956
}

957 958
static struct drm_info_list tegra_debugfs_list[] = {
	{ "framebuffers", tegra_debugfs_framebuffers, 0 },
959
	{ "iova", tegra_debugfs_iova, 0 },
960 961 962 963 964 965 966 967 968 969
};

static int tegra_debugfs_init(struct drm_minor *minor)
{
	return drm_debugfs_create_files(tegra_debugfs_list,
					ARRAY_SIZE(tegra_debugfs_list),
					minor->debugfs_root, minor);
}
#endif

970
static struct drm_driver tegra_drm_driver = {
971 972
	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
			   DRIVER_ATOMIC,
T
Thierry Reding 已提交
973 974 975
	.load = tegra_drm_load,
	.unload = tegra_drm_unload,
	.open = tegra_drm_open,
976
	.preclose = tegra_drm_preclose,
T
Thierry Reding 已提交
977 978
	.lastclose = tegra_drm_lastclose,

979 980 981 982
	.get_vblank_counter = tegra_drm_get_vblank_counter,
	.enable_vblank = tegra_drm_enable_vblank,
	.disable_vblank = tegra_drm_disable_vblank,

983 984 985 986
#if defined(CONFIG_DEBUG_FS)
	.debugfs_init = tegra_debugfs_init,
#endif

987
	.gem_free_object_unlocked = tegra_bo_free_object,
988
	.gem_vm_ops = &tegra_bo_vm_ops,
T
Thierry Reding 已提交
989 990 991 992 993 994

	.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
	.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
	.gem_prime_export = tegra_gem_prime_export,
	.gem_prime_import = tegra_gem_prime_import,

995 996
	.dumb_create = tegra_bo_dumb_create,
	.dumb_map_offset = tegra_bo_dumb_map_offset,
997
	.dumb_destroy = drm_gem_dumb_destroy,
T
Thierry Reding 已提交
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009

	.ioctls = tegra_drm_ioctls,
	.num_ioctls = ARRAY_SIZE(tegra_drm_ioctls),
	.fops = &tegra_drm_fops,

	.name = DRIVER_NAME,
	.desc = DRIVER_DESC,
	.date = DRIVER_DATE,
	.major = DRIVER_MAJOR,
	.minor = DRIVER_MINOR,
	.patchlevel = DRIVER_PATCHLEVEL,
};
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030

int tegra_drm_register_client(struct tegra_drm *tegra,
			      struct tegra_drm_client *client)
{
	mutex_lock(&tegra->clients_lock);
	list_add_tail(&client->list, &tegra->clients);
	mutex_unlock(&tegra->clients_lock);

	return 0;
}

int tegra_drm_unregister_client(struct tegra_drm *tegra,
				struct tegra_drm_client *client)
{
	mutex_lock(&tegra->clients_lock);
	list_del_init(&client->list);
	mutex_unlock(&tegra->clients_lock);

	return 0;
}

1031
static int host1x_drm_probe(struct host1x_device *dev)
1032
{
1033 1034 1035 1036 1037
	struct drm_driver *driver = &tegra_drm_driver;
	struct drm_device *drm;
	int err;

	drm = drm_dev_alloc(driver, &dev->dev);
1038 1039
	if (IS_ERR(drm))
		return PTR_ERR(drm);
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051

	dev_set_drvdata(&dev->dev, drm);

	err = drm_dev_register(drm, 0);
	if (err < 0)
		goto unref;

	return 0;

unref:
	drm_dev_unref(drm);
	return err;
1052 1053
}

1054
static int host1x_drm_remove(struct host1x_device *dev)
1055
{
1056 1057 1058 1059
	struct drm_device *drm = dev_get_drvdata(&dev->dev);

	drm_dev_unregister(drm);
	drm_dev_unref(drm);
1060 1061 1062 1063

	return 0;
}

1064 1065 1066 1067
#ifdef CONFIG_PM_SLEEP
static int host1x_drm_suspend(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
1068
	struct tegra_drm *tegra = drm->dev_private;
1069 1070

	drm_kms_helper_poll_disable(drm);
1071 1072 1073 1074 1075 1076 1077 1078
	tegra_drm_fb_suspend(drm);

	tegra->state = drm_atomic_helper_suspend(drm);
	if (IS_ERR(tegra->state)) {
		tegra_drm_fb_resume(drm);
		drm_kms_helper_poll_enable(drm);
		return PTR_ERR(tegra->state);
	}
1079 1080 1081 1082 1083 1084 1085

	return 0;
}

static int host1x_drm_resume(struct device *dev)
{
	struct drm_device *drm = dev_get_drvdata(dev);
1086
	struct tegra_drm *tegra = drm->dev_private;
1087

1088 1089
	drm_atomic_helper_resume(drm, tegra->state);
	tegra_drm_fb_resume(drm);
1090 1091 1092 1093 1094 1095
	drm_kms_helper_poll_enable(drm);

	return 0;
}
#endif

1096 1097
static SIMPLE_DEV_PM_OPS(host1x_drm_pm_ops, host1x_drm_suspend,
			 host1x_drm_resume);
1098

1099 1100 1101 1102
static const struct of_device_id host1x_drm_subdevs[] = {
	{ .compatible = "nvidia,tegra20-dc", },
	{ .compatible = "nvidia,tegra20-hdmi", },
	{ .compatible = "nvidia,tegra20-gr2d", },
T
Thierry Reding 已提交
1103
	{ .compatible = "nvidia,tegra20-gr3d", },
1104 1105 1106
	{ .compatible = "nvidia,tegra30-dc", },
	{ .compatible = "nvidia,tegra30-hdmi", },
	{ .compatible = "nvidia,tegra30-gr2d", },
T
Thierry Reding 已提交
1107
	{ .compatible = "nvidia,tegra30-gr3d", },
T
Thierry Reding 已提交
1108
	{ .compatible = "nvidia,tegra114-dsi", },
1109
	{ .compatible = "nvidia,tegra114-hdmi", },
T
Thierry Reding 已提交
1110
	{ .compatible = "nvidia,tegra114-gr3d", },
1111
	{ .compatible = "nvidia,tegra124-dc", },
T
Thierry Reding 已提交
1112
	{ .compatible = "nvidia,tegra124-sor", },
1113
	{ .compatible = "nvidia,tegra124-hdmi", },
1114
	{ .compatible = "nvidia,tegra124-dsi", },
1115
	{ .compatible = "nvidia,tegra132-dsi", },
1116
	{ .compatible = "nvidia,tegra210-dc", },
1117
	{ .compatible = "nvidia,tegra210-dsi", },
1118
	{ .compatible = "nvidia,tegra210-sor", },
1119
	{ .compatible = "nvidia,tegra210-sor1", },
1120 1121 1122 1123
	{ /* sentinel */ }
};

static struct host1x_driver host1x_drm_driver = {
1124 1125
	.driver = {
		.name = "drm",
1126
		.pm = &host1x_drm_pm_ops,
1127
	},
1128 1129 1130 1131 1132
	.probe = host1x_drm_probe,
	.remove = host1x_drm_remove,
	.subdevs = host1x_drm_subdevs,
};

1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
static struct platform_driver * const drivers[] = {
	&tegra_dc_driver,
	&tegra_hdmi_driver,
	&tegra_dsi_driver,
	&tegra_dpaux_driver,
	&tegra_sor_driver,
	&tegra_gr2d_driver,
	&tegra_gr3d_driver,
};

1143 1144 1145 1146 1147 1148 1149 1150
static int __init host1x_drm_init(void)
{
	int err;

	err = host1x_driver_register(&host1x_drm_driver);
	if (err < 0)
		return err;

1151
	err = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
	if (err < 0)
		goto unregister_host1x;

	return 0;

unregister_host1x:
	host1x_driver_unregister(&host1x_drm_driver);
	return err;
}
module_init(host1x_drm_init);

static void __exit host1x_drm_exit(void)
{
1165
	platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
1166 1167 1168 1169 1170 1171 1172
	host1x_driver_unregister(&host1x_drm_driver);
}
module_exit(host1x_drm_exit);

MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>");
MODULE_DESCRIPTION("NVIDIA Tegra DRM driver");
MODULE_LICENSE("GPL v2");