1 /* 2 * Copyright (C) 2012 Avionic Design GmbH 3 * Copyright (C) 2012-2013 NVIDIA CORPORATION. All rights reserved. 4 * 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 10 #include <linux/host1x.h> 11 12 #include "drm.h" 13 #include "gem.h" 14 15 #define DRIVER_NAME "tegra" 16 #define DRIVER_DESC "NVIDIA Tegra graphics" 17 #define DRIVER_DATE "20120330" 18 #define DRIVER_MAJOR 0 19 #define DRIVER_MINOR 0 20 #define DRIVER_PATCHLEVEL 0 21 22 struct tegra_drm_file { 23 struct list_head contexts; 24 }; 25 26 static int tegra_drm_load(struct drm_device *drm, unsigned long flags) 27 { 28 struct host1x_device *device = to_host1x_device(drm->dev); 29 struct tegra_drm *tegra; 30 int err; 31 32 tegra = kzalloc(sizeof(*tegra), GFP_KERNEL); 33 if (!tegra) 34 return -ENOMEM; 35 36 dev_set_drvdata(drm->dev, tegra); 37 mutex_init(&tegra->clients_lock); 38 INIT_LIST_HEAD(&tegra->clients); 39 drm->dev_private = tegra; 40 tegra->drm = drm; 41 42 drm_mode_config_init(drm); 43 44 err = host1x_device_init(device); 45 if (err < 0) 46 return err; 47 48 /* 49 * We don't use the drm_irq_install() helpers provided by the DRM 50 * core, so we need to set this manually in order to allow the 51 * DRM_IOCTL_WAIT_VBLANK to operate correctly. 52 */ 53 drm->irq_enabled = true; 54 55 err = drm_vblank_init(drm, drm->mode_config.num_crtc); 56 if (err < 0) 57 return err; 58 59 err = tegra_drm_fb_init(drm); 60 if (err < 0) 61 return err; 62 63 drm_kms_helper_poll_init(drm); 64 65 return 0; 66 } 67 68 static int tegra_drm_unload(struct drm_device *drm) 69 { 70 struct host1x_device *device = to_host1x_device(drm->dev); 71 int err; 72 73 drm_kms_helper_poll_fini(drm); 74 tegra_drm_fb_exit(drm); 75 drm_vblank_cleanup(drm); 76 drm_mode_config_cleanup(drm); 77 78 err = host1x_device_exit(device); 79 if (err < 0) 80 return err; 81 82 return 0; 83 } 84 85 static int tegra_drm_open(struct drm_device *drm, struct drm_file *filp) 86 { 87 struct tegra_drm_file *fpriv; 88 89 fpriv = kzalloc(sizeof(*fpriv), GFP_KERNEL); 90 if (!fpriv) 91 return -ENOMEM; 92 93 INIT_LIST_HEAD(&fpriv->contexts); 94 filp->driver_priv = fpriv; 95 96 return 0; 97 } 98 99 static void tegra_drm_context_free(struct tegra_drm_context *context) 100 { 101 context->client->ops->close_channel(context); 102 kfree(context); 103 } 104 105 static void tegra_drm_lastclose(struct drm_device *drm) 106 { 107 #ifdef CONFIG_DRM_TEGRA_FBDEV 108 struct tegra_drm *tegra = drm->dev_private; 109 110 tegra_fbdev_restore_mode(tegra->fbdev); 111 #endif 112 } 113 114 static struct host1x_bo * 115 host1x_bo_lookup(struct drm_device *drm, struct drm_file *file, u32 handle) 116 { 117 struct drm_gem_object *gem; 118 struct tegra_bo *bo; 119 120 gem = drm_gem_object_lookup(drm, file, handle); 121 if (!gem) 122 return NULL; 123 124 mutex_lock(&drm->struct_mutex); 125 drm_gem_object_unreference(gem); 126 mutex_unlock(&drm->struct_mutex); 127 128 bo = to_tegra_bo(gem); 129 return &bo->base; 130 } 131 132 int tegra_drm_submit(struct tegra_drm_context *context, 133 struct drm_tegra_submit *args, struct drm_device *drm, 134 struct drm_file *file) 135 { 136 unsigned int num_cmdbufs = args->num_cmdbufs; 137 unsigned int num_relocs = args->num_relocs; 138 unsigned int num_waitchks = args->num_waitchks; 139 struct drm_tegra_cmdbuf __user *cmdbufs = 140 (void __user *)(uintptr_t)args->cmdbufs; 141 struct drm_tegra_reloc __user *relocs = 142 (void __user *)(uintptr_t)args->relocs; 143 struct drm_tegra_waitchk __user *waitchks = 144 (void __user *)(uintptr_t)args->waitchks; 145 struct drm_tegra_syncpt syncpt; 146 struct host1x_job *job; 147 int err; 148 149 /* We don't yet support other than one syncpt_incr struct per submit */ 150 if (args->num_syncpts != 1) 151 return -EINVAL; 152 153 job = host1x_job_alloc(context->channel, args->num_cmdbufs, 154 args->num_relocs, args->num_waitchks); 155 if (!job) 156 return -ENOMEM; 157 158 job->num_relocs = args->num_relocs; 159 job->num_waitchk = args->num_waitchks; 160 job->client = (u32)args->context; 161 job->class = context->client->base.class; 162 job->serialize = true; 163 164 while (num_cmdbufs) { 165 struct drm_tegra_cmdbuf cmdbuf; 166 struct host1x_bo *bo; 167 168 if (copy_from_user(&cmdbuf, cmdbufs, sizeof(cmdbuf))) { 169 err = -EFAULT; 170 goto fail; 171 } 172 173 bo = host1x_bo_lookup(drm, file, cmdbuf.handle); 174 if (!bo) { 175 err = -ENOENT; 176 goto fail; 177 } 178 179 host1x_job_add_gather(job, bo, cmdbuf.words, cmdbuf.offset); 180 num_cmdbufs--; 181 cmdbufs++; 182 } 183 184 if (copy_from_user(job->relocarray, relocs, 185 sizeof(*relocs) * num_relocs)) { 186 err = -EFAULT; 187 goto fail; 188 } 189 190 while (num_relocs--) { 191 struct host1x_reloc *reloc = &job->relocarray[num_relocs]; 192 struct host1x_bo *cmdbuf, *target; 193 194 cmdbuf = host1x_bo_lookup(drm, file, (u32)reloc->cmdbuf); 195 target = host1x_bo_lookup(drm, file, (u32)reloc->target); 196 197 reloc->cmdbuf = cmdbuf; 198 reloc->target = target; 199 200 if (!reloc->target || !reloc->cmdbuf) { 201 err = -ENOENT; 202 goto fail; 203 } 204 } 205 206 if (copy_from_user(job->waitchk, waitchks, 207 sizeof(*waitchks) * num_waitchks)) { 208 err = -EFAULT; 209 goto fail; 210 } 211 212 if (copy_from_user(&syncpt, (void __user *)(uintptr_t)args->syncpts, 213 sizeof(syncpt))) { 214 err = -EFAULT; 215 goto fail; 216 } 217 218 job->is_addr_reg = context->client->ops->is_addr_reg; 219 job->syncpt_incrs = syncpt.incrs; 220 job->syncpt_id = syncpt.id; 221 job->timeout = 10000; 222 223 if (args->timeout && args->timeout < 10000) 224 job->timeout = args->timeout; 225 226 err = host1x_job_pin(job, context->client->base.dev); 227 if (err) 228 goto fail; 229 230 err = host1x_job_submit(job); 231 if (err) 232 goto fail_submit; 233 234 args->fence = job->syncpt_end; 235 236 host1x_job_put(job); 237 return 0; 238 239 fail_submit: 240 host1x_job_unpin(job); 241 fail: 242 host1x_job_put(job); 243 return err; 244 } 245 246 247 #ifdef CONFIG_DRM_TEGRA_STAGING 248 static struct tegra_drm_context *tegra_drm_get_context(__u64 context) 249 { 250 return (struct tegra_drm_context *)(uintptr_t)context; 251 } 252 253 static bool tegra_drm_file_owns_context(struct tegra_drm_file *file, 254 struct tegra_drm_context *context) 255 { 256 struct tegra_drm_context *ctx; 257 258 list_for_each_entry(ctx, &file->contexts, list) 259 if (ctx == context) 260 return true; 261 262 return false; 263 } 264 265 static int tegra_gem_create(struct drm_device *drm, void *data, 266 struct drm_file *file) 267 { 268 struct drm_tegra_gem_create *args = data; 269 struct tegra_bo *bo; 270 271 bo = tegra_bo_create_with_handle(file, drm, args->size, args->flags, 272 &args->handle); 273 if (IS_ERR(bo)) 274 return PTR_ERR(bo); 275 276 return 0; 277 } 278 279 static int tegra_gem_mmap(struct drm_device *drm, void *data, 280 struct drm_file *file) 281 { 282 struct drm_tegra_gem_mmap *args = data; 283 struct drm_gem_object *gem; 284 struct tegra_bo *bo; 285 286 gem = drm_gem_object_lookup(drm, file, args->handle); 287 if (!gem) 288 return -EINVAL; 289 290 bo = to_tegra_bo(gem); 291 292 args->offset = drm_vma_node_offset_addr(&bo->gem.vma_node); 293 294 drm_gem_object_unreference(gem); 295 296 return 0; 297 } 298 299 static int tegra_syncpt_read(struct drm_device *drm, void *data, 300 struct drm_file *file) 301 { 302 struct host1x *host = dev_get_drvdata(drm->dev->parent); 303 struct drm_tegra_syncpt_read *args = data; 304 struct host1x_syncpt *sp; 305 306 sp = host1x_syncpt_get(host, args->id); 307 if (!sp) 308 return -EINVAL; 309 310 args->value = host1x_syncpt_read_min(sp); 311 return 0; 312 } 313 314 static int tegra_syncpt_incr(struct drm_device *drm, void *data, 315 struct drm_file *file) 316 { 317 struct host1x *host1x = dev_get_drvdata(drm->dev->parent); 318 struct drm_tegra_syncpt_incr *args = data; 319 struct host1x_syncpt *sp; 320 321 sp = host1x_syncpt_get(host1x, args->id); 322 if (!sp) 323 return -EINVAL; 324 325 return host1x_syncpt_incr(sp); 326 } 327 328 static int tegra_syncpt_wait(struct drm_device *drm, void *data, 329 struct drm_file *file) 330 { 331 struct host1x *host1x = dev_get_drvdata(drm->dev->parent); 332 struct drm_tegra_syncpt_wait *args = data; 333 struct host1x_syncpt *sp; 334 335 sp = host1x_syncpt_get(host1x, args->id); 336 if (!sp) 337 return -EINVAL; 338 339 return host1x_syncpt_wait(sp, args->thresh, args->timeout, 340 &args->value); 341 } 342 343 static int tegra_open_channel(struct drm_device *drm, void *data, 344 struct drm_file *file) 345 { 346 struct tegra_drm_file *fpriv = file->driver_priv; 347 struct tegra_drm *tegra = drm->dev_private; 348 struct drm_tegra_open_channel *args = data; 349 struct tegra_drm_context *context; 350 struct tegra_drm_client *client; 351 int err = -ENODEV; 352 353 context = kzalloc(sizeof(*context), GFP_KERNEL); 354 if (!context) 355 return -ENOMEM; 356 357 list_for_each_entry(client, &tegra->clients, list) 358 if (client->base.class == args->client) { 359 err = client->ops->open_channel(client, context); 360 if (err) 361 break; 362 363 list_add(&context->list, &fpriv->contexts); 364 args->context = (uintptr_t)context; 365 context->client = client; 366 return 0; 367 } 368 369 kfree(context); 370 return err; 371 } 372 373 static int tegra_close_channel(struct drm_device *drm, void *data, 374 struct drm_file *file) 375 { 376 struct tegra_drm_file *fpriv = file->driver_priv; 377 struct drm_tegra_close_channel *args = data; 378 struct tegra_drm_context *context; 379 380 context = tegra_drm_get_context(args->context); 381 382 if (!tegra_drm_file_owns_context(fpriv, context)) 383 return -EINVAL; 384 385 list_del(&context->list); 386 tegra_drm_context_free(context); 387 388 return 0; 389 } 390 391 static int tegra_get_syncpt(struct drm_device *drm, void *data, 392 struct drm_file *file) 393 { 394 struct tegra_drm_file *fpriv = file->driver_priv; 395 struct drm_tegra_get_syncpt *args = data; 396 struct tegra_drm_context *context; 397 struct host1x_syncpt *syncpt; 398 399 context = tegra_drm_get_context(args->context); 400 401 if (!tegra_drm_file_owns_context(fpriv, context)) 402 return -ENODEV; 403 404 if (args->index >= context->client->base.num_syncpts) 405 return -EINVAL; 406 407 syncpt = context->client->base.syncpts[args->index]; 408 args->id = host1x_syncpt_id(syncpt); 409 410 return 0; 411 } 412 413 static int tegra_submit(struct drm_device *drm, void *data, 414 struct drm_file *file) 415 { 416 struct tegra_drm_file *fpriv = file->driver_priv; 417 struct drm_tegra_submit *args = data; 418 struct tegra_drm_context *context; 419 420 context = tegra_drm_get_context(args->context); 421 422 if (!tegra_drm_file_owns_context(fpriv, context)) 423 return -ENODEV; 424 425 return context->client->ops->submit(context, args, drm, file); 426 } 427 428 static int tegra_get_syncpt_base(struct drm_device *drm, void *data, 429 struct drm_file *file) 430 { 431 struct tegra_drm_file *fpriv = file->driver_priv; 432 struct drm_tegra_get_syncpt_base *args = data; 433 struct tegra_drm_context *context; 434 struct host1x_syncpt_base *base; 435 struct host1x_syncpt *syncpt; 436 437 context = tegra_drm_get_context(args->context); 438 439 if (!tegra_drm_file_owns_context(fpriv, context)) 440 return -ENODEV; 441 442 if (args->syncpt >= context->client->base.num_syncpts) 443 return -EINVAL; 444 445 syncpt = context->client->base.syncpts[args->syncpt]; 446 447 base = host1x_syncpt_get_base(syncpt); 448 if (!base) 449 return -ENXIO; 450 451 args->id = host1x_syncpt_base_id(base); 452 453 return 0; 454 } 455 #endif 456 457 static const struct drm_ioctl_desc tegra_drm_ioctls[] = { 458 #ifdef CONFIG_DRM_TEGRA_STAGING 459 DRM_IOCTL_DEF_DRV(TEGRA_GEM_CREATE, tegra_gem_create, DRM_UNLOCKED | DRM_AUTH), 460 DRM_IOCTL_DEF_DRV(TEGRA_GEM_MMAP, tegra_gem_mmap, DRM_UNLOCKED), 461 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_READ, tegra_syncpt_read, DRM_UNLOCKED), 462 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_INCR, tegra_syncpt_incr, DRM_UNLOCKED), 463 DRM_IOCTL_DEF_DRV(TEGRA_SYNCPT_WAIT, tegra_syncpt_wait, DRM_UNLOCKED), 464 DRM_IOCTL_DEF_DRV(TEGRA_OPEN_CHANNEL, tegra_open_channel, DRM_UNLOCKED), 465 DRM_IOCTL_DEF_DRV(TEGRA_CLOSE_CHANNEL, tegra_close_channel, DRM_UNLOCKED), 466 DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT, tegra_get_syncpt, DRM_UNLOCKED), 467 DRM_IOCTL_DEF_DRV(TEGRA_SUBMIT, tegra_submit, DRM_UNLOCKED), 468 DRM_IOCTL_DEF_DRV(TEGRA_GET_SYNCPT_BASE, tegra_get_syncpt_base, DRM_UNLOCKED), 469 #endif 470 }; 471 472 static const struct file_operations tegra_drm_fops = { 473 .owner = THIS_MODULE, 474 .open = drm_open, 475 .release = drm_release, 476 .unlocked_ioctl = drm_ioctl, 477 .mmap = tegra_drm_mmap, 478 .poll = drm_poll, 479 .read = drm_read, 480 #ifdef CONFIG_COMPAT 481 .compat_ioctl = drm_compat_ioctl, 482 #endif 483 .llseek = noop_llseek, 484 }; 485 486 static struct drm_crtc *tegra_crtc_from_pipe(struct drm_device *drm, int pipe) 487 { 488 struct drm_crtc *crtc; 489 490 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) { 491 struct tegra_dc *dc = to_tegra_dc(crtc); 492 493 if (dc->pipe == pipe) 494 return crtc; 495 } 496 497 return NULL; 498 } 499 500 static u32 tegra_drm_get_vblank_counter(struct drm_device *dev, int crtc) 501 { 502 /* TODO: implement real hardware counter using syncpoints */ 503 return drm_vblank_count(dev, crtc); 504 } 505 506 static int tegra_drm_enable_vblank(struct drm_device *drm, int pipe) 507 { 508 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); 509 struct tegra_dc *dc = to_tegra_dc(crtc); 510 511 if (!crtc) 512 return -ENODEV; 513 514 tegra_dc_enable_vblank(dc); 515 516 return 0; 517 } 518 519 static void tegra_drm_disable_vblank(struct drm_device *drm, int pipe) 520 { 521 struct drm_crtc *crtc = tegra_crtc_from_pipe(drm, pipe); 522 struct tegra_dc *dc = to_tegra_dc(crtc); 523 524 if (crtc) 525 tegra_dc_disable_vblank(dc); 526 } 527 528 static void tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) 529 { 530 struct tegra_drm_file *fpriv = file->driver_priv; 531 struct tegra_drm_context *context, *tmp; 532 struct drm_crtc *crtc; 533 534 list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) 535 tegra_dc_cancel_page_flip(crtc, file); 536 537 list_for_each_entry_safe(context, tmp, &fpriv->contexts, list) 538 tegra_drm_context_free(context); 539 540 kfree(fpriv); 541 } 542 543 #ifdef CONFIG_DEBUG_FS 544 static int tegra_debugfs_framebuffers(struct seq_file *s, void *data) 545 { 546 struct drm_info_node *node = (struct drm_info_node *)s->private; 547 struct drm_device *drm = node->minor->dev; 548 struct drm_framebuffer *fb; 549 550 mutex_lock(&drm->mode_config.fb_lock); 551 552 list_for_each_entry(fb, &drm->mode_config.fb_list, head) { 553 seq_printf(s, "%3d: user size: %d x %d, depth %d, %d bpp, refcount %d\n", 554 fb->base.id, fb->width, fb->height, fb->depth, 555 fb->bits_per_pixel, 556 atomic_read(&fb->refcount.refcount)); 557 } 558 559 mutex_unlock(&drm->mode_config.fb_lock); 560 561 return 0; 562 } 563 564 static struct drm_info_list tegra_debugfs_list[] = { 565 { "framebuffers", tegra_debugfs_framebuffers, 0 }, 566 }; 567 568 static int tegra_debugfs_init(struct drm_minor *minor) 569 { 570 return drm_debugfs_create_files(tegra_debugfs_list, 571 ARRAY_SIZE(tegra_debugfs_list), 572 minor->debugfs_root, minor); 573 } 574 575 static void tegra_debugfs_cleanup(struct drm_minor *minor) 576 { 577 drm_debugfs_remove_files(tegra_debugfs_list, 578 ARRAY_SIZE(tegra_debugfs_list), minor); 579 } 580 #endif 581 582 static struct drm_driver tegra_drm_driver = { 583 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, 584 .load = tegra_drm_load, 585 .unload = tegra_drm_unload, 586 .open = tegra_drm_open, 587 .preclose = tegra_drm_preclose, 588 .lastclose = tegra_drm_lastclose, 589 590 .get_vblank_counter = tegra_drm_get_vblank_counter, 591 .enable_vblank = tegra_drm_enable_vblank, 592 .disable_vblank = tegra_drm_disable_vblank, 593 594 #if defined(CONFIG_DEBUG_FS) 595 .debugfs_init = tegra_debugfs_init, 596 .debugfs_cleanup = tegra_debugfs_cleanup, 597 #endif 598 599 .gem_free_object = tegra_bo_free_object, 600 .gem_vm_ops = &tegra_bo_vm_ops, 601 602 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 603 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 604 .gem_prime_export = tegra_gem_prime_export, 605 .gem_prime_import = tegra_gem_prime_import, 606 607 .dumb_create = tegra_bo_dumb_create, 608 .dumb_map_offset = tegra_bo_dumb_map_offset, 609 .dumb_destroy = drm_gem_dumb_destroy, 610 611 .ioctls = tegra_drm_ioctls, 612 .num_ioctls = ARRAY_SIZE(tegra_drm_ioctls), 613 .fops = &tegra_drm_fops, 614 615 .name = DRIVER_NAME, 616 .desc = DRIVER_DESC, 617 .date = DRIVER_DATE, 618 .major = DRIVER_MAJOR, 619 .minor = DRIVER_MINOR, 620 .patchlevel = DRIVER_PATCHLEVEL, 621 }; 622 623 int tegra_drm_register_client(struct tegra_drm *tegra, 624 struct tegra_drm_client *client) 625 { 626 mutex_lock(&tegra->clients_lock); 627 list_add_tail(&client->list, &tegra->clients); 628 mutex_unlock(&tegra->clients_lock); 629 630 return 0; 631 } 632 633 int tegra_drm_unregister_client(struct tegra_drm *tegra, 634 struct tegra_drm_client *client) 635 { 636 mutex_lock(&tegra->clients_lock); 637 list_del_init(&client->list); 638 mutex_unlock(&tegra->clients_lock); 639 640 return 0; 641 } 642 643 static int host1x_drm_probe(struct host1x_device *device) 644 { 645 return drm_host1x_init(&tegra_drm_driver, device); 646 } 647 648 static int host1x_drm_remove(struct host1x_device *device) 649 { 650 drm_host1x_exit(&tegra_drm_driver, device); 651 652 return 0; 653 } 654 655 static const struct of_device_id host1x_drm_subdevs[] = { 656 { .compatible = "nvidia,tegra20-dc", }, 657 { .compatible = "nvidia,tegra20-hdmi", }, 658 { .compatible = "nvidia,tegra20-gr2d", }, 659 { .compatible = "nvidia,tegra20-gr3d", }, 660 { .compatible = "nvidia,tegra30-dc", }, 661 { .compatible = "nvidia,tegra30-hdmi", }, 662 { .compatible = "nvidia,tegra30-gr2d", }, 663 { .compatible = "nvidia,tegra30-gr3d", }, 664 { .compatible = "nvidia,tegra114-dsi", }, 665 { .compatible = "nvidia,tegra114-hdmi", }, 666 { .compatible = "nvidia,tegra114-gr3d", }, 667 { .compatible = "nvidia,tegra124-dc", }, 668 { .compatible = "nvidia,tegra124-sor", }, 669 { /* sentinel */ } 670 }; 671 672 static struct host1x_driver host1x_drm_driver = { 673 .name = "drm", 674 .probe = host1x_drm_probe, 675 .remove = host1x_drm_remove, 676 .subdevs = host1x_drm_subdevs, 677 }; 678 679 static int __init host1x_drm_init(void) 680 { 681 int err; 682 683 err = host1x_driver_register(&host1x_drm_driver); 684 if (err < 0) 685 return err; 686 687 err = platform_driver_register(&tegra_dc_driver); 688 if (err < 0) 689 goto unregister_host1x; 690 691 err = platform_driver_register(&tegra_dsi_driver); 692 if (err < 0) 693 goto unregister_dc; 694 695 err = platform_driver_register(&tegra_sor_driver); 696 if (err < 0) 697 goto unregister_dsi; 698 699 err = platform_driver_register(&tegra_hdmi_driver); 700 if (err < 0) 701 goto unregister_sor; 702 703 err = platform_driver_register(&tegra_dpaux_driver); 704 if (err < 0) 705 goto unregister_hdmi; 706 707 err = platform_driver_register(&tegra_gr2d_driver); 708 if (err < 0) 709 goto unregister_dpaux; 710 711 err = platform_driver_register(&tegra_gr3d_driver); 712 if (err < 0) 713 goto unregister_gr2d; 714 715 return 0; 716 717 unregister_gr2d: 718 platform_driver_unregister(&tegra_gr2d_driver); 719 unregister_dpaux: 720 platform_driver_unregister(&tegra_dpaux_driver); 721 unregister_hdmi: 722 platform_driver_unregister(&tegra_hdmi_driver); 723 unregister_sor: 724 platform_driver_unregister(&tegra_sor_driver); 725 unregister_dsi: 726 platform_driver_unregister(&tegra_dsi_driver); 727 unregister_dc: 728 platform_driver_unregister(&tegra_dc_driver); 729 unregister_host1x: 730 host1x_driver_unregister(&host1x_drm_driver); 731 return err; 732 } 733 module_init(host1x_drm_init); 734 735 static void __exit host1x_drm_exit(void) 736 { 737 platform_driver_unregister(&tegra_gr3d_driver); 738 platform_driver_unregister(&tegra_gr2d_driver); 739 platform_driver_unregister(&tegra_dpaux_driver); 740 platform_driver_unregister(&tegra_hdmi_driver); 741 platform_driver_unregister(&tegra_sor_driver); 742 platform_driver_unregister(&tegra_dsi_driver); 743 platform_driver_unregister(&tegra_dc_driver); 744 host1x_driver_unregister(&host1x_drm_driver); 745 } 746 module_exit(host1x_drm_exit); 747 748 MODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); 749 MODULE_DESCRIPTION("NVIDIA Tegra DRM driver"); 750 MODULE_LICENSE("GPL v2"); 751