linux/drivers/gpu/drm/etnaviv/etnaviv_drv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (C) 2015-2018 Etnaviv Project
   4 */
   5
   6#include <linux/component.h>
   7#include <linux/of_platform.h>
   8#include <drm/drm_of.h>
   9
  10#include "etnaviv_cmdbuf.h"
  11#include "etnaviv_drv.h"
  12#include "etnaviv_gpu.h"
  13#include "etnaviv_gem.h"
  14#include "etnaviv_mmu.h"
  15#include "etnaviv_perfmon.h"
  16
  17/*
  18 * DRM operations:
  19 */
  20
  21
  22static void load_gpu(struct drm_device *dev)
  23{
  24        struct etnaviv_drm_private *priv = dev->dev_private;
  25        unsigned int i;
  26
  27        for (i = 0; i < ETNA_MAX_PIPES; i++) {
  28                struct etnaviv_gpu *g = priv->gpu[i];
  29
  30                if (g) {
  31                        int ret;
  32
  33                        ret = etnaviv_gpu_init(g);
  34                        if (ret)
  35                                priv->gpu[i] = NULL;
  36                }
  37        }
  38}
  39
  40static int etnaviv_open(struct drm_device *dev, struct drm_file *file)
  41{
  42        struct etnaviv_drm_private *priv = dev->dev_private;
  43        struct etnaviv_file_private *ctx;
  44        int i;
  45
  46        ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
  47        if (!ctx)
  48                return -ENOMEM;
  49
  50        for (i = 0; i < ETNA_MAX_PIPES; i++) {
  51                struct etnaviv_gpu *gpu = priv->gpu[i];
  52
  53                if (gpu) {
  54                        drm_sched_entity_init(&gpu->sched,
  55                                &ctx->sched_entity[i],
  56                                &gpu->sched.sched_rq[DRM_SCHED_PRIORITY_NORMAL],
  57                                NULL);
  58                        }
  59        }
  60
  61        file->driver_priv = ctx;
  62
  63        return 0;
  64}
  65
  66static void etnaviv_postclose(struct drm_device *dev, struct drm_file *file)
  67{
  68        struct etnaviv_drm_private *priv = dev->dev_private;
  69        struct etnaviv_file_private *ctx = file->driver_priv;
  70        unsigned int i;
  71
  72        for (i = 0; i < ETNA_MAX_PIPES; i++) {
  73                struct etnaviv_gpu *gpu = priv->gpu[i];
  74
  75                if (gpu) {
  76                        mutex_lock(&gpu->lock);
  77                        if (gpu->lastctx == ctx)
  78                                gpu->lastctx = NULL;
  79                        mutex_unlock(&gpu->lock);
  80
  81                        drm_sched_entity_fini(&gpu->sched,
  82                                              &ctx->sched_entity[i]);
  83                }
  84        }
  85
  86        kfree(ctx);
  87}
  88
  89/*
  90 * DRM debugfs:
  91 */
  92
  93#ifdef CONFIG_DEBUG_FS
  94static int etnaviv_gem_show(struct drm_device *dev, struct seq_file *m)
  95{
  96        struct etnaviv_drm_private *priv = dev->dev_private;
  97
  98        etnaviv_gem_describe_objects(priv, m);
  99
 100        return 0;
 101}
 102
 103static int etnaviv_mm_show(struct drm_device *dev, struct seq_file *m)
 104{
 105        struct drm_printer p = drm_seq_file_printer(m);
 106
 107        read_lock(&dev->vma_offset_manager->vm_lock);
 108        drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p);
 109        read_unlock(&dev->vma_offset_manager->vm_lock);
 110
 111        return 0;
 112}
 113
 114static int etnaviv_mmu_show(struct etnaviv_gpu *gpu, struct seq_file *m)
 115{
 116        struct drm_printer p = drm_seq_file_printer(m);
 117
 118        seq_printf(m, "Active Objects (%s):\n", dev_name(gpu->dev));
 119
 120        mutex_lock(&gpu->mmu->lock);
 121        drm_mm_print(&gpu->mmu->mm, &p);
 122        mutex_unlock(&gpu->mmu->lock);
 123
 124        return 0;
 125}
 126
 127static void etnaviv_buffer_dump(struct etnaviv_gpu *gpu, struct seq_file *m)
 128{
 129        struct etnaviv_cmdbuf *buf = &gpu->buffer;
 130        u32 size = buf->size;
 131        u32 *ptr = buf->vaddr;
 132        u32 i;
 133
 134        seq_printf(m, "virt %p - phys 0x%llx - free 0x%08x\n",
 135                        buf->vaddr, (u64)etnaviv_cmdbuf_get_pa(buf),
 136                        size - buf->user_size);
 137
 138        for (i = 0; i < size / 4; i++) {
 139                if (i && !(i % 4))
 140                        seq_puts(m, "\n");
 141                if (i % 4 == 0)
 142                        seq_printf(m, "\t0x%p: ", ptr + i);
 143                seq_printf(m, "%08x ", *(ptr + i));
 144        }
 145        seq_puts(m, "\n");
 146}
 147
 148static int etnaviv_ring_show(struct etnaviv_gpu *gpu, struct seq_file *m)
 149{
 150        seq_printf(m, "Ring Buffer (%s): ", dev_name(gpu->dev));
 151
 152        mutex_lock(&gpu->lock);
 153        etnaviv_buffer_dump(gpu, m);
 154        mutex_unlock(&gpu->lock);
 155
 156        return 0;
 157}
 158
 159static int show_unlocked(struct seq_file *m, void *arg)
 160{
 161        struct drm_info_node *node = (struct drm_info_node *) m->private;
 162        struct drm_device *dev = node->minor->dev;
 163        int (*show)(struct drm_device *dev, struct seq_file *m) =
 164                        node->info_ent->data;
 165
 166        return show(dev, m);
 167}
 168
 169static int show_each_gpu(struct seq_file *m, void *arg)
 170{
 171        struct drm_info_node *node = (struct drm_info_node *) m->private;
 172        struct drm_device *dev = node->minor->dev;
 173        struct etnaviv_drm_private *priv = dev->dev_private;
 174        struct etnaviv_gpu *gpu;
 175        int (*show)(struct etnaviv_gpu *gpu, struct seq_file *m) =
 176                        node->info_ent->data;
 177        unsigned int i;
 178        int ret = 0;
 179
 180        for (i = 0; i < ETNA_MAX_PIPES; i++) {
 181                gpu = priv->gpu[i];
 182                if (!gpu)
 183                        continue;
 184
 185                ret = show(gpu, m);
 186                if (ret < 0)
 187                        break;
 188        }
 189
 190        return ret;
 191}
 192
 193static struct drm_info_list etnaviv_debugfs_list[] = {
 194                {"gpu", show_each_gpu, 0, etnaviv_gpu_debugfs},
 195                {"gem", show_unlocked, 0, etnaviv_gem_show},
 196                { "mm", show_unlocked, 0, etnaviv_mm_show },
 197                {"mmu", show_each_gpu, 0, etnaviv_mmu_show},
 198                {"ring", show_each_gpu, 0, etnaviv_ring_show},
 199};
 200
 201static int etnaviv_debugfs_init(struct drm_minor *minor)
 202{
 203        struct drm_device *dev = minor->dev;
 204        int ret;
 205
 206        ret = drm_debugfs_create_files(etnaviv_debugfs_list,
 207                        ARRAY_SIZE(etnaviv_debugfs_list),
 208                        minor->debugfs_root, minor);
 209
 210        if (ret) {
 211                dev_err(dev->dev, "could not install etnaviv_debugfs_list\n");
 212                return ret;
 213        }
 214
 215        return ret;
 216}
 217#endif
 218
 219/*
 220 * DRM ioctls:
 221 */
 222
 223static int etnaviv_ioctl_get_param(struct drm_device *dev, void *data,
 224                struct drm_file *file)
 225{
 226        struct etnaviv_drm_private *priv = dev->dev_private;
 227        struct drm_etnaviv_param *args = data;
 228        struct etnaviv_gpu *gpu;
 229
 230        if (args->pipe >= ETNA_MAX_PIPES)
 231                return -EINVAL;
 232
 233        gpu = priv->gpu[args->pipe];
 234        if (!gpu)
 235                return -ENXIO;
 236
 237        return etnaviv_gpu_get_param(gpu, args->param, &args->value);
 238}
 239
 240static int etnaviv_ioctl_gem_new(struct drm_device *dev, void *data,
 241                struct drm_file *file)
 242{
 243        struct drm_etnaviv_gem_new *args = data;
 244
 245        if (args->flags & ~(ETNA_BO_CACHED | ETNA_BO_WC | ETNA_BO_UNCACHED |
 246                            ETNA_BO_FORCE_MMU))
 247                return -EINVAL;
 248
 249        return etnaviv_gem_new_handle(dev, file, args->size,
 250                        args->flags, &args->handle);
 251}
 252
 253#define TS(t) ((struct timespec){ \
 254        .tv_sec = (t).tv_sec, \
 255        .tv_nsec = (t).tv_nsec \
 256})
 257
 258static int etnaviv_ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
 259                struct drm_file *file)
 260{
 261        struct drm_etnaviv_gem_cpu_prep *args = data;
 262        struct drm_gem_object *obj;
 263        int ret;
 264
 265        if (args->op & ~(ETNA_PREP_READ | ETNA_PREP_WRITE | ETNA_PREP_NOSYNC))
 266                return -EINVAL;
 267
 268        obj = drm_gem_object_lookup(file, args->handle);
 269        if (!obj)
 270                return -ENOENT;
 271
 272        ret = etnaviv_gem_cpu_prep(obj, args->op, &TS(args->timeout));
 273
 274        drm_gem_object_put_unlocked(obj);
 275
 276        return ret;
 277}
 278
 279static int etnaviv_ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
 280                struct drm_file *file)
 281{
 282        struct drm_etnaviv_gem_cpu_fini *args = data;
 283        struct drm_gem_object *obj;
 284        int ret;
 285
 286        if (args->flags)
 287                return -EINVAL;
 288
 289        obj = drm_gem_object_lookup(file, args->handle);
 290        if (!obj)
 291                return -ENOENT;
 292
 293        ret = etnaviv_gem_cpu_fini(obj);
 294
 295        drm_gem_object_put_unlocked(obj);
 296
 297        return ret;
 298}
 299
 300static int etnaviv_ioctl_gem_info(struct drm_device *dev, void *data,
 301                struct drm_file *file)
 302{
 303        struct drm_etnaviv_gem_info *args = data;
 304        struct drm_gem_object *obj;
 305        int ret;
 306
 307        if (args->pad)
 308                return -EINVAL;
 309
 310        obj = drm_gem_object_lookup(file, args->handle);
 311        if (!obj)
 312                return -ENOENT;
 313
 314        ret = etnaviv_gem_mmap_offset(obj, &args->offset);
 315        drm_gem_object_put_unlocked(obj);
 316
 317        return ret;
 318}
 319
 320static int etnaviv_ioctl_wait_fence(struct drm_device *dev, void *data,
 321                struct drm_file *file)
 322{
 323        struct drm_etnaviv_wait_fence *args = data;
 324        struct etnaviv_drm_private *priv = dev->dev_private;
 325        struct timespec *timeout = &TS(args->timeout);
 326        struct etnaviv_gpu *gpu;
 327
 328        if (args->flags & ~(ETNA_WAIT_NONBLOCK))
 329                return -EINVAL;
 330
 331        if (args->pipe >= ETNA_MAX_PIPES)
 332                return -EINVAL;
 333
 334        gpu = priv->gpu[args->pipe];
 335        if (!gpu)
 336                return -ENXIO;
 337
 338        if (args->flags & ETNA_WAIT_NONBLOCK)
 339                timeout = NULL;
 340
 341        return etnaviv_gpu_wait_fence_interruptible(gpu, args->fence,
 342                                                    timeout);
 343}
 344
 345static int etnaviv_ioctl_gem_userptr(struct drm_device *dev, void *data,
 346        struct drm_file *file)
 347{
 348        struct drm_etnaviv_gem_userptr *args = data;
 349
 350        if (args->flags & ~(ETNA_USERPTR_READ|ETNA_USERPTR_WRITE) ||
 351            args->flags == 0)
 352                return -EINVAL;
 353
 354        if (offset_in_page(args->user_ptr | args->user_size) ||
 355            (uintptr_t)args->user_ptr != args->user_ptr ||
 356            (u32)args->user_size != args->user_size ||
 357            args->user_ptr & ~PAGE_MASK)
 358                return -EINVAL;
 359
 360        if (!access_ok((void __user *)(unsigned long)args->user_ptr,
 361                       args->user_size))
 362                return -EFAULT;
 363
 364        return etnaviv_gem_new_userptr(dev, file, args->user_ptr,
 365                                       args->user_size, args->flags,
 366                                       &args->handle);
 367}
 368
 369static int etnaviv_ioctl_gem_wait(struct drm_device *dev, void *data,
 370        struct drm_file *file)
 371{
 372        struct etnaviv_drm_private *priv = dev->dev_private;
 373        struct drm_etnaviv_gem_wait *args = data;
 374        struct timespec *timeout = &TS(args->timeout);
 375        struct drm_gem_object *obj;
 376        struct etnaviv_gpu *gpu;
 377        int ret;
 378
 379        if (args->flags & ~(ETNA_WAIT_NONBLOCK))
 380                return -EINVAL;
 381
 382        if (args->pipe >= ETNA_MAX_PIPES)
 383                return -EINVAL;
 384
 385        gpu = priv->gpu[args->pipe];
 386        if (!gpu)
 387                return -ENXIO;
 388
 389        obj = drm_gem_object_lookup(file, args->handle);
 390        if (!obj)
 391                return -ENOENT;
 392
 393        if (args->flags & ETNA_WAIT_NONBLOCK)
 394                timeout = NULL;
 395
 396        ret = etnaviv_gem_wait_bo(gpu, obj, timeout);
 397
 398        drm_gem_object_put_unlocked(obj);
 399
 400        return ret;
 401}
 402
 403static int etnaviv_ioctl_pm_query_dom(struct drm_device *dev, void *data,
 404        struct drm_file *file)
 405{
 406        struct etnaviv_drm_private *priv = dev->dev_private;
 407        struct drm_etnaviv_pm_domain *args = data;
 408        struct etnaviv_gpu *gpu;
 409
 410        if (args->pipe >= ETNA_MAX_PIPES)
 411                return -EINVAL;
 412
 413        gpu = priv->gpu[args->pipe];
 414        if (!gpu)
 415                return -ENXIO;
 416
 417        return etnaviv_pm_query_dom(gpu, args);
 418}
 419
 420static int etnaviv_ioctl_pm_query_sig(struct drm_device *dev, void *data,
 421        struct drm_file *file)
 422{
 423        struct etnaviv_drm_private *priv = dev->dev_private;
 424        struct drm_etnaviv_pm_signal *args = data;
 425        struct etnaviv_gpu *gpu;
 426
 427        if (args->pipe >= ETNA_MAX_PIPES)
 428                return -EINVAL;
 429
 430        gpu = priv->gpu[args->pipe];
 431        if (!gpu)
 432                return -ENXIO;
 433
 434        return etnaviv_pm_query_sig(gpu, args);
 435}
 436
 437static const struct drm_ioctl_desc etnaviv_ioctls[] = {
 438#define ETNA_IOCTL(n, func, flags) \
 439        DRM_IOCTL_DEF_DRV(ETNAVIV_##n, etnaviv_ioctl_##func, flags)
 440        ETNA_IOCTL(GET_PARAM,    get_param,    DRM_AUTH|DRM_RENDER_ALLOW),
 441        ETNA_IOCTL(GEM_NEW,      gem_new,      DRM_AUTH|DRM_RENDER_ALLOW),
 442        ETNA_IOCTL(GEM_INFO,     gem_info,     DRM_AUTH|DRM_RENDER_ALLOW),
 443        ETNA_IOCTL(GEM_CPU_PREP, gem_cpu_prep, DRM_AUTH|DRM_RENDER_ALLOW),
 444        ETNA_IOCTL(GEM_CPU_FINI, gem_cpu_fini, DRM_AUTH|DRM_RENDER_ALLOW),
 445        ETNA_IOCTL(GEM_SUBMIT,   gem_submit,   DRM_AUTH|DRM_RENDER_ALLOW),
 446        ETNA_IOCTL(WAIT_FENCE,   wait_fence,   DRM_AUTH|DRM_RENDER_ALLOW),
 447        ETNA_IOCTL(GEM_USERPTR,  gem_userptr,  DRM_AUTH|DRM_RENDER_ALLOW),
 448        ETNA_IOCTL(GEM_WAIT,     gem_wait,     DRM_AUTH|DRM_RENDER_ALLOW),
 449        ETNA_IOCTL(PM_QUERY_DOM, pm_query_dom, DRM_AUTH|DRM_RENDER_ALLOW),
 450        ETNA_IOCTL(PM_QUERY_SIG, pm_query_sig, DRM_AUTH|DRM_RENDER_ALLOW),
 451};
 452
 453static const struct vm_operations_struct vm_ops = {
 454        .fault = etnaviv_gem_fault,
 455        .open = drm_gem_vm_open,
 456        .close = drm_gem_vm_close,
 457};
 458
 459static const struct file_operations fops = {
 460        .owner              = THIS_MODULE,
 461        .open               = drm_open,
 462        .release            = drm_release,
 463        .unlocked_ioctl     = drm_ioctl,
 464        .compat_ioctl       = drm_compat_ioctl,
 465        .poll               = drm_poll,
 466        .read               = drm_read,
 467        .llseek             = no_llseek,
 468        .mmap               = etnaviv_gem_mmap,
 469};
 470
 471static struct drm_driver etnaviv_drm_driver = {
 472        .driver_features    = DRIVER_GEM |
 473                                DRIVER_PRIME |
 474                                DRIVER_RENDER,
 475        .open               = etnaviv_open,
 476        .postclose           = etnaviv_postclose,
 477        .gem_free_object_unlocked = etnaviv_gem_free_object,
 478        .gem_vm_ops         = &vm_ops,
 479        .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
 480        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 481        .gem_prime_export   = drm_gem_prime_export,
 482        .gem_prime_import   = drm_gem_prime_import,
 483        .gem_prime_res_obj  = etnaviv_gem_prime_res_obj,
 484        .gem_prime_pin      = etnaviv_gem_prime_pin,
 485        .gem_prime_unpin    = etnaviv_gem_prime_unpin,
 486        .gem_prime_get_sg_table = etnaviv_gem_prime_get_sg_table,
 487        .gem_prime_import_sg_table = etnaviv_gem_prime_import_sg_table,
 488        .gem_prime_vmap     = etnaviv_gem_prime_vmap,
 489        .gem_prime_vunmap   = etnaviv_gem_prime_vunmap,
 490        .gem_prime_mmap     = etnaviv_gem_prime_mmap,
 491#ifdef CONFIG_DEBUG_FS
 492        .debugfs_init       = etnaviv_debugfs_init,
 493#endif
 494        .ioctls             = etnaviv_ioctls,
 495        .num_ioctls         = DRM_ETNAVIV_NUM_IOCTLS,
 496        .fops               = &fops,
 497        .name               = "etnaviv",
 498        .desc               = "etnaviv DRM",
 499        .date               = "20151214",
 500        .major              = 1,
 501        .minor              = 2,
 502};
 503
 504/*
 505 * Platform driver:
 506 */
 507static int etnaviv_bind(struct device *dev)
 508{
 509        struct etnaviv_drm_private *priv;
 510        struct drm_device *drm;
 511        int ret;
 512
 513        drm = drm_dev_alloc(&etnaviv_drm_driver, dev);
 514        if (IS_ERR(drm))
 515                return PTR_ERR(drm);
 516
 517        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 518        if (!priv) {
 519                dev_err(dev, "failed to allocate private data\n");
 520                ret = -ENOMEM;
 521                goto out_unref;
 522        }
 523        drm->dev_private = priv;
 524
 525        mutex_init(&priv->gem_lock);
 526        INIT_LIST_HEAD(&priv->gem_list);
 527        priv->num_gpus = 0;
 528
 529        dev_set_drvdata(dev, drm);
 530
 531        ret = component_bind_all(dev, drm);
 532        if (ret < 0)
 533                goto out_bind;
 534
 535        load_gpu(drm);
 536
 537        ret = drm_dev_register(drm, 0);
 538        if (ret)
 539                goto out_register;
 540
 541        return 0;
 542
 543out_register:
 544        component_unbind_all(dev, drm);
 545out_bind:
 546        kfree(priv);
 547out_unref:
 548        drm_dev_unref(drm);
 549
 550        return ret;
 551}
 552
 553static void etnaviv_unbind(struct device *dev)
 554{
 555        struct drm_device *drm = dev_get_drvdata(dev);
 556        struct etnaviv_drm_private *priv = drm->dev_private;
 557
 558        drm_dev_unregister(drm);
 559
 560        component_unbind_all(dev, drm);
 561
 562        drm->dev_private = NULL;
 563        kfree(priv);
 564
 565        drm_dev_unref(drm);
 566}
 567
 568static const struct component_master_ops etnaviv_master_ops = {
 569        .bind = etnaviv_bind,
 570        .unbind = etnaviv_unbind,
 571};
 572
 573static int compare_of(struct device *dev, void *data)
 574{
 575        struct device_node *np = data;
 576
 577        return dev->of_node == np;
 578}
 579
 580static int compare_str(struct device *dev, void *data)
 581{
 582        return !strcmp(dev_name(dev), data);
 583}
 584
 585static int etnaviv_pdev_probe(struct platform_device *pdev)
 586{
 587        struct device *dev = &pdev->dev;
 588        struct component_match *match = NULL;
 589
 590        dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
 591
 592        if (!dev->platform_data) {
 593                struct device_node *core_node;
 594
 595                for_each_compatible_node(core_node, NULL, "vivante,gc") {
 596                        if (!of_device_is_available(core_node))
 597                                continue;
 598
 599                        drm_of_component_match_add(&pdev->dev, &match,
 600                                                   compare_of, core_node);
 601                }
 602        } else {
 603                char **names = dev->platform_data;
 604                unsigned i;
 605
 606                for (i = 0; names[i]; i++)
 607                        component_match_add(dev, &match, compare_str, names[i]);
 608        }
 609
 610        return component_master_add_with_match(dev, &etnaviv_master_ops, match);
 611}
 612
 613static int etnaviv_pdev_remove(struct platform_device *pdev)
 614{
 615        component_master_del(&pdev->dev, &etnaviv_master_ops);
 616
 617        return 0;
 618}
 619
 620static struct platform_driver etnaviv_platform_driver = {
 621        .probe      = etnaviv_pdev_probe,
 622        .remove     = etnaviv_pdev_remove,
 623        .driver     = {
 624                .name   = "etnaviv",
 625        },
 626};
 627
 628static struct platform_device *etnaviv_drm;
 629
 630static int __init etnaviv_init(void)
 631{
 632        struct platform_device *pdev;
 633        int ret;
 634        struct device_node *np;
 635
 636        etnaviv_validate_init();
 637
 638        ret = platform_driver_register(&etnaviv_gpu_driver);
 639        if (ret != 0)
 640                return ret;
 641
 642        ret = platform_driver_register(&etnaviv_platform_driver);
 643        if (ret != 0)
 644                goto unregister_gpu_driver;
 645
 646        /*
 647         * If the DT contains at least one available GPU device, instantiate
 648         * the DRM platform device.
 649         */
 650        for_each_compatible_node(np, NULL, "vivante,gc") {
 651                if (!of_device_is_available(np))
 652                        continue;
 653                pdev = platform_device_register_simple("etnaviv", -1,
 654                                                       NULL, 0);
 655                if (IS_ERR(pdev)) {
 656                        ret = PTR_ERR(pdev);
 657                        of_node_put(np);
 658                        goto unregister_platform_driver;
 659                }
 660                etnaviv_drm = pdev;
 661                of_node_put(np);
 662                break;
 663        }
 664
 665        return 0;
 666
 667unregister_platform_driver:
 668        platform_driver_unregister(&etnaviv_platform_driver);
 669unregister_gpu_driver:
 670        platform_driver_unregister(&etnaviv_gpu_driver);
 671        return ret;
 672}
 673module_init(etnaviv_init);
 674
 675static void __exit etnaviv_exit(void)
 676{
 677        platform_device_unregister(etnaviv_drm);
 678        platform_driver_unregister(&etnaviv_platform_driver);
 679        platform_driver_unregister(&etnaviv_gpu_driver);
 680}
 681module_exit(etnaviv_exit);
 682
 683MODULE_AUTHOR("Christian Gmeiner <christian.gmeiner@gmail.com>");
 684MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
 685MODULE_AUTHOR("Lucas Stach <l.stach@pengutronix.de>");
 686MODULE_DESCRIPTION("etnaviv DRM Driver");
 687MODULE_LICENSE("GPL v2");
 688MODULE_ALIAS("platform:etnaviv");
 689