1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * rcar_du_drv.c -- R-Car Display Unit DRM driver 4 * 5 * Copyright (C) 2013-2015 Renesas Electronics Corporation 6 * 7 * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) 8 */ 9 10#include <linux/clk.h> 11#include <linux/io.h> 12#include <linux/mm.h> 13#include <linux/module.h> 14#include <linux/of_device.h> 15#include <linux/platform_device.h> 16#include <linux/pm.h> 17#include <linux/slab.h> 18#include <linux/wait.h> 19 20#include <drm/drm_atomic_helper.h> 21#include <drm/drm_fb_cma_helper.h> 22#include <drm/drm_fb_helper.h> 23#include <drm/drm_drv.h> 24#include <drm/drm_gem_cma_helper.h> 25#include <drm/drm_probe_helper.h> 26 27#include "rcar_du_drv.h" 28#include "rcar_du_kms.h" 29#include "rcar_du_of.h" 30#include "rcar_du_regs.h" 31 32/* ----------------------------------------------------------------------------- 33 * Device Information 34 */ 35 36static const struct rcar_du_device_info rzg1_du_r8a7743_info = { 37 .gen = 2, 38 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 39 | RCAR_DU_FEATURE_INTERLACED 40 | RCAR_DU_FEATURE_TVM_SYNC, 41 .channels_mask = BIT(1) | BIT(0), 42 .routes = { 43 /* 44 * R8A774[34] has one RGB output and one LVDS output 45 */ 46 [RCAR_DU_OUTPUT_DPAD0] = { 47 .possible_crtcs = BIT(1) | BIT(0), 48 .port = 0, 49 }, 50 [RCAR_DU_OUTPUT_LVDS0] = { 51 .possible_crtcs = BIT(0), 52 .port = 1, 53 }, 54 }, 55 .num_lvds = 1, 56}; 57 58static const struct rcar_du_device_info rzg1_du_r8a7745_info = { 59 .gen = 2, 60 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 61 | RCAR_DU_FEATURE_INTERLACED 62 | RCAR_DU_FEATURE_TVM_SYNC, 63 .channels_mask = BIT(1) | BIT(0), 64 .routes = { 65 /* 66 * R8A7745 has two RGB outputs 67 */ 68 [RCAR_DU_OUTPUT_DPAD0] = { 69 .possible_crtcs = BIT(0), 70 .port = 0, 71 }, 72 [RCAR_DU_OUTPUT_DPAD1] = { 73 .possible_crtcs = BIT(1), 74 .port = 1, 75 }, 76 }, 77}; 78 79static const struct rcar_du_device_info rzg1_du_r8a77470_info = { 80 .gen = 2, 81 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 82 | RCAR_DU_FEATURE_INTERLACED 83 | RCAR_DU_FEATURE_TVM_SYNC, 84 .channels_mask = BIT(1) | BIT(0), 85 .routes = { 86 /* 87 * R8A77470 has two RGB outputs, one LVDS output, and 88 * one (currently unsupported) analog video output 89 */ 90 [RCAR_DU_OUTPUT_DPAD0] = { 91 .possible_crtcs = BIT(0), 92 .port = 0, 93 }, 94 [RCAR_DU_OUTPUT_DPAD1] = { 95 .possible_crtcs = BIT(1), 96 .port = 1, 97 }, 98 [RCAR_DU_OUTPUT_LVDS0] = { 99 .possible_crtcs = BIT(0) | BIT(1), 100 .port = 2, 101 }, 102 }, 103}; 104 105static const struct rcar_du_device_info rcar_du_r8a774a1_info = { 106 .gen = 3, 107 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 108 | RCAR_DU_FEATURE_VSP1_SOURCE 109 | RCAR_DU_FEATURE_INTERLACED 110 | RCAR_DU_FEATURE_TVM_SYNC, 111 .channels_mask = BIT(2) | BIT(1) | BIT(0), 112 .routes = { 113 /* 114 * R8A774A1 has one RGB output, one LVDS output and one HDMI 115 * output. 116 */ 117 [RCAR_DU_OUTPUT_DPAD0] = { 118 .possible_crtcs = BIT(2), 119 .port = 0, 120 }, 121 [RCAR_DU_OUTPUT_HDMI0] = { 122 .possible_crtcs = BIT(1), 123 .port = 1, 124 }, 125 [RCAR_DU_OUTPUT_LVDS0] = { 126 .possible_crtcs = BIT(0), 127 .port = 2, 128 }, 129 }, 130 .num_lvds = 1, 131 .dpll_mask = BIT(1), 132}; 133 134static const struct rcar_du_device_info rcar_du_r8a774b1_info = { 135 .gen = 3, 136 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 137 | RCAR_DU_FEATURE_VSP1_SOURCE 138 | RCAR_DU_FEATURE_INTERLACED 139 | RCAR_DU_FEATURE_TVM_SYNC, 140 .channels_mask = BIT(3) | BIT(1) | BIT(0), 141 .routes = { 142 /* 143 * R8A774B1 has one RGB output, one LVDS output and one HDMI 144 * output. 145 */ 146 [RCAR_DU_OUTPUT_DPAD0] = { 147 .possible_crtcs = BIT(2), 148 .port = 0, 149 }, 150 [RCAR_DU_OUTPUT_HDMI0] = { 151 .possible_crtcs = BIT(1), 152 .port = 1, 153 }, 154 [RCAR_DU_OUTPUT_LVDS0] = { 155 .possible_crtcs = BIT(0), 156 .port = 2, 157 }, 158 }, 159 .num_lvds = 1, 160 .dpll_mask = BIT(1), 161}; 162 163static const struct rcar_du_device_info rcar_du_r8a774c0_info = { 164 .gen = 3, 165 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 166 | RCAR_DU_FEATURE_VSP1_SOURCE, 167 .channels_mask = BIT(1) | BIT(0), 168 .routes = { 169 /* 170 * R8A774C0 has one RGB output and two LVDS outputs 171 */ 172 [RCAR_DU_OUTPUT_DPAD0] = { 173 .possible_crtcs = BIT(0) | BIT(1), 174 .port = 0, 175 }, 176 [RCAR_DU_OUTPUT_LVDS0] = { 177 .possible_crtcs = BIT(0), 178 .port = 1, 179 }, 180 [RCAR_DU_OUTPUT_LVDS1] = { 181 .possible_crtcs = BIT(1), 182 .port = 2, 183 }, 184 }, 185 .num_lvds = 2, 186 .lvds_clk_mask = BIT(1) | BIT(0), 187}; 188 189static const struct rcar_du_device_info rcar_du_r8a774e1_info = { 190 .gen = 3, 191 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 192 | RCAR_DU_FEATURE_VSP1_SOURCE 193 | RCAR_DU_FEATURE_INTERLACED 194 | RCAR_DU_FEATURE_TVM_SYNC, 195 .channels_mask = BIT(3) | BIT(1) | BIT(0), 196 .routes = { 197 /* 198 * R8A774E1 has one RGB output, one LVDS output and one HDMI 199 * output. 200 */ 201 [RCAR_DU_OUTPUT_DPAD0] = { 202 .possible_crtcs = BIT(2), 203 .port = 0, 204 }, 205 [RCAR_DU_OUTPUT_HDMI0] = { 206 .possible_crtcs = BIT(1), 207 .port = 1, 208 }, 209 [RCAR_DU_OUTPUT_LVDS0] = { 210 .possible_crtcs = BIT(0), 211 .port = 2, 212 }, 213 }, 214 .num_lvds = 1, 215 .dpll_mask = BIT(1), 216}; 217 218static const struct rcar_du_device_info rcar_du_r8a7779_info = { 219 .gen = 1, 220 .features = RCAR_DU_FEATURE_INTERLACED 221 | RCAR_DU_FEATURE_TVM_SYNC, 222 .channels_mask = BIT(1) | BIT(0), 223 .routes = { 224 /* 225 * R8A7779 has two RGB outputs and one (currently unsupported) 226 * TCON output. 227 */ 228 [RCAR_DU_OUTPUT_DPAD0] = { 229 .possible_crtcs = BIT(0), 230 .port = 0, 231 }, 232 [RCAR_DU_OUTPUT_DPAD1] = { 233 .possible_crtcs = BIT(1) | BIT(0), 234 .port = 1, 235 }, 236 }, 237}; 238 239static const struct rcar_du_device_info rcar_du_r8a7790_info = { 240 .gen = 2, 241 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 242 | RCAR_DU_FEATURE_INTERLACED 243 | RCAR_DU_FEATURE_TVM_SYNC, 244 .quirks = RCAR_DU_QUIRK_ALIGN_128B, 245 .channels_mask = BIT(2) | BIT(1) | BIT(0), 246 .routes = { 247 /* 248 * R8A7742 and R8A7790 each have one RGB output and two LVDS 249 * outputs. Additionally R8A7790 supports one TCON output 250 * (currently unsupported by the driver). 251 */ 252 [RCAR_DU_OUTPUT_DPAD0] = { 253 .possible_crtcs = BIT(2) | BIT(1) | BIT(0), 254 .port = 0, 255 }, 256 [RCAR_DU_OUTPUT_LVDS0] = { 257 .possible_crtcs = BIT(0), 258 .port = 1, 259 }, 260 [RCAR_DU_OUTPUT_LVDS1] = { 261 .possible_crtcs = BIT(2) | BIT(1), 262 .port = 2, 263 }, 264 }, 265 .num_lvds = 2, 266}; 267 268/* M2-W (r8a7791) and M2-N (r8a7793) are identical */ 269static const struct rcar_du_device_info rcar_du_r8a7791_info = { 270 .gen = 2, 271 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 272 | RCAR_DU_FEATURE_INTERLACED 273 | RCAR_DU_FEATURE_TVM_SYNC, 274 .channels_mask = BIT(1) | BIT(0), 275 .routes = { 276 /* 277 * R8A779[13] has one RGB output, one LVDS output and one 278 * (currently unsupported) TCON output. 279 */ 280 [RCAR_DU_OUTPUT_DPAD0] = { 281 .possible_crtcs = BIT(1) | BIT(0), 282 .port = 0, 283 }, 284 [RCAR_DU_OUTPUT_LVDS0] = { 285 .possible_crtcs = BIT(0), 286 .port = 1, 287 }, 288 }, 289 .num_lvds = 1, 290}; 291 292static const struct rcar_du_device_info rcar_du_r8a7792_info = { 293 .gen = 2, 294 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 295 | RCAR_DU_FEATURE_INTERLACED 296 | RCAR_DU_FEATURE_TVM_SYNC, 297 .channels_mask = BIT(1) | BIT(0), 298 .routes = { 299 /* R8A7792 has two RGB outputs. */ 300 [RCAR_DU_OUTPUT_DPAD0] = { 301 .possible_crtcs = BIT(0), 302 .port = 0, 303 }, 304 [RCAR_DU_OUTPUT_DPAD1] = { 305 .possible_crtcs = BIT(1), 306 .port = 1, 307 }, 308 }, 309}; 310 311static const struct rcar_du_device_info rcar_du_r8a7794_info = { 312 .gen = 2, 313 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 314 | RCAR_DU_FEATURE_INTERLACED 315 | RCAR_DU_FEATURE_TVM_SYNC, 316 .channels_mask = BIT(1) | BIT(0), 317 .routes = { 318 /* 319 * R8A7794 has two RGB outputs and one (currently unsupported) 320 * TCON output. 321 */ 322 [RCAR_DU_OUTPUT_DPAD0] = { 323 .possible_crtcs = BIT(0), 324 .port = 0, 325 }, 326 [RCAR_DU_OUTPUT_DPAD1] = { 327 .possible_crtcs = BIT(1), 328 .port = 1, 329 }, 330 }, 331}; 332 333static const struct rcar_du_device_info rcar_du_r8a7795_info = { 334 .gen = 3, 335 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 336 | RCAR_DU_FEATURE_VSP1_SOURCE 337 | RCAR_DU_FEATURE_INTERLACED 338 | RCAR_DU_FEATURE_TVM_SYNC, 339 .channels_mask = BIT(3) | BIT(2) | BIT(1) | BIT(0), 340 .routes = { 341 /* 342 * R8A7795 has one RGB output, two HDMI outputs and one 343 * LVDS output. 344 */ 345 [RCAR_DU_OUTPUT_DPAD0] = { 346 .possible_crtcs = BIT(3), 347 .port = 0, 348 }, 349 [RCAR_DU_OUTPUT_HDMI0] = { 350 .possible_crtcs = BIT(1), 351 .port = 1, 352 }, 353 [RCAR_DU_OUTPUT_HDMI1] = { 354 .possible_crtcs = BIT(2), 355 .port = 2, 356 }, 357 [RCAR_DU_OUTPUT_LVDS0] = { 358 .possible_crtcs = BIT(0), 359 .port = 3, 360 }, 361 }, 362 .num_lvds = 1, 363 .dpll_mask = BIT(2) | BIT(1), 364}; 365 366static const struct rcar_du_device_info rcar_du_r8a7796_info = { 367 .gen = 3, 368 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 369 | RCAR_DU_FEATURE_VSP1_SOURCE 370 | RCAR_DU_FEATURE_INTERLACED 371 | RCAR_DU_FEATURE_TVM_SYNC, 372 .channels_mask = BIT(2) | BIT(1) | BIT(0), 373 .routes = { 374 /* 375 * R8A7796 has one RGB output, one LVDS output and one HDMI 376 * output. 377 */ 378 [RCAR_DU_OUTPUT_DPAD0] = { 379 .possible_crtcs = BIT(2), 380 .port = 0, 381 }, 382 [RCAR_DU_OUTPUT_HDMI0] = { 383 .possible_crtcs = BIT(1), 384 .port = 1, 385 }, 386 [RCAR_DU_OUTPUT_LVDS0] = { 387 .possible_crtcs = BIT(0), 388 .port = 2, 389 }, 390 }, 391 .num_lvds = 1, 392 .dpll_mask = BIT(1), 393}; 394 395static const struct rcar_du_device_info rcar_du_r8a77965_info = { 396 .gen = 3, 397 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 398 | RCAR_DU_FEATURE_VSP1_SOURCE 399 | RCAR_DU_FEATURE_INTERLACED 400 | RCAR_DU_FEATURE_TVM_SYNC, 401 .channels_mask = BIT(3) | BIT(1) | BIT(0), 402 .routes = { 403 /* 404 * R8A77965 has one RGB output, one LVDS output and one HDMI 405 * output. 406 */ 407 [RCAR_DU_OUTPUT_DPAD0] = { 408 .possible_crtcs = BIT(2), 409 .port = 0, 410 }, 411 [RCAR_DU_OUTPUT_HDMI0] = { 412 .possible_crtcs = BIT(1), 413 .port = 1, 414 }, 415 [RCAR_DU_OUTPUT_LVDS0] = { 416 .possible_crtcs = BIT(0), 417 .port = 2, 418 }, 419 }, 420 .num_lvds = 1, 421 .dpll_mask = BIT(1), 422}; 423 424static const struct rcar_du_device_info rcar_du_r8a77970_info = { 425 .gen = 3, 426 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 427 | RCAR_DU_FEATURE_VSP1_SOURCE 428 | RCAR_DU_FEATURE_INTERLACED 429 | RCAR_DU_FEATURE_TVM_SYNC, 430 .channels_mask = BIT(0), 431 .routes = { 432 /* 433 * R8A77970 and R8A77980 have one RGB output and one LVDS 434 * output. 435 */ 436 [RCAR_DU_OUTPUT_DPAD0] = { 437 .possible_crtcs = BIT(0), 438 .port = 0, 439 }, 440 [RCAR_DU_OUTPUT_LVDS0] = { 441 .possible_crtcs = BIT(0), 442 .port = 1, 443 }, 444 }, 445 .num_lvds = 1, 446}; 447 448static const struct rcar_du_device_info rcar_du_r8a7799x_info = { 449 .gen = 3, 450 .features = RCAR_DU_FEATURE_CRTC_IRQ_CLOCK 451 | RCAR_DU_FEATURE_VSP1_SOURCE, 452 .channels_mask = BIT(1) | BIT(0), 453 .routes = { 454 /* 455 * R8A77990 and R8A77995 have one RGB output and two LVDS 456 * outputs. 457 */ 458 [RCAR_DU_OUTPUT_DPAD0] = { 459 .possible_crtcs = BIT(0) | BIT(1), 460 .port = 0, 461 }, 462 [RCAR_DU_OUTPUT_LVDS0] = { 463 .possible_crtcs = BIT(0), 464 .port = 1, 465 }, 466 [RCAR_DU_OUTPUT_LVDS1] = { 467 .possible_crtcs = BIT(1), 468 .port = 2, 469 }, 470 }, 471 .num_lvds = 2, 472 .lvds_clk_mask = BIT(1) | BIT(0), 473}; 474 475static const struct of_device_id rcar_du_of_table[] = { 476 { .compatible = "renesas,du-r8a7742", .data = &rcar_du_r8a7790_info }, 477 { .compatible = "renesas,du-r8a7743", .data = &rzg1_du_r8a7743_info }, 478 { .compatible = "renesas,du-r8a7744", .data = &rzg1_du_r8a7743_info }, 479 { .compatible = "renesas,du-r8a7745", .data = &rzg1_du_r8a7745_info }, 480 { .compatible = "renesas,du-r8a77470", .data = &rzg1_du_r8a77470_info }, 481 { .compatible = "renesas,du-r8a774a1", .data = &rcar_du_r8a774a1_info }, 482 { .compatible = "renesas,du-r8a774b1", .data = &rcar_du_r8a774b1_info }, 483 { .compatible = "renesas,du-r8a774c0", .data = &rcar_du_r8a774c0_info }, 484 { .compatible = "renesas,du-r8a774e1", .data = &rcar_du_r8a774e1_info }, 485 { .compatible = "renesas,du-r8a7779", .data = &rcar_du_r8a7779_info }, 486 { .compatible = "renesas,du-r8a7790", .data = &rcar_du_r8a7790_info }, 487 { .compatible = "renesas,du-r8a7791", .data = &rcar_du_r8a7791_info }, 488 { .compatible = "renesas,du-r8a7792", .data = &rcar_du_r8a7792_info }, 489 { .compatible = "renesas,du-r8a7793", .data = &rcar_du_r8a7791_info }, 490 { .compatible = "renesas,du-r8a7794", .data = &rcar_du_r8a7794_info }, 491 { .compatible = "renesas,du-r8a7795", .data = &rcar_du_r8a7795_info }, 492 { .compatible = "renesas,du-r8a7796", .data = &rcar_du_r8a7796_info }, 493 { .compatible = "renesas,du-r8a77961", .data = &rcar_du_r8a7796_info }, 494 { .compatible = "renesas,du-r8a77965", .data = &rcar_du_r8a77965_info }, 495 { .compatible = "renesas,du-r8a77970", .data = &rcar_du_r8a77970_info }, 496 { .compatible = "renesas,du-r8a77980", .data = &rcar_du_r8a77970_info }, 497 { .compatible = "renesas,du-r8a77990", .data = &rcar_du_r8a7799x_info }, 498 { .compatible = "renesas,du-r8a77995", .data = &rcar_du_r8a7799x_info }, 499 { } 500}; 501 502MODULE_DEVICE_TABLE(of, rcar_du_of_table); 503 504/* ----------------------------------------------------------------------------- 505 * DRM operations 506 */ 507 508DEFINE_DRM_GEM_CMA_FOPS(rcar_du_fops); 509 510static struct drm_driver rcar_du_driver = { 511 .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, 512 DRM_GEM_CMA_DRIVER_OPS_WITH_DUMB_CREATE(rcar_du_dumb_create), 513 .fops = &rcar_du_fops, 514 .name = "rcar-du", 515 .desc = "Renesas R-Car Display Unit", 516 .date = "20130110", 517 .major = 1, 518 .minor = 0, 519}; 520 521/* ----------------------------------------------------------------------------- 522 * Power management 523 */ 524 525#ifdef CONFIG_PM_SLEEP 526static int rcar_du_pm_suspend(struct device *dev) 527{ 528 struct rcar_du_device *rcdu = dev_get_drvdata(dev); 529 530 return drm_mode_config_helper_suspend(rcdu->ddev); 531} 532 533static int rcar_du_pm_resume(struct device *dev) 534{ 535 struct rcar_du_device *rcdu = dev_get_drvdata(dev); 536 537 return drm_mode_config_helper_resume(rcdu->ddev); 538} 539#endif 540 541static const struct dev_pm_ops rcar_du_pm_ops = { 542 SET_SYSTEM_SLEEP_PM_OPS(rcar_du_pm_suspend, rcar_du_pm_resume) 543}; 544 545/* ----------------------------------------------------------------------------- 546 * Platform driver 547 */ 548 549static int rcar_du_remove(struct platform_device *pdev) 550{ 551 struct rcar_du_device *rcdu = platform_get_drvdata(pdev); 552 struct drm_device *ddev = rcdu->ddev; 553 554 drm_dev_unregister(ddev); 555 556 drm_kms_helper_poll_fini(ddev); 557 558 drm_dev_put(ddev); 559 560 return 0; 561} 562 563static int rcar_du_probe(struct platform_device *pdev) 564{ 565 struct rcar_du_device *rcdu; 566 struct drm_device *ddev; 567 struct resource *mem; 568 int ret; 569 570 /* Allocate and initialize the R-Car device structure. */ 571 rcdu = devm_kzalloc(&pdev->dev, sizeof(*rcdu), GFP_KERNEL); 572 if (rcdu == NULL) 573 return -ENOMEM; 574 575 rcdu->dev = &pdev->dev; 576 rcdu->info = of_device_get_match_data(rcdu->dev); 577 578 platform_set_drvdata(pdev, rcdu); 579 580 /* I/O resources */ 581 mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 582 rcdu->mmio = devm_ioremap_resource(&pdev->dev, mem); 583 if (IS_ERR(rcdu->mmio)) 584 return PTR_ERR(rcdu->mmio); 585 586 /* DRM/KMS objects */ 587 ddev = drm_dev_alloc(&rcar_du_driver, &pdev->dev); 588 if (IS_ERR(ddev)) 589 return PTR_ERR(ddev); 590 591 rcdu->ddev = ddev; 592 ddev->dev_private = rcdu; 593 594 ret = rcar_du_modeset_init(rcdu); 595 if (ret < 0) { 596 if (ret != -EPROBE_DEFER) 597 dev_err(&pdev->dev, 598 "failed to initialize DRM/KMS (%d)\n", ret); 599 goto error; 600 } 601 602 ddev->irq_enabled = 1; 603 604 /* 605 * Register the DRM device with the core and the connectors with 606 * sysfs. 607 */ 608 ret = drm_dev_register(ddev, 0); 609 if (ret) 610 goto error; 611 612 DRM_INFO("Device %s probed\n", dev_name(&pdev->dev)); 613 614 drm_fbdev_generic_setup(ddev, 32); 615 616 return 0; 617 618error: 619 rcar_du_remove(pdev); 620 621 return ret; 622} 623 624static struct platform_driver rcar_du_platform_driver = { 625 .probe = rcar_du_probe, 626 .remove = rcar_du_remove, 627 .driver = { 628 .name = "rcar-du", 629 .pm = &rcar_du_pm_ops, 630 .of_match_table = rcar_du_of_table, 631 }, 632}; 633 634static int __init rcar_du_init(void) 635{ 636 rcar_du_of_init(rcar_du_of_table); 637 638 return platform_driver_register(&rcar_du_platform_driver); 639} 640module_init(rcar_du_init); 641 642static void __exit rcar_du_exit(void) 643{ 644 platform_driver_unregister(&rcar_du_platform_driver); 645} 646module_exit(rcar_du_exit); 647 648MODULE_AUTHOR("Laurent Pinchart <laurent.pinchart@ideasonboard.com>"); 649MODULE_DESCRIPTION("Renesas R-Car Display Unit DRM Driver"); 650MODULE_LICENSE("GPL"); 651