linux/drivers/gpu/drm/mediatek/mtk_drm_drv.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2015 MediaTek Inc.
   4 * Author: YT SHEN <yt.shen@mediatek.com>
   5 */
   6
   7#include <linux/component.h>
   8#include <linux/iommu.h>
   9#include <linux/module.h>
  10#include <linux/of_address.h>
  11#include <linux/of_platform.h>
  12#include <linux/pm_runtime.h>
  13#include <linux/soc/mediatek/mtk-mmsys.h>
  14#include <linux/dma-mapping.h>
  15
  16#include <drm/drm_atomic.h>
  17#include <drm/drm_atomic_helper.h>
  18#include <drm/drm_drv.h>
  19#include <drm/drm_fb_helper.h>
  20#include <drm/drm_fourcc.h>
  21#include <drm/drm_gem.h>
  22#include <drm/drm_gem_cma_helper.h>
  23#include <drm/drm_gem_framebuffer_helper.h>
  24#include <drm/drm_of.h>
  25#include <drm/drm_probe_helper.h>
  26#include <drm/drm_vblank.h>
  27
  28#include "mtk_drm_crtc.h"
  29#include "mtk_drm_ddp.h"
  30#include "mtk_drm_ddp_comp.h"
  31#include "mtk_drm_drv.h"
  32#include "mtk_drm_gem.h"
  33
  34#define DRIVER_NAME "mediatek"
  35#define DRIVER_DESC "Mediatek SoC DRM"
  36#define DRIVER_DATE "20150513"
  37#define DRIVER_MAJOR 1
  38#define DRIVER_MINOR 0
  39
  40static const struct drm_mode_config_helper_funcs mtk_drm_mode_config_helpers = {
  41        .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
  42};
  43
  44static struct drm_framebuffer *
  45mtk_drm_mode_fb_create(struct drm_device *dev,
  46                       struct drm_file *file,
  47                       const struct drm_mode_fb_cmd2 *cmd)
  48{
  49        const struct drm_format_info *info = drm_get_format_info(dev, cmd);
  50
  51        if (info->num_planes != 1)
  52                return ERR_PTR(-EINVAL);
  53
  54        return drm_gem_fb_create(dev, file, cmd);
  55}
  56
  57static const struct drm_mode_config_funcs mtk_drm_mode_config_funcs = {
  58        .fb_create = mtk_drm_mode_fb_create,
  59        .atomic_check = drm_atomic_helper_check,
  60        .atomic_commit = drm_atomic_helper_commit,
  61};
  62
  63static const enum mtk_ddp_comp_id mt2701_mtk_ddp_main[] = {
  64        DDP_COMPONENT_OVL0,
  65        DDP_COMPONENT_RDMA0,
  66        DDP_COMPONENT_COLOR0,
  67        DDP_COMPONENT_BLS,
  68        DDP_COMPONENT_DSI0,
  69};
  70
  71static const enum mtk_ddp_comp_id mt2701_mtk_ddp_ext[] = {
  72        DDP_COMPONENT_RDMA1,
  73        DDP_COMPONENT_DPI0,
  74};
  75
  76static const enum mtk_ddp_comp_id mt2712_mtk_ddp_main[] = {
  77        DDP_COMPONENT_OVL0,
  78        DDP_COMPONENT_COLOR0,
  79        DDP_COMPONENT_AAL0,
  80        DDP_COMPONENT_OD0,
  81        DDP_COMPONENT_RDMA0,
  82        DDP_COMPONENT_DPI0,
  83        DDP_COMPONENT_PWM0,
  84};
  85
  86static const enum mtk_ddp_comp_id mt2712_mtk_ddp_ext[] = {
  87        DDP_COMPONENT_OVL1,
  88        DDP_COMPONENT_COLOR1,
  89        DDP_COMPONENT_AAL1,
  90        DDP_COMPONENT_OD1,
  91        DDP_COMPONENT_RDMA1,
  92        DDP_COMPONENT_DPI1,
  93        DDP_COMPONENT_PWM1,
  94};
  95
  96static const enum mtk_ddp_comp_id mt2712_mtk_ddp_third[] = {
  97        DDP_COMPONENT_RDMA2,
  98        DDP_COMPONENT_DSI3,
  99        DDP_COMPONENT_PWM2,
 100};
 101
 102static const enum mtk_ddp_comp_id mt8173_mtk_ddp_main[] = {
 103        DDP_COMPONENT_OVL0,
 104        DDP_COMPONENT_COLOR0,
 105        DDP_COMPONENT_AAL0,
 106        DDP_COMPONENT_OD0,
 107        DDP_COMPONENT_RDMA0,
 108        DDP_COMPONENT_UFOE,
 109        DDP_COMPONENT_DSI0,
 110        DDP_COMPONENT_PWM0,
 111};
 112
 113static const enum mtk_ddp_comp_id mt8173_mtk_ddp_ext[] = {
 114        DDP_COMPONENT_OVL1,
 115        DDP_COMPONENT_COLOR1,
 116        DDP_COMPONENT_GAMMA,
 117        DDP_COMPONENT_RDMA1,
 118        DDP_COMPONENT_DPI0,
 119};
 120
 121static const struct mtk_mmsys_driver_data mt2701_mmsys_driver_data = {
 122        .main_path = mt2701_mtk_ddp_main,
 123        .main_len = ARRAY_SIZE(mt2701_mtk_ddp_main),
 124        .ext_path = mt2701_mtk_ddp_ext,
 125        .ext_len = ARRAY_SIZE(mt2701_mtk_ddp_ext),
 126        .shadow_register = true,
 127};
 128
 129static const struct mtk_mmsys_driver_data mt2712_mmsys_driver_data = {
 130        .main_path = mt2712_mtk_ddp_main,
 131        .main_len = ARRAY_SIZE(mt2712_mtk_ddp_main),
 132        .ext_path = mt2712_mtk_ddp_ext,
 133        .ext_len = ARRAY_SIZE(mt2712_mtk_ddp_ext),
 134        .third_path = mt2712_mtk_ddp_third,
 135        .third_len = ARRAY_SIZE(mt2712_mtk_ddp_third),
 136};
 137
 138static const struct mtk_mmsys_driver_data mt8173_mmsys_driver_data = {
 139        .main_path = mt8173_mtk_ddp_main,
 140        .main_len = ARRAY_SIZE(mt8173_mtk_ddp_main),
 141        .ext_path = mt8173_mtk_ddp_ext,
 142        .ext_len = ARRAY_SIZE(mt8173_mtk_ddp_ext),
 143};
 144
 145static int mtk_drm_kms_init(struct drm_device *drm)
 146{
 147        struct mtk_drm_private *private = drm->dev_private;
 148        struct platform_device *pdev;
 149        struct device_node *np;
 150        struct device *dma_dev;
 151        int ret;
 152
 153        if (!iommu_present(&platform_bus_type))
 154                return -EPROBE_DEFER;
 155
 156        pdev = of_find_device_by_node(private->mutex_node);
 157        if (!pdev) {
 158                dev_err(drm->dev, "Waiting for disp-mutex device %pOF\n",
 159                        private->mutex_node);
 160                of_node_put(private->mutex_node);
 161                return -EPROBE_DEFER;
 162        }
 163        private->mutex_dev = &pdev->dev;
 164
 165        ret = drmm_mode_config_init(drm);
 166        if (ret)
 167                goto put_mutex_dev;
 168
 169        drm->mode_config.min_width = 64;
 170        drm->mode_config.min_height = 64;
 171
 172        /*
 173         * set max width and height as default value(4096x4096).
 174         * this value would be used to check framebuffer size limitation
 175         * at drm_mode_addfb().
 176         */
 177        drm->mode_config.max_width = 4096;
 178        drm->mode_config.max_height = 4096;
 179        drm->mode_config.funcs = &mtk_drm_mode_config_funcs;
 180        drm->mode_config.helper_private = &mtk_drm_mode_config_helpers;
 181
 182        ret = component_bind_all(drm->dev, drm);
 183        if (ret)
 184                goto put_mutex_dev;
 185
 186        /*
 187         * We currently support two fixed data streams, each optional,
 188         * and each statically assigned to a crtc:
 189         * OVL0 -> COLOR0 -> AAL -> OD -> RDMA0 -> UFOE -> DSI0 ...
 190         */
 191        ret = mtk_drm_crtc_create(drm, private->data->main_path,
 192                                  private->data->main_len);
 193        if (ret < 0)
 194                goto err_component_unbind;
 195        /* ... and OVL1 -> COLOR1 -> GAMMA -> RDMA1 -> DPI0. */
 196        ret = mtk_drm_crtc_create(drm, private->data->ext_path,
 197                                  private->data->ext_len);
 198        if (ret < 0)
 199                goto err_component_unbind;
 200
 201        ret = mtk_drm_crtc_create(drm, private->data->third_path,
 202                                  private->data->third_len);
 203        if (ret < 0)
 204                goto err_component_unbind;
 205
 206        /* Use OVL device for all DMA memory allocations */
 207        np = private->comp_node[private->data->main_path[0]] ?:
 208             private->comp_node[private->data->ext_path[0]];
 209        pdev = of_find_device_by_node(np);
 210        if (!pdev) {
 211                ret = -ENODEV;
 212                dev_err(drm->dev, "Need at least one OVL device\n");
 213                goto err_component_unbind;
 214        }
 215
 216        dma_dev = &pdev->dev;
 217        private->dma_dev = dma_dev;
 218
 219        /*
 220         * Configure the DMA segment size to make sure we get contiguous IOVA
 221         * when importing PRIME buffers.
 222         */
 223        if (!dma_dev->dma_parms) {
 224                private->dma_parms_allocated = true;
 225                dma_dev->dma_parms =
 226                        devm_kzalloc(drm->dev, sizeof(*dma_dev->dma_parms),
 227                                     GFP_KERNEL);
 228        }
 229        if (!dma_dev->dma_parms) {
 230                ret = -ENOMEM;
 231                goto put_dma_dev;
 232        }
 233
 234        ret = dma_set_max_seg_size(dma_dev, (unsigned int)DMA_BIT_MASK(32));
 235        if (ret) {
 236                dev_err(dma_dev, "Failed to set DMA segment size\n");
 237                goto err_unset_dma_parms;
 238        }
 239
 240        /*
 241         * We don't use the drm_irq_install() helpers provided by the DRM
 242         * core, so we need to set this manually in order to allow the
 243         * DRM_IOCTL_WAIT_VBLANK to operate correctly.
 244         */
 245        drm->irq_enabled = true;
 246        ret = drm_vblank_init(drm, MAX_CRTC);
 247        if (ret < 0)
 248                goto err_unset_dma_parms;
 249
 250        drm_kms_helper_poll_init(drm);
 251        drm_mode_config_reset(drm);
 252
 253        return 0;
 254
 255err_unset_dma_parms:
 256        if (private->dma_parms_allocated)
 257                dma_dev->dma_parms = NULL;
 258put_dma_dev:
 259        put_device(private->dma_dev);
 260err_component_unbind:
 261        component_unbind_all(drm->dev, drm);
 262put_mutex_dev:
 263        put_device(private->mutex_dev);
 264        return ret;
 265}
 266
 267static void mtk_drm_kms_deinit(struct drm_device *drm)
 268{
 269        struct mtk_drm_private *private = drm->dev_private;
 270
 271        drm_kms_helper_poll_fini(drm);
 272        drm_atomic_helper_shutdown(drm);
 273
 274        if (private->dma_parms_allocated)
 275                private->dma_dev->dma_parms = NULL;
 276
 277        component_unbind_all(drm->dev, drm);
 278}
 279
 280static const struct file_operations mtk_drm_fops = {
 281        .owner = THIS_MODULE,
 282        .open = drm_open,
 283        .release = drm_release,
 284        .unlocked_ioctl = drm_ioctl,
 285        .mmap = mtk_drm_gem_mmap,
 286        .poll = drm_poll,
 287        .read = drm_read,
 288        .compat_ioctl = drm_compat_ioctl,
 289};
 290
 291/*
 292 * We need to override this because the device used to import the memory is
 293 * not dev->dev, as drm_gem_prime_import() expects.
 294 */
 295struct drm_gem_object *mtk_drm_gem_prime_import(struct drm_device *dev,
 296                                                struct dma_buf *dma_buf)
 297{
 298        struct mtk_drm_private *private = dev->dev_private;
 299
 300        return drm_gem_prime_import_dev(dev, dma_buf, private->dma_dev);
 301}
 302
 303static struct drm_driver mtk_drm_driver = {
 304        .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
 305
 306        .gem_free_object_unlocked = mtk_drm_gem_free_object,
 307        .gem_vm_ops = &drm_gem_cma_vm_ops,
 308        .dumb_create = mtk_drm_gem_dumb_create,
 309
 310        .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
 311        .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
 312        .gem_prime_import = mtk_drm_gem_prime_import,
 313        .gem_prime_get_sg_table = mtk_gem_prime_get_sg_table,
 314        .gem_prime_import_sg_table = mtk_gem_prime_import_sg_table,
 315        .gem_prime_mmap = mtk_drm_gem_mmap_buf,
 316        .gem_prime_vmap = mtk_drm_gem_prime_vmap,
 317        .gem_prime_vunmap = mtk_drm_gem_prime_vunmap,
 318        .fops = &mtk_drm_fops,
 319
 320        .name = DRIVER_NAME,
 321        .desc = DRIVER_DESC,
 322        .date = DRIVER_DATE,
 323        .major = DRIVER_MAJOR,
 324        .minor = DRIVER_MINOR,
 325};
 326
 327static int compare_of(struct device *dev, void *data)
 328{
 329        return dev->of_node == data;
 330}
 331
 332static int mtk_drm_bind(struct device *dev)
 333{
 334        struct mtk_drm_private *private = dev_get_drvdata(dev);
 335        struct drm_device *drm;
 336        int ret;
 337
 338        drm = drm_dev_alloc(&mtk_drm_driver, dev);
 339        if (IS_ERR(drm))
 340                return PTR_ERR(drm);
 341
 342        drm->dev_private = private;
 343        private->drm = drm;
 344
 345        ret = mtk_drm_kms_init(drm);
 346        if (ret < 0)
 347                goto err_free;
 348
 349        ret = drm_dev_register(drm, 0);
 350        if (ret < 0)
 351                goto err_deinit;
 352
 353        drm_fbdev_generic_setup(drm, 32);
 354
 355        return 0;
 356
 357err_deinit:
 358        mtk_drm_kms_deinit(drm);
 359err_free:
 360        drm_dev_put(drm);
 361        return ret;
 362}
 363
 364static void mtk_drm_unbind(struct device *dev)
 365{
 366        struct mtk_drm_private *private = dev_get_drvdata(dev);
 367
 368        drm_dev_unregister(private->drm);
 369        mtk_drm_kms_deinit(private->drm);
 370        drm_dev_put(private->drm);
 371        private->num_pipes = 0;
 372        private->drm = NULL;
 373}
 374
 375static const struct component_master_ops mtk_drm_ops = {
 376        .bind           = mtk_drm_bind,
 377        .unbind         = mtk_drm_unbind,
 378};
 379
 380static const struct of_device_id mtk_ddp_comp_dt_ids[] = {
 381        { .compatible = "mediatek,mt2701-disp-ovl",
 382          .data = (void *)MTK_DISP_OVL },
 383        { .compatible = "mediatek,mt8173-disp-ovl",
 384          .data = (void *)MTK_DISP_OVL },
 385        { .compatible = "mediatek,mt2701-disp-rdma",
 386          .data = (void *)MTK_DISP_RDMA },
 387        { .compatible = "mediatek,mt8173-disp-rdma",
 388          .data = (void *)MTK_DISP_RDMA },
 389        { .compatible = "mediatek,mt8173-disp-wdma",
 390          .data = (void *)MTK_DISP_WDMA },
 391        { .compatible = "mediatek,mt2701-disp-color",
 392          .data = (void *)MTK_DISP_COLOR },
 393        { .compatible = "mediatek,mt8173-disp-color",
 394          .data = (void *)MTK_DISP_COLOR },
 395        { .compatible = "mediatek,mt8173-disp-aal",
 396          .data = (void *)MTK_DISP_AAL},
 397        { .compatible = "mediatek,mt8173-disp-gamma",
 398          .data = (void *)MTK_DISP_GAMMA, },
 399        { .compatible = "mediatek,mt8173-disp-ufoe",
 400          .data = (void *)MTK_DISP_UFOE },
 401        { .compatible = "mediatek,mt2701-dsi",
 402          .data = (void *)MTK_DSI },
 403        { .compatible = "mediatek,mt8173-dsi",
 404          .data = (void *)MTK_DSI },
 405        { .compatible = "mediatek,mt2701-dpi",
 406          .data = (void *)MTK_DPI },
 407        { .compatible = "mediatek,mt8173-dpi",
 408          .data = (void *)MTK_DPI },
 409        { .compatible = "mediatek,mt2701-disp-mutex",
 410          .data = (void *)MTK_DISP_MUTEX },
 411        { .compatible = "mediatek,mt2712-disp-mutex",
 412          .data = (void *)MTK_DISP_MUTEX },
 413        { .compatible = "mediatek,mt8173-disp-mutex",
 414          .data = (void *)MTK_DISP_MUTEX },
 415        { .compatible = "mediatek,mt2701-disp-pwm",
 416          .data = (void *)MTK_DISP_BLS },
 417        { .compatible = "mediatek,mt8173-disp-pwm",
 418          .data = (void *)MTK_DISP_PWM },
 419        { .compatible = "mediatek,mt8173-disp-od",
 420          .data = (void *)MTK_DISP_OD },
 421        { }
 422};
 423
 424static const struct of_device_id mtk_drm_of_ids[] = {
 425        { .compatible = "mediatek,mt2701-mmsys",
 426          .data = &mt2701_mmsys_driver_data},
 427        { .compatible = "mediatek,mt2712-mmsys",
 428          .data = &mt2712_mmsys_driver_data},
 429        { .compatible = "mediatek,mt8173-mmsys",
 430          .data = &mt8173_mmsys_driver_data},
 431        { }
 432};
 433
 434static int mtk_drm_probe(struct platform_device *pdev)
 435{
 436        struct device *dev = &pdev->dev;
 437        struct device_node *phandle = dev->parent->of_node;
 438        const struct of_device_id *of_id;
 439        struct mtk_drm_private *private;
 440        struct device_node *node;
 441        struct component_match *match = NULL;
 442        int ret;
 443        int i;
 444
 445        private = devm_kzalloc(dev, sizeof(*private), GFP_KERNEL);
 446        if (!private)
 447                return -ENOMEM;
 448
 449        private->mmsys_dev = dev->parent;
 450        if (!private->mmsys_dev) {
 451                dev_err(dev, "Failed to get MMSYS device\n");
 452                return -ENODEV;
 453        }
 454
 455        of_id = of_match_node(mtk_drm_of_ids, phandle);
 456        if (!of_id)
 457                return -ENODEV;
 458
 459        private->data = of_id->data;
 460
 461        /* Iterate over sibling DISP function blocks */
 462        for_each_child_of_node(phandle->parent, node) {
 463                const struct of_device_id *of_id;
 464                enum mtk_ddp_comp_type comp_type;
 465                int comp_id;
 466
 467                of_id = of_match_node(mtk_ddp_comp_dt_ids, node);
 468                if (!of_id)
 469                        continue;
 470
 471                if (!of_device_is_available(node)) {
 472                        dev_dbg(dev, "Skipping disabled component %pOF\n",
 473                                node);
 474                        continue;
 475                }
 476
 477                comp_type = (enum mtk_ddp_comp_type)of_id->data;
 478
 479                if (comp_type == MTK_DISP_MUTEX) {
 480                        private->mutex_node = of_node_get(node);
 481                        continue;
 482                }
 483
 484                comp_id = mtk_ddp_comp_get_id(node, comp_type);
 485                if (comp_id < 0) {
 486                        dev_warn(dev, "Skipping unknown component %pOF\n",
 487                                 node);
 488                        continue;
 489                }
 490
 491                private->comp_node[comp_id] = of_node_get(node);
 492
 493                /*
 494                 * Currently only the COLOR, OVL, RDMA, DSI, and DPI blocks have
 495                 * separate component platform drivers and initialize their own
 496                 * DDP component structure. The others are initialized here.
 497                 */
 498                if (comp_type == MTK_DISP_COLOR ||
 499                    comp_type == MTK_DISP_OVL ||
 500                    comp_type == MTK_DISP_OVL_2L ||
 501                    comp_type == MTK_DISP_RDMA ||
 502                    comp_type == MTK_DSI ||
 503                    comp_type == MTK_DPI) {
 504                        dev_info(dev, "Adding component match for %pOF\n",
 505                                 node);
 506                        drm_of_component_match_add(dev, &match, compare_of,
 507                                                   node);
 508                } else {
 509                        struct mtk_ddp_comp *comp;
 510
 511                        comp = devm_kzalloc(dev, sizeof(*comp), GFP_KERNEL);
 512                        if (!comp) {
 513                                ret = -ENOMEM;
 514                                of_node_put(node);
 515                                goto err_node;
 516                        }
 517
 518                        ret = mtk_ddp_comp_init(dev->parent, node, comp,
 519                                                comp_id, NULL);
 520                        if (ret) {
 521                                of_node_put(node);
 522                                goto err_node;
 523                        }
 524
 525                        private->ddp_comp[comp_id] = comp;
 526                }
 527        }
 528
 529        if (!private->mutex_node) {
 530                dev_err(dev, "Failed to find disp-mutex node\n");
 531                ret = -ENODEV;
 532                goto err_node;
 533        }
 534
 535        pm_runtime_enable(dev);
 536
 537        platform_set_drvdata(pdev, private);
 538
 539        ret = component_master_add_with_match(dev, &mtk_drm_ops, match);
 540        if (ret)
 541                goto err_pm;
 542
 543        return 0;
 544
 545err_pm:
 546        pm_runtime_disable(dev);
 547err_node:
 548        of_node_put(private->mutex_node);
 549        for (i = 0; i < DDP_COMPONENT_ID_MAX; i++) {
 550                of_node_put(private->comp_node[i]);
 551                if (private->ddp_comp[i]) {
 552                        put_device(private->ddp_comp[i]->larb_dev);
 553                        private->ddp_comp[i] = NULL;
 554                }
 555        }
 556        return ret;
 557}
 558
 559static int mtk_drm_remove(struct platform_device *pdev)
 560{
 561        struct mtk_drm_private *private = platform_get_drvdata(pdev);
 562        int i;
 563
 564        component_master_del(&pdev->dev, &mtk_drm_ops);
 565        pm_runtime_disable(&pdev->dev);
 566        of_node_put(private->mutex_node);
 567        for (i = 0; i < DDP_COMPONENT_ID_MAX; i++)
 568                of_node_put(private->comp_node[i]);
 569
 570        return 0;
 571}
 572
 573#ifdef CONFIG_PM_SLEEP
 574static int mtk_drm_sys_suspend(struct device *dev)
 575{
 576        struct mtk_drm_private *private = dev_get_drvdata(dev);
 577        struct drm_device *drm = private->drm;
 578        int ret;
 579
 580        ret = drm_mode_config_helper_suspend(drm);
 581
 582        return ret;
 583}
 584
 585static int mtk_drm_sys_resume(struct device *dev)
 586{
 587        struct mtk_drm_private *private = dev_get_drvdata(dev);
 588        struct drm_device *drm = private->drm;
 589        int ret;
 590
 591        ret = drm_mode_config_helper_resume(drm);
 592
 593        return ret;
 594}
 595#endif
 596
 597static SIMPLE_DEV_PM_OPS(mtk_drm_pm_ops, mtk_drm_sys_suspend,
 598                         mtk_drm_sys_resume);
 599
 600static struct platform_driver mtk_drm_platform_driver = {
 601        .probe  = mtk_drm_probe,
 602        .remove = mtk_drm_remove,
 603        .driver = {
 604                .name   = "mediatek-drm",
 605                .pm     = &mtk_drm_pm_ops,
 606        },
 607};
 608
 609static struct platform_driver * const mtk_drm_drivers[] = {
 610        &mtk_ddp_driver,
 611        &mtk_disp_color_driver,
 612        &mtk_disp_ovl_driver,
 613        &mtk_disp_rdma_driver,
 614        &mtk_dpi_driver,
 615        &mtk_drm_platform_driver,
 616        &mtk_mipi_tx_driver,
 617        &mtk_dsi_driver,
 618};
 619
 620static int __init mtk_drm_init(void)
 621{
 622        return platform_register_drivers(mtk_drm_drivers,
 623                                         ARRAY_SIZE(mtk_drm_drivers));
 624}
 625
 626static void __exit mtk_drm_exit(void)
 627{
 628        platform_unregister_drivers(mtk_drm_drivers,
 629                                    ARRAY_SIZE(mtk_drm_drivers));
 630}
 631
 632module_init(mtk_drm_init);
 633module_exit(mtk_drm_exit);
 634
 635MODULE_AUTHOR("YT SHEN <yt.shen@mediatek.com>");
 636MODULE_DESCRIPTION("Mediatek SoC DRM driver");
 637MODULE_LICENSE("GPL v2");
 638