1 /* 2 * Copyright (C) 2014 Traphandler 3 * Copyright (C) 2014 Free Electrons 4 * Copyright (C) 2014 Atmel 5 * 6 * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> 7 * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License version 2 as published by 11 * the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22 #include <linux/clk.h> 23 #include <linux/irq.h> 24 #include <linux/irqchip.h> 25 #include <linux/module.h> 26 #include <linux/pm_runtime.h> 27 28 #include "atmel_hlcdc_dc.h" 29 30 #define ATMEL_HLCDC_LAYER_IRQS_OFFSET 8 31 32 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9n12_layers[] = { 33 { 34 .name = "base", 35 .formats = &atmel_hlcdc_plane_rgb_formats, 36 .regs_offset = 0x40, 37 .id = 0, 38 .type = ATMEL_HLCDC_BASE_LAYER, 39 .nconfigs = 5, 40 .layout = { 41 .xstride = { 2 }, 42 .default_color = 3, 43 .general_config = 4, 44 }, 45 }, 46 }; 47 48 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9n12 = { 49 .min_width = 0, 50 .min_height = 0, 51 .max_width = 1280, 52 .max_height = 860, 53 .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9n12_layers), 54 .layers = atmel_hlcdc_at91sam9n12_layers, 55 }; 56 57 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_at91sam9x5_layers[] = { 58 { 59 .name = "base", 60 .formats = &atmel_hlcdc_plane_rgb_formats, 61 .regs_offset = 0x40, 62 .id = 0, 63 .type = ATMEL_HLCDC_BASE_LAYER, 64 .nconfigs = 5, 65 .layout = { 66 .xstride = { 2 }, 67 .default_color = 3, 68 .general_config = 4, 69 .disc_pos = 5, 70 .disc_size = 6, 71 }, 72 }, 73 { 74 .name = "overlay1", 75 .formats = &atmel_hlcdc_plane_rgb_formats, 76 .regs_offset = 0x100, 77 .id = 1, 78 .type = ATMEL_HLCDC_OVERLAY_LAYER, 79 .nconfigs = 10, 80 .layout = { 81 .pos = 2, 82 .size = 3, 83 .xstride = { 4 }, 84 .pstride = { 5 }, 85 .default_color = 6, 86 .chroma_key = 7, 87 .chroma_key_mask = 8, 88 .general_config = 9, 89 }, 90 }, 91 { 92 .name = "high-end-overlay", 93 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, 94 .regs_offset = 0x280, 95 .id = 2, 96 .type = ATMEL_HLCDC_OVERLAY_LAYER, 97 .nconfigs = 17, 98 .layout = { 99 .pos = 2, 100 .size = 3, 101 .memsize = 4, 102 .xstride = { 5, 7 }, 103 .pstride = { 6, 8 }, 104 .default_color = 9, 105 .chroma_key = 10, 106 .chroma_key_mask = 11, 107 .general_config = 12, 108 .csc = 14, 109 }, 110 }, 111 { 112 .name = "cursor", 113 .formats = &atmel_hlcdc_plane_rgb_formats, 114 .regs_offset = 0x340, 115 .id = 3, 116 .type = ATMEL_HLCDC_CURSOR_LAYER, 117 .nconfigs = 10, 118 .max_width = 128, 119 .max_height = 128, 120 .layout = { 121 .pos = 2, 122 .size = 3, 123 .xstride = { 4 }, 124 .default_color = 6, 125 .chroma_key = 7, 126 .chroma_key_mask = 8, 127 .general_config = 9, 128 }, 129 }, 130 }; 131 132 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_at91sam9x5 = { 133 .min_width = 0, 134 .min_height = 0, 135 .max_width = 800, 136 .max_height = 600, 137 .nlayers = ARRAY_SIZE(atmel_hlcdc_at91sam9x5_layers), 138 .layers = atmel_hlcdc_at91sam9x5_layers, 139 }; 140 141 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d3_layers[] = { 142 { 143 .name = "base", 144 .formats = &atmel_hlcdc_plane_rgb_formats, 145 .regs_offset = 0x40, 146 .id = 0, 147 .type = ATMEL_HLCDC_BASE_LAYER, 148 .nconfigs = 7, 149 .layout = { 150 .xstride = { 2 }, 151 .default_color = 3, 152 .general_config = 4, 153 .disc_pos = 5, 154 .disc_size = 6, 155 }, 156 }, 157 { 158 .name = "overlay1", 159 .formats = &atmel_hlcdc_plane_rgb_formats, 160 .regs_offset = 0x140, 161 .id = 1, 162 .type = ATMEL_HLCDC_OVERLAY_LAYER, 163 .nconfigs = 10, 164 .layout = { 165 .pos = 2, 166 .size = 3, 167 .xstride = { 4 }, 168 .pstride = { 5 }, 169 .default_color = 6, 170 .chroma_key = 7, 171 .chroma_key_mask = 8, 172 .general_config = 9, 173 }, 174 }, 175 { 176 .name = "overlay2", 177 .formats = &atmel_hlcdc_plane_rgb_formats, 178 .regs_offset = 0x240, 179 .id = 2, 180 .type = ATMEL_HLCDC_OVERLAY_LAYER, 181 .nconfigs = 10, 182 .layout = { 183 .pos = 2, 184 .size = 3, 185 .xstride = { 4 }, 186 .pstride = { 5 }, 187 .default_color = 6, 188 .chroma_key = 7, 189 .chroma_key_mask = 8, 190 .general_config = 9, 191 }, 192 }, 193 { 194 .name = "high-end-overlay", 195 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, 196 .regs_offset = 0x340, 197 .id = 3, 198 .type = ATMEL_HLCDC_OVERLAY_LAYER, 199 .nconfigs = 42, 200 .layout = { 201 .pos = 2, 202 .size = 3, 203 .memsize = 4, 204 .xstride = { 5, 7 }, 205 .pstride = { 6, 8 }, 206 .default_color = 9, 207 .chroma_key = 10, 208 .chroma_key_mask = 11, 209 .general_config = 12, 210 .csc = 14, 211 }, 212 }, 213 { 214 .name = "cursor", 215 .formats = &atmel_hlcdc_plane_rgb_formats, 216 .regs_offset = 0x440, 217 .id = 4, 218 .type = ATMEL_HLCDC_CURSOR_LAYER, 219 .nconfigs = 10, 220 .max_width = 128, 221 .max_height = 128, 222 .layout = { 223 .pos = 2, 224 .size = 3, 225 .xstride = { 4 }, 226 .pstride = { 5 }, 227 .default_color = 6, 228 .chroma_key = 7, 229 .chroma_key_mask = 8, 230 .general_config = 9, 231 }, 232 }, 233 }; 234 235 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d3 = { 236 .min_width = 0, 237 .min_height = 0, 238 .max_width = 2048, 239 .max_height = 2048, 240 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d3_layers), 241 .layers = atmel_hlcdc_sama5d3_layers, 242 }; 243 244 static const struct atmel_hlcdc_layer_desc atmel_hlcdc_sama5d4_layers[] = { 245 { 246 .name = "base", 247 .formats = &atmel_hlcdc_plane_rgb_formats, 248 .regs_offset = 0x40, 249 .id = 0, 250 .type = ATMEL_HLCDC_BASE_LAYER, 251 .nconfigs = 7, 252 .layout = { 253 .xstride = { 2 }, 254 .default_color = 3, 255 .general_config = 4, 256 .disc_pos = 5, 257 .disc_size = 6, 258 }, 259 }, 260 { 261 .name = "overlay1", 262 .formats = &atmel_hlcdc_plane_rgb_formats, 263 .regs_offset = 0x140, 264 .id = 1, 265 .type = ATMEL_HLCDC_OVERLAY_LAYER, 266 .nconfigs = 10, 267 .layout = { 268 .pos = 2, 269 .size = 3, 270 .xstride = { 4 }, 271 .pstride = { 5 }, 272 .default_color = 6, 273 .chroma_key = 7, 274 .chroma_key_mask = 8, 275 .general_config = 9, 276 }, 277 }, 278 { 279 .name = "overlay2", 280 .formats = &atmel_hlcdc_plane_rgb_formats, 281 .regs_offset = 0x240, 282 .id = 2, 283 .type = ATMEL_HLCDC_OVERLAY_LAYER, 284 .nconfigs = 10, 285 .layout = { 286 .pos = 2, 287 .size = 3, 288 .xstride = { 4 }, 289 .pstride = { 5 }, 290 .default_color = 6, 291 .chroma_key = 7, 292 .chroma_key_mask = 8, 293 .general_config = 9, 294 }, 295 }, 296 { 297 .name = "high-end-overlay", 298 .formats = &atmel_hlcdc_plane_rgb_and_yuv_formats, 299 .regs_offset = 0x340, 300 .id = 3, 301 .type = ATMEL_HLCDC_OVERLAY_LAYER, 302 .nconfigs = 42, 303 .layout = { 304 .pos = 2, 305 .size = 3, 306 .memsize = 4, 307 .xstride = { 5, 7 }, 308 .pstride = { 6, 8 }, 309 .default_color = 9, 310 .chroma_key = 10, 311 .chroma_key_mask = 11, 312 .general_config = 12, 313 .csc = 14, 314 }, 315 }, 316 }; 317 318 static const struct atmel_hlcdc_dc_desc atmel_hlcdc_dc_sama5d4 = { 319 .min_width = 0, 320 .min_height = 0, 321 .max_width = 2048, 322 .max_height = 2048, 323 .nlayers = ARRAY_SIZE(atmel_hlcdc_sama5d4_layers), 324 .layers = atmel_hlcdc_sama5d4_layers, 325 }; 326 static const struct of_device_id atmel_hlcdc_of_match[] = { 327 { 328 .compatible = "atmel,at91sam9n12-hlcdc", 329 .data = &atmel_hlcdc_dc_at91sam9n12, 330 }, 331 { 332 .compatible = "atmel,at91sam9x5-hlcdc", 333 .data = &atmel_hlcdc_dc_at91sam9x5, 334 }, 335 { 336 .compatible = "atmel,sama5d2-hlcdc", 337 .data = &atmel_hlcdc_dc_sama5d4, 338 }, 339 { 340 .compatible = "atmel,sama5d3-hlcdc", 341 .data = &atmel_hlcdc_dc_sama5d3, 342 }, 343 { 344 .compatible = "atmel,sama5d4-hlcdc", 345 .data = &atmel_hlcdc_dc_sama5d4, 346 }, 347 { /* sentinel */ }, 348 }; 349 MODULE_DEVICE_TABLE(of, atmel_hlcdc_of_match); 350 351 int atmel_hlcdc_dc_mode_valid(struct atmel_hlcdc_dc *dc, 352 struct drm_display_mode *mode) 353 { 354 int vfront_porch = mode->vsync_start - mode->vdisplay; 355 int vback_porch = mode->vtotal - mode->vsync_end; 356 int vsync_len = mode->vsync_end - mode->vsync_start; 357 int hfront_porch = mode->hsync_start - mode->hdisplay; 358 int hback_porch = mode->htotal - mode->hsync_end; 359 int hsync_len = mode->hsync_end - mode->hsync_start; 360 361 if (hsync_len > 0x40 || hsync_len < 1) 362 return MODE_HSYNC; 363 364 if (vsync_len > 0x40 || vsync_len < 1) 365 return MODE_VSYNC; 366 367 if (hfront_porch > 0x200 || hfront_porch < 1 || 368 hback_porch > 0x200 || hback_porch < 1 || 369 mode->hdisplay < 1) 370 return MODE_H_ILLEGAL; 371 372 if (vfront_porch > 0x40 || vfront_porch < 1 || 373 vback_porch > 0x40 || vback_porch < 0 || 374 mode->vdisplay < 1) 375 return MODE_V_ILLEGAL; 376 377 return MODE_OK; 378 } 379 380 static irqreturn_t atmel_hlcdc_dc_irq_handler(int irq, void *data) 381 { 382 struct drm_device *dev = data; 383 struct atmel_hlcdc_dc *dc = dev->dev_private; 384 unsigned long status; 385 unsigned int imr, isr; 386 int i; 387 388 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_IMR, &imr); 389 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 390 status = imr & isr; 391 if (!status) 392 return IRQ_NONE; 393 394 if (status & ATMEL_HLCDC_SOF) 395 atmel_hlcdc_crtc_irq(dc->crtc); 396 397 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 398 struct atmel_hlcdc_layer *layer = dc->layers[i]; 399 400 if (!(ATMEL_HLCDC_LAYER_STATUS(i) & status) || !layer) 401 continue; 402 403 atmel_hlcdc_layer_irq(layer); 404 } 405 406 return IRQ_HANDLED; 407 } 408 409 static struct drm_framebuffer *atmel_hlcdc_fb_create(struct drm_device *dev, 410 struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) 411 { 412 return drm_fb_cma_create(dev, file_priv, mode_cmd); 413 } 414 415 static void atmel_hlcdc_fb_output_poll_changed(struct drm_device *dev) 416 { 417 struct atmel_hlcdc_dc *dc = dev->dev_private; 418 419 if (dc->fbdev) { 420 drm_fbdev_cma_hotplug_event(dc->fbdev); 421 } else { 422 dc->fbdev = drm_fbdev_cma_init(dev, 24, 423 dev->mode_config.num_crtc, 424 dev->mode_config.num_connector); 425 if (IS_ERR(dc->fbdev)) 426 dc->fbdev = NULL; 427 } 428 } 429 430 static const struct drm_mode_config_funcs mode_config_funcs = { 431 .fb_create = atmel_hlcdc_fb_create, 432 .output_poll_changed = atmel_hlcdc_fb_output_poll_changed, 433 .atomic_check = drm_atomic_helper_check, 434 .atomic_commit = drm_atomic_helper_commit, 435 }; 436 437 static int atmel_hlcdc_dc_modeset_init(struct drm_device *dev) 438 { 439 struct atmel_hlcdc_dc *dc = dev->dev_private; 440 struct atmel_hlcdc_planes *planes; 441 int ret; 442 int i; 443 444 drm_mode_config_init(dev); 445 446 ret = atmel_hlcdc_create_outputs(dev); 447 if (ret) { 448 dev_err(dev->dev, "failed to create panel: %d\n", ret); 449 return ret; 450 } 451 452 planes = atmel_hlcdc_create_planes(dev); 453 if (IS_ERR(planes)) { 454 dev_err(dev->dev, "failed to create planes\n"); 455 return PTR_ERR(planes); 456 } 457 458 dc->planes = planes; 459 460 dc->layers[planes->primary->layer.desc->id] = 461 &planes->primary->layer; 462 463 if (planes->cursor) 464 dc->layers[planes->cursor->layer.desc->id] = 465 &planes->cursor->layer; 466 467 for (i = 0; i < planes->noverlays; i++) 468 dc->layers[planes->overlays[i]->layer.desc->id] = 469 &planes->overlays[i]->layer; 470 471 ret = atmel_hlcdc_crtc_create(dev); 472 if (ret) { 473 dev_err(dev->dev, "failed to create crtc\n"); 474 return ret; 475 } 476 477 dev->mode_config.min_width = dc->desc->min_width; 478 dev->mode_config.min_height = dc->desc->min_height; 479 dev->mode_config.max_width = dc->desc->max_width; 480 dev->mode_config.max_height = dc->desc->max_height; 481 dev->mode_config.funcs = &mode_config_funcs; 482 483 return 0; 484 } 485 486 static int atmel_hlcdc_dc_load(struct drm_device *dev) 487 { 488 struct platform_device *pdev = to_platform_device(dev->dev); 489 const struct of_device_id *match; 490 struct atmel_hlcdc_dc *dc; 491 int ret; 492 493 match = of_match_node(atmel_hlcdc_of_match, dev->dev->parent->of_node); 494 if (!match) { 495 dev_err(&pdev->dev, "invalid compatible string\n"); 496 return -ENODEV; 497 } 498 499 if (!match->data) { 500 dev_err(&pdev->dev, "invalid hlcdc description\n"); 501 return -EINVAL; 502 } 503 504 dc = devm_kzalloc(dev->dev, sizeof(*dc), GFP_KERNEL); 505 if (!dc) 506 return -ENOMEM; 507 508 dc->wq = alloc_ordered_workqueue("atmel-hlcdc-dc", 0); 509 if (!dc->wq) 510 return -ENOMEM; 511 512 dc->desc = match->data; 513 dc->hlcdc = dev_get_drvdata(dev->dev->parent); 514 dev->dev_private = dc; 515 516 ret = clk_prepare_enable(dc->hlcdc->periph_clk); 517 if (ret) { 518 dev_err(dev->dev, "failed to enable periph_clk\n"); 519 goto err_destroy_wq; 520 } 521 522 pm_runtime_enable(dev->dev); 523 524 ret = drm_vblank_init(dev, 1); 525 if (ret < 0) { 526 dev_err(dev->dev, "failed to initialize vblank\n"); 527 goto err_periph_clk_disable; 528 } 529 530 ret = atmel_hlcdc_dc_modeset_init(dev); 531 if (ret < 0) { 532 dev_err(dev->dev, "failed to initialize mode setting\n"); 533 goto err_periph_clk_disable; 534 } 535 536 drm_mode_config_reset(dev); 537 538 pm_runtime_get_sync(dev->dev); 539 ret = drm_irq_install(dev, dc->hlcdc->irq); 540 pm_runtime_put_sync(dev->dev); 541 if (ret < 0) { 542 dev_err(dev->dev, "failed to install IRQ handler\n"); 543 goto err_periph_clk_disable; 544 } 545 546 platform_set_drvdata(pdev, dev); 547 548 drm_kms_helper_poll_init(dev); 549 550 /* force connectors detection */ 551 drm_helper_hpd_irq_event(dev); 552 553 return 0; 554 555 err_periph_clk_disable: 556 pm_runtime_disable(dev->dev); 557 clk_disable_unprepare(dc->hlcdc->periph_clk); 558 559 err_destroy_wq: 560 destroy_workqueue(dc->wq); 561 562 return ret; 563 } 564 565 static void atmel_hlcdc_dc_unload(struct drm_device *dev) 566 { 567 struct atmel_hlcdc_dc *dc = dev->dev_private; 568 569 if (dc->fbdev) 570 drm_fbdev_cma_fini(dc->fbdev); 571 flush_workqueue(dc->wq); 572 drm_kms_helper_poll_fini(dev); 573 drm_mode_config_cleanup(dev); 574 drm_vblank_cleanup(dev); 575 576 pm_runtime_get_sync(dev->dev); 577 drm_irq_uninstall(dev); 578 pm_runtime_put_sync(dev->dev); 579 580 dev->dev_private = NULL; 581 582 pm_runtime_disable(dev->dev); 583 clk_disable_unprepare(dc->hlcdc->periph_clk); 584 destroy_workqueue(dc->wq); 585 } 586 587 static int atmel_hlcdc_dc_connector_plug_all(struct drm_device *dev) 588 { 589 struct drm_connector *connector, *failed; 590 int ret; 591 592 mutex_lock(&dev->mode_config.mutex); 593 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 594 ret = drm_connector_register(connector); 595 if (ret) { 596 failed = connector; 597 goto err; 598 } 599 } 600 mutex_unlock(&dev->mode_config.mutex); 601 return 0; 602 603 err: 604 list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 605 if (failed == connector) 606 break; 607 608 drm_connector_unregister(connector); 609 } 610 mutex_unlock(&dev->mode_config.mutex); 611 612 return ret; 613 } 614 615 static void atmel_hlcdc_dc_connector_unplug_all(struct drm_device *dev) 616 { 617 mutex_lock(&dev->mode_config.mutex); 618 drm_connector_unplug_all(dev); 619 mutex_unlock(&dev->mode_config.mutex); 620 } 621 622 static void atmel_hlcdc_dc_lastclose(struct drm_device *dev) 623 { 624 struct atmel_hlcdc_dc *dc = dev->dev_private; 625 626 drm_fbdev_cma_restore_mode(dc->fbdev); 627 } 628 629 static int atmel_hlcdc_dc_irq_postinstall(struct drm_device *dev) 630 { 631 struct atmel_hlcdc_dc *dc = dev->dev_private; 632 unsigned int cfg = 0; 633 int i; 634 635 /* Enable interrupts on activated layers */ 636 for (i = 0; i < ATMEL_HLCDC_MAX_LAYERS; i++) { 637 if (dc->layers[i]) 638 cfg |= ATMEL_HLCDC_LAYER_STATUS(i); 639 } 640 641 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, cfg); 642 643 return 0; 644 } 645 646 static void atmel_hlcdc_dc_irq_uninstall(struct drm_device *dev) 647 { 648 struct atmel_hlcdc_dc *dc = dev->dev_private; 649 unsigned int isr; 650 651 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, 0xffffffff); 652 regmap_read(dc->hlcdc->regmap, ATMEL_HLCDC_ISR, &isr); 653 } 654 655 static int atmel_hlcdc_dc_enable_vblank(struct drm_device *dev, 656 unsigned int pipe) 657 { 658 struct atmel_hlcdc_dc *dc = dev->dev_private; 659 660 /* Enable SOF (Start Of Frame) interrupt for vblank counting */ 661 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IER, ATMEL_HLCDC_SOF); 662 663 return 0; 664 } 665 666 static void atmel_hlcdc_dc_disable_vblank(struct drm_device *dev, 667 unsigned int pipe) 668 { 669 struct atmel_hlcdc_dc *dc = dev->dev_private; 670 671 regmap_write(dc->hlcdc->regmap, ATMEL_HLCDC_IDR, ATMEL_HLCDC_SOF); 672 } 673 674 static const struct file_operations fops = { 675 .owner = THIS_MODULE, 676 .open = drm_open, 677 .release = drm_release, 678 .unlocked_ioctl = drm_ioctl, 679 #ifdef CONFIG_COMPAT 680 .compat_ioctl = drm_compat_ioctl, 681 #endif 682 .poll = drm_poll, 683 .read = drm_read, 684 .llseek = no_llseek, 685 .mmap = drm_gem_cma_mmap, 686 }; 687 688 static struct drm_driver atmel_hlcdc_dc_driver = { 689 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | 690 DRIVER_MODESET | DRIVER_PRIME | 691 DRIVER_ATOMIC, 692 .lastclose = atmel_hlcdc_dc_lastclose, 693 .irq_handler = atmel_hlcdc_dc_irq_handler, 694 .irq_preinstall = atmel_hlcdc_dc_irq_uninstall, 695 .irq_postinstall = atmel_hlcdc_dc_irq_postinstall, 696 .irq_uninstall = atmel_hlcdc_dc_irq_uninstall, 697 .get_vblank_counter = drm_vblank_no_hw_counter, 698 .enable_vblank = atmel_hlcdc_dc_enable_vblank, 699 .disable_vblank = atmel_hlcdc_dc_disable_vblank, 700 .gem_free_object = drm_gem_cma_free_object, 701 .gem_vm_ops = &drm_gem_cma_vm_ops, 702 .prime_handle_to_fd = drm_gem_prime_handle_to_fd, 703 .prime_fd_to_handle = drm_gem_prime_fd_to_handle, 704 .gem_prime_import = drm_gem_prime_import, 705 .gem_prime_export = drm_gem_prime_export, 706 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, 707 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, 708 .gem_prime_vmap = drm_gem_cma_prime_vmap, 709 .gem_prime_vunmap = drm_gem_cma_prime_vunmap, 710 .gem_prime_mmap = drm_gem_cma_prime_mmap, 711 .dumb_create = drm_gem_cma_dumb_create, 712 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 713 .dumb_destroy = drm_gem_dumb_destroy, 714 .fops = &fops, 715 .name = "atmel-hlcdc", 716 .desc = "Atmel HLCD Controller DRM", 717 .date = "20141504", 718 .major = 1, 719 .minor = 0, 720 }; 721 722 static int atmel_hlcdc_dc_drm_probe(struct platform_device *pdev) 723 { 724 struct drm_device *ddev; 725 int ret; 726 727 ddev = drm_dev_alloc(&atmel_hlcdc_dc_driver, &pdev->dev); 728 if (!ddev) 729 return -ENOMEM; 730 731 ret = atmel_hlcdc_dc_load(ddev); 732 if (ret) 733 goto err_unref; 734 735 ret = drm_dev_register(ddev, 0); 736 if (ret) 737 goto err_unload; 738 739 ret = atmel_hlcdc_dc_connector_plug_all(ddev); 740 if (ret) 741 goto err_unregister; 742 743 return 0; 744 745 err_unregister: 746 drm_dev_unregister(ddev); 747 748 err_unload: 749 atmel_hlcdc_dc_unload(ddev); 750 751 err_unref: 752 drm_dev_unref(ddev); 753 754 return ret; 755 } 756 757 static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) 758 { 759 struct drm_device *ddev = platform_get_drvdata(pdev); 760 761 atmel_hlcdc_dc_connector_unplug_all(ddev); 762 drm_dev_unregister(ddev); 763 atmel_hlcdc_dc_unload(ddev); 764 drm_dev_unref(ddev); 765 766 return 0; 767 } 768 769 #ifdef CONFIG_PM_SLEEP 770 static int atmel_hlcdc_dc_drm_suspend(struct device *dev) 771 { 772 struct drm_device *drm_dev = dev_get_drvdata(dev); 773 struct drm_crtc *crtc; 774 775 if (pm_runtime_suspended(dev)) 776 return 0; 777 778 drm_modeset_lock_all(drm_dev); 779 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 780 atmel_hlcdc_crtc_suspend(crtc); 781 drm_modeset_unlock_all(drm_dev); 782 return 0; 783 } 784 785 static int atmel_hlcdc_dc_drm_resume(struct device *dev) 786 { 787 struct drm_device *drm_dev = dev_get_drvdata(dev); 788 struct drm_crtc *crtc; 789 790 if (pm_runtime_suspended(dev)) 791 return 0; 792 793 drm_modeset_lock_all(drm_dev); 794 list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) 795 atmel_hlcdc_crtc_resume(crtc); 796 drm_modeset_unlock_all(drm_dev); 797 return 0; 798 } 799 #endif 800 801 static SIMPLE_DEV_PM_OPS(atmel_hlcdc_dc_drm_pm_ops, 802 atmel_hlcdc_dc_drm_suspend, atmel_hlcdc_dc_drm_resume); 803 804 static const struct of_device_id atmel_hlcdc_dc_of_match[] = { 805 { .compatible = "atmel,hlcdc-display-controller" }, 806 { }, 807 }; 808 809 static struct platform_driver atmel_hlcdc_dc_platform_driver = { 810 .probe = atmel_hlcdc_dc_drm_probe, 811 .remove = atmel_hlcdc_dc_drm_remove, 812 .driver = { 813 .name = "atmel-hlcdc-display-controller", 814 .pm = &atmel_hlcdc_dc_drm_pm_ops, 815 .of_match_table = atmel_hlcdc_dc_of_match, 816 }, 817 }; 818 module_platform_driver(atmel_hlcdc_dc_platform_driver); 819 820 MODULE_AUTHOR("Jean-Jacques Hiblot <jjhiblot@traphandler.com>"); 821 MODULE_AUTHOR("Boris Brezillon <boris.brezillon@free-electrons.com>"); 822 MODULE_DESCRIPTION("Atmel HLCDC Display Controller DRM Driver"); 823 MODULE_LICENSE("GPL"); 824 MODULE_ALIAS("platform:atmel-hlcdc-dc"); 825